六、萊文斯坦編輯距離
前邊的幾種距離計算方法都是針對相同長度的詞項,萊文斯坦編輯距離可以計算兩個長度不同的單詞之間的距離;萊文斯坦編輯距離是通過新增、刪除、或者將一個字元替換為另外一個字元所需的最小編輯次數;
我們假設兩個單詞u、v的長度分別為i、j,則其可以分以下幾種情況進行計算
當有一個單詞的長度為0的時候,則編輯距離為不為零的單詞的長度;
從編輯距離的定義上來看,在單詞的變化過程中,每個字元的變化都可以看做是在其字首子字串的編輯距離基礎上,進行當前字元的刪除、新增、替換操作;
name當u和v的長度都不為0的時候,存在三種轉化的可能
u的不包含最後一個字元的字首轉化為v的前提下,這是刪除字元的情形;
此時的數學公式為
u已經整個轉化為v不包含最後一個字元的字首的前提下,這是新增字元的情形;
此時的數學公式為
u字首已經轉化為v的字首的前提下,這個時候需要根據兩個詞的最後一個字元是否相等,如果不等的話就需要替換;
此時的數學公式為
然後取三種情況裡最小的值作為編輯距離即可;
以上都是從遞迴的角度進行的抽象描述,可能直接理解起來有點困難;我們還是通過vlna轉變為vlan為例,使用矩陣了直觀的瞭解一下這個演算法;
矩陣的第一行可以理解為vlna的字首子串轉化為v的編輯距離;
v => v,編輯距離為0;
vl => v,編輯距離為1;
同樣的道理,矩陣的第一列是vlna的子串v分別轉化為vlan的所有字首子串的編輯距離;
接下來我們就可以看看第二行第二列的編輯距離是怎麼計算的;
第二行第一列表示v=>vl的編輯距離為1,即
此時只需要在這個基礎上刪除l即可;這就是對應刪除字元的情況,此時編輯距離為2;
第一行第二列表示vl=>v的編輯距離為1,即
此時只需要在這個基礎上增加字元l即可;這就是對應新增字元的情況,此時編輯距離為2;
第一行第一列表示v=>v的編輯距離為0,即
由於此時由於兩個單詞裡的字元都是l,所以字元不需要替換,這就是對應字元替換的情況,此時編輯距離為0;
通過以上分析我們可以知道編輯距離為0;同時我們也可以發現矩陣中每個位置的編輯距離是跟其左邊、左上、上邊的編輯距離有關的,只需要計算三者中的最小者作為編輯距離即可;
通過以上的編輯距離矩陣,我們最終只關注最後一個單元格的值,而其計算只需要關注其上一行和當前行的編輯距離;
為了計算方便,我們可以在u和v前邊分別加一個空白佔位符,這樣對每個字元都存在三個方向位置的編輯距離了;
我們使用如下的方法計算編輯距離和編輯距離矩陣;
import copy
import pandas as pd
def levenshtein_edit_distance(u, v):
u = u.lower()
v = v.lower()
distance = 0
if len(u) == 0:
distance = len(v)
elif len(v) == 0:
distance = len(u)
else:
edit_matrix = []
pre_row = [0] * (len(v) + 1)
current_row = [0] * (len(v) + 1)
# 初始化補白行的編輯距離
for i in range(len(u) +1):
pre_row[i] = i
for i in range(len(u)):
current_row[0] = i + 1
for j in range(len(v)):
cost = 0 if u[i] == v[j] else 1
current_row[j+1] = min(current_row[j] + 1, pre_row[j+1] + 1, pre_row[j] + cost)
for j in range(len(pre_row)):
pre_row[j] = current_row[j]
edit_matrix.append(copy.copy(current_row))
distance = current_row[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
我們使用相同的關鍵字,使用如下程式碼進行測試
vlan = 'vlan'
vlna = 'vlna'
http='http'
words = [vlan, vlna, http]
input_word = 'vlna'
for word in words:
distance, martrix = levenshtein_edit_distance(input_word, word)
print(f"{input_word} and {word} levenshtein edit distance is {distance}")
print('the complate edit distance matrix')
print(martrix)
vlna and vlan levenshtein edit distance is 2
the complate edit distance matrix
v l n a
v 0 1 2 3
l 1 0 1 2
a 2 1 1 1
n 3 2 1 2
vlna and vlna levenshtein edit distance is 0
the complate edit distance matrix
v l n a
v 0 1 2 3
l 1 0 1 2
n 2 1 0 1
a 3 2 1 0
vlna and http levenshtein edit distance is 4
the complate edit distance matrix
v l n a
h 1 2 3 4
t 2 2 3 4
t 3 3 3 4
p 4 4 4 4
七、餘弦距離
餘弦距離是一個跟餘弦相似度關聯的的概念;我們可以使用向量來表示不同的單詞,而兩個不同單詞向量之間的餘弦值便是餘弦相似度;兩者之間的夾角越小則餘弦值越小,則兩者約相似;
根據向量的內積公式可以得到如下的餘弦相似度公式;
餘弦相似度越大,則兩個單詞越相似,而距離則正好相反,則可得餘弦距離為
要計算餘弦距離就需要首先將單詞轉化為向量,我們可以通過scipy.stats.itemfreq來將單詞進行字元袋向量化;我們通過以下方法計算每個詞中的每個字元出現的次數;
from scipy.stats import itemfreq
def boc_term_vectors(words):
words = [word.lower() for word in words]
unique_chars = np.unique(np.hstack([list(word) for word in words]))
word_term_counts = [{char:count for char,count in itemfreq(list(word))} for word in words]
boc_vectors = [np.array([
int(word_term_count.get(char, 0)) for char in unique_chars])
for word_term_count in word_term_counts
]
return list(unique_chars), boc_vectors
使用以下程式碼測試一下
vlan = 'vlan'
vlna = 'vlna'
http='http'
words = [vlan, vlna, http]
chars, (boc_vlan,boc_vlna,boc_http) = boc_term_vectors(words)
print(f'all chars {chars}')
print(f"vlan {boc_vlan}")
print(f"vlna {boc_vlna}")
print(f"http {boc_http}")
all chars ['a', 'h', 'l', 'n', 'p', 't', 'v']
vlan [1 0 1 1 0 0 1]
vlna [1 0 1 1 0 0 1]
http [0 1 0 0 1 2 0]
我們可以根據公式使用以下方法計算餘弦距離;
def cosin_distance(u, v):
distance = 1.0 - np.dot(u,v)/(np.sqrt(sum(np.square(u))) * np.sqrt(sum(np.square(v))))
return distance
使用相同的關鍵字,使用以下程式碼測試餘弦距離;
vlan = 'vlan'
vlna = 'vlna'
http='http'
words = [vlan, vlna, http]
chars, boc_words = boc_term_vectors(words)
input_word =vlna
boc_input = boc_words[1]
for word, boc_word in zip(words, boc_words):
print(f'{input_word} and {word} cosine distance id {cosin_distance(boc_input, boc_word)}')
vlna and vlan cosine distance id 0.0
vlna and vlna cosine distance id 0.0
vlna and http cosine distance id 1.0