Может ли кто-нибудь привести пример подобия косинуса очень простым графическим способом?

Статья о косинусном сходстве в Википедии

Можете ли вы показать здесь векторы (в виде списка или что-то в этом роде), а затем выполнить математические вычисления, и мы посмотрим, как это работает?


person TIMEX    schedule 17.11.2009    source источник
comment
Попробуйте взять копию книги «Геометрия и смысл» Widdows (нажмите. uchicago.edu/presssite/), я прочитал его некоторое время назад и пожалел, что не получил его несколько лет назад, отличный вводный текст.   -  person Nathan Howell    schedule 03.01.2010


Ответы (11)


Вот два очень коротких текста для сравнения:

  1. Julie loves me more than Linda loves me

  2. 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 градусов, который представляет собой некоторое «расстояние» от нуля или полного совпадения.

person Bill Bell    schedule 17.11.2009
comment
Это именно то, что я искал. Точно. Считается ли это самой простой формой модели векторного пространства? - person TIMEX; 17.11.2009
comment
Я действительно рад, что это было полезно для тебя, Алекс. Приносим извинения за задержку с ответом. Я давно не посещал StackOverflow. Собственно это пример внутреннего пространства продукта. В Википедии есть основная дискуссия. - person Bill Bell; 28.11.2009
comment
Отличный пример! Большое спасибо! - person makaroni4; 03.12.2012
comment
Есть ли способ нормализовать длину документа? - person sinθ; 12.01.2014
comment
Вы должны использовать нормализацию длины, а перед этим попробуйте использовать логарифмическое взвешивание частоты для всех векторов терминов. Если вы уже имеете дело с нормализованными векторами, то это скалярное произведение A.B. - person Ali Gajani; 10.05.2014
comment
Более подробный пример с использованием нормализации длины и TF-IDF: site.uottawa. ca / ~ diana / csi4107 / cosine_tf_idf_example.pdf - person Mike B.; 12.08.2014
comment
Чрезвычайно иронично, что 6 лет было недостаточно, чтобы получить базовое форматирование ответа на запрос о разъяснении в очень простой графической форме. - person sehe; 06.10.2015
comment
Если мы используем косинус для вычисления расстояний, можем ли мы отобразить сходство документов, если у нас более двух документов? Рассмотрим этот пример: c: [Julie likes me as a friend.] Будет ли он по-прежнему 2D, или он станет 3D? - person Arash Howaida; 04.01.2017
comment
@ArashHowaida: Косинусное сходство между любыми двумя документами - это просто число, и только положительные косинусы представляют интерес. Таким образом, вам нужен только линейный сегмент между 0 и 1, чтобы отобразить сходство набора документов. То есть сходство документов может варьироваться от нуля до единицы. Одно измерение. - person Bill Bell; 04.01.2017
comment
@ Билл Белл О, 1D. Это интересно. Это наиболее распространенный способ отобразить сходство документов или существуют 2D-методы (с использованием дополнительных показателей)? В частности, я хочу построить кластеры документов, возможно, через TF-IDF и косинус, но не знаю, как создать диаграмму рассеяния. - person Arash Howaida; 04.01.2017
comment
@ArashHowaida: Если вы используете Python, вам может быть полезен github.com/brandomr/document_cluster. - person Bill Bell; 04.01.2017
comment
@Bill Bell Я запустил это решение github для своих документов, и оно отлично работает. OP может также найти отличное и простое изображение кластеризации. Просто из любопытства, те из нас, кто не знаком с некоторыми вещами, скрытыми там, например, с многомерным масштабированием, после того, как мы создадим наш сюжет документа, как мы должны маркировать оси? Находятся ли они исключительно в векторном пространстве или это «прогнозируемые» расстояния TF-IDF? Или нет агрегатов? - person Arash Howaida; 09.01.2017
comment
@ArashHowaida: «Красота в глазах смотрящего». В документе сюжетные элементы на похожие темы сгруппированы вместе. Если человек-наблюдатель воспринимает, что одна часть диаграммы состоит из книг, все они представляют собой романтический юмор, то это то, что они «есть». - person Bill Bell; 09.01.2017
comment
Это здорово, просто использовал это, чтобы убедиться, что моя функция haskell правильно вычисляет косинусное сходство. - person liminal18; 22.01.2017
comment
@ liminal18: Признаюсь, я никогда не ожидал этого. - person Bill Bell; 22.01.2017
comment
@BillBell, я не понимал, как количество слов в каждом документе сопоставляется с векторами a и b. Пытался читать в разном порядке, но не знал, как читать. .спасибо за отличный ответ! - person vagabond; 09.02.2018
comment
@vagabond: У тебя должны быть глаза орла! Сколько людей, должно быть, пропустили эту ошибку? Левый столбец становится строкой «a», правый - строкой «b». Спасибо, что указали на это! - person Bill Bell; 09.02.2018
comment
У кого-нибудь есть какие-либо объяснения того, как он попал: в этом случае вы можете представить это как угол около 35 градусов? И что он имел в виду под каким-то «расстоянием» от нуля или идеального согласия? - person Greg Hilston; 31.05.2018
comment
Теперь я вижу, что мы просто делаем обратный косинус ... но оставляя комментарий в качестве моего второго вопроса, все еще в силе. - person Greg Hilston; 31.05.2018

Я предполагаю, что вам больше интересно понять, «почему» работает косинусное сходство (почему оно дает хорошее указание на сходство), а не «как» он рассчитывается (конкретные операции, используемые для расчета). Если вас интересует последнее, см. Ссылку, указанную Даниэлем в этом сообщении, а также соответствующий 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 нормализуются.

person mjv    schedule 17.11.2009

Вот моя реализация на 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));
        }
    }
}
person tranmq    schedule 17.11.2009
comment
это здорово, спасибо, мне понравилось, как вы объяснили Magnitude =) - person liminal18; 22.01.2017
comment
Это здорово, но что, если мы работаем с файлами или строками. - person Talha; 23.12.2019

Для простоты уменьшаю вектор 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 градусам.

person userPS    schedule 26.12.2012

Этот код 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))
person Victor Yan    schedule 19.02.2013
comment
Можете ли вы объяснить, почему вы использовали set в строке all_items = set (counter1.keys ()). Union (set (counter2.keys ())). - person Ghos3t; 12.04.2019
comment
@ Ghos3t, то есть получить список отдельных слов из обоих документов - person Jobs; 26.09.2019

Используя пример @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)))
person Andrew U    schedule 03.10.2013

Это простой 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]])
person Nilani Algiriyage    schedule 03.03.2014

Простой код 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));
  }
person nikoo28    schedule 21.10.2016
comment
Это не простой графический способ, это просто код. Хотя другие тоже сделали ту же ошибку: / - person Skrylar; 19.04.2018

Позвольте мне попытаться объяснить это с помощью кода 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]])

Оба выходных значения одинаковы! Значения подобия косинуса, вычисленные вручную, такие же, как значения подобия косинуса, вычисленные функцией!

Следовательно, это простое объяснение показывает, как вычисляются значения косинусного сходства. Надеюсь, вы нашли это объяснение полезным.

person Yi Xiang Chong    schedule 18.05.2021

Два вектора A и B существуют в двухмерном или трехмерном пространстве, угол между этими векторами - это cos-подобие.

Если угол больше (может достигать максимум 180 градусов), то это Cos 180 = -1, а минимальный угол равен 0 градусов. cos 0 = 1 означает, что векторы выровнены друг относительно друга и, следовательно, векторы похожи.

cos 90 = 0 (этого достаточно, чтобы сделать вывод, что векторы A и B совсем не похожи, и поскольку расстояние не может быть отрицательным, значения косинуса будут лежать от 0 до 1. Следовательно, больший угол подразумевает уменьшение подобия (визуализация также имеет смысл)

person Drool    schedule 22.09.2018

Вот простой код 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))
person Jake Morfe    schedule 05.12.2020