比較する2つの非常に短いテキストを次に示します。
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
しかし、私たちは言葉そのものには興味がありません。カウントのこれら2つの垂直ベクトルのみに関心があります。たとえば、各テキストには「私」のインスタンスが2つあります。これら2つのベクトルの1つの関数、つまり、それらの間の角度のコサインを計算することにより、これら2つのテキストが互いにどれだけ近いかを決定します。
ここでも、2つのベクトルは次のとおりです。
a: [2, 0, 1, 1, 0, 2, 1, 1]
b: [2, 1, 1, 0, 1, 1, 1, 1]
それらの間の角度のコサインは約0.822です。
これらのベクトルは8次元です。コサイン類似性を使用する利点は、視覚化する人間の能力を超える質問を可能な質問に変換することです。この場合、これはゼロまたは完全な一致からの「距離」である約35度の角度と考えることができます。
「why」コサインの類似性が機能する理由(類似性の良い指標を提供する理由)ではなく、「how "計算されます(計算に使用される特定の操作)。後者に関心がある場合は、この投稿でダニエルが示したリファレンスと 関連SO質問 を参照してください。
方法とさらにその理由の両方を説明するには、最初は問題を単純化し、2次元でのみ機能することが役立ちます。これを2Dで取得すると、3次元で考えるのが簡単になり、もちろんより多くの次元で想像するのは難しくなりますが、それまでに線形代数を使用して数値計算を行い、用語で考えるのに役立ちますこれらを描画することはできませんが、n次元の線/ベクトル/「平面」/「球体」。
したがって、2次元:テキストの類似性に関して、これは2つの異なる用語、たとえば「ロンドン」と「パリ」に焦点を当てることを意味します、比較する2つのドキュメントのそれぞれでこれらの単語がそれぞれ何回見つかったかをカウントします。これにより、ドキュメントごとにx-y平面上の点が得られます。たとえば、Doc1にパリが1回、ロンドンに4回ある場合、(1,4)のポイントがこのドキュメントを提示します(ドキュメントのこの小さな評価に関して)。または、ベクトルの観点から言えば、このDoc1ドキュメントはOriginからポイント(1,4)に向かう矢印になります。このイメージを念頭に置いて、2つのドキュメントが類似していることの意味と、これがベクトルにどのように関係するかを考えてみましょう。
非常に類似したドキュメント(この限定されたディメンションセットに関して)は、パリへの参照の数が非常に多く、ロンドンへの参照の数が非常に多く、またはこれらの参照の比率が同じである可能性があります。パリへの2つの参照とロンドンへの8つの参照を含むドキュメントDoc2も非常に類似しており、テキストが長いか、都市名の反復性が同じである場合があります。たぶん、両方のドキュメントはロンドンについてのガイドであり、パリへの参照を渡すだけです(そして、その都市がいかにクールでないか;-).
現在、類似性の低いドキュメントには、両方の都市への参照が含まれている場合がありますが、比率は異なります。たぶんDoc2はパリを1回、ロンドンを7回しか引用しないでしょう。
xy平面に戻ると、これらの仮想文書を描画すると、それらが非常に類似している場合、それらのベクトルが重複することがわかります(ただし、一部のベクトルは)、および共通点が少なくなるにつれて、これらのベクトルは発散し始め、それらの間の角度が広くなります。
ベクトル間の角度を測定することにより、ベクトルの類似性を把握でき、この角度の余弦、何をどのように説明するかに応じて、この類似性を示すニース0対1または-1対1の値があります。角度が小さいほど、コサイン値は大きく(1に近く)なり、類似度も高くなります。
極端な場合、Doc1がパリのみを引用し、Doc2がロンドンのみを引用する場合、文書にはまったく共通点はありません。 Doc1は、x軸にベクトル、y軸にDoc2、角度90度、コサイン0を持ちます。この場合、これらのドキュメントは互いに直交していると言えます。
寸法の追加:
この類似性の直感的な感覚は、小さな角度(または大きな余弦)で表されます。たとえば、「アムステルダム」という言葉をミックスに取り入れることで、物事を3次元で想像できます。それぞれに2つの参照がある場合、特定の方向に向かうベクトルがあり、この方向がパリとロンドンをそれぞれ3回引用しているが、アムステルダムなどは引用していない文書と比較する方法を見ることができます。 10か100の都市のためのこの豪華なスペース。描くのは難しいですが、概念化するのは簡単です。
式自体についてのいくつかの言葉を言うだけでまとめます。私が言ったように、他の参考文献は計算に関する良い情報を提供します。
最初に2次元。 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]でこれを行う2つの方法
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));
}
2つのベクトルAとBは2D空間または3D空間に存在し、これらのベクトル間の角度はcos類似度です。
Cos 180 = -1であり、最小角度が0度の場合、角度がさらに大きい場合(最大180度に達することができます)。 cos 0 = 1は、ベクトルが互いに整列しているため、ベクトルが類似していることを意味します。
cos 90 = 0(ベクトルAとBがまったく類似していないと結論付けるのに十分であり、距離が負であるため、コサイン値は0から1になります。理にかなっています)