您当前的位置: 首页 >  自然语言处理

IT之一小佬

暂无认证

  • 0浏览

    0关注

    1192博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

自然语言处理(nlp)之词袋模型及句子相似度

IT之一小佬 发布时间:2021-05-04 23:06:58 ,浏览量:0

        本博文将会介绍NLP中常见的词袋模型(Bag of Words)以及如何利用词袋模型来计算句子间的相似度(余弦相似度,cosine similarity)。   首先,让我们来看一下,什么是词袋模型。我们以下面两个简单句子为例:

sent1 = "I love sky, I love sea."
sent2 = "I like running, I love reading."

        通常,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]
print(sents)
print(texts)

输出的结果如下:

['I love sky, I love sea.', 'I like running, I love reading.']
[['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)

输出如下:

{'sea', 'I', 'love', 'like', 'reading', '.', 'sky', 'running', ','}

        可以看到,语料库中一共是8个单词及标点。接下来,对语料库中的单词及标点建立数字映射,便于后续的句子的向量表示。代码如下:

#  对语料库中的单词及标点建立数字映射
corpus_dict = dict(zip(corpus, range(len(corpus))))
print(corpus_dict)

输出如下:

{'sea': 0, 'I': 1, 'love': 2, 'like': 3, 'reading': 4, '.': 5, 'sky': 6, 'running': 7, ',': 8}

        下一步,也就是词袋模型的关键一步,就是建立句子的向量表示。这个表示向量并不是简单地以单词或标点出现与否来选择0,1数字,而是把单词或标点的出现频数作为其对应的数字表示,结合刚才的语料库字典,句子的向量表示的代码如下:

#  建立句子的向量表示
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, 1), (1, 2), (2, 2), (3, 0), (4, 0), (5, 1), (6, 1), (7, 0), (8, 1)]
[(0, 0), (1, 2), (2, 1), (3, 1), (4, 1), (5, 1), (6, 0), (7, 1), (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代码如下:

#  计算两个句子的相似度
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

        这样,我们就通过句子的词袋模型,得到了它们间的句子相似度。

全部代码如下:

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(sents)
print(texts)

#  构建语料库
all_list = []
for text in texts:
    all_list += text
corpus = set(all_list)
print(corpus)

#  对语料库中的单词及标点建立数字映射
corpus_dict = dict(zip(corpus, range(len(corpus))))
print(corpus_dict)


#  建立句子的向量表示
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)

#  计算两个句子的相似度
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)

运行结果:

['I love sky, I love sea.', 'I like running, I love reading.']
[['I', 'love', 'sky', ',', 'I', 'love', 'sea', '.'], ['I', 'like', 'running', ',', 'I', 'love', 'reading', '.']]
{'sea', 'I', 'love', 'like', 'reading', '.', 'sky', 'running', ','}
{'sea': 0, 'I': 1, 'love': 2, 'like': 3, 'reading': 4, '.': 5, 'sky': 6, 'running': 7, ',': 8}
[(0, 1), (1, 2), (2, 2), (3, 0), (4, 0), (5, 1), (6, 1), (7, 0), (8, 1)]
[(0, 0), (1, 2), (2, 1), (3, 1), (4, 1), (5, 1), (6, 0), (7, 1), (8, 1)]
两个句子的余弦相似度为: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(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_sentence = sent1
test_corpus_1 = dictionary.doc2bow(word_tokenize(new_sentence))

print(similarity[test_corpus_1])
cosine_sim = similarity[test_corpus_1][1]
print('利用gensim计算得到两个句子的相似度:%.4f。' % cosine_sim)

输出结果如下:

['I love sky, I love sea.', 'I like running, I love reading.']
[['I', 'love', 'sky', ',', 'I', 'love', 'sea', '.'], ['I', 'like', 'running', ',', 'I', 'love', 'reading', '.']]
Similarity index with 2 documents in 0 shards (stored under -Similarity-index)
[0.99999994 0.73029673]
利用gensim计算得到两个句子的相似度:0.7303。
关注
打赏
1665675218
查看更多评论
立即登录/注册

微信扫码登录

0.0410s