文本匹配一直是自然语言处理(NLP)领域一个基础且重要的方向,一般研究两段文本之间的关系。
文本相似度计算、自然语言推理、问答系统、信息检索等,都可以看作针对不同数据和场景的文本匹配应用。
比如信息检索可以归结为搜索词和文档资源的匹配,问答系统可以归结为问题和候选答案的匹配,复述问题可以归结为两个同义句的匹配,这些自然语言处理任务在很大程度上都可以抽象成文本匹配问题。
而文本匹配整体流程基本上都可以分为两个大的步骤:1)提取文本特征,如果是文段,则首先利用主题模型提取文段的主题文本,再将主题文本的文本特征提取;2)计算文本特征的相似度,相似度较高的文本对,则认为是匹配对较高的文本对,目的就达到了。
词粒度文本匹配,即是用于计算两个词语之间的文本特征相似度,是文本匹配中最为简单的任务。
计算两个词语之间的文本相似度,可以用word2vec模型来进行比较。
导入gensim的库和对应的语料,语料中包含多个用空格(’ \n ')分开的词语
import gensim
txt_path = 'data/C000008_test.txt'
sentences = [i.split() for i in open(txt_path, 'r', encoding='utf-8').read().split('\n')]
sentences[:3]
使用上述语料训练word2vec模型,并计算两个词语之间的相似度
model = gensim.models.Word2Vec(
sentences, vector_size=50, window=5, min_count=1, workers=4)
# compare two word
print(model.wv.similarity('中国', '澳大利亚'))
0.07167525
基于句子的短文本匹配,是文本匹配领域研究比较多的一个内容,而且最近几年也有比较的文章和模型发布,详情可以参看深度文本匹配survey这篇文章。
本次的基于句粒度文本匹配,选用的SimCSE模型。SimCSE可以看成是SimBERT的简化版,这个模型去掉了SimBERT的生成部分,仅保留检索模型。
SimCSE训练过程中是没有标签,所以把每个句子自身视为相似句传入,本质上来说就是(自己,自己)作为正例、(自己,别人)作为负例来训练对比学习模型。
代码非常简单,主要使用的了hugging face的预训练模型,输出文本句的embeddings特征,然后计算特征相似度即可
from transformers import AutoModel, AutoTokenizer
import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
tokenizer = AutoTokenizer.from_pretrained("princeton-nlp/sup-simcse-bert-base-uncased")
model = AutoModel.from_pretrained("princeton-nlp/sup-simcse-bert-base-uncased")
# Tokenize input texts
texts = [
"There's a kid on a skateboard.",
"A kid is skateboarding.",
"A kid is inside the house."
]
inputs = tokenizer(texts, padding=True, truncation=True, return_tensors="pt")
import torch
from scipy.spatial.distance import cosine
# Get the embeddings
with torch.no_grad():
embeddings = model(**inputs, output_hidden_states=True, return_dict=True).pooler_output
print(embeddings.shape)
embeddings = embeddings.detach().numpy()
from sklearn.metrics.pairwise import cosine_similarity
cos1 = cosine_similarity([embeddings[0]], [embeddings[1]])
cos2 = cosine_similarity([embeddings[0]], [embeddings[2]])
print("Cosine similarity between \"%s\" and \"%s\" is: %.3f" % (texts[0], texts[1], cos1))
print("Cosine similarity between \"%s\" and \"%s\" is: %.3f" % (texts[0], texts[2], cos2))
篇章粒度的文本匹配,首先要使用主题模型对文章主题进行提取,笔者这里使用的gensim库中的LDA模型,算是比较精度的主题模型,但是精度可能就没有那么好。
from nltk.tokenize import RegexpTokenizer
from gensim import corpora, models
import gensim
tokenizer = RegexpTokenizer(r'\w+')
# create sample documents
doc_a = "Brocolli is good to eat. My brother likes to eat good brocolli, but not my mother."
doc_b = "My mother spends a lot of time driving my brother around to baseball practice."
doc_c = "Some health experts suggest that driving may cause increased tension and blood pressure."
doc_d = "I often feel pressure to perform well at school, but my mother never seems to drive my brother to do better."
doc_e = "Health professionals say that brocolli is good for your health."
# compile sample documents into a list
doc_set = [doc_a, doc_b, doc_c, doc_d, doc_e]
# list for tokenized documents in loop
texts = []
# loop through document list
for i in doc_set:
# clean and tokenize document string
raw = i.lower()
tokens = tokenizer.tokenize(raw)
# add tokens to list
texts.append(tokens)
# turn our tokenized documents into a id <-> term dictionary
dictionary = corpora.Dictionary(texts)
# convert tokenized documents into a document-term matrix
corpus = [dictionary.doc2bow(text) for text in texts]
# generate LDA model
ldamodel = gensim.models.ldamodel.LdaModel(corpus, num_topics=2, id2word = dictionary, passes=20)
然后通过LDA模型求出所有文章的主题词,并计算各个文章之间的相似度矩阵。
如果存在文章较多的情况下,这样的操作是比较耗内存的,不建议这样做,当然,这里是做一个demo,就无所谓了。
index = gensim.similarities.MatrixSimilarity(ldamodel[corpus])
最后便可以求出任一文章与其他文章的相似度
def get_text_sim(text):
doc_bow = [dictionary.doc2bow(text) for text in [tokenizer.tokenize(text.lower())]]
print(doc_bow)
vec_lda = ldamodel[doc_bow]
sims = index[vec_lda]
return sims
get_text_sim(doc_a)
[[(0, 2), (1, 1), (2, 1), (3, 2), (4, 2), (5, 1), (6, 1), (7, 1), (8, 2), (9, 1), (10, 2)]]
array([[0.99999994, 0.99999577, 0.07365547, 0.9999525 , 0.08877519]],
dtype=float32)
NLP之文本匹配及语义匹配应用介绍
深度文本匹配survey
NLP 语义匹配:业务场景、数据集及比赛
·