本文做为笔者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), 欢迎你们关注哦~~