Используя только python и numpy

Зачем использовать K-Neighbours?

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

Как работает K-Neighbours?

K-Neighbours оценивает значения, исследуя близлежащие точки данных. Модель K-Neighbors работает, сначала сохраняя данные обучения. Затем, делая прогнозы, он берет каждую точку в данных тестирования и находит своих «соседей» в данных обучения, сравнивая расстояние. Модель предварительно настроена на получение некоторого числа соседей «K». Если проблема заключается в классификации, он смотрит на счетчики классов соседей и устанавливает прогноз как класс с наибольшим счетчиком. Если проблема заключается в регрессии, он устанавливает прогноз как среднее целевых значений соседей.

Установка K

Для гиперпараметра «K» необходимо установить нечетное число для классификации и четное число для регрессии. Чем больше значение «K», тем меньше вероятность переобучения модели. Однако, если вы установите слишком большое значение «K», оно может быстро и сильно не соответствовать.

Кодирование с нуля

Я буду писать регрессор с нуля, создав класс python. Я собираюсь сделать его в стиле Scikit-learn с помощью методов .fit и .predict. Первое, что нужно сделать, это создать метод __init__ для создания некоторых атрибутов класса. Некоторые атрибуты, которые нам понадобятся, - это значение «K», место для сохранения данных обучающей функции (значения X) и место для сохранения данных обучающей цели ( y значений).

import numpy as np
class KNRegressor():
    def __init__(self, k=6):
        self.k = k
        self.X_train = None
        self.y_train = None

Следующее, что нужно сделать, это создать метод .fit. Все, что для этого нужно, - это взять данные X и y и сохранить их в объекте.

    def fit(self, X_train, y_train):
         self.X_train = X_train
         self.y_train = y_train

Было бы неплохо иметь вспомогательную функцию для расчета расстояния. В этом посте я буду придерживаться евклидова расстояния, известного нам всем. Формула для расстояния аналогична теореме Пифагора. По сути, мы находим расстояния между двумя точками на графике, но это n-мерный график. Я нарисовал здесь формулу, где a представляет собой строку X из тестового набора, b представляет строку X значений из обучающего набора, i представляет функцию , а n представляет собой общее количество функций.

Ключом к этому в Python является использование векторной математики. Векторная математика включает в себя поэлементную арифметику с упорядоченными списками. Массивы Numpy автоматически работают с векторной математикой при ответе на обычные арифметические операторы в Python. Формула для вычисления расстояния состоит в том, чтобы взять два вектора и вычесть один из другого. Затем возведите этот вектор разности в квадрат и найдите его сумму. Наконец, извлеките квадратный корень из этой суммы и верните расстояние.

    def euclidean_distance(self, v1, v2):
            difs = v1 - v2
            squares = difs**2
            
            # use try and except in case
            # vectors have length of 1
            try: 
                summation = sum(squares)
            except:
                summation = squares
                
            distance = np.sqrt(summation)
       
            return distance

Еще одно хорошее применение вспомогательной функции - это поиск индексов для наименьших значений K в массиве расстояний. Для реализации этой функции мы можем воспользоваться встроенными в Python методами enumerate и sorted. При перечислении массива к каждой точке добавляется индекс в форме, аналогичной вложенному списку. Вызовите функцию list для перечисления, и вы получите список кортежей. Затем вы можете отсортировать список кортежей, используя второе значение каждого кортежа (первое значение - это индекс). Чтобы отсортировать по второму значению, просто напишите лямбда-функцию lambda x: x [1] и передайте ее как ключ параметра sorted. Теперь создайте список для хранения индексов под названием near_indices. Наконец, переберите первые K индексов отсортированного списка и вырежьте первое значение для каждого кортежа внутри него, добавьте каждое значение к ближайшим_индексам и верните ближайшие_индексы. .

    def find_K_nearest_indices(self, distances):
        # will create a nested list like structure
        enum_dist = list(enumerate(distances))
        # sort by distance
        sort_dist = sorted(enum_dist, key = lambda x: x[1])
        # create a list for storing indices
        nearest_indices = []
 
        # loop over first K tuples in "sort_dist"
        for t in sort_dist[:self.k]:
        
        # and record the first value of each tuple
             nearest_indices.append(t[0])
        # return the nearest indices
        return nearest_indices

Теперь перейдем к методу прогнозирования. Давайте начнем с составления списка под названием «прогнозы», чтобы сохранить наши прогнозы. Теперь мы найдем K ближайших соседей для каждого значения X. Мы можем сделать это, перебрав каждую строку тестовых данных X, а затем для каждой из этих строк перебрать каждую строку обучающих данных X. Это образует вложенный цикл. Внутри внешнего цикла мы создаем список под названием «расстояния» для записи расстояний. Затем во внутреннем цикле мы находим и записываем эти расстояния, используя нашу вспомогательную функцию euclidean _ distance. После этого мы можем закрыть внутренний цикл. Distance теперь содержит расстояния между одним тестовым значением и всеми тренировочными значениями. Затем мы используем нашу вторую вспомогательную функцию find_K_nearest_indices, чтобы найти индексы для соседей. Затем мы берем средние значения точек в этих индексах в наших данных y, добавляем среднее значение к прогнозам и закрываем внешний цикл. Все, что остается делать после этого, - это возвращать прогнозы!

    def predict(self, X_test):
       # list for recording predictions
       predictions = []
       # loop over X_test
       for i in range(len(X_test)):
           # list for recording distances
           distances = []
           # loop over X_train
           for j in range(len(self.X_train)):
                # find distances
                distances.append(self.euclidean_distance(
                    X_test[i], self.X_train[j]))
            # find nearest indices
            indices = self.find_K_nearest_indices(distances)
            # record mean of y values for those indices
            predictions.append(self.y_train[indices].mean())
       # return the predictions
       return predictions

Вот и все!

Чтобы использовать регрессор, создайте экземпляр модели с помощью класса, затем просто вызовите .fit и передайте обучающие данные X и y. Затем вызовите .predict для тестовых данных X, и метод вернет набор прогнозов. При необходимости укажите значение K при создании объекта, если вы хотите, чтобы в качестве K было число, отличное от 6.

model = KNRegressor()
model.fit(X_train, y_train)
model.predict(X_test)