1.6. Ближайшие соседи
sklearn.neighbors предоставляет функциональность для методов обучения на основе соседей без учителя и с учителем. Методы ближайших соседей без учителя являются основой многих других методов обучения, в частности, методов обучения на основе многообразий и спектрального кластерирования. Методы обучения на основе соседей с учителем делятся на два типа: классификация для данных с дискретными метками и регрессия для данных с непрерывными метками.
Принцип методов ближайших соседей заключается в поиске заданного числа обучающих выборок, ближайших по расстоянию к новой точке, и прогнозировании метки на основе этих выборок. Количество выборок может быть заданным пользователем константой (обучение k-ближайших соседей) или изменяться в зависимости от локальной плотности точек (обучение соседей на основе радиуса). Расстояние, как правило, может быть любым метрическим показателем: стандартное евклидово расстояние является наиболее распространенным выбором. Методы ближайших соседей известны как методы машинного обучения, не выполняющие обобщения, так как они просто «запоминают» все обучающие данные (возможно, преобразуя их в структуру быстрого индексирования, например, дерево с шаром или дерево KD).
Несмотря на простоту, методы ближайших соседей успешно применяются во многих задачах классификации и регрессии, включая распознавание рукописных цифр и спутниковые изображения. Будучи непараметрическим методом, он часто эффективен в ситуациях классификации, где граница принятия решения имеет очень нерегулярную форму.
Классы в sklearn.neighbors могут обрабатывать массивы NumPy или scipy.sparse матрицы в качестве входных данных. Для плотных матриц поддерживается большое количество возможных метрик расстояния. Для разреженных матриц поддерживаются произвольные метрики Минковского для поиска.
Существует много алгоритмов обучения, которые в основе своей полагаются на методы ближайших соседей. Одним примером является оценка плотности ядра, обсуждаемая в разделе оценка плотности.
1.6.1. Методы ближайших соседей без учителя
NearestNeighbors реализует обучение без учителя с использованием ближайших соседей. Он выступает в качестве унифицированного интерфейса для трех разных алгоритмов поиска ближайших соседей: BallTree, KDTree и алгоритм грубой силы, основанный на функциях в sklearn.metrics.pairwise. Выбор алгоритма поиска соседей контролируется ключевым словом 'algorithm', которое должно быть одним из ['auto', 'ball_tree', 'kd_tree', 'brute']. При использовании значения по умолчанию 'auto', алгоритм пытается определить наилучший подход на основе обучающих данных. Обсуждение сильных и слабых сторон каждого варианта см. в разделе Алгоритмы ближайших соседей.
Предупреждение
Что касается алгоритмов ближайших соседей, если у двух соседей \(k+1\) и \(k\) идентичные расстояния, но разные метки, результат будет зависеть от порядка обучающих данных.
1.6.1.1. Поиск ближайших соседей
Для простой задачи поиска ближайших соседей между двумя наборами данных можно использовать алгоритмы без учителя в sklearn.neighbors:
>>> from sklearn.neighbors import NearestNeighbors
>>> import numpy as np
>>> X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
>>> nbrs = NearestNeighbors(n_neighbors=2, algorithm='ball_tree').fit(X)
>>> distances, indices = nbrs.kneighbors(X)
>>> indices
array([[0, 1],
[1, 0],
[2, 1],
[3, 4],
[4, 3],
[5, 4]]...)
>>> distances
array([[0. , 1. ],
[0. , 1. ],
[0. , 1.41421356],
[0. , 1. ],
[0. , 1. ],
[0. , 1.41421356]])
Поскольку набор запросов совпадает с обучающим набором, ближайшим соседом каждой точки является сама точка с расстоянием ноль.
Также возможно эффективно создать разреженный граф, показывающий связи между соседними точками:
>>> nbrs.kneighbors_graph(X).toarray()
array([[1., 1., 0., 0., 0., 0.],
[1., 1., 0., 0., 0., 0.],
[0., 1., 1., 0., 0., 0.],
[0., 0., 0., 1., 1., 0.],
[0., 0., 0., 1., 1., 0.],
[0., 0., 0., 0., 1., 1.]])
Набор данных структурирован таким образом, что точки, близкие по порядку индекса, близки и в параметрическом пространстве, что приводит к примерно блочно-диагональной матрице k-ближайших соседей. Такой разреженный граф полезен в различных ситуациях, где используются пространственные отношения между точками для обучения без учителя: в частности, см. Isomap, LocallyLinearEmbedding и SpectralClustering.
1.6.1.2. Классы KDTree и BallTree
В качестве альтернативы можно напрямую использовать классы KDTree или BallTree для поиска ближайших соседей. Это функциональность, обернутая классом NearestNeighbors, используемым выше. У деревьев с шаром и KD-деревьев одинаковый интерфейс; мы покажем здесь пример использования KD-дерева:
>>> from sklearn.neighbors import KDTree
>>> import numpy as np
>>> X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
>>> kdt = KDTree(X, leaf_size=30, metric='euclidean')
>>> kdt.query(X, k=2, return_distance=False)
array([[0, 1],
[1, 0],
[2, 1],
[3, 4],
[4, 3],
[5, 4]]...)
Дополнительную информацию о доступных параметрах поиска ближайших соседей, включая указание стратегий запроса, метрик расстояния и т. д., см. в документации класса KDTree и BallTree. Для получения списка допустимых метрик используйте KDTree.valid_metrics и BallTree.valid_metrics.
>>> from sklearn.neighbors import KDTree, BallTree >>> KDTree.valid_metrics ['euclidean', 'l2', 'minkowski', 'p', 'manhattan', 'cityblock', 'l1', 'chebyshev', 'infinity'] >>> BallTree.valid_metrics ['euclidean', 'l2', 'minkowski', 'p', 'manhattan', 'cityblock', 'l1', 'chebyshev', 'infinity', 'seuclidean', 'mahalanobis', 'hamming', 'canberra', 'braycurtis', 'jaccard', 'dice', 'rogerstanimoto', 'russellrao', 'sokalmichener', 'sokalsneath', 'haversine', 'pyfunc']
1.6.2. Классификация с использованием ближайших соседей
Классификация на основе ближайших соседей — это тип обучения на основе примеров или необобщающего обучения: она не пытается построить общую внутреннюю модель, а просто хранит примеры обучающих данных. Классификация вычисляется по простому голосованию большинства ближайших соседей каждой точки: точке запроса присваивается класс данных, который имеет наибольшее количество представителей среди ближайших соседей точки.
scikit-learn реализует два разных классификатора на основе ближайших соседей: KNeighborsClassifier реализует обучение на основе \(k\) ближайших соседей каждой точки запроса, где \(k\) — целое число, заданное пользователем. RadiusNeighborsClassifier реализует обучение на основе числа соседей в пределах фиксированного радиуса \(r\) каждой обучающей точки, где \(r\) — значение с плавающей точкой, заданное пользователем.
Классификация k-ближайших соседей в KNeighborsClassifier является наиболее часто используемым методом. Оптимальный выбор значения \(k\) сильно зависит от данных: в общем случае, большее \(k\) подавляет влияние шума, но делает границы классификации менее четкими.
В случаях, когда данные не равномерно распределены, классификация соседей на основе радиуса в RadiusNeighborsClassifier может быть лучшим выбором. Пользователь указывает фиксированный радиус \(r\), таким образом, точки в более разреженных окрестностях используют меньше ближайших соседей для классификации. Для многомерных параметрических пространств этот метод становится менее эффективным из-за так называемого «проклятия размерности».
В базовой классификации ближайших соседей используются равномерные веса: значение, присваиваемое точке запроса, вычисляется по простому голосованию большинства ближайших соседей. В некоторых ситуациях лучше взвесить соседей таким образом, чтобы более близкие соседи вносили больший вклад в подгонку. Это можно сделать с помощью ключевого слова weights. Значение по умолчанию, weights = 'uniform', присваивает равномерные веса каждому соседу. weights = 'distance' присваивает веса, пропорциональные обратному расстоянию от точки запроса. В качестве альтернативы, можно предоставить пользовательскую функцию от расстояния для вычисления весов.
Примеры
- Классификация с использованием ближайших соседей: пример классификации с использованием ближайших соседей.
1.6.3. Регрессия на основе ближайших соседей
Регрессия на основе ближайших соседей может быть использована в случаях, когда метки данных являются непрерывными, а не дискретными переменными. Метка, присваиваемая точке запроса, вычисляется на основе среднего значения меток ее ближайших соседей.
scikit-learn реализует два разных регрессора на основе ближайших соседей: KNeighborsRegressor реализует обучение на основе \(k\) ближайших соседей каждой точки запроса, где \(k\) — целое значение, заданное пользователем. RadiusNeighborsRegressor реализует обучение на основе соседей в пределах фиксированного радиуса \(r\) от точки запроса, где \(r\) — числовое значение, заданное пользователем.
В базовой регрессии на основе ближайших соседей используются равные веса: то есть каждая точка в локальном окрестности в равной степени влияет на классификацию точки запроса. В некоторых случаях может быть целесообразно взвесить точки таким образом, чтобы близлежащие точки вносили больший вклад в регрессию, чем удаленные. Это можно сделать с помощью ключевого слова weights. Значение по умолчанию, weights = 'uniform', присваивает равные веса всем точкам. weights = 'distance' присваивает веса, пропорциональные обратной величине расстояния от точки запроса. В качестве альтернативы можно предоставить пользовательскую функцию от расстояния, которая будет использоваться для вычисления весов.
Использование многовыходных ближайших соседей для регрессии продемонстрировано в Заполнение лица с помощью многовыходных оценщиков. В этом примере входными данными X являются пиксели верхней половины лиц, а выходными данными Y — пиксели нижней половины этих лиц.
Примеры
- Регрессия на основе ближайших соседей: пример регрессии с использованием ближайших соседей.
- Заполнение лица с помощью многовыходных оценщиков: пример многовыходной регрессии с использованием ближайших соседей.
1.6.4. Алгоритмы ближайших соседей
1.6.4.1. Прямой метод
Быстрое вычисление ближайших соседей является активной областью исследований в машинном обучении. Самая наивная реализация поиска соседей включает вычисление расстояний между всеми парами точек в наборе данных: для \(N\) образцов в \(D\) измерениях этот подход масштабируется как \(O[D N^2]\). Эффективные поиски соседей с использованием прямого метода могут быть очень конкурентоспособными для небольших наборов данных. Однако, по мере увеличения количества образцов \(N\), прямой метод быстро становится невозможным. В классах в sklearn.neighbors, поиск соседей с использованием прямого метода задается с помощью ключевого слова algorithm = 'brute', и вычисляется с использованием процедур, доступных в sklearn.metrics.pairwise.
1.6.4.2. Дерево KD
Для решения вычислительных проблем прямого метода были изобретены различные структуры данных на основе деревьев. В целом, эти структуры пытаются уменьшить необходимое количество вычислений расстояний, эффективно кодируя агрегированную информацию о расстоянии для выборки. Основная идея заключается в том, что если точка \(A\) очень удалена от точки \(B\), а точка \(B\) очень близка к точке \(C\), то мы знаем, что точки \(A\) и \(C\) очень удалены, не имея необходимости явно вычислять их расстояние. Таким образом, вычислительная стоимость поиска ближайшего соседа может быть уменьшена до \(O[D N \log(N)]\) или лучше. Это существенное улучшение по сравнению с прямым методом для больших \(N\).
Ранним подходом к использованию этой агрегированной информации была структура данных дерево KD (сокращение от k-мерного дерева), которая обобщает двумерные квадро-деревья и трехмерные окта-деревья на произвольное число измерений. Дерево KD представляет собой структуру двоичного дерева, которая рекурсивно разбивает параметрическое пространство вдоль осей данных, деля его на вложенные ортотропные области, в которые помещаются точки данных. Построение дерева KD очень быстрое: так как разделение выполняется только по осям данных, не нужно вычислять \(D\)-мерные расстояния. После построения ближайший сосед точки запроса может быть определен всего за \(O[\log(N)]\) вычислений расстояний. Хотя подход с деревом KD очень быстрый для поиска ближайших соседей в низкоразмерных (\(D < 20\)) случаях, он становится неэффективным по мере увеличения \(D\): это одно из проявлений так называемого «проклятия размерности». В scikit-learn поиск ближайших соседей с использованием дерева KD задается с помощью ключевого слова algorithm = 'kd_tree', и вычисляется с использованием класса KDTree.
Ссылки
- “Многомерные двоичные деревья, используемые для ассоциативного поиска”, Бентли, Дж.Л., Communications of the ACM (1975)
1.6.4.3. Дерево шаров
Для решения проблем неэффективности деревьев KD в многомерных пространствах была разработана структура данных дерево шаров. В то время как деревья KD разбивают данные по осям Декартовой системы координат, деревья шаров разбивают данные в последовательности вложенных гиперсфер. Это делает построение дерева более затратным, чем построение дерева KD, но приводит к структуре данных, которая может быть очень эффективной для высокоструктурированных данных, даже в очень высоких измерениях.
Дерево шаров рекурсивно делит данные на узлы, определенные центроидом \(C\) и радиусом \(r\), так что каждая точка в узле находится внутри гиперсферы, определенной \(r\) и \(C\). Количество кандидатных точек для поиска ближайшего соседа уменьшается с помощью треугольного неравенства:
В этой установке одного вычисления расстояния между тестовой точкой и центроидом достаточно для определения нижней и верхней границ расстояния до всех точек внутри узла. Благодаря сферической геометрии узлов дерева шаров, оно может превосходить дерево KD в высоких измерениях, хотя фактическая производительность сильно зависит от структуры обучающих данных. В scikit-learn поиск ближайших соседей, основанный на дереве шаров, задается с помощью ключевого слова algorithm = 'ball_tree', и вычисляется с использованием класса BallTree. В качестве альтернативы пользователь может работать напрямую с классом BallTree.
Ссылки
- “Пять алгоритмов построения дерева шаров”, Омохундро, С.М., Технический отчет Международного компьютерного научного института (1989)
Выбор алгоритма ближайших соседей
Оптимальный алгоритм для заданного набора данных является сложным выбором и зависит от ряда факторов:
-
количество выборок \(N\) (т.е.
n_samples) и размерность \(D\) (т.е.n_features).- Время запроса метода грубой силы растет как \(O[DN]\)
- Время запроса дерева шаров растет приблизительно как \(O[D \log(N)]\)
- Время запроса дерева KD изменяется с \(D\) таким образом, что трудно точно охарактеризовать. Для малых \(D\) (менее 20 или около того) стоимость составляет приблизительно \(O[D\log(N)]\), и запрос дерева KD может быть очень эффективным. Для больших \(D\) стоимость возрастает почти до \(O[DN]\), и накладные расходы, связанные со структурой дерева, могут привести к запросам, которые медленнее, чем метод грубой силы.
Для небольших наборов данных (\(N\) меньше 30 или около того), \(\log(N)\) сопоставимо с \(N\), и алгоритмы грубой силы могут быть более эффективными, чем подход на основе дерева. И
KDTree, иBallTreeрешают эту проблему, предоставляя параметр размер листа: это управляет количеством выборок, при котором запрос переключается на метод грубой силы. Это позволяет обоим алгоритмам приблизиться к эффективности вычисления грубой силы для малых \(N\). -
структура данных: внутренняя размерность данных и/или разреженность данных. Внутренняя размерность относится к размерности \(d \le D\) многообразия, на котором лежат данные, которые могут быть линейно или нелинейно встроены в параметрическое пространство. Разреженность относится к степени, в которой данные заполняют параметрическое пространство (это следует отличать от понятия, используемого в «разреженных» матрицах. Матрица данных может не иметь нулевых элементов, но структура все еще может быть «разреженной» в этом смысле).
- Время запроса метода грубой силы не изменяется структурой данных.
- Время запроса дерева шаров и дерева KD могут сильно зависеть от структуры данных. В общем случае, более разреженные данные с меньшей внутренней размерностью приводят к более быстрому времени запроса. Поскольку внутреннее представление дерева KD согласовано с осями параметров, оно обычно не покажет такого же улучшения, как дерево шаров, для произвольно структурированных данных.
Наборы данных, используемые в машинном обучении, как правило, очень структурированы и очень подходят для запросов на основе деревьев.
-
количество соседей \(k\), запрашиваемых для точки запроса.
- Время запроса метода грубой силы в значительной степени не зависит от значения \(k\)
- Время запроса дерева шаров и дерева KD будет замедляться по мере увеличения \(k\). Это происходит из-за двух эффектов: во-первых, больший \(k\) приводит к необходимости поиска большей части параметрического пространства. Во-вторых, использование \(k > 1\) требует внутренней очереди результатов по мере обхода дерева.
По мере того, как \(k\) становится большим по сравнению с \(N\), возможность обрезки ветвей в запросе на основе дерева уменьшается. В этой ситуации запросы грубой силы могут быть более эффективными.
- количество точек запроса. И дерево шаров, и дерево KD требуют фазы построения. Стоимость этого построения становится незначительной при амортизации по многим запросам. Однако, если будет выполнено только небольшое количество запросов, построение может составить значительную часть общей стоимости. Если потребуется очень мало точек запроса, метод грубой силы предпочтительнее метода на основе дерева.
В настоящее время algorithm = 'auto' выбирает 'brute', если выполняются какие-либо из следующих условий:
- входные данные разреженные
metric = 'precomputed'- \(D > 15\)
- \(k >= N/2\)
-
effective_metric_не находится в спискеVALID_METRICSни для'kd_tree', ни для'ball_tree'
В противном случае выбирается первый из 'kd_tree' и 'ball_tree', у которого effective_metric_ есть в его списке VALID_METRICS. Этот эвристический подход основан на следующих предположениях:
- количество точек запроса по крайней мере того же порядка, что и количество точек обучения
-
leaf_sizeблизко к своему значению по умолчанию30 - когда \(D > 15\), внутренняя размерность данных, как правило, слишком высока для методов на основе деревьев
Влияние leaf_size
Как отмечалось выше, для небольших размеров выборки поиск грубой силы может быть более эффективным, чем запрос на основе дерева. Этот факт учитывается в дереве шаров и дереве KD, путем внутреннего переключения на поиск грубой силы внутри узлов листа. Уровень этого переключения можно указать с помощью параметра leaf_size. Выбор этого параметра имеет много последствий:
- время построения
-
Больший
leaf_sizeприводит к более быстрому времени построения дерева, потому что нужно создать меньше узлов - время запроса
-
И большой, и малый
leaf_sizeмогут привести к неэффективному времени запроса. Дляleaf_size, приближающегося к 1, накладные расходы, связанные с проходом по узлам, могут значительно замедлить время запроса. Дляleaf_size, приближающегося к размеру обучающего набора, запросы становятся по существу запросами грубой силы. Хорошим компромиссом между ними являетсяleaf_size = 30, значение параметра по умолчанию. - память
-
По мере увеличения
leaf_size, память, необходимая для хранения структуры дерева, уменьшается. Это особенно важно в случае дерева шаров, которое хранит \(D\)-мерный центроид для каждого узла. Необходимое хранилище памяти дляBallTreeсоставляет приблизительно1 / leaf_sizeот размера обучающего набора.
leaf_size не используется для запросов грубой силы.
Допустимые метрики для алгоритмов ближайших соседей
Список доступных метрик можно найти в документации класса DistanceMetric и в метриках, перечисленных в sklearn.metrics.pairwise.PAIRWISE_DISTANCE_FUNCTIONS. Обратите внимание, что метрика «косинус» использует cosine_distances.
Список допустимых метрик для любого из вышеперечисленных алгоритмов можно получить, используя их атрибут valid_metric. Например, допустимые метрики для KDTree можно сгенерировать следующим образом:
>>> from sklearn.neighbors import KDTree >>> print(sorted(KDTree.valid_metrics)) ['chebyshev', 'cityblock', 'euclidean', 'infinity', 'l1', 'l2', 'manhattan', 'minkowski', 'p']
1.6.5. Классификатор ближайших центроидов
Классификатор NearestCentroid — это простой алгоритм, который представляет каждый класс центроидом его членов. По сути, это делает его похожим на фазу обновления меток алгоритма KMeans. Кроме того, у него нет параметров для выбора, что делает его хорошим базовым классификатором. Однако он страдает при работе с невыпуклыми классами, а также когда классы имеют резко отличающиеся дисперсии, поскольку предполагается одинаковая дисперсия во всех измерениях. См. Линейный дискриминантный анализ (LinearDiscriminantAnalysis) и Квадратичный дискриминантный анализ (QuadraticDiscriminantAnalysis) для более сложных методов, которые не делают этого предположения. Использование по умолчанию NearestCentroid просто:
>>> from sklearn.neighbors import NearestCentroid >>> import numpy as np >>> X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]]) >>> y = np.array([1, 1, 1, 2, 2, 2]) >>> clf = NearestCentroid() >>> clf.fit(X, y) NearestCentroid() >>> print(clf.predict([[-0.8, -1]])) [1]
1.6.5.1. Классификатор ближайших сжатых центроидов
Классификатор NearestCentroid имеет параметр shrink_threshold, который реализует классификатор ближайших сжатых центроидов. По сути, значение каждого признака для каждого центроида делится на внутриклассовую дисперсию этого признака. Затем значения признаков уменьшаются на shrink_threshold. Наиболее заметно, что если значение конкретного признака пересекает ноль, оно устанавливается в ноль. По сути, это удаляет признак из влияния на классификацию. Это полезно, например, для удаления шумных признаков.
В примере ниже использование небольшого порога сжатия увеличивает точность модели с 0,81 до 0,82.
Примеры
- Классификация ближайших центроидов: пример классификации с использованием ближайших центроидов с различными порогами сжатия.
1.6.6. Преобразователь ближайших соседей
Многие оценки scikit-learn опираются на ближайших соседей: несколько классификаторов и регрессоров, такие как KNeighborsClassifier и KNeighborsRegressor, но также и некоторые методы кластеризации, такие как DBSCAN и SpectralClustering, и некоторые вложения многообразий, такие как TSNE и Isomap.
Все эти оценки могут вычислять внутренне ближайших соседей, но большинство из них также принимают предварительно вычисленных ближайших соседей разреженного графа, как предоставлено kneighbors_graph и radius_neighbors_graph. В режиме mode='connectivity', эти функции возвращают бинарный разреженный граф смежности, как требуется, например, в SpectralClustering. В то время как с mode='distance', они возвращают разреженный граф расстояний, как требуется, например, в DBSCAN. Для включения этих функций в конвейер scikit-learn можно также использовать соответствующие классы KNeighborsTransformer и RadiusNeighborsTransformer. Преимущества этого API разреженного графа многочисленны.
Во-первых, предварительно вычисленный граф можно повторно использовать несколько раз, например, при изменении параметра оценки. Это можно сделать вручную пользователем или с помощью свойств кэширования конвейера scikit-learn:
>>> import tempfile >>> from sklearn.manifold import Isomap >>> from sklearn.neighbors import KNeighborsTransformer >>> from sklearn.pipeline import make_pipeline >>> from sklearn.datasets import make_regression >>> cache_path = tempfile.gettempdir() # we use a temporary folder here >>> X, _ = make_regression(n_samples=50, n_features=25, random_state=0) >>> estimator = make_pipeline( ... KNeighborsTransformer(mode='distance'), ... Isomap(n_components=3, metric='precomputed'), ... memory=cache_path) >>> X_embedded = estimator.fit_transform(X) >>> X_embedded.shape (50, 3)
Во-вторых, предварительное вычисление графа может обеспечить более точный контроль над оценкой ближайших соседей, например, включение многопроцессорной обработки через параметр n_jobs, который может быть недоступен во всех оценках.
Наконец, предварительное вычисление может выполняться пользовательскими оценками для использования различных реализаций, таких как приближенные методы ближайших соседей или реализации со специальными типами данных. Предварительно вычисленные ближайшие соседи разреженный граф должны быть отформатированы как в выходе radius_neighbors_graph:
- матрица CSR (хотя COO, CSC или LIL будут приняты).
- только явно хранить ближайшие окрестности каждой выборки относительно обучающих данных. Это должно включать те, что имеют расстояние 0 от точки запроса, включая диагональ матрицы при вычислении ближайших окрестностей между обучающими данными и самими собой.
- каждый
dataстроки должен хранить расстояние в порядке возрастания (необязательно. Неупорядоченные данные будут стабильно отсортированы, добавляя вычислительную нагрузку). - все значения в данных должны быть неотрицательными.
- не должно быть повторяющихся
indicesв любой строке (см. scipy/scipy#5807). - если алгоритму передаётся предварительно вычисленная матрица с k ближайшими соседями (в отличие от радиусной окрестности), по крайней мере k соседей должны быть сохранены в каждой строке (или k+1, как объяснено в следующем примечании).
Примечание
Когда запрашивается определенное количество соседей (используя KNeighborsTransformer), определение n_neighbors неоднозначно, поскольку оно может либо включать каждую точку обучения в качестве своего соседа, либо исключать их. Ни один выбор не является идеальным, так как включение их приводит к другому количеству соседей, не являющихся самими собой, во время обучения и тестирования, а исключение их приводит к различию между fit(X).transform(X) и fit_transform(X), что противоречит API scikit-learn. В KNeighborsTransformer мы используем определение, которое включает каждую точку обучения в качестве собственного соседа в подсчёте n_neighbors. Однако, по соображениям совместимости с другими оценками, которые используют другое определение, будет вычислен один дополнительный сосед, когда mode == 'distance'. Чтобы максимизировать совместимость со всеми оценками, безопасным выбором является всегда включение одного дополнительного соседа в пользовательской оценке ближайших соседей, так как ненужные соседи будут отфильтрованы последующими оценками.
Примеры
-
Приближенные ближайшие соседи в TSNE: пример конвейеризации
KNeighborsTransformerиTSNE. Также предлагает две пользовательские оценки ближайших соседей на основе внешних пакетов. -
Кэширование ближайших соседей: пример конвейеризации
KNeighborsTransformerиKNeighborsClassifierдля включения кэширования графа соседей во время поиска по сетке гиперпараметров.
1.6.7. Анализ компонент окрестностей
Анализ компонент окрестностей (NCA, NeighborhoodComponentsAnalysis) — алгоритм обучения метрике расстояний, призванный повысить точность классификации ближайших соседей по сравнению со стандартным евклидовым расстоянием. Алгоритм непосредственно максимизирует стохастический вариант оценки ближайших k-соседей (KNN) с оставлением одного элемента в обучающей выборке. Он также может научиться низкоразмерной линейной проекции данных, которая может использоваться для визуализации данных и быстрой классификации.
На приведенной выше иллюстрации мы рассматриваем некоторые точки из случайного набора данных. Мы сосредоточены на стохастической классификации KNN точки № 3. Толщина связи между образцом 3 и другой точкой пропорциональна их расстоянию и может рассматриваться как относительный вес (или вероятность), который правило стохастического ближайшего соседа присвоит этой точке. В исходном пространстве образец 3 имеет много стохастических соседей из различных классов, поэтому правильный класс не очень вероятен. Однако в спроектированном пространстве, обученном NCA, единственными стохастическими соседями с существенным весом являются соседи из того же класса, что и образец 3, что гарантирует правильную классификацию последнего. Более подробную информацию см. в математической формулировке.
1.6.7.1. Классификация
В сочетании с классификатором ближайших соседей (KNeighborsClassifier) NCA привлекателен для классификации, потому что он естественным образом обрабатывает многоклассовые задачи без увеличения размера модели и не вводит дополнительные параметры, требующие настройки пользователем.
Практически показано, что классификация NCA хорошо работает для наборов данных различного размера и сложности. В отличие от связанных методов, таких как линейный дискриминантный анализ, NCA не делает никаких предположений о распределении классов. Классификация ближайших соседей может естественным образом создавать сильно нерегулярные границы решений.
Для использования этой модели для классификации необходимо объединить экземпляр NeighborhoodComponentsAnalysis, который обучает оптимальное преобразование, с экземпляром KNeighborsClassifier, который выполняет классификацию в спроектированном пространстве. Вот пример использования двух классов:
>>> from sklearn.neighbors import (NeighborhoodComponentsAnalysis,
... KNeighborsClassifier)
>>> from sklearn.datasets import load_iris
>>> from sklearn.model_selection import train_test_split
>>> from sklearn.pipeline import Pipeline
>>> X, y = load_iris(return_X_y=True)
>>> X_train, X_test, y_train, y_test = train_test_split(X, y,
... stratify=y, test_size=0.7, random_state=42)
>>> nca = NeighborhoodComponentsAnalysis(random_state=42)
>>> knn = KNeighborsClassifier(n_neighbors=3)
>>> nca_pipe = Pipeline([('nca', nca), ('knn', knn)])
>>> nca_pipe.fit(X_train, y_train)
Pipeline(...)
>>> print(nca_pipe.score(X_test, y_test))
0.96190476...
График показывает границы решений для классификации ближайших соседей и классификации с помощью анализа компонент окрестностей на наборе данных iris, при обучении и оценке только по двум признакам, для целей визуализации.
1.6.7.2. Снижение размерности
NCA можно использовать для выполнения контролируемого снижения размерности. Данные входных данных проецируются на линейное подпространство, состоящее из направлений, которые минимизируют целевую функцию NCA. Желаемую размерность можно задать с помощью параметра n_components. Например, на следующем рисунке показано сравнение снижения размерности с помощью анализа главных компонент (PCA), линейного дискриминантного анализа (LinearDiscriminantAnalysis) и анализа компонент окрестностей (NeighborhoodComponentsAnalysis) на наборе данных Digits, содержащем \(n_{samples} = 1797\) и \(n_{features} = 64\). Набор данных разделяется на обучающую и тестовую выборки равного размера, затем стандартизируется. Для оценки вычисляется точность классификации 3 ближайших соседей на 2-мерных спроектированных точках, полученных каждым методом. Каждый образец данных относится к одному из 10 классов.
Примеры
1.6.7.3. Математическая формулировка
Цель NCA — обучить оптимальную линейную матрицу преобразования размера (n_components, n_features), которая максимизирует сумму по всем образцам \(i\) вероятности \(p_i\), что \(i\) правильно классифицирован, т. е.:
где \(N\) = n_samples и \(p_i\) — вероятность того, что образец \(i\) правильно классифицирован в соответствии с правилом стохастических ближайших соседей в обученном вложенном пространстве:
где \(C_i\) — множество точек в том же классе, что и образец \(i\), а \(p_{i j}\) — softmax по евклидовым расстояниям во вложенном пространстве:
Расстояние Махаланобиса
NCA можно рассматривать как обучение метрике расстояния Махаланобиса (в квадрате):
где \(M = L^T L\) — симметричная положительно полуопределённая матрица размера (n_features, n_features).
1.6.7.4. Реализация
Эта реализация соответствует тому, что объясняется в оригинальной статье [1]. Для метода оптимизации в настоящее время используется L-BFGS-B из библиотеки scipy с полным вычислением градиента на каждой итерации, чтобы избежать настройки скорости обучения и обеспечить стабильное обучение.
Дополнительную информацию см. в примерах ниже и в строке документации для NeighborhoodComponentsAnalysis.fit.
1.6.7.5. Сложность
1.6.7.5.1. Обучение
NCA хранит матрицу парных расстояний, занимающую n_samples ** 2 памяти. Время выполнения зависит от количества итераций, выполняемых алгоритмом оптимизации. Однако максимальное количество итераций можно установить с помощью аргумента max_iter. Для каждой итерации время выполнения равно O(n_components x n_samples x min(n_samples, n_features)).
1.6.7.5.2. Преобразование
Здесь операция transform возвращает \(LX^T\), поэтому её время выполнения равно n_components * n_features * n_samples_test. В операции нет дополнительной сложности с точки зрения памяти.
Ссылки
© 2007–2025 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.6/modules/neighbors.html