5.詞項相似度分析
將從分析詞項相似度入手,或者更准確的說,將從分析單獨的單詞標識相似度入手。雖然詞項相似度分析沒有在實際應用中大量使用,但是仍可以作為理解文本相似度分析的一個很好的出發點。當然,一些應用程序和用例(如自動填充程序、拼寫檢查和文本校正器)也會使用詞項相似度分析中的部分技術來糾正拼寫錯誤的詞項。在這里,將選取一些單詞並計算它們之間的相似度,然后應用不同的單詞標識方法了距離度量進行相似度分析。將使用如下單詞標識方法:
- 字符向量化。
- 字符(Bag of Character)袋向量化。
字符向量化是非常簡單的過程,它將詞項的每個字符映射到唯一的對應數字。可以使用以下代碼來實現:
import
numpy as np
from
scipy.stats
import
itemfreq
def
vectorize_terms(terms):
terms
=
[term.lower()
for
term
in
terms]
terms
=
[np.array(
list
(term))
for
term
in
terms]
terms
=
[np.array([
ord
(char)
for
char
in
term])
for
term
in
terms]
return
terms
|
該函數輸入一列單詞或詞項,並返回相應的字符向量。字符袋向量化與詞袋模型非常類似,區別是在這里會計算單詞中每個字符的頻率。字符袋向量化過程中不考慮字符序列或單詞序列。以下函數可以幫助我們完成上述過程:
from
scipy.stats
import
itemfreq
def
boc_term_vectors(word_list):
word_list
=
[word.lower()
for
word
in
word_list]
unique_chars
=
np.unique(
np.hstack([
list
(word)
for
word
in
word_list]))
word_list_term_counts
=
[{char: count
for
char, count
in
itemfreq(
list
(word))}
for
word
in
word_list]
boc_vectors
=
[np.array([
int
(word_term_counts.get(char,
0
))
for
char
in
unique_chars])
for
word_term_counts
in
word_list_term_counts]
return
list
(unique_chars), boc_vectors
|
在這個函數中,輸入的單詞或詞項,然后從所有單詞中提取出特殊字符。與詞袋模型類似,這就是特征列表,不同的是,在詞袋模型中特殊詞(而不是字符)才是特征。有了 unique_chars 的這個列表之后,就可以得到所有單詞中每個字符的計數,並構建字符袋向量。
可以在下面的代碼段中看到前述函數的應用。將使用四個示例詞項,並計算它們之間的相似度:
root
=
'Believe'
term1
=
'beleive'
term2
=
'bargain'
term3
=
'Elephant'
terms
=
[root, term1, term2, term3]
vec_root, vec_term1, vec_term2, vec_term3
=
vectorize_terms(terms)
print
(
'''
root: {}
term1: {}
term2: {}
term3: {}
'''
.
format
(vec_root, vec_term1, vec_term2, vec_term3))
|
結果:
root: [
98
101
108
105
101
118
101
]
term1: [
98
101
108
101
105
118
101
]
term2: [
98
97
114
103
97
105
110
]
term3: [
101
108
101
112
104
97
110
116
]
|
features, (boc_root, boc_term1, boc_term2, boc_term3)
=
boc_term_vectors(terms)
print
(
'Features:'
, features)
print
(
'''
root: {}
term1: {}
term2: {}
term3: {}
'''
.
format
(boc_root, boc_term1, boc_term2, boc_term3))
|
結果:
Features: [
'a'
,
'b'
,
'e'
,
'g'
,
'h'
,
'i'
,
'l'
,
'n'
,
'p'
,
'r'
,
't'
,
'v'
]
root: [
0
1
3
0
0
1
1
0
0
0
0
1
]
term1: [
0
1
3
0
0
1
1
0
0
0
0
1
]
term2: [
2
1
0
1
0
1
0
1
0
1
0
0
]
term3: [
1
0
2
0
1
0
1
1
1
0
1
0
]
|
可以看到如何輕松的將文本轉換為數字向量表示形式。現在,要使用幾個距離度量來計算根詞和前面代碼段中其他三個詞之間的相似度。有很多距離指標可以用來計算和度量相似度。將介紹以下五個度量:
- 漢明距離(Hamming distance)。
- 曼哈頓距離(Manhattan distance)。
- 歐幾里距離(Euclidean distance)。
- 萊文斯坦編輯距離(Levenshtein edit distance)。
- 余弦距離(Cosin額 distance)和相似度。
下面將介紹每個距離度量的概念,並使用 numpy 數組來實現必要的計算和數學公式。之后,通過計算示例詞項的相似度來具體介紹每個距離度量概念。首先,設置一些必要的變量以存儲根詞項,其他詞項以及它們的向量化表示,如下代碼所示:
# build the term vectors here
root_term
=
root
root_vector
=
vec_root
root_boc_vector
=
boc_root
terms
=
[term1, term2, term3]
vector_terms
=
[vec_term1, vec_term2, vec_term3]
boc_vector_terms
=
[boc_term1, boc_term2, boc_term3]
|
現在,以及做好了計算相似度度量的准備,將使用詞項及其向量表示來測量相似度。
漢明距離
漢明距離在信息和通信領域官方使用,它是非常流行的距離度量方法。漢明距離是兩個長度相等的字符之間的測量距離。它的正式定義是兩個長度相等的字符串之間互異字符或符號的位置的數量。考慮長度為 n 的兩個詞項 u 和 v,漢明距離的數學表達式為:
可以通過將不匹配的數目除以詞項的總數來獲得歸一化的漢明距離,如下所示:
其中,n 表示詞項的長度。
以下函數可以計算兩個詞項之間的漢明距離,還可以計算歸一化距離:
def
hamming_distance(u, v, norm
=
False
):
if
u.shape !
=
v.shape:
raise
ValueError(
'The vectors must have equal lengths.'
)
return
(u !
=
v).
sum
()
if
not
norm
else
(u !
=
v).mean()
|
現在,使用以下代碼來衡量詞根項和其他詞項之間的漢明距離:
# HAMMING DISTANCE DEMO
for
term, vector_term
in
zip
(terms, vector_terms):
print
(
'Hamming distance between root: {} and term: {} is {}'
.
format
(root_term,
term,
hamming_distance(root_vector, vector_term, norm
=
False
)))
|
結果:
Hamming distance between root: Believe
and
term: beleive
is
2
Hamming distance between root: Believe
and
term: bargain
is
6
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
ValueError Traceback (most recent call last)
<ipython
-
input
-
5
-
be6181236c26>
in
<module>()
3
print
(
'Hamming distance between root: {} and term: {} is {}'
.
format
(root_term,
4
term,
-
-
-
-
>
5
hamming_distance(root_vector, vector_term, norm
=
False
)))
6
7
<ipython
-
input
-
1
-
2034efa805fa
>
in
hamming_distance(u, v, norm)
1
def
hamming_distance(u, v, norm
=
False
):
2
if
u.shape !
=
v.shape:
-
-
-
-
>
3
raise
ValueError(
'The vectors must have equal lengths.'
)
4
return
(u !
=
v).
sum
()
if
not
norm
else
(u !
=
v).mean()
ValueError: The vectors must have equal lengths.
|
for
term, vector_term
in
zip
(terms, vector_terms):
print
(
'Normalized Hamming distance between root: {} and term: {} is {}'
.
format
(root_term,
term,
round
(hamming_distance(root_vector, vector_term, norm
=
True
),
2
)))
|
Normalized Hamming distance between root: Believe
and
term: beleive
is
0.29
Normalized Hamming distance between root: Believe
and
term: bargain
is
0.86
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
ValueError Traceback (most recent call last)
<ipython
-
input
-
6
-
2474b1c55a95
>
in
<module>()
2
print
(
'Normalized Hamming distance between root: {} and term: {} is {}'
.
format
(root_term,
3
term,
-
-
-
-
>
4
round
(hamming_distance(root_vector, vector_term, norm
=
True
),
2
)))
5
6
<ipython
-
input
-
1
-
2034efa805fa
>
in
hamming_distance(u, v, norm)
1
def
hamming_distance(u, v, norm
=
False
):
2
if
u.shape !
=
v.shape:
-
-
-
-
>
3
raise
ValueError(
'The vectors must have equal lengths.'
)
4
return
(u !
=
v).
sum
()
if
not
norm
else
(u !
=
v).mean()
ValueError: The vectors must have equal lengths.
|
從前面的輸出可以看出,在忽略大小寫的情況下,'Believe' 和 'beleive' 最為相似,其漢明距離為 2 或 0.29 (歸一化漢明距離),與 'bargain' 一詞的漢明距離值為 6 或 0.86 (這里,數值越小,詞項越相似)。詞項 'Elephant' 則引發了程序異常,因為該詞的長度是 8,而詞根 'Believe' 的長度為 7,所以不能計算漢明距離,因為漢明的假設前提是距離長度相等。
曼哈頓距離
曼哈頓距離度量在概念上類似於漢明距離,區別是曼哈頓距離計算兩個字符串的每個位置上對應字符之間的差值,而不是計算不匹配字符的數量。曼哈頓距離也稱為城市街區距離、L1范數、計程車度量,正式定義是基於嚴格水平或垂直路徑的網格中兩個點之間的距離,而非通常計算的歐幾里得對角距離,其數學表示為:
其中,u 和 v 是長度為 n 的兩個詞項。與漢明距離相同,這里的假設前提是兩個詞項的長度相同。還可以通過絕對差的和除以詞項長度來計算曼哈頓歸一化距離。表達式如下:
其中,n 是詞項 u 和 v 的長度,以下函數有助於計算曼哈頓距離,也可以計算曼哈頓歸一化距離:
def
manhattan_distance(u, v, norm
=
False
):
if
u.shape !
=
v.shape:
raise
ValueError(
'The vectors must have equal lengths.'
)
return
abs
(u
-
v).
sum
()
if
not
norm
else
abs
(u
-
v).mean()
|
現在,使用上述函數計算根詞項和其他詞項之間的曼哈頓距離,如下代碼所示:
# MANHATTAN DISTANCE DEMO
for
term, vector_term
in
zip
(terms, vector_terms):
print
(
'Manhattan distance between root: {} and term: {} is {}'
.
format
(root_term,
term,
manhattan_distance(root_vector, vector_term, norm
=
False
)))
|
結果:
Manhattan distance between root: Believe
and
term: beleive
is
8
Manhattan distance between root: Believe
and
term: bargain
is
38
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
ValueError Traceback (most recent call last)
<ipython
-
input
-
14
-
b8740bf6df30>
in
<module>()
3
print
(
'Manhattan distance between root: {} and term: {} is {}'
.
format
(root_term,
4
term,
-
-
-
-
>
5
manhattan_distance(root_vector, vector_term, norm
=
False
)))
6
7
<ipython
-
input
-
11
-
f76af5f725e0>
in
manhattan_distance(u, v, norm)
1
def
manhattan_distance(u, v, norm
=
False
):
2
if
u.shape !
=
v.shape:
-
-
-
-
>
3
raise
ValueError(
'The vectors must have equal lengths.'
)
4
return
abs
(u
-
v).
sum
()
if
not
norm
else
abs
(u
-
v).mean()
ValueError: The vectors must have equal lengths.
|
for
term, vector_term
in
zip
(terms, vector_terms):
print
(
'Normalized Manhattan distance between root: {} and term: {} is {}'
.
format
(root_term,
term,
round
(manhattan_distance(root_vector, vector_term, norm
=
True
),
2
)))
|
結果:
Normalized Manhattan distance between root: Believe
and
term: beleive
is
1.14
Normalized Manhattan distance between root: Believe
and
term: bargain
is
5.43
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
ValueError Traceback (most recent call last)
<ipython
-
input
-
15
-
d5f6b06389a1>
in
<module>()
2
print
(
'Normalized Manhattan distance between root: {} and term: {} is {}'
.
format
(root_term,
3
term,
-
-
-
-
>
4
round
(manhattan_distance(root_vector, vector_term, norm
=
True
),
2
)))
5
6
<ipython
-
input
-
11
-
f76af5f725e0>
in
manhattan_distance(u, v, norm)
1
def
manhattan_distance(u, v, norm
=
False
):
2
if
u.shape !
=
v.shape:
-
-
-
-
>
3
raise
ValueError(
'The vectors must have equal lengths.'
)
4
return
abs
(u
-
v).
sum
()
if
not
norm
else
abs
(u
-
v).mean()
ValueError: The vectors must have equal lengths.
|
從這些結果可以看出,正如預期的那樣,在忽略大小寫的情況下,'Believe' 和 ‘beleive’ 之間的距離相似度最高,距離度量的分為 8 或 1.14,與 'bargain' 一詞的距離度量得分為 38 或 5.43(這里得分越小,詞約相似)。詞項 'Elephant' 則在運行中產生錯誤,因為它與根詞項的長度不同,就像之前在計算漢明距離時一樣。
歐幾里得距離
歐幾里得距離也稱為歐幾里得范數,L2 范數或 L2 距離,它的正式定義是兩點之間最短的直線距離。數學上可以表示為:
其中,u 和 v 兩點是場景中的向量化文本詞項,每個長度都為 n。以下函數有助於計算兩個詞項之間的歐幾里得距離:
def
euclidean_distance(u,v):
if
u.shape !
=
v.shape:
raise
ValueError(
'The vectors must have equal lengths.'
)
distance
=
np.sqrt(np.
sum
(np.square(u
-
v)))
return
distance
|
現在,使用上述函數計算詞項之間的歐幾里得距離,如下代碼所示:
# EUCLIDEAN DISTANCE DEMO
for
term, vector_term
in
zip
(terms, vector_terms):
print
(
'Euclidean distance between root: {} and term: {} is {}'
.
format
(root_term,
term,
round
(euclidean_distance(root_vector, vector_term),
2
)))
|
結果:
Euclidean distance between root: Believe
and
term: beleive
is
5.66
Euclidean distance between root: Believe
and
term: bargain
is
17.94
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
ValueError Traceback (most recent call last)
<ipython
-
input
-
18
-
ac390028bdbb>
in
<module>()
3
print
(
'Euclidean distance between root: {} and term: {} is {}'
.
format
(root_term,
4
term,
-
-
-
-
>
5
round
(euclidean_distance(root_vector, vector_term),
2
)))
6
<ipython
-
input
-
17
-
654771f3cf5d
>
in
euclidean_distance(u, v)
1
def
euclidean_distance(u,v):
2
if
u.shape !
=
v.shape:
-
-
-
-
>
3
raise
ValueError(
'The vectors must have equal lengths.'
)
4
distance
=
np.sqrt(np.
sum
(np.square(u
-
v)))
5
return
distance
ValueError: The vectors must have equal lengths.
|
從上述輸出可以看出,'Believe' 和 ‘beleive’ 這兩個詞最相似,得分為 5.66,‘bargain’ 得分為 17.94,‘Elephant’ 則給出了一個 ValueError,因為這里也是測量字符串相等時的長度。
萊文斯坦編輯距離
萊文斯坦編輯距離通常也稱為萊文斯坦距離,屬於基於編輯距離的度量,它用於根據字符差異來測量兩個字符串之間的距離——類似於漢明距離的度量概念。兩個詞項之間的萊文斯坦編輯距離可以定義為通過添加、刪除或替換將一個詞項轉變成另一個詞項所需的最少編輯次數。這些替代是基於字符的替代,其中單次操作可以編輯單個字符。此外,如前所述,這兩個詞項的長度在這里不必相對。在數學上,可以將兩個詞項之間的萊文斯坦編輯距離表示為 ldn,v(|u|,|v|),其中,u 和 v 是詞項,而 |u| 和 |v| 使其長度。該距離可以用於下公式表示:
其中,i 和 j 是詞項 u 和 v 的索引。上面最小項的第三個方程包含一個由 Cui≠vi 表示的成本函數,它具有如下限制條件:
上式為指示函數,它說明了匹配兩個詞項的對應字符所需的成本(該方程代表了匹配操作或不匹配操作)。上一個最小值中的第一個方程表示刪除操作,第二個方程表示插入操作。這樣,函數 ldn,v(i,j) 就涵蓋了前面提到的插入、刪除和添加所有操作,它表示在詞項 u 的第 i 個字母到詞項 v 的第 j 個字母之間的萊文斯坦編輯距離。對於萊文斯坦編輯距離還有幾個有趣的邊界條件:
- 兩個詞項之間編輯距離的最小值是兩個詞項長度的差值。
- 兩個詞項之間編輯距離的最大值可以是較大詞項的長度。
- 如果兩個詞項完全一樣,則編輯距離為零。
- 當且僅當兩個詞項具有相同的長度時,兩個詞項之間萊文斯坦編輯距離的上線為漢明距離。
- 萊文斯坦編輯距離作為距離度量也滿足三角不等式特性。
有很多種計算萊文斯坦編輯距離的方法,在這里,以兩個詞項為例介紹萊文斯坦編輯距離計算。考慮根詞項 'believe' 和另一個詞項 'beleive'(在計算中忽略大小寫)。兩者的編輯距離應為 2,因為需要進行以下兩個操作完成 ‘beleive’ 到詞項的變換:
- 'beleive' → 'beliive'(將 e 替換為 i)。
- ‘beleive’ → 'believe'(替換為 e)。
為了實現這一點,需要構建一個矩陣,它可以通過比較第一詞項的每個字符和第二詞項的每個字符,計算兩個詞項所有字符之間的萊文斯坦距離。為了便於計算,遵循一種動態規划方法,根據最終計算結果的到兩個此項之間的編輯距離。對於給定的兩個詞項,這里的算法應生成的萊文斯坦編輯距離如表:
b | e | l | i | e | v | e | |
b | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
e | 1 | 0 | 1 | 2 | 3 | 4 | 5 |
l | 2 | 1 | 0 | 1 | 2 | 3 | 4 |
e | 3 | 2 | 1 | 1 | 1 | 2 | 3 |
i | 4 | 3 | 2 | 1 | 2 | 2 | 3 |
v | 5 | 4 | 3 | 2 | 2 | 2 | 3 |
e | 6 | 5 | 4 | 3 | 2 | 3 | 2 |
由表可見,對詞項中的沒對字符都計算編輯距離,如前所述,圖中突出顯示的最終編輯距離值即為兩個詞項之間的實際編輯距離。該算法也稱為瓦克納-費舍爾(Wagner-Fischer)算法,如果想了解更多細節,可以參考瓦格納(R. Wagner)和費舍爾(M. Fischer)的 “字符串到字符校正問題”(The String-to-String Correction Problem)一文。
上述算法使用的是 O(mn) 空間,因為它們雖然包含整個距離矩陣,但是只需存儲前一行和當前的距離行便足以獲得最終結果。將在代碼中執行相同的操作,同時也會將結果存儲在一個矩陣中,以便於在最后將其顯示出來。以下函數可以實現萊文斯坦編輯距離計算:
import
copy
import
pandas as pd
import
numpy as np
def
levenshtein_edit_distance(u, v):
# convert to lower case
u
=
u.lower()
v
=
v.lower()
# base cases
if
u
=
=
v:
return
0
,
0
elif
len
(u)
=
=
0
:
return
len
(v),
0
elif
len
(v)
=
=
0
:
return
len
(u),
0
# initialize edit distance matrix
edit_matrix
=
[]
# initialize two distance matrices
du
=
[
0
]
*
(
len
(v)
+
1
)
dv
=
[
0
]
*
(
len
(v)
+
1
)
# du: the previous row of edit distances
for
i
in
range
(
len
(du)):
du[i]
=
i
# dv : the current row of edit distances
for
i
in
range
(
len
(u)):
dv[
0
]
=
i
+
1
# compute cost as per algorithm
for
j
in
range
(
len
(v)):
cost
=
0
if
u[i]
=
=
v[j]
else
1
dv[j
+
1
]
=
min
(dv[j]
+
1
, du[j
+
1
]
+
1
, du[j]
+
cost)
# assign dv to du for next iteration
for
j
in
range
(
len
(du)):
du[j]
=
dv[j]
# copy dv to the edit matrix
edit_matrix.append(copy.copy(dv))
# compute the final edit distance and edit matrix
distance
=
dv[
len
(v)]
edit_matrix
=
np.array(edit_matrix)
edit_matrix
=
edit_matrix.T
edit_matrix
=
edit_matrix[
1
:,]
edit_matrix
=
pd.DataFrame(data
=
edit_matrix,
index
=
list
(v),
columns
=
list
(u))
return
distance, edit_matrix
|
該函數返回兩個詞項 u 和 v 之間最終的萊文斯坦編輯距離以及完整的編輯矩陣,其中 u 和 v 是我們的輸入。請記住,需要以原始字符串格式,而不是以它們的向量表示來傳遞詞項。此外,在這里不考慮字符串的大小寫,並將所有字符全部轉換為小寫。
以下代碼段使用上述函數來計算示例詞項之間的萊文斯坦編輯距離:
# LEVENSHTEIN EDIT DISTANCE DEMO
for
term
in
terms:
edit_d, edit_m
=
levenshtein_edit_distance(root_term, term)
print
(
'Computing distance between root: {} and term: {}'
.
format
(root_term,
term))
print
(
'Levenshtein edit distance is {}'
.
format
(edit_d))
print
(
'The complete edit distance matrix is depicted below'
)
print
(edit_m)
print
(
'-'
*
30
)
|
結果:
Computing distance between root: Believe
and
term: Believe
Levenshtein edit distance
is
0
The complete edit distance matrix
is
depicted below
0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Computing distance between root: Believe
and
term: beleive
Levenshtein edit distance
is
2
The complete edit distance matrix
is
depicted below
b e l i e v e
b
0
1
2
3
4
5
6
e
1
0
1
2
3
4
5
l
2
1
0
1
2
3
4
e
3
2
1
1
1
2
3
i
4
3
2
1
2
2
3
v
5
4
3
2
2
2
3
e
6
5
4
3
2
3
2
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Computing distance between root: Believe
and
term: bargain
Levenshtein edit distance
is
6
The complete edit distance matrix
is
depicted below
b e l i e v e
b
0
1
2
3
4
5
6
a
1
1
2
3
4
5
6
r
2
2
2
3
4
5
6
g
3
3
3
3
4
5
6
a
4
4
4
4
4
5
6
i
5
5
5
4
5
5
6
n
6
6
6
5
5
6
6
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Computing distance between root: Believe
and
term: Elephant
Levenshtein edit distance
is
7
The complete edit distance matrix
is
depicted below
b e l i e v e
e
1
1
2
3
4
5
6
l
2
2
1
2
3
4
5
e
3
2
2
2
2
3
4
p
4
3
3
3
3
3
4
h
5
4
4
4
4
4
4
a
6
5
5
5
5
5
5
n
7
6
6
6
6
6
6
t
8
7
7
7
7
7
7
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
|
從上述輸出可以看出,'Believe' 和 'beleive' 彼此很接近,編輯距離為 2,‘Believe’ 與 ‘bargain’ 'Elephant' 之間的距離為 6,表示總共需要 6 個編輯距離。通過編輯距離矩陣可以更詳細地了解算法如何通過每次迭代計算距離。
余弦距離和相似度
余弦距離是一個可以從余弦相似推導出的度量,反之亦然。考慮兩個詞項,它們以向量化形式表示,余弦相似度給出了當它們在內積空間中為非零正量時,它們之間角度的余弦值。因此具有相似方向的詞項向量將擁有更接近於 1 (cos0o) 的相似度得分,表示向量在相同方面上彼此非常接近(它們之間的夾角接近零度)。相似度得分接近 0 (cos90o) 則表示兩個詞項向量的夾角接近直角,它們是無關詞項。相似度得分接近 -1 (cos180o) 表示彼此是完全相反的詞項。如圖,其中 u 和 v 是在向量空間中的詞項向量。
可以從向量的位置看出它們的關系,這些圖可以更清楚地顯示出向量彼此靠近還是彼此遠離的,它們之間夾角的余弦值給出了余弦相似度度量。可以將余弦相似度正式定義為兩個詞項向量 u 和 v 的點積除以它們的 L2 范數的乘積。在數學上,可以使用如下表達式表示兩個向量之間的點積:
其中 θ 是 u 和 v 之間的角度,|| u || 表示向量 u 的 L2 范數,|| v || 表示向量 v 的 L2 范數,可以從上面的公式導出余弦相似度的表達式:
其中 cs(u,v) 是 u 和 v 之間的余弦相似度得分,這里 ui 和 vi 是兩個向量的各類特征或組件,這些特征或組件的總數為 n。在示例中,將使用字符包向量化來構建這些詞項向量,n 表示待分析的所有詞項的特殊字符的數量。在這里需要注意一件很重要的事情,那就是通常余弦相似度得分的范圍從 -1 到 +1,但是如果使用字符袋(基於字符頻率)或詞袋(基於詞頻),那么分數的范圍將會是從 0 到 1,因為詞頻向量永遠不會是負的,所以兩個向量之間的角度不能超過 90o。可用如下公式來表示 余弦距離:
其中 cs(u,v) 表示詞項向量 u 和 v 之間的余弦距離。以下函數給出了基於上述公式實現余弦距離的計算過程:
def
cosine_distance(u, v):
distance
=
1.0
-
(np.dot(u, v)
/
(np.sqrt(
sum
(np.square(u)))
*
np.sqrt(
sum
(np.square(v))))
)
return
distance
|
接下來,將使用之前創建的字符袋表示來測試示例詞項之間的相似度,如下代碼段所示:
# COSINE DISTANCE\SIMILARITY DEMO
for
term, boc_term
in
zip
(terms, boc_vector_terms):
print
(
'Analyzing similarity between root: {} and term: {}'
.
format
(root_term,
term))
distance
=
round
(cosine_distance(root_boc_vector, boc_term),
2
)
similarity
=
1
-
distance
print
(
'Cosine distance is {}'
.
format
(distance))
print
(
'Cosine similarity is {}'
.
format
(similarity))
print
(
'-'
*
40
)
|
結果:
Analyzing similarity between root: Believe
and
term: beleive
Cosine distance
is
-
0.0
Cosine similarity
is
1.0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Analyzing similarity between root: Believe
and
term: bargain
Cosine distance
is
0.82
Cosine similarity
is
0.18000000000000005
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Analyzing similarity between root: Believe
and
term: Elephant
Cosine distance
is
0.39
Cosine similarity
is
0.61
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
|
這些向量表示不考慮字符的順序,因此 "Believe" 一詞和 "beleive" 的相似度為 1.0 或 100% 完全相似,因為它們有着具有相同頻率的相同字符。可以看出將其與 WordNet 這樣的語義字典組合使用的好處,即可以在用戶鍵入拼寫錯誤的單詞時,通過測量單詞之間的相似度來提供語義上和語法上正確的單詞,從而提供正確的拼寫建議。在這里,可以嘗試使用不同的特征,如同時提取兩個字符並計算其頻率來構建詞項向量,而非示例中所示的使用單個字符的頻率構建向量。這樣就對詞語中的部分字符順序加入了考量。當測量大文件或長句子之間的相似度時,這種距離度量十分有效。