บทความคล้ายคลึงกันของโคไซน์ใน Wikipedia
คุณสามารถแสดงเวกเตอร์ที่นี่ (ในรายการหรืออะไรก็ได้) แล้วทำคณิตศาสตร์และให้เราดูว่ามันทำงานอย่างไร
ฉันเป็นมือใหม่
บทความคล้ายคลึงกันของโคไซน์ใน Wikipedia
คุณสามารถแสดงเวกเตอร์ที่นี่ (ในรายการหรืออะไรก็ได้) แล้วทำคณิตศาสตร์และให้เราดูว่ามันทำงานอย่างไร
ฉันเป็นมือใหม่
คำตอบ:
นี่คือข้อความสั้น ๆ สองข้อที่จะเปรียบเทียบ:
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 องศาซึ่งเป็น 'ระยะทาง' จากศูนย์หรือข้อตกลงที่สมบูรณ์แบบ
ฉันเดาว่าคุณสนใจที่จะรับข้อมูลเชิงลึกเกี่ยวกับ " ทำไม " ความเหมือนโคไซน์ทำงาน (ทำไมมันถึงบ่งบอกถึงความคล้ายคลึงกันได้ดี) แทนที่จะคำนวณ" วิธี " (การปฏิบัติการเฉพาะที่ใช้สำหรับการคำนวณ) ถ้าหากคุณสนใจของคุณอยู่ในหลังโปรดดูข้อมูลอ้างอิงที่ระบุโดยแดเนียลในโพสต์นี้เช่นเดียวกับกิจการที่เกี่ยวข้องกันดังนั้นคำถาม
ในการอธิบายทั้งวิธีการและสาเหตุอื่น ๆ เพิ่มเติมสาเหตุที่เป็นประโยชน์ในตอนแรกเพื่อทำให้ปัญหาง่ายขึ้นและใช้งานได้ในสองมิติเท่านั้น เมื่อคุณได้รับในรูปแบบ 2D มันจะง่ายกว่าที่จะคิดในสามมิติและแน่นอนยากที่จะจินตนาการในมิติอื่น ๆ อีกมากมาย แต่จากนั้นเราสามารถใช้พีชคณิตเชิงเส้นเพื่อทำการคำนวณเชิงตัวเลขและช่วยเราคิดในแง่ ของเส้น / เวกเตอร์ / "ระนาบ" / "ทรงกลม" ใน n มิติแม้ว่าเราจะวาดมันไม่ได้
ดังนั้นในสองมิติด้วยความคล้ายคลึงกันกับข้อความนี่หมายความว่าเราจะมุ่งเน้นไปที่คำสองคำที่แตกต่างกันพูดคำว่า "ลอนดอน" และ "ปารีส" และเราจะนับว่าคำเหล่านี้พบได้กี่ครั้งในแต่ละคำ เอกสารสองรายการที่เราต้องการเปรียบเทียบ สิ่งนี้ทำให้เรามีจุดหนึ่งในระนาบ xy ตัวอย่างเช่นหาก Doc1 มีปารีสครั้งเดียวและลอนดอนสี่ครั้งจุดที่ (1,4) จะนำเสนอเอกสารนี้ (เกี่ยวกับการประเมินเอกสารฉบับย่อนี้) หรือหากพูดในแง่ของเวกเตอร์เอกสาร Doc1 นี้จะเป็นลูกศรที่ไปจากจุดเริ่มต้นไปยังจุดที่หนึ่ง ด้วยภาพนี้ในใจลองคิดดูว่าเอกสารสองฉบับมีความคล้ายคลึงกันอย่างไรและมันเกี่ยวข้องกับเวกเตอร์อย่างไร
เอกสารที่คล้ายกันมาก (อีกครั้งเกี่ยวกับชุดข้อมูลที่มีขนาด จำกัด นี้) จะมีหมายเลขอ้างอิงไปยังปารีสจำนวนเท่ากันและมีจำนวนอ้างอิงถึงลอนดอนเท่ากันหรืออาจจะมีอัตราส่วนเดียวกันกับเอกสารอ้างอิงเหล่านี้ เอกสาร Doc2 ที่มี 2 refs ไปปารีสและ 8 refs ไปลอนดอนก็จะคล้ายกันมากอาจมีเพียงข้อความยาวหรือซ้ำชื่อของเมือง แต่ในสัดส่วนเดียวกัน บางทีเอกสารทั้งสองเป็นแนวทางเกี่ยวกับลอนดอนเพียงส่งผ่านการอ้างอิงถึงปารีสเท่านั้น (และวิธีที่เมืองที่ไม่สงบเป็น ;-) แค่ล้อเล่น !!!
ตอนนี้เอกสารที่คล้ายกันน้อยกว่าอาจรวมถึงการอ้างอิงไปยังเมืองทั้งสอง แต่ในสัดส่วนที่แตกต่างกัน บางที Doc2 อาจอ้างถึงปารีสเพียงครั้งเดียวและลอนดอนถึงเจ็ดครั้ง
กลับไปที่ระนาบ xy ของเราถ้าเราวาดเอกสารสมมุติเหล่านี้เราจะเห็นว่าเมื่อพวกมันคล้ายกันมากเวกเตอร์ของพวกเขาทับซ้อนกัน (แม้ว่าเวกเตอร์บางตัวอาจยาวกว่า) และเมื่อพวกเขาเริ่มมีสามัญน้อยกว่าเวกเตอร์เหล่านี้ก็เริ่มแตก เพื่อให้ได้มุมที่กว้างขึ้นระหว่างพวกเขา
โดยการวัดมุมระหว่างเวกเตอร์เราจะได้ความคิดที่ดีเกี่ยวกับความคล้ายคลึงกันของพวกมันและทำให้สิ่งต่าง ๆ ง่ายขึ้นโดยการเอาโคไซน์ของมุมนี้เรามีค่า 0 ถึง 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))
สูตรนี้ดูคล้ายกับสูตรผลิตภัณฑ์ dot:
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):
(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 : 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 มุมระหว่างเวกเตอร์เหล่านั้นคือความคล้ายคลึงกัน
หากมุมมีมากขึ้น (สามารถเข้าถึงสูงสุด 180 องศา) ซึ่งเป็น Cos 180 = -1 และมุมต่ำสุดคือ 0 องศา cos 0 = 1 หมายถึงเวกเตอร์อยู่ในแนวเดียวกันและดังนั้นเวกเตอร์จึงมีความคล้ายคลึงกัน
cos 90 = 0 (ซึ่งเพียงพอที่จะสรุปได้ว่าเวกเตอร์ A และ B ไม่เหมือนกันเลยและเนื่องจากระยะทางไม่สามารถลบได้ค่าโคไซน์จะอยู่ระหว่าง 0 ถึง 1 ดังนั้นมุมที่มากกว่าหมายถึงการลดความคล้ายคลึงกัน มีเหตุผล)