本文做爲筆者NLP入門系列文章第一篇,之後咱們就要步入NLP時代。
本文將會介紹NLP中常見的詞袋模型(Bag of Words)以及如何利用詞袋模型來計算句子間的類似度(餘弦類似度,cosine similarity)。
首先,讓咱們來看一下,什麼是詞袋模型。咱們如下面兩個簡單句子爲例:python
sent1 = "I love sky, I love sea." sent2 = "I like running, I love reading."
一般,NLP沒法一會兒處理完整的段落或句子,所以,第一步每每是分句和分詞。這裏只有句子,所以咱們只須要分詞便可。對於英語句子,可使用NLTK中的word_tokenize函數,對於中文句子,則可以使用jieba模塊。故第一步爲分詞,代碼以下:web
from nltk import word_tokenize sents = [sent1, sent2] texts = [[word for word in word_tokenize(sent)] for sent in sents]
輸出的結果以下:算法
[['I', 'love', 'sky', ',', 'I', 'love', 'sea', '.'], ['I', 'like', 'running', ',', 'I', 'love', 'reading', '.']]
分詞完畢。下一步是構建語料庫,即全部句子中出現的單詞及標點。代碼以下:微信
all_list = [] for text in texts: all_list += text corpus = set(all_list) print(corpus)
輸出以下:app
{'love', 'running', 'reading', 'sky', '.', 'I', 'like', 'sea', ','}
能夠看到,語料庫中一共是8個單詞及標點。接下來,對語料庫中的單詞及標點創建數字映射,便於後續的句子的向量表示。代碼以下:函數
corpus_dict = dict(zip(corpus, range(len(corpus)))) print(corpus_dict)
輸出以下:code
{'running': 1, 'reading': 2, 'love': 0, 'sky': 3, '.': 4, 'I': 5, 'like': 6, 'sea': 7, ',': 8}
雖然單詞及標點並無按照它們出現的順序來創建數字映射,不過這並不會影響句子的向量表示及後續的句子間的類似度。
下一步,也就是詞袋模型的關鍵一步,就是創建句子的向量表示。這個表示向量並非簡單地以單詞或標點出現與否來選擇0,1數字,而是把單詞或標點的出現頻數做爲其對應的數字表示,結合剛纔的語料庫字典,句子的向量表示的代碼以下:token
# 創建句子的向量表示 def vector_rep(text, corpus_dict): vec = [] for key in corpus_dict.keys(): if key in text: vec.append((corpus_dict[key], text.count(key))) else: vec.append((corpus_dict[key], 0)) vec = sorted(vec, key= lambda x: x[0]) return vec vec1 = vector_rep(texts[0], corpus_dict) vec2 = vector_rep(texts[1], corpus_dict) print(vec1) print(vec2)
輸出以下:ip
[(0, 2), (1, 0), (2, 0), (3, 1), (4, 1), (5, 2), (6, 0), (7, 1), (8, 1)] [(0, 1), (1, 1), (2, 1), (3, 0), (4, 1), (5, 2), (6, 1), (7, 0), (8, 1)]
讓咱們稍微逗留一下子,來看看這個向量。在第一句中I出現了兩次,在預料庫字典中,I對應的數字爲5,所以在第一句中5出現2次,在列表中的元組即爲(5,2),表明單詞I在第一句中出現了2次。以上的輸出可能並不那麼直觀,真實的兩個句子的表明向量應爲:it
[2, 0, 0, 1, 1, 2, 0, 1, 1] [1, 1, 1, 0, 1, 2, 1, 0, 1]
OK,詞袋模型到此結束。接下來,咱們會利用剛纔獲得的詞袋模型,即兩個句子的向量表示,來計算類似度。
在NLP中,若是獲得了兩個句子的向量表示,那麼,通常會選擇用餘弦類似度做爲它們的類似度,而向量的餘弦類似度即爲兩個向量的夾角的餘弦值。其計算的Python代碼以下:
from math import sqrt def similarity_with_2_sents(vec1, vec2): inner_product = 0 square_length_vec1 = 0 square_length_vec2 = 0 for tup1, tup2 in zip(vec1, vec2): inner_product += tup1[1]*tup2[1] square_length_vec1 += tup1[1]**2 square_length_vec2 += tup2[1]**2 return (inner_product/sqrt(square_length_vec1*square_length_vec2)) cosine_sim = similarity_with_2_sents(vec1, vec2) print('兩個句子的餘弦類似度爲: %.4f。'%cosine_sim)
輸出結果以下:
兩個句子的餘弦類似度爲: 0.7303。
這樣,咱們就經過句子的詞袋模型,獲得了它們間的句子類似度。
固然,在實際的NLP項目中,若是須要計算兩個句子的類似度,咱們只需調用gensim模塊便可,它是NLP的利器,可以幫助咱們處理不少NLP任務。下面爲用gensim計算兩個句子的類似度的代碼:
sent1 = "I love sky, I love sea." sent2 = "I like running, I love reading." from nltk import word_tokenize sents = [sent1, sent2] texts = [[word for word in word_tokenize(sent)] for sent in sents] print(texts) from gensim import corpora from gensim.similarities import Similarity # 語料庫 dictionary = corpora.Dictionary(texts) # 利用doc2bow做爲詞袋模型 corpus = [dictionary.doc2bow(text) for text in texts] similarity = Similarity('-Similarity-index', corpus, num_features=len(dictionary)) print(similarity) # 獲取句子的類似度 new_sensence = sent1 test_corpus_1 = dictionary.doc2bow(word_tokenize(new_sensence)) cosine_sim = similarity[test_corpus_1][1] print("利用gensim計算獲得兩個句子的類似度: %.4f。"%cosine_sim)
輸出結果以下:
[['I', 'love', 'sky', ',', 'I', 'love', 'sea', '.'], ['I', 'like', 'running', ',', 'I', 'love', 'reading', '.']] Similarity index with 2 documents in 0 shards (stored under -Similarity-index) 利用gensim計算獲得兩個句子的類似度: 0.7303。
注意,若是在運行代碼時出現如下warning:
gensim\utils.py:1209: UserWarning: detected Windows; aliasing chunkize to chunkize_serial warnings.warn("detected Windows; aliasing chunkize to chunkize_serial") gensim\matutils.py:737: FutureWarning: Conversion of the second argument of issubdtype from `int` to `np.signedinteger` is deprecated. In future, it will be treated as `np.int32 == np.dtype(int).type`. if np.issubdtype(vec.dtype, np.int):
若是想要去掉這些warning,則在導入gensim模塊的代碼前添加如下代碼便可:
import warnings warnings.filterwarnings(action='ignore',category=UserWarning,module='gensim') warnings.filterwarnings(action='ignore',category=FutureWarning,module='gensim')
本文到此結束,感謝閱讀!若是不當之處,請速聯繫筆者,歡迎你們交流!祝您好運~
注意:本人現已開通微信公衆號: Python爬蟲與算法(微信號爲:easy_web_scrape), 歡迎你們關注哦~~