График k-Nearest-Neighbor с 8 функциями? - PullRequest
9 голосов
/ 15 мая 2019

Я новичок в машинном обучении и хотел бы настроить небольшой пример, используя k-nearest-Neighbor-method с библиотекой Python Scikit.

. Преобразование и подгонка данных работает отличноно я не могу понять, как построить график, показывающий точки данных, окруженные их "соседством".

набор данных , который я использую, выглядит так:

Head of dataset. Таким образом, есть 8 функций, плюс один столбец «результата».

Насколько я понимаю, я получаю массив, показывающий euclidean-distances всех точек данных, используя kneighbors_graph из Scikit.Поэтому моей первой попыткой было «просто» построить ту матрицу, которую я получил в результате этого метода.Вот так:

def kneighbors_graph(self):
    self.X_train = self.X_train.values[:10,] #trimming down the data to only 10 entries
    A = neighbors.kneighbors_graph(self.X_train, 9, 'distance')
    plt.spy(A)
    plt.show()

Однако график результатов на самом деле не отображает ожидаемую связь между точками данных.Result graph with kneighbors_graph - method

Поэтому я попытался настроить образец, который вы можете найти на каждой странице о Scikit, Iris_dataset.К сожалению, он использует только две функции, так что это не совсем то, что я ищу, но я все же хотел получить хотя бы первый вывод:

  def plot_classification(self):
    h = .02
    n_neighbors = 9
    self.X = self.X.values[:10, [1,4]] #trim values to 10 entries and only columns 2 and 5 (indices 1, 4)
    self.y = self.y[:10, ] #trim outcome column, too

    clf = neighbors.KNeighborsClassifier(n_neighbors, weights='distance')
    clf.fit(self.X, self.y)

    x_min, x_max = self.X[:, 0].min() - 1, self.X[:, 0].max() + 1
    y_min, y_max = self.X[:, 1].min() - 1, self.X[:, 1].max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]) #no errors here, but it's  not moving on until computer crashes

    cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA','#00AAFF'])
    cmap_bold = ListedColormap(['#FF0000', '#00FF00','#00AAFF'])
    Z = Z.reshape(xx.shape)
    plt.figure()
    plt.pcolormesh(xx, yy, Z, cmap=cmap_light)
    plt.scatter(self.X[:, 0], self.X[:, 1], c=self.y, cmap=cmap_bold)
    plt.xlim(xx.min(), xx.max())
    plt.ylim(yy.min(), yy.max())
    plt.title("Classification (k = %i)" % (n_neighbors))

Однако этот код не работает вообщеи я не могу понять, почему.Это никогда не заканчивается, поэтому я не получаю никаких ошибок, с которыми я мог бы работать.Мой компьютер просто падает после ожидания в течение нескольких минут.

Строка, с которой борется код: Z = clf.predict (np.c_ [xx.ravel (), yy.ravel ()]) part

Итак, мои вопросы:

Во-первых, я не понимаю, зачем мне вообще нужны прогнозы fit и для построения соседей.Разве евклидово расстояние не должно быть достаточным для построения желаемого графика?(желаемый график выглядит примерно так: наличие двух цветов для диабета или нет; стрелка и т. д. не требуется; кредит фотографии: это руководство ).

desired graph

Где моя ошибка в коде / почему часть предсказания дает сбой?

Есть ли способ отображения данных с помощью all функции?Я понимаю, что у меня не может быть 8 осей, но я бы хотел, чтобы евклидово расстояние вычислялось для всех 8 функций, а не только для двух из них (с двумя это не очень точно, не так ли?).

Обновление

Вот рабочий пример с кодом радужной оболочки, но моим набором данных диабета: он использует первые две функции моего набора данных.Единственное отличие, которое я вижу в своем коде, - это обрезка массива -> здесь нужны первые две функции, а я хотел функции 2 и 5, поэтому я обрезал их по-другому.Но я не понимаю, почему мой не работает.Итак, вот рабочий код;скопируйте и вставьте его, он работает с набором данных, который я предоставил ранее:

from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import neighbors, datasets

diabetes = pd.read_csv('data/diabetes_data.csv')
columns_to_iterate = ['glucose', 'diastolic', 'triceps', 'insulin', 'bmi', 'dpf', 'age']
for column in columns_to_iterate:
    mean_value = diabetes[column].mean(skipna=True)
    diabetes = diabetes.replace({column: {0: mean_value}})
    diabetes[column] = diabetes[column].astype(np.float64)
X = diabetes.drop(columns=['diabetes'])
y = diabetes['diabetes'].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2,
                                                                        random_state=1, stratify=y)
n_neighbors = 6

X = X.values[:, :2]
y = y
h = .02

cmap_light = ListedColormap(['#FFAAAA', '#AAFFAA', '#00AAFF'])
cmap_bold = ListedColormap(['#FF0000', '#00FF00', '#00AAFF'])

clf = neighbors.KNeighborsClassifier(n_neighbors, weights='distance')
clf.fit(X, y)

x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                     np.arange(y_min, y_max, h))

Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

Z = Z.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, Z, cmap=cmap_light)

plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.title("3-Class classification (k = %i)" % (n_neighbors))
plt.show()

Outcome of sample code

Ответы [ 2 ]

5 голосов
/ 21 мая 2019

Попробуйте эти два простых куска кода, оба строят трехмерный график с 6 переменными, построение данных более высокого измерения всегда сложно, но вы можете поиграть с ним и проверить, можно ли его настроить, чтобы получить желаемый график соседства.

Первый довольно интуитивно понятен, но он дает вам случайные лучи или прямоугольники (зависит от вашего количества переменных), вы не можете построить более 6 переменных, он всегда выдавал ошибку при использовании большего количества измерений, но вам придетсябыть достаточно изобретательным, чтобы как-то использовать две другие переменные.Это будет иметь смысл, когда вы увидите второй фрагмент кода.

первый фрагмент кода

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
X, Y, Z, U, V, W = zip(*df)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.quiver(X, Y, Z, U, V, W)    
ax.set_xlim([-2, 2])
ax.set_ylim([-2, 2])
ax.set_zlim([-2, 2])
ax.legend()
plt.show()

второй фрагмент кода

здесь я использую ageИ ИМТ, как цвет и форма ваших точек данных, вы можете снова получить график окрестностей для 6 переменных, настроив этот код, и использовать две другие переменные для различения по цвету или форме.

fig = plt.figure(figsize=(8, 6))
t = fig.suptitle('name_of_your_graph', fontsize=14)
ax = fig.add_subplot(111, projection='3d')

xs = list(df['pregnancies'])
ys = list(df['glucose'])
zs = list(df['bloodPressure'])
data_points = [(x, y, z) for x, y, z in zip(xs, ys, zs)]

ss = list(df['skinThickness'])
colors = ['red' if age_group in range(0,35) else 'yellow' for age_group in list(df['age'])]
markers = [',' if q > 33 else 'x' if q in range(19,32) else 'o' for q in list(df['BMI'])]

for data, color, size, mark in zip(data_points, colors, ss, markers):
    x, y, z = data
    ax.scatter(x, y, z, alpha=0.4, c=color, edgecolors='none', s=size, marker=mark)

ax.set_xlabel('pregnancies')
ax.set_ylabel('glucose')
ax.set_zlabel('bloodPressure')

Публикуйте своиответ.Я работаю над аналогичной проблемой, которая может помочь.Если в случае, если вы не смогли построить все 8-D, вы также можете построить несколько графов окрестностей, используя каждый раз комбинацию из 6 различных переменных.

4 голосов
/ 25 мая 2019

Оглавление:

  1. Отношения между объектами
  2. Желаемый график
  3. Почему подходят и предсказывают?
  4. Построение 8 объектов?

Отношения между признаками:

Научный термин, характеризующий "связь" между признаками, - корреляция .Эта область в основном исследована во время PCA (Анализ основных компонентов) .Идея в том, что не все ваши функции важны или, по крайней мере, некоторые из них сильно коррелированы.Думайте об этом как о сходстве: если две функции сильно коррелированы, поэтому они содержат одну и ту же информацию, и, следовательно, вы можете отбросить одну из них.Используя панд это выглядит так:

import pandas as pd
import seaborn as sns
from pylab import rcParams
import matplotlib.pyplot as plt


def plot_correlation(data):
    '''
    plot correlation's matrix to explore dependency between features 
    '''
    # init figure size
    rcParams['figure.figsize'] = 15, 20
    fig = plt.figure()
    sns.heatmap(data.corr(), annot=True, fmt=".2f")
    plt.show()
    fig.savefig('corr.png')

# load your data 
data  = pd.read_csv('diabetes.csv')

# plot correlation & densities
plot_correlation(data)

На выходе получается следующая корреляционная матрица: enter image description here

Так что здесь 1 означает общеекорреляция и, как и ожидалось, диагональ - это все единицы, потому что объект полностью коррелирует с самим собой.Кроме того, чем меньше число, тем меньше взаимосвязанных функций.

Здесь мы должны рассмотреть корреляции между характеристиками и корреляции между результатами и характеристиками.Между функциями: более высокие корреляции означают, что мы можем отбросить одну из них.Однако высокая корреляция между функцией и результатом означает, что функция важна и содержит много информации.На нашем графике последняя строка представляет корреляцию между характеристиками и результатом.Соответственно, самые высокие значения / наиболее важные характеристики - это «глюкоза» (0,47) и «MBI» (0,29).Кроме того, корреляция между этими двумя относительно низка (0,22), что означает, что они не похожи.

Мы можем проверить эти результаты, используя графики плотности для каждого объекта, имеющие отношение к результату.Это не так сложно, так как у нас есть только два результата: 0 или 1. Так что это будет выглядеть так в коде:

import pandas as pd
from pylab import rcParams
import matplotlib.pyplot as plt


def plot_densities(data):
    '''
    Plot features densities depending on the outcome values
    '''
    # change fig size to fit all subplots beautifully 
    rcParams['figure.figsize'] = 15, 20

    # separate data based on outcome values 
    outcome_0 = data[data['Outcome'] == 0]
    outcome_1 = data[data['Outcome'] == 1]

    # init figure
    fig, axs = plt.subplots(8, 1)
    fig.suptitle('Features densities for different outcomes 0/1')
    plt.subplots_adjust(left = 0.25, right = 0.9, bottom = 0.1, top = 0.95,
                        wspace = 0.2, hspace = 0.9)

    # plot densities for outcomes
    for column_name in names[:-1]: 
        ax = axs[names.index(column_name)]
        #plt.subplot(4, 2, names.index(column_name) + 1)
        outcome_0[column_name].plot(kind='density', ax=ax, subplots=True, 
                                    sharex=False, color="red", legend=True,
                                    label=column_name + ' for Outcome = 0')
        outcome_1[column_name].plot(kind='density', ax=ax, subplots=True, 
                                     sharex=False, color="green", legend=True,
                                     label=column_name + ' for Outcome = 1')
        ax.set_xlabel(column_name + ' values')
        ax.set_title(column_name + ' density')
        ax.grid('on')
    plt.show()
    fig.savefig('densities.png')

# load your data 
data  = pd.read_csv('diabetes.csv')
names = list(data.columns)

# plot correlation & densities
plot_densities(data)

На выходе получаются следующие графики плотности: enter image description here

На графиках, когда зеленая и красная кривые почти совпадают (перекрываются), это означает, что функция не разделяет результаты.В случае «ИМТ» вы можете увидеть некоторое разделение (небольшое горизонтальное смещение между обеими кривыми), а в «Глюкозе» это гораздо яснее (это согласуется со значениями корреляции).

=> Вывод из этого: если нам нужно выбрать только 2 функции, то нужно выбрать «Глюкоза» и «MBI».

Нужный график

Мне нечего сказать по этому поводу, за исключением того, что график представляет собой базовое объяснение концепции k-ближайшего соседа.Это просто не представление классификации.

Почему подходят и предсказывают

Ну, это базовое и жизненно важное машинное обучение (ML)концепция.У вас есть набор данных = [input, related_outputs], и вы хотите построить алгоритм ML, который хорошо научится связывать входы с их related_outputs.Это двухэтапная процедура.Сначала вы тренируете / обучаете свой алгоритм тому, как это делается.На этом этапе вы просто даете ему входные данные и ответы, как вы делаете с ребенком.Второй шаг - тестирование;теперь, когда ребенок научился, вы хотите проверить его / его.Таким образом, вы предоставляете ей / ему подобные данные и проверяете, верны ли ее / его ответы.Теперь вы не хотите давать ей / ему те же данные, которые он выучил, потому что даже если она / он дает правильные ответы, она / она, возможно, просто запомнили ответы из фазы обучения (это называется переоснащение ) и поэтому она / он ничему не научился.

Аналогично с вашим алгоритмом, вы сначала разбиваете свой набор данных на тренировочные данные и данные тестирования.Затем вы вписываете свои тренировочные данные в свой алгоритм или классификатор.Это называется этапом обучения.После этого вы проверяете, насколько хорош ваш классификатор и может ли он правильно классифицировать новые данные.Это этап тестирования.На основе результатов тестирования вы оцениваете эффективность своей классификации, используя, например, различные метрики оценки , например, точность.Правило большого пальца здесь состоит в том, чтобы использовать 2/3 данных для обучения и 1/3 для тестирования.

Создание 8 объектов?

Простой ответ - нет, вы не можете, и если можете, скажите, пожалуйста, как.

Забавный ответ: для визуализации 8 измерений это просто ... простопредставьте n-измерения, а затем пусть n = 8 или просто визуализируйте 3-D и кричите на него 8.

Логический ответ: Итак, мы живем в физическом слове и объектах, которые видим3-мерные, так что это технически своего рода предел.Тем не менее, вы можете визуализировать 4-е измерение в виде цвета, как в здесь , вы также можете использовать время в качестве 5-го измерения и сделать свой график анимацией.@Rohan предложил в своих формах ответа, но его код не работал для меня, и я не вижу, как это обеспечило бы хорошее представление производительности алгоритма.Во всяком случае, цвета, время, формы ... через некоторое время у вас кончается и вы застряли.Это одна из причин, почему люди делают PCA.Вы можете прочитать об этом аспекте проблемы в разделе уменьшение размерности .

Так что же произойдет, если мы согласимся на 2 объекта после PCA, а затем проведем обучение, тестирование, оценку и построение графика?.

Итак, вы можете использовать следующий код для достижения этой цели:

import warnings 
import numpy as np
import pandas as pd
from pylab import rcParams
import matplotlib.pyplot as plt
from sklearn import neighbors
from matplotlib.colors import ListedColormap
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
# filter warnings
warnings.filterwarnings("ignore")

def accuracy(k, X_train, y_train, X_test, y_test):
    '''
    compute accuracy of the classification based on k values 
    '''
    # instantiate learning model and fit data
    knn = KNeighborsClassifier(n_neighbors=k)    
    knn.fit(X_train, y_train)

    # predict the response
    pred = knn.predict(X_test)

    # evaluate and return  accuracy
    return accuracy_score(y_test, pred)

def classify_and_plot(X, y):
    ''' 
    split data, fit, classify, plot and evaluate results 
    '''
    # split data into training and testing set
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.33, random_state = 41)

    # init vars
    n_neighbors = 5
    h           = .02  # step size in the mesh

    # Create color maps
    cmap_light = ListedColormap(['#FFAAAA', '#AAAAFF'])
    cmap_bold  = ListedColormap(['#FF0000', '#0000FF'])

    rcParams['figure.figsize'] = 5, 5
    for weights in ['uniform', 'distance']:
        # we create an instance of Neighbours Classifier and fit the data.
        clf = neighbors.KNeighborsClassifier(n_neighbors, weights=weights)
        clf.fit(X_train, y_train)

        # Plot the decision boundary. For that, we will assign a color to each
        # point in the mesh [x_min, x_max]x[y_min, y_max].
        x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
        y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
        xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                             np.arange(y_min, y_max, h))
        Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

        # Put the result into a color plot
        Z = Z.reshape(xx.shape)
        fig = plt.figure()
        plt.pcolormesh(xx, yy, Z, cmap=cmap_light)

        # Plot also the training points, x-axis = 'Glucose', y-axis = "BMI"
        plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold, edgecolor='k', s=20)   
        plt.xlim(xx.min(), xx.max())
        plt.ylim(yy.min(), yy.max())
        plt.title("0/1 outcome classification (k = %i, weights = '%s')" % (n_neighbors, weights))
        plt.show()
        fig.savefig(weights +'.png')

        # evaluate
        y_expected  = y_test
        y_predicted = clf.predict(X_test)

        # print results
        print('----------------------------------------------------------------------')
        print('Classification report')
        print('----------------------------------------------------------------------')
        print('\n', classification_report(y_expected, y_predicted))
        print('----------------------------------------------------------------------')
        print('Accuracy = %5s' % round(accuracy(n_neighbors, X_train, y_train, X_test, y_test), 3))
        print('----------------------------------------------------------------------')


# load your data 
data  = pd.read_csv('diabetes.csv')
names = list(data.columns)

# we only take the best two features and prepare them for the KNN classifier
rows_nbr = 30 # data.shape[0]
X_prime  = np.array(data.iloc[:rows_nbr, [1,5]])
X        = X_prime # preprocessing.scale(X_prime)
y        = np.array(data.iloc[:rows_nbr, 8])

# classify, evaluate and plot results
classify_and_plot(X, y)

Это приводит к следующим графикам границ решения с использованием весов = 'равномерное' и весов = 'расстояние' (длячитайте разницу между обоими ходами здесь ):

enter image description here enter image description here

Примечаниечто: ось x = 'глюкоза', ось y = 'BMI'

Улучшения:

значение K What kзначение для использования?сколько соседей считать.Низкие значения k означают меньшую зависимость между данными, но большие значения означают более длительное время выполнения.Так что это компромисс.Вы можете использовать этот код, чтобы найти значение k, обеспечивающее наивысшую точность:

best_n_neighbours = np.argmax(np.array([accuracy(k, X_train, y_train, X_test, y_test) for k in range(1, int(rows_nbr/2))])) + 1
print('For best accuracy use k = ', best_n_neighbours)

Использование дополнительных данных Таким образом, при использовании всех данных вы можете столкнуться с проблемами памяти (какЯ сделал) другой, то вопрос переоснащения.Вы можете преодолеть это, предварительно обработав ваши данные.Рассматривайте это как масштабирование и форматирование ваших данных.В коде просто используйте:

from sklearn import preprocessing 
X = preprocessing.scale(X_prime)

Полный код можно найти в этом gist

...