Более быстрый алгоритм kNN в Python

1

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

Может ли кто-нибудь предложить способ ускорить это? Я не использую np.sqrt для расстояния L2, потому что это ненужно и фактически замедляет все это совсем немного.

class GlobalWeightedKNN:
    """
    A k-NN classifier with feature weights

    Returns: predictions of k-NN.
    """

    def __init__(self):
        self.X_train = None
        self.y_train = None
        self.k = None
        self.weights = None
        self.predictions = list()

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

    def predict(self, testing_data):
        """
        Takes a 2d array of query cases.

        Returns a list of predictions for k-NN classifier
        """

        np.fromiter((self.__helper(qc) for qc in testing_data), float)  
        return self.predictions


    def __helper(self, qc):
        neighbours = np.fromiter((self.__weighted_euclidean(qc, x) for x in self.X_train), float)
        neighbours = np.array([neighbours]).T 
        indexes = np.array([range(len(self.X_train))]).T
        neighbours = np.append(indexes, neighbours, axis=1)

        # Sort by second column - distances
        neighbours = neighbours[neighbours[:,1].argsort()]  
        k_cases = neighbours[ :self.k]
        indexes = [x[0] for x in k_cases]

        y_answers = [self.y_train[int(x)] for x in indexes]
        answer = max(set(y_answers), key=y_answers.count)  # get most common value
        self.predictions.append(answer)


    def __weighted_euclidean(self, qc, other):
        """
        Custom weighted euclidean distance

        returns: floating point number
        """

        return np.sum( ((qc - other)**2) * self.weights )
  • 0
    KNN - это очень медленный алгоритм прогнозирования (O (n * m) на выборку) в любом случае (если только вы не идете по пути простого нахождения приближенных соседей, используя такие вещи, как KD-Trees, LSH и так далее ...). Но, тем не менее, ваша реализация может быть улучшена, например, избегая необходимости хранить все расстояния и сортировку. Вместо этого вы можете сохранить приоритетную очередь (куча, посмотрите на модуль heapq ) с размером K и хранить там только текущих ближайших соседей.
  • 0
    Вы не удалили циклы for, вы просто поместили их в выражения генератора. Это все еще алгоритм O [N ^ 2] ... и scipy, и scikit-learn имеют основанные на дереве алгоритмы ближайших соседей, которые будут O [Nlog (N)]. Я бы предложил использовать один из них.
Показать ещё 1 комментарий
Теги:
machine-learning
knn

1 ответ

2

Scikit-learn использует дерево KD или дерево шаров для вычисления ближайших соседей в O[N log(N)] времени. Ваш алгоритм является прямым подходом, требующим O[N^2] времени, а также использует вложенные for-loops в выражениях генератора Python, которые будут добавлять значительные вычислительные накладные расходы по сравнению с оптимизированным кодом.

Если вы хотите вычислить взвешенную классификацию k-соседей, используя быструю реализацию O[N log(N)], вы можете использовать sklearn.neighbors.KNeighborsClassifier с взвешенной метрикой Минковски, установив p=2 (для евклидова расстояния) и настройки w к вашим желаемым весам. Например:

from sklearn.neighbors import KNeighborsClassifier

model = KNeighborsClassifier(metric='wminkowski', p=2,
                             metric_params=dict(w=weights))
model.fit(X_train, y_train)
y_predicted = model.predict(X_test)
  • 0
    Большое спасибо, я понятия не имел, что в sklearn была эта опция. Я только что проверил его с помощью «грубого» алгоритма (мой подход), и, что интересно, на самом деле он занимает 4782 секунды вместо 2079 секунд с моим. Однако KD Tree невероятно быстр, наверняка, когда я использую чрезвычайно большие наборы данных, я по умолчанию использую эту опцию, а не мою реализацию, даже если не гарантировано, что найдем ближайших соседей, это чертовски близко. Спасибо!
  • 0
    KD-дерево / Ball-дерево - это точный алгоритм, поэтому он гарантированно найдет ближайших соседей.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню