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

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

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

Я новичок.

10 ответов


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

  1. Julie loves me more than Linda loves me

  2. Jane likes me more than Julie loves me

мы хотим знать, как похожи эти тексты, чисто с точки зрения количества слов (и игнорируя порядок слов). Начнем с составления списка слов из обоих текстов:--6-->

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


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

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

Так... в двух измерениях: в отношении сходства текста это это означает, что мы сосредоточимся на двух различных терминах, скажем слова "Лондон" и "Париж", и мы посчитаем, сколько раз каждое из этих слов находится в каждом из двух документов, которые мы хотим сравнить. Это дает нам для каждого документа точку в плоскости x-y, например, если бы Doc1 имел Париж один раз, а Лондон четыре раза, точка в (1,4) представила бы этот документ (в отношении этой уменьшительной оценки документов). Или, говоря с точки зрения векторов, этот документ Doc1 будет стрелкой от начала координат до точки (1,4). Имея в виду этот образ, давайте подумаем о том, что значит быть похожим для двух документов и как это относится к векторам.

назад на наш X-Y самолет, если мы нарисуем эти гипотетические документы, мы видим, что когда они очень похожи, их векторы перекрываются (хотя некоторые векторы могут быть длиннее), и поскольку они начинают иметь меньше общего, эти векторы начинают расходиться, иметь больший угол между ними.

БАМ! путем измерения угол между векторами, мы можем получить хорошее представление о их схожести , и, чтобы сделать вещи еще проще, взяв Косинус этого угла у нас есть хороший 0 до 1 (или -1 до 1, в зависимости от того, что и как мы учитывайте) значение, которое указывает на это сходство. Чем меньше угол, тем больше (ближе к 1) значение Косинуса, а также тем больше сходство.

в крайнем случае, если Doc1 ссылается только на Париж, а Doc2-только на Лондон, документы не имеют абсолютно ничего общего. Doc1 будет иметь свой вектор на оси x, Doc2 на оси y, угол 90 градусов, Косинус 0. (Кстати вот что мы имеем в виду, когда говорим, что две вещи ортогональны одной другой)

добавление размеров:
С этим интуитивным чувством сходства, выраженным как небольшой угол (или большой Косинус), мы теперь можем представить вещи в 3 измерениях, скажем, вводя слово "Амстердам" в микс. И представьте себе, довольно хорошо, как документ с двумя ссылками каждого будет иметь вектор, идущий в определенном направлении, и мы можем видеть, как это направление будет сравниваться с документом, цитирующим Париж и Лондон 3 раза каждый, но не Амстердам так далее.. Как уже было сказано, мы можем попытаться представить это причудливое пространство для 10 или 100 городов, трудно нарисовать, но легко концептуализировать.

мы просто сказать несколько слов о самой формуле. Как уже говорилось, другие ссылки дают хорошую информацию о расчетах.

снова сначала в 2 измерениях. Формула косинуса угла между двумя векторами получена из тригонометрической разности (между углом 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]])

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));
}
}

простой код 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));
  }

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

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

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