#python #algorithm #numpy #matplotlib
#python #алгоритм #numpy #matplotlib
Вопрос:
Чего я хочу:
Для отображения результатов моего простого алгоритма классификации (см. Ниже) в виде цветовой карты в python (данные представлены в 2D), где каждому классу присваивается цвет, а достоверность прогноза в любом месте 2D-карты пропорциональна насыщенности цвета, связанного с предсказанием класса. Изображение ниже как бы иллюстрирует, что я хочу для двоичного файла (задача с двумя классами), в котором красные части могут указывать на сильную уверенность в классе 1, тогда как синие части будут говорить о классе 2. Промежуточные цвета предполагают неопределенность в отношении любого из них. Очевидно, я хочу, чтобы цветовая схема обобщалась на несколько классов, поэтому мне понадобилось бы много цветов, и тогда масштаб изменился бы с белого (неопределенность) на очень яркий цвет, связанный с классом.
иллюстрация http://www.nicolacarlon.it/out.png
Некоторый пример кода:
В моем примере кода используется простой алгоритм kNN, в котором ближайшим k точкам данных разрешено «голосовать» за класс новой точки на карте. Достоверность прогноза просто определяется относительной частотой класса-победителя из k, который проголосовал. Я не имел дела со связями, и я знаю, что есть лучшие вероятностные версии этого метода, но все, что я хочу, это визуализировать мои данные, чтобы показать зрителю вероятность того, что класс находится в определенной части 2D-плоскости.
import numpy as np
import matplotlib.pyplot as plt
# Generate some training data from three classes
n = 100 # Number of covariates (sample points) for each class in training set.
mean1, mean2, mean3 = [-1.5,0], [1.5, 0], [0,1.5]
cov1, cov2, cov3 = [[1,0],[0,1]], [[1,0],[0,1]], [[1,0],[0,1]]
X1 = np.asarray(np.random.multivariate_normal(mean1,cov1,n))
X2 = np.asarray(np.random.multivariate_normal(mean2,cov2,n))
X3 = np.asarray(np.random.multivariate_normal(mean3,cov3,n))
plt.plot(X1[:,0], X1[:,1], 'ro', X2[:,0], X2[:,1], 'bo', X3[:,0], X3[:,1], 'go' )
plt.axis('equal'); plt.show() #Display training data
# Prepare the data set as a 3n*3 array where each row is a data point and its associated class
D = np.zeros((3*n,3))
D[0:n,0:2] = X1; D[0:n,2] = 1
D[n:2*n,0:2] = X2; D[n:2*n,2] = 2
D[2*n:3*n,0:2] = X3; D[2*n:3*n,2] = 3
def kNN(x, D, k=3):
x = np.asarray(x)
dist = np.linalg.norm(x-D[:,0:2], axis=1)
i = dist.argsort()[:k] #Return k indices of smallest to highest entries
counts = np.bincount(D[i,2].astype(int))
predicted_class = np.argmax(counts)
confidence = float(np.max(counts))/k
return predicted_class, confidence
print(kNN([-2,0], D, 20))
Ответ №1:
Итак, вы можете вычислить два числа для каждой точки в 2D плоскости
- достоверность (0 .. 1)
- класс (целое число)
Одна из возможностей — рассчитать собственную карту RGB и показать ее с imshow
помощью . Вот так:
import numpy as np
import matplotlib.pyplot as plt
# color vector with N x 3 colors, where N is the maximum number of classes and the colors are in RGB
mycolors = np.array([
[ 0, 0, 1],
[ 0, 1, 0],
[ 1, 0, 1],
[ 1, 1, 0],
[ 0, 1, 1],
[ 0, 0, 0],
[ 0, .5, 1]])
# negate the colors
mycolors = 1 - mycolors
# extents of the area
x0 = -2
x1 = 2
y0 = -2
y1 = 2
# grid over the area
X, Y = np.meshgrid(np.linspace(x0, x1, 1000), np.linspace(y0, y1, 1000))
# calculate the classification and probabilities
classes = classify_func(X, Y)
probabilities = prob_func(X, Y)
# create the basic color map by the class
img = mycolors[classes]
# fade the color by the probability (black for zero prob)
img *= probabilities[:,:,None]
# reverse the negative image back
img = 1 - img
# draw it
plt.imshow(img, extent=[x0,x1,y0,y1], origin='lower')
plt.axis('equal')
# save it
plt.savefig("mymap.png")
Хитрость создания отрицательных цветов заключается только в том, чтобы упростить математику для понимания. Код, конечно, может быть написан намного плотнее.
Я создал две очень простые функции для имитации классификации и вероятностей:
def classify_func(X, Y):
return np.round(abs(X Y)).astype('int')
def prob_func(X,Y):
return 1 - 2*abs(abs(X Y)-classify_func(X,Y))
Первый дает для данной области целочисленные значения от 0 до 4, а последний дает плавно изменяющиеся вероятности.
Результат:
Если вам не нравится, как цвета исчезают в направлении нулевой вероятности, вы всегда можете создать некоторую нелинейность, которая применяется при умножении на вероятности.
Здесь функциям classify_func
и prob_func
в качестве аргументов задаются два массива, первый из которых представляет собой координаты X, в которых должны быть вычислены значения, а второй — координаты Y. Это хорошо работает, если базовые вычисления полностью векторизованы. С кодом в вопросе это не так, поскольку он вычисляет только отдельные значения.
В этом случае код немного меняется:
x = np.linspace(x0, x1, 1000)
y = np.linspace(y0, y1, 1000)
classes = np.empty((len(y), len(x)), dtype='int')
probabilities = np.empty((len(y), len(x)))
for yi, yv in enumerate(y):
for xi, xv in enumerate(x):
classes[yi, xi], probabilities[yi, xi] = kNN((xv, yv), D)
Кроме того, поскольку ваши оценки достоверности не равны 0 .. 1, их необходимо масштабировать:
probabilities -= np.amin(probabilities)
probabilities /= np.amax(probabilities)
После того, как это будет сделано, ваша карта должна выглядеть следующим образом с экстентами -4, -4 ..4,4 (согласно цветовой карте: зеленый = 1, пурпурный = 2, желтый = 3):
Векторизировать или не векторизировать — вот в чем вопрос
Этот вопрос всплывает время от времени. В Интернете много информации о векторизации, но поскольку быстрый поиск не выявил никаких кратких резюме, я приведу здесь несколько мыслей. Это довольно субъективный вопрос, поэтому все просто отражает мое скромное мнение. У других людей могут быть разные мнения.
Необходимо учитывать три фактора:
- Производительность
- разборчивость
- использование памяти
Обычно (но не всегда) векторизация делает код быстрее, сложнее для понимания и потребляет больше памяти. Использование памяти обычно не является большой проблемой, но с большими массивами об этом стоит подумать (сотни мегабайт обычно в порядке, гигабайты доставляют хлопоты).
Помимо тривиальных случаев (элементарные простые операции, простые матричные операции), мой подход:
- напишите код без векторизации и проверьте, что он работает
- профилируйте код
- векторизируйте внутренние циклы, если это необходимо и возможно (1D векторизация)
- создайте 2D-векторизацию, если она простая
Например, операция попиксельной обработки изображений может привести к ситуации, когда я получаю одномерную векторизацию (для каждой строки). Тогда внутренний цикл (для каждого пикселя) выполняется быстро, а внешний цикл (для каждой строки) на самом деле не имеет значения. Код может выглядеть намного проще, если он не пытается использоваться со всеми возможными входными измерениями.
Я такой паршивый алгоритмист, что в более сложных случаях мне нравится сверять свой векторизованный код с невекторизованными версиями. Поэтому я почти всегда сначала создаю невекторизованный код, прежде чем оптимизировать его вообще.
Иногда векторизация не дает никакого преимущества в производительности. Например, удобная функция numpy.vectorize
может использоваться для векторизации практически любой функции, но в ее документации указано:
Функция векторизации предоставляется в первую очередь для удобства, а не для производительности. Реализация по сути представляет собой цикл for .
(Эта функция также могла быть использована в приведенном выше коде. Я выбрал версию цикла для удобства чтения для людей, с которыми не очень знаком numpy
.)
Векторизация дает большую производительность, только если базовые векторизованные функции выполняются быстрее. Иногда они есть, иногда нет. Только профилирование и опыт подскажут. Кроме того, не всегда необходимо векторизировать все. У вас может быть алгоритм обработки изображений, который имеет как векторизованные, так и попиксельные операции. Это numpy.vectorize
очень полезно.
Я бы попытался векторизовать алгоритм поиска kNN выше, по крайней мере, в одном измерении. Условного кода нет (это не было бы ограничителем показа, но это усложнило бы ситуацию), и алгоритм довольно прост. Потребление памяти будет увеличиваться, но при одномерной векторизации это не имеет значения.
И может случиться так, что по пути вы заметите, что n-мерное обобщение не намного сложнее. Затем сделайте это, если позволяет память.
Комментарии:
1. Большое спасибо! Это выглядит многообещающе. Не могли бы вы, возможно, посоветовать мне, как заставить мою текущую функцию
kNN
, которая возвращает вашиprobabilties
иclasses
значения, работать с входнымиX
данными иY
изmeshgrid
? В том виде , в каком я имею его сейчас, моя функция принимает только одну координатуx
…2. Теперь он там есть. Кроме того, пожалуйста, обратите внимание, что я добавил недостающее
origin='lower'
вimshow
. В противном случае изображение будет перевернуто вверх ногами. Также обратите внимание, что масштабирование изображения немного смещено (масштабный коэффициент n / ( n 1), где n — количество пикселей. Это можно исправить, но это привело бы к излишнему запутыванию кода, и обычно никто все равно не замечает, что 1/1000.3. Это здорово, я могу взять это отсюда, большое спасибо!
4. Просто продолжение: о чем бы вы посоветовали мне подумать при создании подобного кода? Должен ли я с самого начала думать, что код должен быть векторизован для скорости, поскольку мы вычисляем алгоритм kNN для многих точек?
5. @Jack: Кратко: Да, вы, вероятно, должны в этом случае, но смотрите Мой Отредактированный ответ для более подробного обсуждения. Если вам нужны дополнительные инструкции о том, как это сделать, попробуйте сами, а затем создайте новый вопрос на SO, когда вы застряли. Удачи!