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


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

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

Я новичок.

10 163

10 ответов:

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

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

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

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

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

очень похожие документы (опять же в отношении этого ограниченного набора измерений) будут иметь то же самое количество ссылок на Париж, и то же самое количество ссылок на Лондон, или, может быть, они могут иметь то же соотношение этих ссылок (скажем, документ Doc2, с 2 ссылками на Париж и 8 ссылками на Лондон, также будет иметь одинаковое соотношение этих ссылок). очень похоже, только, может быть, более длинный текст или как-то более повторяющиеся названия городов, но в той же пропорции: возможно, оба документа являются путеводителями по Лондону, только делая мимолетные ссылки на Париж (и как не круто, что город; -) просто шучу!!!). Теперь менее похожие документы также могут включать ссылки на оба города, но в разных пропорциях, возможно, Doc2 будет ссылаться только на Париж один раз и Лондон 7 раз.

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

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

вот что мы имеем в виду, когда говорим, что две вещи ортогональны одной еще один)

добавление размеров:
Благодаря этому интуитивному ощущению сходства, выраженному в виде малого угла (или большого Косинуса), мы теперь можем представить себе вещи в 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

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