누군가 매우 간단한 그래픽 방식으로 코사인 유사성의 예를 제시 할 수 있습니까?


201

Wikipedia의 코사인 유사성 기사

여기에 벡터를 (목록 또는 무언가로) 보여준 다음 수학을해서 어떻게 작동하는지 볼 수 있습니까?

나는 초보자입니다.


1
Widdows ( press.uchicago.edu/presssite/… ) 의 Geometry and Meaning (지오메트리 및 의미) 사본을 선택 하여 잠시 후 읽어 보았고 몇 년 전에이 책을 소개하기를 원했습니다.
Nathan Howell

답변:


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

우리는 단어 자체에 관심이 없습니다. 우리는이 두 개의 수직 벡터 수에만 관심이 있습니다. 예를 들어, 각 텍스트에는 'me'의 두 인스턴스가 있습니다. 우리는이 두 벡터의 하나의 함수, 즉 그 사이의 각도의 코사인을 계산하여이 두 텍스트가 서로 얼마나 가까운 지 결정할 것입니다.

두 벡터는 다음과 같습니다.

a: [2, 0, 1, 1, 0, 2, 1, 1]

b: [2, 1, 1, 0, 1, 1, 1, 1]

그들 사이의 각도의 코사인은 약 0.822입니다.

이 벡터는 8 차원입니다. 코사인 유사성을 사용한다는 장점은 인간 능력을 넘어서는 질문을 시각화 할 수있는 것으로 변환한다는 것입니다. 이 경우, 이것을 0도 또는 완벽한 일치로부터 '거리'인 약 35 도의 각도로 생각할 수 있습니다.


12
이것이 바로 내가 찾던 것입니다. 바로 그거죠. 이것이 "벡터 공간 모델"의 가장 단순한 형태로 간주됩니까?
TIMEX

2
알렉스, 이것이 당신에게 도움이 되었기 때문에 정말 기쁩니다. 응답이 지연되어 죄송합니다. 한동안 StackOverflow를 방문하지 않았습니다. 실제로 이것은 "내부 제품 공간"의 예입니다. 위키 백과에 대한 기본 토론이 있습니다.
Bill Bell

1
문서 길이를 정규화하는 방법이 있습니까?
sinθ

1
길이 정규화를 사용해야하며 그 전에 모든 항 벡터에 대해 로그 빈도 가중치를 사용하십시오. 이미 정규화 된 벡터를 다루는 경우 AB의 내적입니다.
Ali Gajani

4
길이 정규화 및 TF-IDF를 사용하는 자세한 예 : site.uottawa.ca/~diana/csi4107/cosine_tf_idf_example.pdf
Mike B.

121

코사인 유사성이 어떻게 계산 되는지 (계산에 사용 된 특정 연산 )보다는 " "코사인 유사성이 작동하는지 (유사성에 대한 좋은 표시를 제공하는 이유)에 대한 통찰력을 얻는 데 더 관심이 있다고 생각합니다 . 후자에 관심이 있다면,이 게시물에 Daniel이 표시 한 참조 및 관련 SO Question을 참조하십시오 .

방법과 이유를 설명하기 위해 처음에는 문제를 단순화하고 2 차원에서만 작동하는 것이 유용합니다. 이것을 2D로 가져 오면 3 차원으로 생각하기가 더 쉽고 물론 더 많은 차원으로 상상하기는 어렵지만, 선형 대수를 사용하여 숫자 계산을 수행하고 용어로 생각하는 데 도움을 줄 수 있습니다 우리가 이것을 그릴 수는 없지만 n 차원의 선 / 벡터 / "평면"/ "구"의 집합.

따라서 두 가지 차원에서 : 텍스트 유사성과 관련하여 이것은 "London"과 "Paris"라는 두 가지 용어에 중점을두고 각 단어가 몇 번이나 나오는지를 계산합니다. 비교하고자하는 두 문서. 이것은 각 문서마다 xy 평면의 한 점을 제공합니다. 예를 들어, Doc1이 파리를 한 번, 런던을 네 번 가졌다면 (1,4) 지점이이 문서를 제시 할 것입니다 (문서의이 작은 평가와 관련하여). 또는 벡터 측면에서 말하면이 Doc1 문서는 원점에서 점 (1,4)으로가는 화살표입니다. 이 이미지를 염두에두고 두 문서가 유사하다는 것이 무슨 의미이며 이것이 벡터와 어떤 관련이 있는지 생각해 봅시다.

매우 유사한 문서 (이 제한된 차원 세트와 관련하여)는 파리에 대한 참조 수가 동일하거나 런던에 대한 참조 수가 동일하거나 이러한 참조의 비율이 동일 할 수 있습니다. 파리에 대한 2 개의 참조, 런던에 대한 8 개의 참조를 가진 문서 Doc2도 매우 유사하지만, 더 긴 텍스트 또는 도시 이름에 대해 더 반복적이지만 동일한 비율로만 가능합니다. 어쩌면 두 문서 모두 런던에 대한 안내 일뿐입니다.

이제 덜 유사한 문서에도 두 도시에 대한 참조가 포함될 수 있지만 비율은 다릅니다. 아마도 Doc2는 파리를 한 번만, 런던을 일곱 번만 인용했을 것입니다.

xy 평면으로 돌아가서이 가상의 문서를 그리면 매우 유사 할 때 벡터가 겹치며 (일부 벡터는 더 길어질 수 있지만) 공통성이 떨어지기 시작하면 벡터가 갈라지기 시작합니다. 그들 사이에 더 넓은 각도를 갖도록

벡터 사이의 각도 를 측정함으로써 , 우리는 그들의 유사성에 대한 좋은 아이디어를 얻을 수 있고, 이 각도 의 코사인 을 취함으로써, 일을 더 쉽게 만들 수 있습니다 , 우리는 좋은 0 대 1 또는 -1 대 1 값을 나타냅니다 우리가 무엇을 어떻게 설명 하느냐에 따라 이러한 유사성. 각도가 작을수록 코사인 값이 커지고 (1에 가까워짐) 유사성이 높아집니다.

Doc1이 파리 만 인용하고 Doc2가 런던 만 인용한다면, 문서는 공통점이 없습니다. Doc1은 x 축에 벡터, y 축에 Doc2, 각도 90도, 코사인 0을 갖습니다.이 경우 우리는이 문서들이 서로 직교한다고 말할 것입니다.

치수 추가 :
작은 각도 (또는 큰 코사인)로 표현 된 유사성에 대한 직관적 인 느낌으로 이제 "Amsterdam"이라는 단어를 혼합하여 3 차원으로 사물을 상상할 수 있습니다. 각각에 대한 참조는 특정 방향으로 진행되는 벡터를 가지며,이 방향이 파리와 런던을 각각 3 회 인용하지만 암스테르담 등을 인용하지 않는 문서와 어떻게 비교되는지 볼 수 있습니다. 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)yxycossinxy


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

이것은 당신이 Magnitude =)를 설명하는 방법을 사랑해 주셔서 감사합니다
liminal18

훌륭하지만 파일이나 문자열로 작업한다면 어떨까요?
Talha

21

간단하게하기 위해 벡터 a와 b를 줄입니다.

Let :
    a : [1, 1, 0]
    b : [1, 0, 1]

그런 다음 코사인 유사성 (Theta) :

 (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 도입니다.


18

이 파이썬 코드는 알고리즘을 구현하려는 빠르고 더러운 시도입니다.

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

"all_items = set (counter1.keys ()). union (set (counter2.keys ()))"줄에서 set을 사용한 이유를 설명 할 수 있습니까?
Ghos3t

@ Ghos3t, 즉 두 문서에서 구별되는 단어 목록을 얻는 것입니다.
Jobs

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

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

1
"간단하고 그래픽적인 방법"이 아니라 코드입니다. 다른 사람들도 같은 오류를
만들었지 만

-1

2 개의 벡터 A 및 B는 2D 공간 또는 3D 공간에 존재하며, 이들 벡터 사이의 각도는 cos 유사성이다.

각도가 더 크면 (최대 180도에 도달 할 수 있음) Cos 180 = -1이고 최소 각도는 0 도입니다. cos 0 = 1은 벡터가 서로 정렬되어 벡터가 유사 함을 의미합니다.

cos 90 = 0 (벡터 A와 B가 전혀 유사하지 않다는 결론에 충분하며 거리가 음수 일 수 없으므로 코사인 값은 0에서 1 사이입니다. 따라서 각도가 많을수록 유사성이 감소 함을 의미합니다 (시각화도 함) 말이된다)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.