??本文作為筆者NLP入門系列文章第一篇映挂,以后我們就要步入NLP時代绵载。
??本文將會介紹NLP中常見的詞袋模型(Bag of Words)以及如何利用詞袋模型來計算句子間的相似度(余弦相似度羊苟,cosine similarity)胶惰。
??首先鹉戚,讓我們來看一下垫桂,什么是詞袋模型师幕。我們以下面兩個簡單句子為例:
sent1 = "I love sky, I love sea."
sent2 = "I like running, I love reading."
??通常,NLP無法一下子處理完整的段落或句子诬滩,因此霹粥,第一步往往是分句和分詞。這里只有句子疼鸟,因此我們只需要分詞即可后控。對于英語句子,可以使用NLTK中的word_tokenize函數(shù)空镜,對于中文句子浩淘,則可使用jieba模塊。故第一步為分詞吴攒,代碼如下:
from nltk import word_tokenize
sents = [sent1, sent2]
texts = [[word for word in word_tokenize(sent)] for sent in sents]
輸出的結(jié)果如下:
[['I', 'love', 'sky', ',', 'I', 'love', 'sea', '.'], ['I', 'like', 'running', ',', 'I', 'love', 'reading', '.']]
??分詞完畢张抄。下一步是構(gòu)建語料庫,即所有句子中出現(xiàn)的單詞及標(biāo)點洼怔。代碼如下:
all_list = []
for text in texts:
all_list += text
corpus = set(all_list)
print(corpus)
輸出如下:
{'love', 'running', 'reading', 'sky', '.', 'I', 'like', 'sea', ','}
??可以看到署惯,語料庫中一共是8個單詞及標(biāo)點。接下來镣隶,對語料庫中的單詞及標(biāo)點建立數(shù)字映射极谊,便于后續(xù)的句子的向量表示。代碼如下:
corpus_dict = dict(zip(corpus, range(len(corpus))))
print(corpus_dict)
輸出如下:
{'running': 1, 'reading': 2, 'love': 0, 'sky': 3, '.': 4, 'I': 5, 'like': 6, 'sea': 7, ',': 8}
??雖然單詞及標(biāo)點并沒有按照它們出現(xiàn)的順序來建立數(shù)字映射安岂,不過這并不會影響句子的向量表示及后續(xù)的句子間的相似度怀酷。
??下一步,也就是詞袋模型的關(guān)鍵一步嗜闻,就是建立句子的向量表示。這個表示向量并不是簡單地以單詞或標(biāo)點出現(xiàn)與否來選擇0桅锄,1數(shù)字琉雳,而是把單詞或標(biāo)點的出現(xiàn)頻數(shù)作為其對應(yīng)的數(shù)字表示,結(jié)合剛才的語料庫字典友瘤,句子的向量表示的代碼如下:
# 建立句子的向量表示
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)
輸出如下:
[(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出現(xiàn)了兩次,在預(yù)料庫字典中,I對應(yīng)的數(shù)字為5脆诉,因此在第一句中5出現(xiàn)2次醇份,在列表中的元組即為(5,2)尼变,代表單詞I在第一句中出現(xiàn)了2次骂租。以上的輸出可能并不那么直觀蚊夫,真實的兩個句子的代表向量應(yīng)為:
[2, 0, 0, 1, 1, 2, 0, 1, 1]
[1, 1, 1, 0, 1, 2, 1, 0, 1]
??OK券时,詞袋模型到此結(jié)束邮旷。接下來黄选,我們會利用剛才得到的詞袋模型,即兩個句子的向量表示婶肩,來計算相似度办陷。
??在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)
輸出結(jié)果如下:
兩個句子的余弦相似度為: 0.7303。
??這樣辱揭,我們就通過句子的詞袋模型离唐,得到了它們間的句子相似度。
??當(dāng)然问窃,在實際的NLP項目中亥鬓,如果需要計算兩個句子的相似度,我們只需調(diào)用gensim模塊即可域庇,它是NLP的利器嵌戈,能夠幫助我們處理很多NLP任務(wù)。下面為用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)
輸出結(jié)果如下:
[['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熟呛。
注意,如果在運行代碼時出現(xiàn)以下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尉姨,則在導(dǎo)入gensim模塊的代碼前添加以下代碼即可:
import warnings
warnings.filterwarnings(action='ignore',category=UserWarning,module='gensim')
warnings.filterwarnings(action='ignore',category=FutureWarning,module='gensim')
??本文到此結(jié)束庵朝,感謝閱讀!如果不當(dāng)之處又厉,請速聯(lián)系筆者九府,歡迎大家交流!祝您好運~
注意:本人現(xiàn)已開通微信公眾號: Python爬蟲與算法(微信號為:easy_web_scrape)覆致, 歡迎大家關(guān)注哦~~