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

201

Статья Cosine Similarity в Википедии

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

Я новичок

TIMEX
источник
1
Попробуйте подобрать копию «Геометрия и смысл по вдовам» ( press.uchicago.edu/presssite/… ), я прочитал ее некоторое время назад и хотел бы иметь ее несколько лет назад, отличный вводный текст.
Натан Хауэлл

Ответы:

463

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

  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 градусов, что является некоторым «расстоянием» от нуля или идеальным соглашением.

Билл Белл
источник
12
Это именно то, что я искал. Именно. Считается ли это самой простой формой «модели векторного пространства»?
TIMEX
2
Я очень рад, что это было полезно для тебя, Алекс. Извините за задержку с ответом. Я давно не был в StackOverflow. На самом деле это пример «внутреннего пространства продукта». Там в основном обсуждение википедии.
Билл Белл
1
Есть ли способ нормализации по длине документа?
sinθ
1
Вы должны использовать нормализацию длины, а перед этим попытаться использовать логарифмическое взвешивание частоты для всех векторов терминов. Если вы уже имеете дело с нормализованными векторами, то это скалярное произведение AB
Али Гаджани,
4
Более подробный пример с использованием нормализации длины и TF-IDF: site.uottawa.ca/~diana/csi4107/cosine_tf_idf_example.pdf
Майк Б.,
121

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

Чтобы объяснить как и даже более того, почему, полезно, во-первых, упростить проблему и работать только в двух измерениях. Как только вы получите это в 2D, вам будет легче думать об этом в трех измерениях, и, конечно, сложнее представить это во многих других измерениях, но к тому времени мы сможем использовать линейную алгебру для выполнения численных расчетов, а также, чтобы помочь нам мыслить в терминах линий / векторов / "плоскостей" / "сфер" в n измерениях, хотя мы не можем их нарисовать.

Таким образом, в двух измерениях : в отношении сходства текста это означает, что мы сосредоточимся на двух разных терминах, скажем, слова «Лондон» и «Париж», и посчитаем, сколько раз каждое из этих слов встречается в каждом из два документа, которые мы хотим сравнить. Это дает нам для каждого документа точку в плоскости xy. Например, если у 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координаты нормализуются.

MJV
источник
25

Вот моя реализация в 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));
        }
    }
}
tranmq
источник
это здорово, спасибо, мне понравилось, как ты объяснил Величину =)
liminal18
Это здорово, но что, если мы работаем с файлами или строками.
Талха,
21

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

UserPS
источник
18

Этот код 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))
Виктор Ян
источник
Можете ли вы объяснить, почему вы использовали set в строке "all_items = set (counter1.keys ()). Union (set (counter2.keys ()))".
Ghos3t
@ Ghos3t, то есть, чтобы получить список отдельных слов из обоих документов
Работа
7

Используя пример @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)))
Эндрю У
источник
5

Это простой 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]])
Нилани Алгириаге
источник
3
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 
* @author Xiao Ma
* mail : 409791952@qq.com
*
*/
  public class SimilarityUtil {

public static double consineTextSimilarity(String[] left, String[] right) {
    Map<String, Integer> leftWordCountMap = new HashMap<String, Integer>();
    Map<String, Integer> rightWordCountMap = new HashMap<String, Integer>();
    Set<String> uniqueSet = new HashSet<String>();
    Integer temp = null;
    for (String leftWord : left) {
        temp = leftWordCountMap.get(leftWord);
        if (temp == null) {
            leftWordCountMap.put(leftWord, 1);
            uniqueSet.add(leftWord);
        } else {
            leftWordCountMap.put(leftWord, temp + 1);
        }
    }
    for (String rightWord : right) {
        temp = rightWordCountMap.get(rightWord);
        if (temp == null) {
            rightWordCountMap.put(rightWord, 1);
            uniqueSet.add(rightWord);
        } else {
            rightWordCountMap.put(rightWord, temp + 1);
        }
    }
    int[] leftVector = new int[uniqueSet.size()];
    int[] rightVector = new int[uniqueSet.size()];
    int index = 0;
    Integer tempCount = 0;
    for (String uniqueWord : uniqueSet) {
        tempCount = leftWordCountMap.get(uniqueWord);
        leftVector[index] = tempCount == null ? 0 : tempCount;
        tempCount = rightWordCountMap.get(uniqueWord);
        rightVector[index] = tempCount == null ? 0 : tempCount;
        index++;
    }
    return consineVectorSimilarity(leftVector, rightVector);
}

/**
 * The resulting similarity ranges from −1 meaning exactly opposite, to 1
 * meaning exactly the same, with 0 usually indicating independence, and
 * in-between values indicating intermediate similarity or dissimilarity.
 * 
 * For text matching, the attribute vectors A and B are usually the term
 * frequency vectors of the documents. The cosine similarity can be seen as
 * a method of normalizing document length during comparison.
 * 
 * In the case of information retrieval, the cosine similarity of two
 * documents will range from 0 to 1, since the term frequencies (tf-idf
 * weights) cannot be negative. The angle between two term frequency vectors
 * cannot be greater than 90°.
 * 
 * @param leftVector
 * @param rightVector
 * @return
 */
private static double consineVectorSimilarity(int[] leftVector,
        int[] rightVector) {
    if (leftVector.length != rightVector.length)
        return 1;
    double dotProduct = 0;
    double leftNorm = 0;
    double rightNorm = 0;
    for (int i = 0; i < leftVector.length; i++) {
        dotProduct += leftVector[i] * rightVector[i];
        leftNorm += leftVector[i] * leftVector[i];
        rightNorm += rightVector[i] * rightVector[i];
    }

    double result = dotProduct
            / (Math.sqrt(leftNorm) * Math.sqrt(rightNorm));
    return result;
}

public static void main(String[] args) {
    String left[] = { "Julie", "loves", "me", "more", "than", "Linda",
            "loves", "me" };
    String right[] = { "Jane", "likes", "me", "more", "than", "Julie",
            "loves", "me" };
    System.out.println(consineTextSimilarity(left,right));
}
}
user1472571
источник
3

Простой код 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));
  }
nikoo28
источник
1
Это не «простой графический способ», а просто код. Хотя другие тоже допустили такую ​​же ошибку: /
Скрылар
-1

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

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

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

сочиться
источник