本文作为笔者NLP入门系列文章第一篇,以后我们就要步入NLP时代。
本文将会介绍NLP中常见的词袋模型(Bag of Words)以及如何利用词袋模型来计算句子间的相似度(余弦相似度,cosine similarity)。
首先,让我们来看一下,什么是词袋模型。我们以下面两个简单句子为例:
通常,NLP无法一下子处理完整的段落或句子,因此,第一步往往是分句和分词。这里只有句子,因此我们只需要分词即可。对于英语句子,可以使用NLTK中的word_tokenize函数,对于中文句子,则可使用jieba模块。故第一步为分词,代码如下:
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)输出如下:
{'love', 'running', 'reading', 'sky', '.', 'I', 'like', 'sea', ','}可以看到,语料库中一共是8个单词及标点。接下来,对语料库中的单词及标点建立数字映射,便于后续的句子的向量表示。代码如下:
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} 虽然单词及标点并没有按照它们出现的顺序来建立数字映射,不过这并不会影响句子的向量表示及后续的句子间的相似度。
下一步,也就是词袋模型的关键一步,就是建立句子的向量表示。这个表示向量并不是简单地以单词或标点出现与否来选择0,1数字,而是把单词或标点的出现频数作为其对应的数字表示,结合刚才的语料库字典,句子的向量表示的代码如下:
输出如下:
[(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次。以上的输出可能并不那么直观,真实的两个句子的代表向量应为:
[2, 0, 0, 1, 1, 2, 0, 1, 1] [1, 1, 1, 0, 1, 2, 1, 0, 1] OK,词袋模型到此结束。接下来,我们会利用刚才得到的词袋模型,即两个句子的向量表示,来计算相似度。
在NLP中,如果得到了两个句子的向量表示,那么,一般会选择用余弦相似度作为它们的相似度,而向量的余弦相似度即为两个向量的夹角的余弦值。其计算的Python代码如下:
输出结果如下:
两个句子的余弦相似度为: 0.7303。 这样,我们就通过句子的词袋模型,得到了它们间的句子相似度。
当然,在实际的NLP项目中,如果需要计算两个句子的相似度,我们只需调用gensim模块即可,它是NLP的利器,能够帮助我们处理很多NLP任务。下面为用gensim计算两个句子的相似度的代码: