Статья о сходстве косинуса в Википедии
Можете ли вы показать здесь векторы (в списке или что-то еще) а затем выполните математику и посмотрим, как она работает?
Я новичок.
Статья о сходстве косинуса в Википедии
Можете ли вы показать здесь векторы (в списке или что-то еще) а затем выполните математику и посмотрим, как она работает?
Я новичок.
Вот два очень коротких текста для сравнения:
Julie loves me more than Linda loves me
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 градусов, что является некоторым "расстоянием" от нуля или идеальным соглашением.
Я предполагаю, что вы больше заинтересованы в том, чтобы получить представление о том, "почему" работает косинусное сходство (почему оно дает хорошее представление о сходстве), а не о том, "как" оно рассчитывается (конкретные операции, используемые для вычисления). Если вас интересует последнее, см. Ссылку, указанную Даниэлем в этом посте, а также соответствующий вопрос 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. В этом случае мы бы сказали, что эти документы ортогональны друг другу.
Добавление размеров:
Благодаря этому интуитивному ощущению сходства, выраженному в виде небольшого угла (или большого косинуса), мы можем теперь представить вещи в 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)
значению y
для первого вектора и т.д. Единственная проблема заключается в том, что x
, y
и т.д. не являются точно значениями cos
и sin
для этих значений нужно читать на круге блока. То, где знаменатель формулы вступает в силу: путем деления на произведение длины этих векторов координаты x
и y
нормализуются.
Здесь моя реализация в С#.
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):
(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 : [email protected]
*
*/
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. Следовательно, больший угол подразумевает уменьшение сходства (также визуализируя его имеет смысл)