文本匹配,顾名思义,就是描述两段文本之间的关系,是否指向同一语义;比如两句话是否描述同一件事,或者两句话是否是上下文/问题与答案的关系。例:
小宝宝生病怎么办 狗宝宝生病怎么办 明天天气怎么样 明天预报有雨 先帝创业未半而中道崩殂
今天下三分,益州疲弊,此诚危急存亡之秋也
文本匹配任务在自然语言处理中是非常重要的基础任务之一,有很多应用场景;如信息检索、问答系统、智能对话、文本鉴别、智能推荐、文本数据去重等,但文本匹配或者说自然语言处理仍然存在很多难点。
如语言不规范,同一句话可以有多种表达方式;如”股市跳水、股市大跌、股市一片绿”
歧义,同一个词语或句子在不同语境可能表达不同意思;如”割韭菜”,”领盒饭”,”能穿多少穿多少”,在不同语境下语义完全不同
不规范或错误的输入; 如 ” yyds”,”举个栗子”
需要知识依赖;如丰田supra绰号”牛魔王”等。
常见的文本匹配算法如下表,按传统模型和深度模型简单的分为两类:
算法
类型Jaccord传统模型BM25传统模型VSM传统模型SimHash传统模型Levenshtein传统模型cdssm深度模型arc-ii深度模型match_pyramid深度模型mvlstm深度模型bimpm深度模型drcn深度模型esim深度模型textmatching深度模型bert-base深度模型albert-base深度模型
albert-large
深度模型raberta深度模型sbert深度模型
接下来,让我们通过一个例子,来具体看一下各个算法的实现过程及匹配效果(其中并没有实现深度学习类算法)。
举个栗子:
序号句1句2真实结果1
内蒙古锡林郭勒盟多伦县县医院 多伦县医院 1(匹配) 绵阳市四零四医院 四川绵阳404医院 1(匹配) 邓州市人民医院 南召县人民医院 0(不匹配)
1.Jaccard杰卡德相似系数
jaccard相似度是一种非常直观的相似度计算方式,即两句子分词后词语的交集中词语数与并集中词语数之比。
def jaccard(list1, list2):
"""
jaccard相似系数
:param list1:第一句话的词列表
:param list2: 第二句话的词列表
:return:相似度,float值
"""
list1, list2 = set(list1), set(list2) #去重
intersection = list1.intersection(list2) # 交集
union = list1.union(list2) # 并集
Similarity = 1.0 * len(intersection) / len(union) #交集比并集
return Similarity
a.分词
内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院
绵阳市 四 零 四 医院 / 四川 绵阳 404 医院
邓州市 人民 医院 / 南召县 人民 医院
b.去重求交集–并集
多伦县(交集) — 内蒙古、锡林郭勒盟、多伦县、县医院、医院(并集)
医院(交集) — 绵阳市、四、零、医院、四川、绵阳、404(并集)
人民、医院(交集) — 邓州市、人民、医院、南召县(并集)
c.相似度
文本对相似度真实标签
内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院
1/5=0.21
绵阳市 四 零 四 医院 / 四川 绵阳 404 医院
1/7 = 0.141邓州市 人民 医院 / 南召县 人民 医院2/4 = 0.50
- Levenshtein编辑距离
一个句子转换为另一个句子需要的编辑次数,编辑包括删除、替换、添加,然后使用最长句子的长度归一化得相似度。
def Levenshtein(text1, text2):
"""
Levenshtein编辑距离
:param text1:字符串1
:param text2:字符串2
:return:相似度,float值
"""
import Levenshtein
distance = Levenshtein.distance(text1, text2)
Similarity = 1 - distance * 1.0 / max(len(text1), len(text2))
return Similarity
a.分词-计字数
内 蒙 古 锡 林 郭 勒 盟 多 伦 县 县 医 院 (14) / 多 伦 县 医 院(5)
绵 阳 市 四 零 四 医 院(8) / 四 川 绵 阳 4 0 4 医 院(9)
邓 州 市 人 民 医 院 (7) / 南 召 县 人 民 医 院(7)
b.计算编辑距离
内 蒙 古 锡 林 郭 勒 盟多 伦 县 县 医 院 ——->删除内、蒙、古、锡、林、郭、勒、盟、县
绵 阳 市 四 零 四医 院 ——->加 四、川
邓 州 市 人 民 医 院 ——->替换邓(南)、 州(召)、 市(县)
文本对距离真实标签
内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院
0.3571
绵阳市 四 零 四 医院 / 四川 绵阳 404 医院
0.3331邓州市 人民 医院 / 南召县 人民 医院0.5710
3 simhash相似度
先计算两句子的simhash二进制编码,然后使用海明距离计算,最后使用两句的最大simhash值归一化得相似度。
def simhash_(text1, text2):
"""
simhash相似度
:param s1: 字符串1
:param s2: 字符串2
:return: 相似度,float值
"""
from simhash import Simhash
text1_simhash = Simhash(text1, f=64) #计算simhash向量
text2_simhash = Simhash(text2, f=64) #计算simhash向量
distance = text1_simhash.distance(text2_simhash) #汉明距离
Similarity = 1 - distance / max(len(bin(text1_simhash.value)), len(bin(text2_simhash.value))) #相似度
return Similarity
a.分词
内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院
绵阳市 四 零 四 医院 / 四川 绵阳 404 医院
邓州市 人民 医院 / 南召县 人民 医院
b.计算词权重(此处用tfidf计算,其他方法也可以)
内蒙古5 锡林郭勒盟5 多伦县2 县医院5 多伦县7 医院1 绵阳市3 四6 零3 四6 医院1 四川5 绵阳5 4045 医院1
邓州市
7
人民
4
医院
1
南召县
7
人民
4
医院
1
c.hash函数映射词向量;先将词映射到二进制编码,然后用b步骤中的权重值替换1,b步骤中权重值的负数替换0
词二进制编码加权重
内蒙古
1111000-5 5 5 5 5 -5 -5 -5
锡林郭勒盟
1100000-5 5 5 -5 -5 -5 -5 -5
多伦县
1111011-2 2 2 2 2 -2 2 2
县医院
101100105 -5 5 5 -5 -5 5 -5
多伦县
1111011-7 7 7 7 7 -7 7 7
医院
100100011 -1 -1 1 -1 -1 -1 1
绵阳市
101111113 -3 3 3 3 3 3 3
四
111001006 6 6 -6 -6 6 -6 -6
零
1011110-3 3 -3 3 3 3 3 -3
四
111001006 6 6 -6 -6 6 -6 -6
医院
100100011 -1 -1 1 -1 -1 -1 1
四川
1100001-5 5 5 -5 -5 -5 -5 5
绵阳
111010-5 -5 5 5 5 -5 5 -5
404
1010-5 -5 -5 -5 5 -5 5 -5医院100100011 -1 -1 1 -1 -1 -1 1邓州市101010117 -7 7 -7 7 -7 7 7人民100010114 -4 -4 -4 4 -4 4 4医院100100011 -1 -1 1 -1 -1 -1 1 南召县111010107 7 7 -7 7 -7 7 -7人民100010114 -4 -4 -4 4 -4 4 4医院100100011 -1 -1 1 -1 -1 -1 1
d.合并(将一段文本内的词向量进行累加)
文本合并向量simhash值
内蒙古锡林郭勒盟多伦县县医院
-7 7 17 7 -3 -17 -3 -13-1 1 1 1 -1 -1 -1 -1
多伦县医院
-6 6 6 8 6 -8 6 8-1 1 1 1 1 -1 1 1
绵阳市四零四医院
13 11 11 -5 -7 17 -7 -111 1 1 -1 -1 -1 -1 -1
四川 绵阳 404 医院
-14 -6 4 -4 4 -16 4 -4-1 -1 1 -1 1 -1 1 -1邓州市人民医院 12 -12 2 -10 10 -12 10 121 -1 1 -1 1 -1 1 1 南召县人民医院12 2 2 -10 10 -12 10 -21 1 1 -1 1 -1 1 -1
e海明距离判断相似度
简单的说,海明距离可以理解为,两个二进制串之间相同位置不同的个数。举个例子,[1,1,1,0,0,0]和[1,1,1,1,1,1]的海明距离就是3。
文本对海明距离真实标签
内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院
31
绵阳市 四 零 四 医院 / 四川 绵阳 404 医院
41邓州市 人民 医院 / 南召县 人民 医院20
4 Bm25相似度
一句话概况其主要思想:对Query(待查询语句)进行语素解析,生成语素qi;然后,对于每个搜索结果D,计算每个语素qi与D的相关性得分,最后,将qi相对于D的相关性得分进行加权求和,从而得到Query与D的相关性得分。
其中,Q表示Query,qi表示Q解析之后的一个语素(对中文而言,我们可以把对Query的分词作为语素分析,每个词看成语素qi。);d表示一个搜索结果文档;Wi表示语素qi的权重,为词语的逆文档频率;R(qi,d)表示语素qi与文档d的相关性得分。
其中,N是待匹配总文本数
n(qt)是出现当前词的文本数
其中 _f(qi, D)_为单词在当前候选文档中的词频
k1、b_为调节因子,通常设为 _k1=2, b=0.75
| D|为当前候选文本数(与目标文本匹配的总条数)
_avgdl_为语料库中所有文档的平均长度。
#Bm25
import math
import jieba
class BM25(object):
def __init__(self, docs):#docs是一个包含所有文本的列表,每个元素是一个文本
self.D = len(docs) #总文本数
self.avgdl = sum([len(doc)+0.0 for doc in docs]) / self.D #平均文本长度
self.docs = docs #文本库列表
self.f = [] # 列表的每一个元素是一个dict,dict存储着一个文档中每个词的出现次数
self.df = {} # 存储每个词及出现了该词的文档数量
self.idf = {} # 存储每个词的idf值
self.k1 = 1.5
self.b = 0.75
self.init()
def init(self):
for doc in self.docs: #对每个文本
tmp = {} #定义一个字典存储词出现频次
for word in doc:
tmp[word] = tmp.get(word, 0) + 1 # 存储每个文档中每个词的出现次数
self.f.append(tmp)
for k in tmp.keys():
self.df[k] = self.df.get(k, 0) + 1
for k, v in self.df.items():
self.idf[k] = math.log(self.D-v+0.5)-math.log(v+0.5) #计算idf
def sim(self, doc, index):
score = 0
for word in doc:
if word not in self.f[index]:
continue
d = len(self.docs[index])
score += (self.idf[word]*self.f[index][word]*(self.k1+1)
/ (self.f[index][word]+self.k1*(1-self.b+self.b*d
/ self.avgdl)))
return score
def simall(self, doc):
scores = []
for index in range(self.D):
score = self.sim(doc, index)
scores.append(score)
return scores
if __name__ == '__main__':
sents1 = ["多伦县医院", #数据库
"四川绵阳404医院",
"南召县人民医院"]
sents2 = ["内蒙古锡林郭勒盟多伦县县医院","绵阳市四零四医院","邓州市人民医院"]#待匹配文本
doc = []
for sent in sents1:
words = list(jieba.cut(sent))
doc.append(words)
print(doc)
s = BM25(doc)
print(s.f)
print(s.idf)
for k in sents2:
print(s.simall(jieba.lcut(k))) #打印相似度匹配结果
内蒙古锡林郭勒盟多伦县县医院绵阳市四零四医院
邓州市 人民 医院
多伦县医院 -1.68
-1.69-1.94
四川 绵阳 404 医院
-2.28
-1.69
-1.94南召县 人民 医院-2.28-1.69
-1.43
5. VSM算法
VSM算法的思路主要分为两步:
(1) 用向量表示句子,用向量表示句子的方法很多,简单的有onehot,词频法,基于语义的有word2vec/fastText/glove/bert/elmo等,本例中使用基于简单的词频的向量化方式。
(2)计算两向量的余弦距离(曼哈顿距离、欧几里得距离、明式距离、切比雪夫距离)得相似度。
#tfidf_余弦
def sim_vecidf(self, s1, s2):
"""词向量通过idf加权平均后计算余弦距离"""
v1, v2 = [], []
# 1. 词向量idf加权平均
for s in jieba.cut(s1):
idf_v = idf.get(s, 1)
if s in voc:
v1.append(1.0 * idf_v * voc[s])
v1 = np.array(v1).mean(axis=0)
for s in jieba.lcut(s2):
idf_v = idf.get(s, 1)
if s in voc:
v2.append(1.0 * idf_v * voc[s])
v2 = np.array(v2).mean(axis=0)
# 2. 计算cosine
sim = self.cosine(v1, v2)
return sim
a.句子向量化
a1.取句子对的唯一词元组
set(内蒙古 锡林郭勒盟 多伦县 县医院 /多伦县 医院) = (内蒙古 锡林郭勒盟 多伦县 县医院 医院)
set(绵阳市 四 零 四 医院 / 四川 绵阳 404 医院) = (绵阳市 四 零 医院 四川 绵阳 404 )
set(邓州市 人民 医院 / 南召县 人民 医院) = (邓州市 人民 医院 南召县)
a2.根据每个句子在元组中的词频建立向量表示
句子向量
内蒙古 锡林郭勒盟 多伦县 县医院
1 1 1 1 0
多伦县 医院
0 0 1 0 1
绵阳市 四 零 四 医院
1 2 1 1 0 0 0
四川 绵阳 404 医院
0 0 0 1 1 1 1邓州市 人民 医院1 1 1 0 南召县 人民 医院0 1 1 1
b.计算余弦距离
句子距离
内蒙古 锡林郭勒盟 多伦县 县医院 / 多伦县 医院
0.3535
绵阳市 四 零 四 医院 / 四川 绵阳 404 医院
0.1889邓州市 人民 医院 / 南召县 人民 医院0.6666
6.BERT模型+余弦相似度
BERT是谷歌在2018年推出的深度语言表示模型,是关于语言理解的深度双向transformers的预训练模型,开启了预训练模型的新篇章。它可以学习文本的语义信息,通过向量形式的输出可以用于下游任务。也就说,它自己已经在大规模预料上训练好的参数,我们在用的时候只需要在这个基础上训练更新参数。bert模型可以解决多种自然语言处理问题,如单文本分类、语句对分类、序列标注等。在解决文本匹配任务时,有两种思路,第一种直接把文本匹配任务作为语句对分类任务,模型输入语句对,输出是否匹配的标签;第二种利用bert模型预训练文本对的上下文嵌入向量,再通过余弦相似度等相似度计算方法验证文本对是否匹配,在此基础上还有很多基于bert模型的变种,篇幅有限不做一一讲述。BERT模型详情请转:(2条消息) 图解Transformer(完整版)_龙心尘-CSDN博客_transformer,在此不做赘述。接下来简单介绍一下bert预训练文本嵌入+余弦相似度的算法框架。
a.首先使用大量公域文本数据对BERT模型进行预训练(或直接用谷歌预训练好的模型)
b.将文本直接输入模型
c.对模型输出的语义向量C,或中间隐层向量,计算余弦相似度,得到匹配结果。
基于深度学习的匹配算法种类繁多,如基于CNN网络、RNN网络、LSTM网络等及各种变种层出不穷,在此不一一列举实现。
传统的文本匹配方法主要关注文本间字与字,词与词的匹配关系,无法准确识别不同表达方式下不同文本的同一指向关系,即语义关系。因此在这一背景下,要对多源异构的海量地址数据进行匹配,传统的文本匹配方法已不再适用,深度学习方法大行其道。但深度学习方法也有自身的局限性,比如对海量文本和算力的高要求等,都使得深度学习方法的普适性大打折扣,因此没有最好的文本匹配算法,只有当前条件下最适合的文本匹配算法。
参考文献:
传统文本匹配算法详解(附代码) – 知乎 (zhihu.com)
文本匹配利器:从孪生网络到Sentence-BERT综述 (360doc.com)
(2条消息) SimHash算法原理_qq_33905939的博客-CSDN博客
一种基于深度学习BERT算法的短文本相似匹配的方法 – 百度文库 (baidu.com)
深度学习文本匹配简述 – ZhangHT97 – 博客园 (cnblogs.com)
文本匹配模型TextMatching – 简书 (jianshu.com)
(2条消息) BERT模型的结构,特点和实践_dream6104的专栏-CSDN博客_bert模型结构
Original: https://blog.csdn.net/weixin_41187013/article/details/118182217
Author: 井底哇哇
Title: 文本匹配算法综述
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/528030/
转载文章受原作者版权保护。转载请注明原作者出处!