在作天然語言處理的過程當中,咱們常常會遇到須要找出類似語句的場景,或者找出句子的近似表達,這時候咱們就須要把相似的句子歸到一塊兒,這裏面就涉及到句子類似度計算的問題,那麼本節就來了解一下怎麼樣來用 Python 實現句子類似度的計算。
html
句子類似度計算咱們一共歸類瞭如下幾種方法:git
編輯距離計算github
傑卡德係數計算算法
TF 計算數據庫
TFIDF 計算數組
Word2Vec 計算bash
下面咱們來一一瞭解一下這幾種算法的原理和 Python 實現。微信
編輯距離,英文叫作 Edit Distance,又稱 Levenshtein 距離,是指兩個字串之間,由一個轉成另外一個所需的最少編輯操做次數,若是它們的距離越大,說明它們越是不一樣。許可的編輯操做包括將一個字符替換成另外一個字符,插入一個字符,刪除一個字符。
網絡
例如咱們有兩個字符串:string 和 setting,若是咱們想要把 string 轉化爲 setting,須要這麼兩步:框架
第一步,在 s 和 t 之間加入字符 e。
第二步,把 r 替換成 t。
因此它們的編輯距離差就是 2,這就對應着兩者要進行轉化所要改變(添加、替換、刪除)的最小步數。
那麼用 Python 怎樣來實現呢,咱們能夠直接使用 distance 庫:
import distance
def edit_distance(s1, s2):
return distance.levenshtein(s1, s2)
s1 = 'string'
s2 = 'setting'
print(edit_distance(s1, s2))複製代碼
這裏咱們直接使用 distance 庫的 levenshtein() 方法,傳入兩個字符串,便可獲取兩個字符串的編輯距離了。
運行結果以下:
2複製代碼
這裏的 distance 庫咱們能夠直接使用 pip3 來安裝:
pip3 install distance複製代碼
這樣若是咱們想要獲取類似的文本的話能夠直接設定一個編輯距離的閾值來實現,如設置編輯距離爲 2,下面是一個樣例:
import distance
def edit_distance(s1, s2):
return distance.levenshtein(s1, s2)
strings = [
'你在幹什麼',
'你在幹啥子',
'你在作什麼',
'你好啊',
'我喜歡吃香蕉'
]
target = '你在幹啥'
results = list(filter(lambda x: edit_distance(x, target) <= 2, strings))
print(results)複製代碼
這裏咱們定義了一些字符串,而後定義了一個目標字符串,而後用編輯距離 2 的閾值進行設定,最後獲得的結果就是編輯距離在 2 及之內的結果,運行結果以下:
['你在幹什麼', '你在幹啥子']複製代碼
經過這種方式咱們能夠大體篩選出相似的句子,可是發現一些句子例如「你在作什麼」 就沒有被識別出來,但他們的意義確實是相差不大的,所以,編輯距離並非一個好的方式,可是簡單易用。
傑卡德係數,英文叫作 Jaccard index, 又稱爲 Jaccard 類似係數,用於比較有限樣本集之間的類似性與差別性。Jaccard 係數值越大,樣本類似度越高。
實際上它的計算方式很是簡單,就是兩個樣本的交集除以並集獲得的數值,當兩個樣本徹底一致時,結果爲 1,當兩個樣本徹底不一樣時,結果爲 0。
算法很是簡單,就是交集除以並集,下面咱們用 Python 代碼來實現一下:
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
def jaccard_similarity(s1, s2):
def add_space(s):
return ' '.join(list(s))
# 將字中間加入空格
s1, s2 = add_space(s1), add_space(s2)
# 轉化爲TF矩陣
cv = CountVectorizer(tokenizer=lambda s: s.split())
corpus = [s1, s2]
vectors = cv.fit_transform(corpus).toarray()
# 求交集
numerator = np.sum(np.min(vectors, axis=0))
# 求並集
denominator = np.sum(np.max(vectors, axis=0))
# 計算傑卡德係數
return 1.0 * numerator / denominator
s1 = '你在幹嗎呢'
s2 = '你在幹什麼呢'
print(jaccard_similarity(s1, s2))複製代碼
這裏咱們使用了 Sklearn 庫中的 CountVectorizer 來計算句子的 TF 矩陣,而後利用 Numpy 來計算兩者的交集和並集,隨後計算傑卡德係數。
這裏值得學習的有 CountVectorizer 的用法,經過它的 fit_transform() 方法咱們能夠將字符串轉化爲詞頻矩陣,例如這裏有兩句話「你在幹嗎呢」和「你在幹什麼呢」,首先 CountVectorizer 會計算出不重複的有哪些字,會獲得一個字的列表,結果爲:
['麼', '什', '你', '呢', '嘛', '在', '幹']複製代碼
這個其實能夠經過以下代碼來獲取,就是獲取詞表內容:
cv.get_feature_names()複製代碼
接下來經過轉化以後,vectors 變量就變成了:
[[0 0 1 1 1 1 1]
[1 1 1 1 0 1 1]]複製代碼
它對應的是兩個句子對應詞表的詞頻統計,這裏是兩個句子,因此結果是一個長度爲 2 的二維數組,好比第一句話「你在幹嗎呢」中不包含「麼」字,那麼第一個「麼」字對應的結果就是0,即數量爲 0,依次類推。
後面咱們使用了 np.min() 方法並傳入了 axis 爲 0,實際上就是獲取了每一列的最小值,這樣實際上就是取了交集,np.max() 方法是獲取了每一列的最大值,實際上就是取了並集。
兩者分別取和便是交集大小和並集大小,而後做商便可,結果以下:
0.5714285714285714複製代碼
這個數值越大,表明兩個字符串越接近,不然反之,所以咱們也可使用這個方法,並經過設置一個類似度閾值來進行篩選。
第三種方案就是直接計算 TF 矩陣中兩個向量的類似度了,實際上就是求解兩個向量夾角的餘弦值,就是點乘積除以兩者的模長,公式以下:
cosθ=a·b/|a|*|b|複製代碼
上面咱們已經得到了 TF 矩陣,下面咱們只須要求解兩個向量夾角的餘弦值就行了,代碼以下:
from sklearn.feature_extraction.text import CountVectorizer
import numpy as np
from scipy.linalg import norm
def tf_similarity(s1, s2):
def add_space(s):
return ' '.join(list(s))
# 將字中間加入空格
s1, s2 = add_space(s1), add_space(s2)
# 轉化爲TF矩陣
cv = CountVectorizer(tokenizer=lambda s: s.split())
corpus = [s1, s2]
vectors = cv.fit_transform(corpus).toarray()
# 計算TF係數
return np.dot(vectors[0], vectors[1]) / (norm(vectors[0]) * norm(vectors[1]))
s1 = '你在幹嗎呢'
s2 = '你在幹什麼呢'
print(tf_similarity(s1, s2))複製代碼
在在這裏咱們使用了 np.dot() 方法獲取了向量的點乘積,而後經過 norm() 方法獲取了向量的模長,通過計算獲得兩者的 TF 係數,結果以下:
0.7302967433402214複製代碼
另外除了計算 TF 係數咱們還能夠計算 TFIDF 係數,TFIDF 實際上就是在詞頻 TF 的基礎上再加入 IDF 的信息,IDF 稱爲逆文檔頻率,不瞭解的能夠看下阮一峯老師的講解:http://www.ruanyifeng.com/blog/2013/03/tf-idf.html,裏面對 TFIDF 的講解也是十分透徹的。
下面咱們仍是藉助於 Sklearn 中的模塊 TfidfVectorizer 來實現,代碼以下:
from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np
from scipy.linalg import norm
def tfidf_similarity(s1, s2):
def add_space(s):
return ' '.join(list(s))
# 將字中間加入空格
s1, s2 = add_space(s1), add_space(s2)
# 轉化爲TF矩陣
cv = TfidfVectorizer(tokenizer=lambda s: s.split())
corpus = [s1, s2]
vectors = cv.fit_transform(corpus).toarray()
# 計算TF係數
return np.dot(vectors[0], vectors[1]) / (norm(vectors[0]) * norm(vectors[1]))
s1 = '你在幹嗎呢'
s2 = '你在幹什麼呢'
print(tfidf_similarity(s1, s2))複製代碼
這裏的 vectors 變量實際上就對應着 TFIDF 值,內容以下:
[[0. 0. 0.4090901 0.4090901 0.57496187 0.4090901 0.4090901 ]
[0.49844628 0.49844628 0.35464863 0.35464863 0. 0.35464863 0.35464863]]複製代碼
運行結果以下:
0.5803329846765686複製代碼
因此經過 TFIDF 係數咱們也能夠進行類似度的計算。
Word2Vec,顧名思義,其實就是將每個詞轉換爲向量的過程。若是不瞭解的話能夠參考:https://blog.csdn.net/itplus/article/details/37969519。
這裏咱們能夠直接下載訓練好的 Word2Vec 模型,模型的連接地址爲:https://pan.baidu.com/s/1TZ8GII0CEX32ydjsfMc0zw,是使用新聞、百度百科、小說數據來訓練的 64 維的 Word2Vec 模型,數據量很大,總體效果還不錯,咱們能夠直接下載下來使用,這裏咱們使用的是 news_12g_baidubaike_20g_novel_90g_embedding_64.bin 數據,而後實現 Sentence2Vec,代碼以下:
import gensim
import jieba
import numpy as np
from scipy.linalg import norm
model_file = './word2vec/news_12g_baidubaike_20g_novel_90g_embedding_64.bin'
model = gensim.models.KeyedVectors.load_word2vec_format(model_file, binary=True)
def vector_similarity(s1, s2):
def sentence_vector(s):
words = jieba.lcut(s)
v = np.zeros(64)
for word in words:
v += model[word]
v /= len(words)
return v
v1, v2 = sentence_vector(s1), sentence_vector(s2)
return np.dot(v1, v2) / (norm(v1) * norm(v2))複製代碼
在獲取 Sentence Vector 的時候,咱們首先對句子進行分詞,而後對分好的每個詞獲取其對應的 Vector,而後將全部 Vector 相加並求平均,這樣就可獲得 Sentence Vector 了,而後再計算其夾角餘弦值便可。
調用示例以下:
s1 = '你在幹嗎'
s2 = '你正作什麼'
vector_similarity(s1, s2)複製代碼
結果以下:
0.6701133967824016複製代碼
這時若是咱們再回到最初的例子看下效果:
strings = [
'你在幹什麼',
'你在幹啥子',
'你在作什麼',
'你好啊',
'我喜歡吃香蕉'
]
target = '你在幹啥'
for string in strings:
print(string, vector_similarity(string, target))複製代碼
依然是前面的例子,咱們看下它們的匹配度結果是多少,運行結果以下:
你在幹什麼 0.8785495016487204
你在幹啥子 0.9789649689827049
你在作什麼 0.8781992402695274
你好啊 0.5174225914249863
我喜歡吃香蕉 0.582990841450621複製代碼
能夠看到相近的語句類似度都能到 0.8 以上,而不一樣的句子類似度都不足 0.6,這個區分度就很是大了,能夠說有了 Word2Vec 咱們能夠結合一些語義信息來進行一些判斷,效果明顯也好不少。
因此整體來講,Word2Vec 計算的方式是很是好的。
另外學術界還有一些可能更好的研究成果,這個能夠參考知乎上的一些回答:https://www.zhihu.com/question/29978268/answer/54399062。
以上即是進行句子類似度計算的基本方法和 Python 實現,本節代碼地址:https://github.com/AIDeepLearning/SentenceDistance。
嗨~ 給你們重磅推薦一本書!上市兩月就已經重印 4 次的 Python 爬蟲書!它就是由靜覓博客博主崔慶才所做的《Python3網絡爬蟲開發實戰》!!!同時文末還有抽獎贈書活動,不容錯過!!!
本書《Python3網絡爬蟲開發實戰》全面介紹了利用 Python3 開發網絡爬蟲的知識,書中首先詳細介紹了各類類型的環境配置過程和爬蟲基礎知識,還討論了 urllib、requests 等請求庫和 Beautiful Soup、XPath、pyquery 等解析庫以及文本和各種數據庫的存儲方法,另外本書經過多個真實新鮮案例介紹了分析 Ajax 進行數據爬取,Selenium 和 Splash 進行動態網站爬取的過程,接着又分享了一些切實可行的爬蟲技巧,好比使用代理爬取和維護動態代理池的方法、ADSL 撥號代理的使用、各種驗證碼(圖形、極驗、點觸、宮格等)的破解方法、模擬登陸網站爬取的方法及 Cookies 池的維護等等。
此外,本書的內容還遠遠不止這些,做者還結合移動互聯網的特色探討了使用 Charles、mitmdump、Appium 等多種工具實現 App 抓包分析、加密參數接口爬取、微信朋友圈爬取的方法。此外本書還詳細介紹了 pyspider 框架、Scrapy 框架的使用和分佈式爬蟲的知識,另外對於優化及部署工做,本書還包括 Bloom Filter 效率優化、Docker 和 Scrapyd 爬蟲部署、分佈式爬蟲管理框架Gerapy 的分享。
全書共 604 頁,足足兩斤重呢~ 訂價爲 99 元!
看書就先看看誰寫的嘛,咱們來了解一下~
崔慶才,靜覓博客博主(https://cuiqingcai.com),博客 Python 爬蟲博文閱讀量已過百萬,北京航空航天大學碩士,天善智能、網易雲課堂講師,微軟小冰大數據工程師,有多個大型分佈式爬蟲項目經驗,樂於技術分享,文章通俗易懂 ^_^
附皁片一張 ~(@^_^@)~
更多詳請點擊➡️juejin.im/post/5b1eb3…