jaccard相似度_自然语言处理之文本相似度计算

文 | 光大科技大数据部 卢格润

在金融科技的业务场景下,我们不可避免地应用到自然语言处理(NLP)的技术去解决问题,比如智能问答系统、资讯舆情的分析等……

在自然语言处理中,很多实际应用具有共性问题,本文就以文本相似度的计算为例介绍自然语言处理解决问题的思路。

  • 文本向量化

  • 相似度的计算

  • 相关示例

  • 总结

文本向量化

无论是中文、英文还是标点符号,都是机器无法直接做计算的,需要先将文本转化为可计算和比较的数学表示的形式。

如何将一句话转化为数学的形式呢?人们能想到最简单的方式就是统计一句话有多少个词;然后很自然的比较两句话的相似程度就看它们有多少个词一样就好了;再比上两个句子的总词数,就能得到一个 0-1 之间的相似度了。这就是 Jaccard 相似度计算方法:

仅从一个词语是否出现的角度,进行数学表示,进而进行相似度计算显然是没有充分利用文本中的信息的。人们希望对一句话数学的表示能够更多地体现它包含的语义信息。因此人们想到,比起统计词的总数,记录每个出现的词和其出现的频率会获得更多的信息,这就是构造 Bag of words(词袋)。比如下列一个片段:

1.it was the best of times

2.it was the worst of times

3.it was the age of wisdom

4.it was the age of foolishness

这个片段中出现的所有词构成一个词袋["it","was","the","best","of","times","worst","age","wisdom","foolishness"],词袋中共有10个词,根据每个词是否出现可以将每个句子转化为10维的向量。对应词语在整个片段中出现的顺序,第一句话可以转化为向量, 相应地,第二、三、四句话分别为,,。

在词袋的基础上能获得哪些语义特征呢?首先每个词的重要性是不同的,希望比较相似度时更重要的词能占有更多的权重。因此需要想办法能够给句子中的词做关键词排序,一个经典的评估一个单词重要程度的方法是 TF-IDF(词频-逆文档频率)。人们在阅读时知道一个词重要是因为人们读过很多书,学习到了这个词是重要的。TF-IDF 在做重要性排序时,也是学习了很多文档才获得的有关重要性的信息。基本思想是一个词在一篇文章中出现的频率越高且在其它文章中出现的频率越低,这个词就越重要。计算一个词在一篇文档中的重要性时考虑:

该词在该文档中出现的频率该文档所有词出现的词频总数 语料库的文档总数包含该次的文档数

词语的权重就是 。对于之前例子中的向量,每个元素都需要乘上这个对应的权重。

之后人们又想到,词袋模型并不能保留原本语句的语序信息,而我们或许可以通过语序得到一些同义词,这些词本身就比另一些词更相似。这时一个假设被提出:如果两个词的上下文相似,那么这两个词更有可能是同义词。Word2Vec[1]就是利用这一假设训练出词向量来表示一个词。Word2Vec 有两种训练方法,这里介绍通过上下文预测中间这个词被称为 CBOW(Continuous Bag-of-Words Model)的方法(另一种训练方法SKIP-GRAM与它正相反,通过中间词预测上下文)。它其实是一个三层神经网络模型。如图所示:

17b884ed0dbfe008137b9c1427c2534c.png

以图中的神经网络为例,它Input Layer 是一个词上下文各两个词的 one-hot 编码4个10维的向量:,实际的 Output 是一个概率分布,训练目标是让这个概率分布尽可能接近所要预测词的 one-hot 编码.

具体过程如下:

  1. 这里one-hot 编码是 10维向量,假设需要得到的是 8 维词向量。

  2. 通过训练得到两个权值矩阵 ()和 ()。

  3. 从 Input Layer 到 Hidden Layer,分别乘上 W 得到4个 8 维的向量,再取平均得到一个 8 维向量;

  4. 从 Hidden Layer 到 Output Layer, 又与 相乘,再用 Softmax 处理得到一个概率分布。

  5. 这个概率分布与要预测的词的 one-hot 编码越接近越好。

  6. 事实上,就是我们需要的8维词向量;

有了每个词对应的词向量后,我们可以将句子转化为一组向量表示。例如词向量为8维,片段一中的每句话有6个词,就将每个句子转化为6个8维的向量作为一组。

将词转化为词向量也就称之为词嵌入(Word Embedding),这里所说的 Word2Vec 是实现词嵌入的一种算法。

由词嵌入再往下就要讲到预训练语言模型了。预训练语言模型是 NLP 中很重要的研究领域,它的想法等同要让一个小孩去写作,要先让其识字、读书、理解文字的意义之后再动笔写东西。为了让机器实现这一想法,先通过一些任务预训练(Pre-training)出一个模型,这个模型能够提取出一些通用的语义特征,然后再在一些具体的任务上比如文本分类、序列标注上做精调(Fine-tuning),这样通过之前对语义理解能够去做各种各样的下游任务。

其实词嵌入就相当于预训练语言模型中预训练的这一部分。当然现在的预训练语言模型已经在 CBOW 的基础上改进了很多,并且运用了特征抽取器使学习到的语义能够更好地运用于下游任务。

关于预训练语言模型这里暂不做更多的介绍,先继续讲讲将句子转化为能比较的向量以后该怎样计算相似度。

相似度的计算

从第一部分的内容我们将一个句子转化成了两种形式:一种是一个句子转化为一个向量的形式;另一种是句子中的每个词都转化为一个向量(Word2Vec)。

通常比较两个长度相等的向量(N 维)可以计算余弦距离或欧式距离:

  • 余弦相似度:
  • 欧氏距离:

而 Word2Vec 将每个句子转化为了一组 N 维向量,要比较两组 N 维向量,我们需要再将每组 N 维向量转化为一个 N 维向量再计算余弦相似度或欧氏距离。这个过程称为 Sentence embedding。

首先可以将一个句子中所有词向量取平均;另外可以结合第一部分所说的 TF-IDF 给所有词向量赋予权重后做加权平均。

还有一种取权重做加权的方法称为SIF(smooth inverse frequency)[2],这种方法的核心是通过去掉一组句子的共有成分来保留各自关键的部分,以之前的片段为例,共有4个句子,每个句子包含6个词向量,每个词向量有8维:

  • 首先对每个词 计算一个权重为 其中 为超参数,为 在所有句子中的词频(通过在训练集句子中的词频做估计),得词频越低权重越大;
  • 对每个句子中6个词向量根据对应词的权重做加权平均得到4个8维的句向量 ~;
  • ~作为列得到一个矩阵 X,X 的第一主成分为 u(通过奇异值分解得到);
  • 最后减去在 u 上的投影得到最终的句向量 :

相关示例

利用gensim 库[3]训练 word2vec 模型:

from gensim.models import Word2Vec
#text为语料库
model = Word2Vec(text, size=8, window=5, min_count=1)
model.save("word2vec.model")
sentence0=['it', 'was', 'the', 'best', 'of', 'times']
sentence1=['it', 'was', 'the', 'worst', 'of', 'times']
#vector0和vector1分别存两组词向量
vector0=dict()
for word in sentence0:
    vector0[word]=model.wv[word]
vector1=dict()
for word in sentence1:
    vector1[word]=model.wv[word]

在实际训练时 text 为我们所准备的语料库,size 是我们所需要的词向量维度,window 是每次去上下文共多少个词训练,min_count 为词最少出现的次数。之后通过这个 word2vec.model 我们可以将需要计算相似度的句子做一个 word embedding。

利用 gensim 库训练 TF-IDF 模型:

from gensim.models import TfidfModel
from gensim.corpora import Dictionary
#document为一组句子,包括sentence0和sentence0
dct = Dictionary(document)  
corpus = [dct.doc2bow(line) for line in document]
model1= TfidfModel(corpus) 

根据TF-IDF模型找到每个词向量与对应的权重,每个句子中所有词向量加权平均得到句向量 s0 和 s1:

s0=0
wd=dict(model1[corpus[0]])
for word in sentence0:
    w=wd[dct.token2id[word]]
    s0+= vector0[word]*w
s0=s0/len(sentence0)
s1=0
wd1=dict(model1[corpus[1]])
for word in sentence1:
    w=wd1[dct.token2id[word]]
    s1+= vector1[word]*w
s1=s1/len(sentence1)

利用公式计算余弦相似度:

import numpy as np
d1=np.dot(s0,s1)/(np.linalg.norm(s0)*np.linalg.norm(s1))

总结

NLP 是一个发展很快的人工智能领域,新的算法不断出现打败之前的算法,本文讲解了做文本相似度计算在内的自然语言处理任务一个很基本的思路:都是需要先在大量的语料上进行学习,然后根据所学将文本用数学表示,再根据数学表示的形式进行之后的处理。

本文介绍的方法也存在一些明显缺点,主要有两点:1、没有考虑多义词;2、没法根据具体任务做动态优化。

在此希望对自然语言处理感兴趣的读者能持续关注这一专题,之后会继续给大家分享现在的 NLP 算法是如何处理这些问题的。

参考资料

[1]

Efficient Estimation of Word Representation in Vector Space: https://arxiv.org/pdf/1301.3781v3.pdf

[2]

Sanjeev Arora, et al. 2017. A Simple but Tough-to-Beat Baseline for Sentence Embeddings: https://openreview.net/pdf?id=SyK00v5xx

[3]

GENSIM: https://radimrehurek.com/gensim/models/word2vec.html

我们是光大科技公司的追光实验室团队,将不定期推出数据挖掘和算法相关的数据科学原创文章。团队定位打造基于知识驱动的机器学习实验室,由实践经验丰富的数据分析挖掘工程师和专注算法的数据科学家精心准备相关作品,志在分享结合实际业务的理论应用和算法创新,以及其中的心得体会。

往期回顾
评分卡建模工具之变量聚类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值