Некоторые примеры кода Python, показывающие, как косинусное сходство равно скалярному произведению для нормализованных векторов.
Импорт:
import matplotlib.pyplot as plt import pandas as pd import numpy as np from sklearn import preprocessing from sklearn.metrics.pairwise import cosine_similarity, linear_kernel from scipy.spatial.distance import cosine
Сделайте и нанесите на карту поддельные 2-мерные данные.
n_samples = 100 n_features = 2 X = np.random.randn(n_samples, n_features) plt.figure(figsize=(5, 5)) plt.scatter(X[:,0], X[:,1])
График после нормализации данных. Смотрите, как нормы становятся 1.
X_normalized = preprocessing.normalize(X, norm='l2') plt.figure(figsize=(5, 5)) plt.scatter(X_normalized[:,0], X_normalized[:,1])
Численные примеры скалярного произведения против. косинусное сходство:
n_samples = 8 n_features = 5 X = np.random.uniform(0, 2, size=(n_samples, n_features)) Y = np.random.uniform(-1, 3, size=(n_samples, n_features))
В общем случае скалярное произведение не равно косинусоидальному подобию:
np.allclose(np.dot(X, Y.T), cosine_similarity(X, Y))
Из [22]:
False
Нормализация:
X_normalized = preprocessing.normalize(X, norm='l2') Y_normalized = preprocessing.normalize(Y, norm='l2')
Нормализация не меняет подобия косинуса:
np.allclose(cosine_similarity(X, Y), cosine_similarity(X_normalized, Y_normalized))
Из [26]:
True
После нормализации скалярное произведение равно косинусному сходству:
np.allclose(np.dot(X_normalized, Y_normalized.T), cosine_similarity(X_normalized, Y_normalized))
Из [27]:
True
Как упоминалось в sklearn здесь:
нормализованные векторы, и в этом случае cosine_similarity эквивалентен linear_kernel, только медленнее.
Это тоже можно показать:
np.allclose(linear_kernel(X_normalized, Y_normalized), cosine_similarity(X_normalized, Y_normalized))
Из [28]:
True
После нормализации евклидово расстояние уменьшится до sqrt (1 + 1-2dot (x, y)), то есть sqrt (2–2 * cosine_similarity). Сравните результаты ниже:
np.sqrt(sum((X_normalized[0] - Y_normalized[0])**2)), np.linalg.norm(X_normalized[0] - Y_normalized[0])
Из [29]:
(0.7509789525594854, 0.7509789525594854)
In [30]:
np.sqrt(2 - 2 * np.dot(X_normalized[0], Y_normalized[0])), np.linalg.norm(X_normalized[0] - Y_normalized[0])
Из [30]:
(0.7509789525594853, 0.7509789525594854)
In [31]:
np.sqrt(2 - 2 * cosine_similarity(X_normalized[0].reshape(1, -1), Y_normalized[0].reshape(1, -1)))
Из [31]:
array([[0.75097895]])
Полезным следствием этого является то, что теперь мы можем использовать евклидово расстояние вместо косинусного подобия, когда последнее не поддерживается. Примером может служить KNN от sklearn. Как упоминалось здесь, косинусное расстояние не допускается, но евклидово:
метрика: строка или вызываемая, по умолчанию «minkowski» - метрика расстояния, используемая для дерева. По умолчанию используется метрика Минковского, а при p = 2 она эквивалентна стандартной евклидовой метрике.
Код находится здесь: https://github.com/yang-zhang/yang-zhang.github.io/blob/master/ds_math/normalize_vs_cosine.ipynb