Czy ktoś może podać przykład podobieństwa cosinus w bardzo prosty, graficzny sposób?


201

Cosinus Podobieństwo artykuł na Wikipedii

Czy możesz tutaj pokazać wektory (na liście lub coś), a następnie wykonać matematykę i zobaczyć, jak to działa?

Jestem początkujący.


1
Spróbuj podnieść kopię „Geometrii i znaczenia” autorstwa Widdows ( press.uchicago.edu/presssite/… ), przeczytałem ją jakiś czas temu i żałowałem, że nie miałem jej wiele lat temu, świetny tekst wprowadzający.
Nathan Howell

Odpowiedzi:


463

Oto dwa bardzo krótkie teksty do porównania:

  1. Julie loves me more than Linda loves me

  2. Jane likes me more than Julie loves me

Chcemy wiedzieć, jak podobne są te teksty, wyłącznie pod względem liczby słów (i ignorując ich kolejność). Zaczynamy od sporządzenia listy słów z obu tekstów:

me Julie loves Linda than more likes Jane

Teraz liczymy, ile razy każde z tych słów pojawia się w każdym tekście:

   me   2   2
 Jane   0   1
Julie   1   1
Linda   1   0
likes   0   1
loves   2   1
 more   1   1
 than   1   1

Jednak nie interesują nas same słowa. Interesują nas tylko te dwa wektory pionowe zliczeń. Na przykład w każdym tekście są dwa wystąpienia „ja”. Zamierzamy zadecydować, jak blisko są te dwa teksty, obliczając jedną funkcję tych dwóch wektorów, mianowicie cosinus kąta między nimi.

Te dwa wektory to znowu:

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

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

Cosinus kąta między nimi wynosi około 0,822.

Wektory te są 8-wymiarowe. Zaletą stosowania podobieństwa cosinus jest to, że przekształca pytanie, które jest poza ludzką zdolnością do wizualizacji, na takie, które może być. W tym przypadku można to traktować jako kąt około 35 stopni, co stanowi pewną „odległość” od zera lub idealną zgodność.


12
Właśnie tego szukałem. Dokładnie. Czy jest to uważane za najprostszą formę „modelu przestrzeni wektorowej”?
TIMEX,

2
Bardzo się cieszę, że ci się przydało, Alex. Przepraszam za opóźnienie w odpowiedzi. Od dłuższego czasu nie odwiedziłem StackOverflow. W rzeczywistości jest to przykład „wewnętrznej przestrzeni produktu”. Podstawowa dyskusja na temat wikipedii.
Bill Bell

1
Czy istnieje sposób na znormalizowanie długości dokumentu?
sinθ

1
Musisz użyć normalizacji długości, a wcześniej spróbuj użyć ważenia częstotliwości dziennika dla wszystkich wektorów terminów. Jeśli masz już do czynienia ze znormalizowanymi wektorami, to jest to iloczyn
skalarny

4
Bardziej szczegółowy przykład z wykorzystaniem normalizacji długości i TF-IDF: site.uottawa.ca/~diana/csi4107/cosine_tf_idf_example.pdf
Mike B.

121

Zgaduję, że jesteś bardziej zainteresowany uzyskaniem wglądu w „ dlaczego ” podobieństwo cosinus działa (dlaczego zapewnia dobrą wskazówkę podobieństwa), a nie „ jak ” jest obliczane (konkretne operacje użyte do obliczeń). Jeśli interesuje Cię to drugie, zapoznaj się z odnośnikiem wskazanym przez Daniela w tym poście, a także związanym z nim pytaniem SO .

Aby wyjaśnić, w jaki sposób, a jeszcze bardziej, dlaczego, na początku przydatne jest uproszczenie problemu i praca tylko w dwóch wymiarach. Po otrzymaniu tego w 2D łatwiej jest myśleć o tym w trzech wymiarach i oczywiście trudniej jest sobie wyobrazić w wielu innych wymiarach, ale do tego czasu możemy użyć algebry liniowej do obliczeń numerycznych, a także pomóc nam myśleć w kategoriach linii / wektorów / „płaszczyzn” / „sfer” w n wymiarach, nawet jeśli nie możemy ich narysować.

Zatem w dwóch wymiarach : w odniesieniu do podobieństwa tekstu oznacza to, że skupilibyśmy się na dwóch odrębnych terminach, wypowiadając słowa „Londyn” i „Paryż”, i policzylibyśmy, ile razy każde z tych słów znajduje się w każdym dwa dokumenty, które chcemy porównać. To daje nam, dla każdego dokumentu, punkt na płaszczyźnie xy. Na przykład, jeśli Doc1 miał kiedyś Paryż, a czterokrotnie Londyn, punkt w punkcie (1,4) przedstawiłby ten dokument (w odniesieniu do tej niewielkiej oceny dokumentów). Lub, mówiąc w kategoriach wektorów, ten dokument Doc1 byłby strzałką przechodzącą od początku do punktu (1,4). Mając na uwadze ten obraz, zastanówmy się, co to znaczy, że dwa dokumenty są podobne i jak to odnosi się do wektorów.

BARDZO podobne dokumenty (ponownie w odniesieniu do tego ograniczonego zestawu wymiarów) miałyby taką samą liczbę odniesień do Paryża ORAZ taką samą liczbę odniesień do Londynu, a może mogłyby mieć taki sam stosunek tych odniesień. Dokument, Doc2, z 2 referencjami do Paryża i 8 referencjami do Londynu, również byłby bardzo podobny, tylko z dłuższym tekstem lub w jakiś sposób bardziej powtarzalnymi nazwami miast, ale w tej samej proporcji. Być może oba dokumenty są przewodnikami po Londynie, tylko mimochodem nawiązując do Paryża (i jak niemiłe jest to miasto ;-) Żartowałem !!!.

Teraz mniej podobne dokumenty mogą również zawierać odniesienia do obu miast, ale w różnych proporcjach. Może Doc2 zacytuje Paryż tylko raz, a Londyn siedem razy.

Wracając do naszej płaszczyzny xy, jeśli narysujemy te hipotetyczne dokumenty, widzimy, że gdy są BARDZO podobne, ich wektory nachodzą na siebie (chociaż niektóre wektory mogą być dłuższe), a ponieważ zaczynają mieć mniej wspólnych, wektory te zaczynają się rozchodzić, mieć między nimi szerszy kąt.

Mierząc kąt między wektorami, możemy uzyskać dobre wyobrażenie o ich podobieństwie , a dla uproszczenia rzeczy, biorąc Cosinus tego kąta, mamy niezłą wartość od 0 do 1 lub od -1 do 1, która wskazuje na to podobieństwo, w zależności od tego, co i jak rozliczamy. Im mniejszy kąt, tym większa (bliższa 1) wartość cosinus, a także wyższe podobieństwo.

Skrajnie, jeśli Doc1 przytacza tylko Paryż, a Doc2 tylko Londyn, dokumenty nie mają absolutnie nic wspólnego. Doc1 miałby wektor na osi x, Doc2 na osi y, kąt 90 stopni, Cosinus 0. W tym przypadku powiedzielibyśmy, że te dokumenty są do siebie prostopadłe.

Dodawanie wymiarów :
Dzięki intuicyjnemu wyczuciu podobieństwa wyrażonemu jako mały kąt (lub duży cosinus) możemy teraz wyobrazić sobie rzeczy w 3 wymiarach, powiedzmy, dodając słowo „Amsterdam” do mieszanki i całkiem dobrze wyobrażając sobie, jak dokument z dwoma odniesienia do każdego z nich miałyby wektor zmierzający w określonym kierunku, i możemy zobaczyć, jak ten kierunek porównałby się z dokumentem trzykrotnie wymieniającym Paryż i Londyn, ale nie Amsterdamem itp. Jak już powiedziano, możemy spróbować wyobrazić sobie tę fantazję miejsce na 10 lub 100 miast. Trudno go narysować, ale jest łatwy do konceptualizacji.

Podsumowując, powiem kilka słów o samej formule . Jak już powiedziałem, inne odniesienia dostarczają dobrych informacji o obliczeniach.

Najpierw w dwóch wymiarach. Wzór na cosinus kąta między dwoma wektorami pochodzi z różnicy trygonometrycznej (między kątem a i kątem b):

cos(a - b) = (cos(a) * cos(b)) + (sin (a) * sin(b))

Ta formuła wygląda bardzo podobnie do formuły produktu kropkowego:

Vect1 . Vect2 =  (x1 * x2) + (y1 * y2)

gdzie cos(a)odpowiada na xwartości i sin(a)na ywartości dla pierwszego wektora, itp Jedynym problemem jest to, że x, yitp nie są dokładnie cosi sinwartości, dla należy czytać na okręgu jednostkowym te wartości. Tam właśnie zaczyna się mianownik formuły: dzieląc przez iloczyn długości tych wektorów, współrzędne xi ynormalizują się.


25

Oto moja implementacja w 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));
        }
    }
}

to jest niesamowite, dziękuję. Kochałem, jak wyjaśniłeś Magnitude =)
liminal18,

To świetnie, ale co jeśli pracujemy z plikami lub łańcuchami.
Talha,

21

Dla uproszczenia redukuję wektor aib:

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

Następnie podobieństwo cosinus (Theta):

 (Theta) = (1*1 + 1*0 + 0*1)/sqrt((1^2 + 1^2))* sqrt((1^2 + 1^2)) = 1/2 = 0.5

wtedy odwrotność cos 0,5 wynosi 60 stopni.


18

Ten kod Pythona to moja szybka i nieprzyzwoita próba zaimplementowania algorytmu:

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

Czy możesz wyjaśnić, dlaczego użyłeś set w wierszu „all_items = set (counter1.keys ()). Union (set (counter2.keys ()))”.
Ghos3t

@ Ghos3t, czyli uzyskać listę odrębnych słów z obu dokumentów
Jobs

7

Na przykładzie @Bill Bell można to zrobić na dwa sposoby w [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)))

lub wykorzystując wydajność metody crossprod () ...

e = crossprod(a, b) / (sqrt(crossprod(a, a)) * sqrt(crossprod(b, b)))

5

Jest to prosty Pythonkod, który implementuje podobieństwo cosinus.

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

Prosty kod JAVA do obliczania podobieństwa cosinus

/**
   * 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
To nie jest „prosty, graficzny sposób”, ale tylko kod. Chociaż inni też popełnili ten sam błąd: /
Skrylar,

-1

Dwa wektory A i B istnieją w przestrzeni 2D lub przestrzeni 3D, kąt między tymi wektorami jest podobny.

Jeśli kąt jest większy (może osiągnąć maks. 180 stopni), co wynosi Cos 180 = -1, a minimalny kąt wynosi 0 stopni. cos 0 = 1 oznacza, że ​​wektory są wyrównane względem siebie, a zatem wektory są podobne.

cos 90 = 0 (co wystarcza do stwierdzenia, że ​​wektory A i B wcale nie są podobne, a ponieważ odległość nie może być ujemna, wartości cosinus będą wynosić od 0 do 1. W związku z tym większy kąt implikuje zmniejszenie podobieństwa (wizualizacja również to ma sens)

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.