В этой статье мы собираемся создать собственный алгоритм KNN с нуля и применить его к 23 различным наборам данных с использованием библиотек Numpy и Pandas.

Алгоритм K ближайших соседей

K Nearest Neighbours — один из самых простых алгоритмов прогнозирования в категории контролируемого машинного обучения.

Алгоритм работает на основе двух критериев: —

  • Количество соседей для включения в кластер.
  • Расстояние соседей от точки тестовых данных.

На изображении выше показано количество соседей (k = количество соседей), которые учитываются при прогнозировании значения для точки тестовых данных.

Предварительная обработка данных

Здесь мы используем набор данных бриллианты, содержащий 10 признаков, из которых 3 являются категориальными, а остальные 7 — числовыми.

Удаление выбросов

Мы можем использовать функцию boxplot() для создания диаграмм и проверки наличия каких-либо выбросов в наборе данных.

Как мы видим, в наборе данных есть выбросы. Поэтому мы удаляем эти выбросы, используя метод IQR (или выбираем любой другой метод по вашему выбору).

# IQR
def remove_outlier_IQR(df, field_name):
    iqr = 1.5 * (np.percentile(df[field_name], 75) -
                 np.percentile(df[field_name], 25))
    df.drop(df[df[field_name] > (
        iqr + np.percentile(df[field_name], 75))].index, inplace=True)
    df.drop(df[df[field_name] < (np.percentile(
        df[field_name], 25) - iqr)].index, inplace=True)
    return df

Печать формы фрейма данных до и после удаления выбросов с использованием IQR.

print('Shape of df before IQR:',df.shape)

df2 = remove_outlier_IQR(df, 'carat')
df2 = remove_outlier_IQR(df2, 'depth')
df2 = remove_outlier_IQR(df2, 'price')
df2 = remove_outlier_IQR(df2, 'table')
df2 = remove_outlier_IQR(df2, 'height_mm')
df2 = remove_outlier_IQR(df2, 'length_mm')
df_final = remove_outlier_IQR(df2, 'width_mm')
print('The Shape of df after IQR:',df_final.shape)

→ Форма df до IQR: (53940, 10)
→ Форма df после IQR: (46518, 10)

Кодирование категориальных признаков

В наборе данных есть 3 категориальных признака. Давайте напечатаем и посмотрим уникальные значения каждой функции.

print('Unique values of cat features:\n')
print('color:', cat_df.color.unique())
print('cut_quality:', cat_df.cut_quality.unique())
print('clarity:', cat_df.clarity.unique())

Это уникальные значения категориальных признаков.

Поэтому для кодирования этих функций мы используем LabelEncoder и фиктивные переменные (или вы также можете использовать OneHotEncoder)

Мы можем использовать LabelEncoder() для преобразования cut_quality в числовые значения, такие как 0, 1, 2, ….. потому что cut_quality имеет порядковые данные.

# Label encoding using the LabelEncoder function from sklearn
from sklearn.preprocessing import LabelEncoder
label_encoder = LabelEncoder()

df_final['cut_quality'] = label_encoder.fit_transform(df_final['cut_quality'])
df_final.head(2)

Затем мы используем функцию get_dummies() из библиотеки pandas, чтобы получить фиктивные переменные для категорий цвет и четкость.

# using dummy variables for the remaing categories
df_final = pd.get_dummies(df_final,columns=['color','clarity'])
df_final.head()

df_final.shape
--> (46518, 23)

Разделение данных для обучения и тестирования

Разделяем данные для обучения и тестирования с помощью метода train_test_split() из библиотеки sklearn. Размер test_size остается равным 25% от исходного набора данных.

data = df_final.copy()
# Using sklearn for scaling and splitting
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

X = data.drop(columns=['price'])
y = data['price']

# Scaling the data
scaler = StandardScaler()
scaled_df = scaler.fit_transform(X)

X_train, X_test, y_train, y_test = train_test_split(
    scaled_df, y, test_size=0.25)
print("X train shape: {} and y train shape: {}".format(
    X_train.shape, y_train.shape))
print("X test shape: {} and y test shape: {}".format(X_test.shape, y_test.shape))

Модель Sklearn KNN

Сначала мы используем модель регрессора KNN от sklearn. Для выбора оптимального значения k мы повторяем цикл for, устанавливая значение k от 1 до 10. В нашем случае полученное оптимальное значение k равно 5. Таким образом, используя это значение k = 5, мы обучаем модель, делаем прогнозы и печатаем прогнозы. ценности.

# Finding the optimal k value
from sklearn import neighbors
from sklearn.metrics import mean_squared_error
from math import sqrt
import matplotlib.pyplot as plt
rmse_val = []  
for K in range(10):
    K = K+1
    model = neighbors.KNeighborsRegressor(n_neighbors=K)

    model.fit(X_train, y_train)  
    pred = model.predict(X_test)  
    error = sqrt(mean_squared_error(y_test, pred))  
    rmse_val.append(error)  
    print('RMSE value for k = ', K, 'is:', error)

# Using the optimal k value.
from sklearn import neighbors

model = neighbors.KNeighborsRegressor(n_neighbors=5)

model.fit(X_train, y_train)  # fit the model
pred = model.predict(X_test)
pred

После sklearn мы переходим к кодированию нашей собственной модели KNN из sklearn с использованием NumPy и pandas.

Модель KNN с нуля

Мы конвертируем данные поезда и теста в массивы NumPy. Затем мы объединяем X_train и y_train в матрицу. Матрица будет содержать 22 столбца данных X_train и 1 столбец y_train в конце (т.е. последний столбец).

train = np.array(X_train)
test = np.array(X_test)
y_train = np.array(y_train)
# reshaping the array from columns to rows
y_train = y_train.reshape(-1, 1)
# combining the training dataset and the y_train into a matrix
train_df = np.hstack([train, y_train])
train_df[0:2]

Теперь для каждой строки (точки данных) набора тестовых данных мы находим евклидово расстояние между каждой точкой данных поезда и точкой тестовых данных. Мы используем цикл for для перебора каждой точки тестового набора данных, чтобы найти расстояния и сложить их в обучающий набор данных train_df соответственно.

Шаги:

  1. Мы находим расстояния между одной контрольной точкой и каждой точкой набора данных поезда.
  2. Мы изменяем расстояния, используя reshape(-1,1), чтобы преобразовать это в массив из 1 столбца и 11630 строк.
  3. Затем с помощью np.hstack() мы складываем этот массив расстояний в набор данных train_df.
  4. Теперь мы сортируем эту матрицу от меньшего к большему на основе столбца расстояния.
  5. Затем мы берем значения y_train из первых 5 строк и берем их среднее значение, чтобы получить значение прогноза.
  6. Мы повторяем описанные выше шаги для каждой контрольной точки и прогнозируем значения соответственно и сохраняем эти значения в массиве.
preds = []
for i in range(len(test)):
    distances = np.sqrt(np.sum((train - test[i])**2, axis = 1))
    distances = distances.reshape(-1,1)
    matrix = np.hstack([train_df, distances])
    sorted_matrix = matrix[matrix[:,-1].argsort()]
    neighbours = [sorted_matrix[i][-2] for i in range(5)]
    pred_value = np.mean(neighbours)
    preds.append(pred_value)
knn_scratch_pred = np.array(preds)
knn_scratch_pred

Сравнение Sklearn и нашей модели KNN

Для сравнения значений прогноза, полученных от sklearn и нашего метода knn_method, мы создаем фрейм данных pandas pred_df, как показано в коде ниже.

sklearn_pred = pred.reshape(-1,1)
my_knn_pred = knn_scratch_pred.reshape(-1,1)
predicted_values = np.hstack([sklearn_pred,my_knn_pred])
pred_df = pd.DataFrame(predicted_values,columns=['sklearn_preds','my_knn_preds'])
pred_df

Мы видим, что предсказанные значения нашего knn_algorithm в точности аналогичны значениям, полученным с помощью библиотеки sklearn. Это показывает, что наша интуиция и метод верны и очень точны.

Полный файл кода и набор данных можно найти на Github.

Подпишитесь на Dipankar Medhi, чтобы узнать больше 🚀потрясающих материалов о машинном обучении и науке о данных.