Статья о косинусном сходстве в Википедии
Можете ли вы показать здесь векторы (в виде списка или что-то в этом роде), а затем выполнить математические вычисления, и мы посмотрим, как это работает?
Статья о косинусном сходстве в Википедии
Можете ли вы показать здесь векторы (в виде списка или что-то в этом роде), а затем выполнить математические вычисления, и мы посмотрим, как это работает?
Вот два очень коротких текста для сравнения:
Julie loves me more than Linda loves me
Jane likes me more than Julie loves me
Мы хотим знать, насколько похожи эти тексты, исключительно с точки зрения количества слов (и игнорирования порядка слов). Начнем с составления списка слов из обоих текстов:
me Julie loves Linda than more likes Jane
Теперь посчитаем, сколько раз каждое из этих слов встречается в каждом тексте:
me 2 2
Jane 0 1
Julie 1 1
Linda 1 0
likes 0 1
loves 2 1
more 1 1
than 1 1
Однако нас не интересуют сами слова. Нас интересуют только эти два вертикальных вектора отсчетов. Например, в каждом тексте есть два экземпляра «я». Мы собираемся решить, насколько близки эти два текста друг к другу, вычислив одну функцию этих двух векторов, а именно косинус угла между ними.
Два вектора, опять же:
a: [2, 0, 1, 1, 0, 2, 1, 1]
b: [2, 1, 1, 0, 1, 1, 1, 1]
Косинус угла между ними составляет около 0,822.
Эти векторы 8-мерные. Достоинство использования косинусного подобия явно состоит в том, что оно преобразует вопрос, который невозможно представить себе, в вопрос, который может быть. В этом случае вы можете думать об этом как об угле около 35 градусов, который представляет собой некоторое «расстояние» от нуля или полного совпадения.
c: [Julie likes me as a friend.]
Будет ли он по-прежнему 2D, или он станет 3D?
- person Arash Howaida; 04.01.2017
Я предполагаю, что вам больше интересно понять, «почему» работает косинусное сходство (почему оно дает хорошее указание на сходство), а не «как» он рассчитывается (конкретные операции, используемые для расчета). Если вас интересует последнее, см. Ссылку, указанную Даниэлем в этом сообщении, а также соответствующий SO Вопрос.
Чтобы объяснить и как, и тем более почему, полезно сначала упростить задачу и работать только в двух измерениях. Как только вы получите это в 2D, вам будет легче думать об этом в трех измерениях и, конечно, сложнее представить себе во многих других измерениях, но к тому времени мы сможем использовать линейную алгебру для числовых вычислений, а также помочь нам мыслить терминами. линий / векторов / "плоскостей" / "сфер" в n измерениях, даже если мы не можем их нарисовать.
Итак, в двух измерениях: что касается схожести текста, это означает, что мы сосредоточимся на двух различных терминах, произнесем слова «Лондон» и «Париж» и посчитаем, сколько раз каждый из эти слова можно найти в каждом из двух документов, которые мы хотим сравнить. Это дает нам для каждого документа точку на плоскости x-y. Например, если в Doc1 один раз был Париж, а четыре раза - Лондон, точка (1,4) представит этот документ (в отношении этой крошечной оценки документов). Или, говоря в терминах векторов, этот документ Doc1 будет стрелкой, идущей от начала координат к точке (1,4). Имея в виду это изображение, давайте подумаем о том, что означает совпадение двух документов и как это соотносится с векторами.
ОЧЕНЬ похожие документы (опять же в отношении этого ограниченного набора размеров) будут иметь одинаковое количество ссылок на Париж И такое же количество ссылок на Лондон, или, может быть, они могут иметь такое же соотношение этих ссылок. Документ Doc2 с 2 ссылками на Париж и 8 ссылками на Лондон также будет очень похож, только с более длинным текстом или более повторяющимися названиями городов, но в той же пропорции. Может быть, оба документа являются путеводителями по Лондону, только вскользь упоминается Париж (и какой он классный город ;-) Шучу !!!.
Теперь менее похожие документы могут также содержать ссылки на оба города, но в разных пропорциях. Может быть, Doc2 процитирует Париж только один раз, а Лондон семь раз.
Вернемся к нашей плоскости xy, если мы нарисуем эти гипотетические документы, мы увидим, что когда они ОЧЕНЬ похожи, их векторы перекрываются (хотя некоторые векторы могут быть длиннее), и когда они начинают иметь меньше общего , эти векторы начинают расходиться, угол между ними становится шире.
Измеряя угол между векторами, мы можем получить хорошее представление об их сходстве. Чтобы упростить задачу, взяв косинус этого угла, мы получим хорошее значение от 0 до 1 или от -1 до 1, которое указывает на это сходство, в зависимости от того, что и как мы учитываем. Чем меньше угол, тем больше (ближе к 1) значение косинуса, а также больше сходство.
В крайнем случае, если Doc1 цитирует только Париж, а Doc2 только Лондон, в документах нет абсолютно ничего общего. Doc1 будет иметь вектор на оси x, Doc2 на оси y, угол 90 градусов, косинус 0. В этом случае мы бы сказали, что эти документы ортогональны друг другу.
Добавление размеров:
Благодаря этому интуитивному ощущению сходства, выраженному в виде небольшого угла (или большого косинуса), мы можем теперь представить вещи в трех измерениях, например, добавив слово «Амстердам» в смесь. , и достаточно хорошо визуализировать, как документ с двумя ссылками на каждый будет иметь вектор, идущий в определенном направлении, и мы можем увидеть, как это направление будет сравниваться с документом, в котором по три раза упоминаются Париж и Лондон, но не Амстердам и т. д. сказал, мы можем попробовать представить себе это причудливое пространство для 10 или 100 городов. Трудно нарисовать, но легко осмыслить.
В заключение скажу несколько слов о самой формуле. Как я уже сказал, другие ссылки предоставляют хорошую информацию о расчетах.
Первый в двух измерениях. Формула для косинуса угла между двумя векторами выводится из тригонометрической разницы (между углом a и углом b):
cos(a - b) = (cos(a) * cos(b)) + (sin (a) * sin(b))
Эта формула очень похожа на формулу скалярного произведения:
Vect1 . Vect2 = (x1 * x2) + (y1 * y2)
где cos(a)
соответствует значению x
, а sin(a)
значению y
для первого вектора и т. д. Единственная проблема заключается в том, что x
, y
и т. д. не являются точными значениями cos
и sin
, так как эти значения должны быть прочитаны на единичный круг. Вот тут-то и вступает в действие знаменатель формулы: делением на произведение длины этих векторов координаты x
и y
нормализуются.
Вот моя реализация на C #.
using System;
namespace CosineSimilarity
{
class Program
{
static void Main()
{
int[] vecA = {1, 2, 3, 4, 5};
int[] vecB = {6, 7, 7, 9, 10};
var cosSimilarity = CalculateCosineSimilarity(vecA, vecB);
Console.WriteLine(cosSimilarity);
Console.Read();
}
private static double CalculateCosineSimilarity(int[] vecA, int[] vecB)
{
var dotProduct = DotProduct(vecA, vecB);
var magnitudeOfA = Magnitude(vecA);
var magnitudeOfB = Magnitude(vecB);
return dotProduct/(magnitudeOfA*magnitudeOfB);
}
private static double DotProduct(int[] vecA, int[] vecB)
{
// I'm not validating inputs here for simplicity.
double dotProduct = 0;
for (var i = 0; i < vecA.Length; i++)
{
dotProduct += (vecA[i] * vecB[i]);
}
return dotProduct;
}
// Magnitude of the vector is the square root of the dot product of the vector with itself.
private static double Magnitude(int[] vector)
{
return Math.Sqrt(DotProduct(vector, vector));
}
}
}
Для простоты уменьшаю вектор a и b:
Let :
a : [1, 1, 0]
b : [1, 0, 1]
Затем косинусное подобие (тета):
(Theta) = (1*1 + 1*0 + 0*1)/sqrt((1^2 + 1^2))* sqrt((1^2 + 1^2)) = 1/2 = 0.5
то обратное значение cos 0,5 равно 60 градусам.
Этот код Python - моя быстрая и грязная попытка реализовать алгоритм:
import math
from collections import Counter
def build_vector(iterable1, iterable2):
counter1 = Counter(iterable1)
counter2 = Counter(iterable2)
all_items = set(counter1.keys()).union(set(counter2.keys()))
vector1 = [counter1[k] for k in all_items]
vector2 = [counter2[k] for k in all_items]
return vector1, vector2
def cosim(v1, v2):
dot_product = sum(n1 * n2 for n1, n2 in zip(v1, v2) )
magnitude1 = math.sqrt(sum(n ** 2 for n in v1))
magnitude2 = math.sqrt(sum(n ** 2 for n in v2))
return dot_product / (magnitude1 * magnitude2)
l1 = "Julie loves me more than Linda loves me".split()
l2 = "Jane likes me more than Julie loves me or".split()
v1, v2 = build_vector(l1, l2)
print(cosim(v1, v2))
Используя пример @Bill Bell, это можно сделать двумя способами в [R]
a = c(2,1,0,2,0,1,1,1)
b = c(2,1,1,1,1,0,1,1)
d = (a %*% b) / (sqrt(sum(a^2)) * sqrt(sum(b^2)))
или воспользоваться производительностью метода crossprod () ...
e = crossprod(a, b) / (sqrt(crossprod(a, a)) * sqrt(crossprod(b, b)))
Это простой Python
код, который реализует косинусное сходство.
from scipy import linalg, mat, dot
import numpy as np
In [12]: matrix = mat( [[2, 1, 0, 2, 0, 1, 1, 1],[2, 1, 1, 1, 1, 0, 1, 1]] )
In [13]: matrix
Out[13]:
matrix([[2, 1, 0, 2, 0, 1, 1, 1],
[2, 1, 1, 1, 1, 0, 1, 1]])
In [14]: dot(matrix[0],matrix[1].T)/np.linalg.norm(matrix[0])/np.linalg.norm(matrix[1])
Out[14]: matrix([[ 0.82158384]])
Простой код JAVA для вычисления косинусного сходства
/**
* Method to calculate cosine similarity of vectors
* 1 - exactly similar (angle between them is 0)
* 0 - orthogonal vectors (angle between them is 90)
* @param vector1 - vector in the form [a1, a2, a3, ..... an]
* @param vector2 - vector in the form [b1, b2, b3, ..... bn]
* @return - the cosine similarity of vectors (ranges from 0 to 1)
*/
private double cosineSimilarity(List<Double> vector1, List<Double> vector2) {
double dotProduct = 0.0;
double normA = 0.0;
double normB = 0.0;
for (int i = 0; i < vector1.size(); i++) {
dotProduct += vector1.get(i) * vector2.get(i);
normA += Math.pow(vector1.get(i), 2);
normB += Math.pow(vector2.get(i), 2);
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
Позвольте мне попытаться объяснить это с помощью кода Python и некоторых графических математических формул.
Предположим, в нашем коде есть два очень коротких текста:
texts = ["I am a boy", "I am a girl"]
И мы хотим сравнить следующий текст запроса, чтобы увидеть, насколько он близок к приведенным выше текстам, используя быстрые оценки сходства косинуса:
query = ["I am a boy scout"]
Как нам вычислить оценки сходства косинусов? Во-первых, давайте построим матрицу tfidf на Python для этих текстов:
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(texts)
Затем давайте проверим значения нашей матрицы tfidf и ее словаря:
print(tfidf_matrix.toarray())
# output
array([[0.57973867, 0.81480247, 0. ],
[0.57973867, 0. , 0.81480247]])
Здесь мы получаем матрицу tfidf со значениями tfidf 2 x 3 или 2 документа / текст x 3 термина. Это наша матрица терминов документа tfidf. Давайте посмотрим, что это за 3 термина, позвонив по номеру vectorizer.vocabulary_
print(vectorizer.vocabulary_)
# output
{'am': 0, 'boy': 1, 'girl': 2}
Это говорит нам, что наши 3 члена в нашей матрице tfidf - это «am», «boy» и «girl». «am» находится в столбце 0, «мальчик» - в столбце 1, а «девушка» - в столбце 2. Термины «I» и «a» были удалены векторизатором, поскольку они являются игнорируемыми словами.
Теперь у нас есть матрица tfidf, мы хотим сравнить текст нашего запроса с нашими текстами и посмотреть, насколько близок наш запрос к нашим текстам. Для этого мы можем вычислить оценки косинусного сходства запроса по сравнению с матрицей текстов tfidf. Но сначала нам нужно вычислить tfidf нашего запроса:
query = ["I am a boy scout"]
query_tfidf = vectorizer.transform([query])
print(query_tfidf.toarray())
#output
array([[0.57973867, 0.81480247, 0. ]])
Здесь мы вычислили tfidf нашего запроса. В нашем query_tfidf есть вектор значений tfidf [0.57973867, 0.81480247, 0. ]
, который мы будем использовать для вычисления наших оценок умножения косинусного сходства. Если я не ошибаюсь, значения query_tfidf или vectorizer.transform([query])
получаются путем простого выбора строки или документа из tfidf_matrix, у которого больше всего слов, совпадающих с запросом. Например, строка 1 или документ / текст 1 tfidf_matrix имеет наибольшее совпадение слов с текстом запроса, который содержит am (0,57973867) и boy (0,81480247), поэтому строка 1 tfidf_matrix со значениями [0.57973867, 0.81480247, 0. ]
выбирается в качестве значений для query_tfidf. (Примечание. Было бы хорошо, если бы кто-то мог объяснить это подробнее)
После вычисления нашего query_tfidf, мы можем теперь матрично умножить или скалярно умножить наш вектор query_tfidf на наш текст tfidf_matrix, чтобы получить оценки косинусного сходства.
Напомним, что оценка или формула косинусного сходства равна следующему:
cosine similarity score = (A . B) / ||A|| ||B||
Здесь A = наш вектор query_tfidf и B = каждая строка нашего tfidf_matrix
Обратите внимание: А. B = A * B ^ T, или скалярное произведение B = A умножить на B Транспонировать.
Зная формулу, давайте вручную вычислим наши оценки косинусного сходства для query_tfidf, а затем сравним наш ответ со значениями, предоставленными функцией cosine_similarity sklearn.metrics. Рассчитаем вручную:
query_tfidf_arr = query_tfidf.toarray()
tfidf_matrix_arr = tfidf_matrix.toarray()
cosine_similarity_1 = np.dot(query_tfidf_arr, tfidf_matrix_arr[0].T) /
(np.linalg.norm(query_tfidf_arr) * np.linalg.norm(tfidf_matrix_arr[0]))
cosine_similarity_2 = np.dot(query_tfidf_arr, tfidf_matrix_arr[1].T) /
(np.linalg.norm(query_tfidf_arr) * np.linalg.norm(tfidf_matrix_arr[1]))
manual_cosine_similarities = [cosine_similarity_1[0], cosine_similarity_2[0]]
print(manual_cosine_similarities)
#output
[1.0, 0.33609692727625745]
Наши рассчитанные вручную оценки косинусного сходства дают значения [1.0, 0.33609692727625745]
. Давайте сравним наш вычисленный вручную показатель сходства косинуса со значением ответа, предоставленным функцией cosine_similarity sklearn.metrics:
from sklearn.metrics.pairwise import cosine_similarity
function_cosine_similarities = cosine_similarity(query_tfidf, tfidf_matrix)
print(function_cosine_similarities)
#output
array([[1.0 , 0.33609693]])
Оба выходных значения одинаковы! Значения подобия косинуса, вычисленные вручную, такие же, как значения подобия косинуса, вычисленные функцией!
Следовательно, это простое объяснение показывает, как вычисляются значения косинусного сходства. Надеюсь, вы нашли это объяснение полезным.
Два вектора A и B существуют в двухмерном или трехмерном пространстве, угол между этими векторами - это cos-подобие.
Если угол больше (может достигать максимум 180 градусов), то это Cos 180 = -1, а минимальный угол равен 0 градусов. cos 0 = 1 означает, что векторы выровнены друг относительно друга и, следовательно, векторы похожи.
cos 90 = 0 (этого достаточно, чтобы сделать вывод, что векторы A и B совсем не похожи, и поскольку расстояние не может быть отрицательным, значения косинуса будут лежать от 0 до 1. Следовательно, больший угол подразумевает уменьшение подобия (визуализация также имеет смысл)
Вот простой код Python для вычисления косинусного сходства:
import math
def dot_prod(v1, v2):
ret = 0
for i in range(len(v1)):
ret += v1[i] * v2[i]
return ret
def magnitude(v):
ret = 0
for i in v:
ret += i**2
return math.sqrt(ret)
def cos_sim(v1, v2):
return (dot_prod(v1, v2)) / (magnitude(v1) * magnitude(v2))