Некоторые примеры кода 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