在工作中一直使用余弦相似度算法計算兩段文本的相似度和兩個用戶的相似度。一直弄不明白多維的余弦相似度公式是怎么推導來的。今天終於花費時間把公式推導出來,其實很簡單,都是高中學過的知識,只是很多年沒用了,都還給老師了。本文還通過一個例子演示如果使用余弦相似度計算兩段文本的相似度。
余弦函數在三角形中的計算公式為:

在直角坐標系中,向量表示的三角形的余弦函數是怎么樣的呢?下圖中向量a用坐標(x1,y1)表示,向量b用坐標(x2,y2)表示。

向量a和向量b在直角坐標中的長度為
向量a和向量b之間的距離我們用向量c表示,就是上圖中的黃色直線,那么向量c在直角坐標系中的長度為
,將a,b,c帶入三角函數的公式中得到如下的公式:





這是2維空間中余弦函數的公式,那么多維空間余弦函數的公式就是:


余弦相似度
余弦相似度量:計算個體間的相似度。
相似度越小,距離越大。相似度越大,距離越小。
假設有3個物品,item1,item2和item3,用向量表示分別為:
item1[1,1,0,0,1],
item2[0,0,1,2,1],
item3[0,0,1,2,0],
即五維空間中的3個點。用歐式距離公式計算item1、itme2之間的距離,以及item2和item3之間的距離,分別是:
item1-item2=

item2-item3=

用余弦函數計算item1和item2夾角間的余弦值為:


用余弦函數計算item2和item3夾角間的余弦值為:


由此可得出item1和item2相似度小,兩個之間的距離大(距離為7),item2和itme3相似度大,兩者之間的距離小(距離為1)。
余弦相似度算法:一個向量空間中兩個向量夾角間的余弦值作為衡量兩個個體之間差異的大小,余弦值接近1,夾角趨於0,表明兩個向量越相似,余弦值接近於0,夾角趨於90度,表明兩個向量越不相似。
余弦相似度
余弦相似度量:計算個體間的相似度。
相似度越小,距離越大。相似度越大,距離越小。
余弦相似度算法:一個向量空間中兩個向量夾角間的余弦值作為衡量兩個個體之間差異的大小,余弦值接近1,夾角趨於0,表明兩個向量越相似,余弦值接近於0,夾角趨於90度,表明兩個向量越不相似。
下面我們介紹使用余弦相似度計算兩段文本的相似度。思路:1、分詞;2、列出所有詞;3、分詞編碼;4、詞頻向量化;5、套用余弦函數計量兩個句子的相似度。
句子A:這只皮靴號碼大了。那只號碼合適。
句子B:這只皮靴號碼不小,那只更合適。
1、分詞:
使用結巴分詞對上面兩個句子分詞后,分別得到兩個列表:
listA=[‘這‘, ‘只‘, ‘皮靴‘, ‘號碼‘, ‘大‘, ‘了‘, ‘那‘, ‘只‘, ‘號碼‘, ‘合適‘]
listB=[‘這‘, ‘只‘, ‘皮靴‘, ‘號碼‘, ‘不小‘, ‘那‘, ‘只‘, ‘更合‘, ‘合適‘]
2、列出所有詞,將listA和listB放在一個set中,得到:
set={'不小', '了', '合適', '那', '只', '皮靴', '更合', '號碼', '這', '大'}
將上述set轉換為dict,key為set中的詞,value為set中詞出現的位置,即‘這’:1這樣的形式。
dict1={'不小': 0, '了': 1, '合適': 2, '那': 3, '只': 4, '皮靴': 5, '更合': 6, '號碼': 7, '這': 8, '大': 9},可以看出“不小”這個詞在set中排第1,下標為0。
3、將listA和listB進行編碼,將每個字轉換為出現在set中的位置,轉換后為:
listAcode=[8, 4, 5, 7, 9, 1, 3, 4, 7, 2]
listBcode=[8, 4, 5, 7, 0, 3, 4, 6, 2]
我們來分析listAcode,結合dict1,可以看到8對應的字是“這”,4對應的字是“只”,9對應的字是“大”,就是句子A和句子B轉換為用數字來表示。
4、對listAcode和listBcode進行oneHot編碼,就是計算每個分詞出現的次數。oneHot編號后得到的結果如下:
listAcodeOneHot = [0, 1, 1, 1, 2, 1, 0, 2, 1, 1]
listBcodeOneHot = [1, 0, 1, 1, 2, 1, 1, 1, 1, 0]
下圖總結了句子從分詞,列出所有詞,對分詞進行編碼,計算詞頻的過程


5、得出兩個句子的詞頻向量之后,就變成了計算兩個向量之間夾角的余弦值,值越大相似度越高。
listAcodeOneHot = [0, 1, 1, 1, 2, 1, 0, 2, 1, 1]
listBcodeOneHot = [1, 0, 1, 1, 2, 1, 1, 1, 1, 0]

根據余弦相似度,句子A和句子B相似度很高。
代碼如下:
1 import jieba 2 import math 3 s1 = '這只皮靴號碼大了。那只號碼合適' 4 s1_cut = [i for i in jieba.cut(s1, cut_all=True) if i != ''] 5 s2 = '這只皮靴號碼不小,那只更合適' 6 s2_cut = [i for i in jieba.cut(s2, cut_all=True) if i != ''] 7 print(s1_cut) 8 print(s2_cut) 9 word_set = set(s1_cut).union(set(s2_cut)) 10 print(word_set) 11 12 word_dict = dict() 13 i = 0 14 for word in word_set: 15 word_dict[word] = i 16 i += 1 17 print(word_dict) 18 19 s1_cut_code = [word_dict[word] for word in s1_cut] 20 print(s1_cut_code) 21 s1_cut_code = [0]*len(word_dict) 22 23 for word in s1_cut: 24 s1_cut_code[word_dict[word]]+=1 25 print(s1_cut_code) 26 27 s2_cut_code = [word_dict[word] for word in s2_cut] 28 print(s2_cut_code) 29 s2_cut_code = [0]*len(word_dict) 30 for word in s2_cut: 31 s2_cut_code[word_dict[word]]+=1 32 print(s2_cut_code) 33 34 # 計算余弦相似度 35 sum = 0 36 sq1 = 0 37 sq2 = 0 38 for i in range(len(s1_cut_code)): 39 sum += s1_cut_code[i] * s2_cut_code[i] 40 sq1 += pow(s1_cut_code[i], 2) 41 sq2 += pow(s2_cut_code[i], 2) 42 43 try: 44 result = round(float(sum) / (math.sqrt(sq1) * math.sqrt(sq2)), 2) 45 except ZeroDivisionError: 46 result = 0.0 47 print(result)
總結,例子很簡單,只是比較短短兩句話的相似度,算是機器學習入門的一個小案例。可以看到這是使用機器學習算法處理業務的通用步驟,先是取特征,然后特征向量化,最后代入數學公式中。
歡迎關注本人的公眾號:
python_ai_bigdata,了解更多關於大數據和機器學習的知識。