赞
踩
文本语义匹配是自然语言处理中一个重要的基础问题,NLP 领域的很多任务都可以抽象为文本匹配任务。例如,信息检索可以归结为查询项和文档的匹配,问答系统可以归结为问题和候选答案的匹配,对话系统可以归结为对话和回复的匹配。语义匹配在搜索优化、推荐系统、快速检索排序、智能客服上都有广泛的应用。如何提升文本匹配的准确度,是自然语言处理领域的一个重要挑战。
让我们来看一个简单的例子,比较各候选句子哪句和原句语义更相近:
比较结果:
LCQMC数据集比释义语料库更通用,因为它侧重于意图匹配而不是释义。LCQMC数据集包含 260,068 个带有人工标注的问题对。
使用准确率Accuracy来评估,即:
准确率(Accuracy)=预测正确的条目数/预测总条目数准确率=预测正确的条目数/预测总条目数
也可以使用文本相似度与标签的皮尔逊系数进行评估,不匹配的文本相似度应该更低。
任务名称 | 难度 |
---|---|
任务1:数据集读取 | 低、1 |
任务2:文本数据分析 | 低、1 |
任务3:文本相似度(统计特征) | 中、2 |
任务4:文本相似度(词向量与句子编码) | 高、3 |
任务5:文本匹配模型(LSTM孪生网络) | 搞、2 |
任务6:文本匹配模型(Sentence-BERT模型) | 高、3 |
任务7:文本匹配模型(SimCSE模型) | 高、3 |
自然语言处理(Natural Language Processing, NLP)是计算机科学、人工智能和语言学的交叉领域,其目标是使计算机能够理解、生成和处理人类语言。常见的 NLP 技术包括语音识别、文本分析、机器翻译等。这些技术都是基于人工智能和机器学习的算法来实现的。
文本匹配是自然语言处理中的一种常见任务。它可以用来判断两个文本之间的相似度或相关性。常见的文本匹配任务包括:文本相似性匹配、问答匹配、查询-文档匹配等。这些任务的具体实现可以使用机器学习技术,例如使用神经网络模型进行文本嵌入,然后使用余弦相似度或其他相似度度量来计算文本之间的相似度。
LCQMC(Large-scale Chinese Question Matching Corpus)是一个大规模的中文文本匹配数据集。它包含超过 400,000 个标记为重复或非重复的问题对。该数据集由中国科学院自动化研究所(CASIA)深度学习技术与应用国家工程实验室(NEL-DLT)创建。
LCQMC 数据集中的问题涵盖广泛的主题,并以口语化的中文编写,使其成为文本匹配模型具有挑战性的数据集。该数据集通常用于训练和评估各种中文文本匹配模型的性能,例如基于神经网络的模型。它还用于中文自然语言处理的研究,例如文本匹配、文本分类和其他 NLP 任务。该数据集为研究人员提供了一个基准,用于评估其模型的性能并将其与最先进的方法进行比较。
import pandas as pd
def load_lcqmc():
'''LCQMC文本匹配数据集
'''
train = pd.read_csv('https://mirror.coggle.club/dataset/LCQMC.train.data.zip',
sep='\t', names=['query1', 'query2', 'label'])
valid = pd.read_csv('https://mirror.coggle.club/dataset/LCQMC.valid.data.zip',
sep='\t', names=['query1', 'query2', 'label'])
test = pd.read_csv('https://mirror.coggle.club/dataset/LCQMC.test.data.zip',
sep='\t', names=['query1', 'query2', 'label'])
return train, valid, test
这一步非常简单,如果要仔细研究数据,我们也可以wget下载到本地看看。
这个数据集就是两个文本是否相似,然后给个标签。
train['len_q1'] = train.query1.apply(lambda x:len(x))
train['len_q2'] = train.query2.apply(lambda x:len(x))
train['len_diff'] = (train['len_q1']-train['len_q2']).apply(lambda x:abs(x))
train_match = train[train['label']==1]
train_mismatch = train[train['label']==0]
train_match['len_q1'].mean(),train_match['len_q2'].mean(),train_match['len_diff'].mean(),train_mismatch['len_q1'].mean(),train_mismatch['len_q2'].mean(),train_mismatch['len_diff'].mean()
我们可以看到,不相似文本的差别与相似文本相比,长度差别更大。
import jieba counts = {} def count_text(txt,counts): # 读取文本 # txt = open("data.txt", "r", encoding='utf-8').read() # 使用精确模式对文本进行分词 words = jieba.lcut(txt) # 通过键值对的形式存储词语及其出现的次数 for word in words: # 去掉词语中的空格 word = word.replace(' ', '') # 如果词语长度为1,则忽略统计 if len(word) == 0: continue # 进行累计 else: counts[word] = counts.get(word, 0) + 1 if __name__ == '__main__': for txt in train.query1: count_text(txt, counts) for txt in train.query2: count_text(txt, counts) for txt in test.query1: count_text(txt, counts) for txt in test.query1: count_text(txt, counts) for txt in valid.query1: count_text(txt, counts) for txt in valid.query1: count_text(txt, counts)
我们得到,数据集一共有40171条数据,单词总数为3397960个。
出现较多的词汇除了常见语气词、介词,有:手机、牌子等。
备注:
在LCQMC数据集中数据采用三列进行存储,其中label为是否含义相同的标签。在任务2中我们希望各位同学,能对中文文本进出初步的分析,找到相似文本对和不相似文本对的差异。
query1 | query2 | label | |
---|---|---|---|
0 | 喜欢打篮球的男生喜欢什么样的女生 | 爱打篮球的男生喜欢什么样的女生 | 1 |
1 | 我手机丢了,我想换个手机 | 我想买个新手机,求推荐 | 1 |
2 | 大家觉得她好看吗 | 大家觉得跑男好看吗? | 0 |
jieba是一个中文分词库,用于将中文句子分解为词组。它使用了基于前缀词典的最大匹配算法,并支持用户自定义词典。要使用Jieba库,首先需要安装它。使用 pip 可以轻松安装:
pip install jieba
安装完成后,可以使用下面的代码对句子进行分词:
import jieba
sentence = "我在学习使用jieba分词"
seg_list = jieba.cut(sentence)
print(" ".join(seg_list))
输出结果是:我 在 学习 使用 jieba 分词。还可以使用jieba.lcut() or jieba.lcut_for_search() 获取词组列表。
利用jieba.cut对数据进行处理。
def _tokenize(self, text): # 文本分词 tokens = list(jieba.cut(text)) token_ids = [] for token in tokens: if token in self.vocab_mapping: # 如果当前词存在于词表,将词转换为词的ID. token_id = self.vocab_mapping[token] token_ids.append(token_id) else: # OOV情况处理 # 如果该词为多字的词,将其拆分多个字,分别将这些字转换为相应的ID; # 如果该词为单字,则从词表中随机采样一个词,其ID作为该词的ID if len(token) > 1: for t in list(token): if t in self.vocab_mapping: token_ids.append(self.vocab_mapping[t]) else: token_ids.append(np.random.choice(len(self.vocab_mapping), 1)[0]) else: token_ids.append(np.random.choice(len(self.vocab_mapping), 1)[0]) # 对文本进行填充或者截断 token_ids, attention_mask = self._pad_truncate(token_ids) return token_ids, attention_mask
利用TF-IDF计算相似文章步骤:
1)使用jieba分词,找出两篇文章的关键词
2)每篇文章各取出若干个关键词(比如20个),合并成一个集合,计算每篇文章对于这个集合中的词的词频(为了避免文章长度的差异,可以使用相对词频)
3)生成两篇文章各自的词频向量
4)计算两个向量的余弦相似度,值越大就表示越相似
可以参考:https://gitee.com/qmckw/tfidf.git
得到tiidf向量以后,我们可以计算余弦相似度。
文本统计特征是指对文本进行统计并得到的一些数值,可以用来描述文本的特征。基础的文本特征包括:
文本长度: 文本中的字符数或单词数
字符频率: 每个字符在文本中出现的次数或频率
单词频率: 每个单词在文本中出现的次数或频率
句子长度: 文本中句子的平均长度
句子数量: 文本中句子的数量
上述文本特征都是无监督的,不局限语言和模型,而且计算快速,在任务3中我们需要大家使用python统计相似的文本和不相似文本的基础统计特征。
步骤1:使用jieba分词,然后使用word2vec训练词向量
步骤2:计算单词的TFIDF或BM25权重
在前面计算余弦值时,我已经用tiidf计算了每个句子中每个词汇的tfidf值。
计算bm25权重,参考:https://blog.csdn.net/Tink1995/article/details/104745144
权重即为idf值,
Mean-Pooling
平均池化,那么我们可以通过分段取均值的方式进行调整向量维度,通过池化可以适当调小维度,减小运算量。以最开始完成的tf idf得到的向量为例池化。
假设窗格大小包含了最大的句向量,即对向量求均值。
显然,平均池化可以较好的体现整个句子的特性。
Max-Pooling
而maxpool比较能反应处贡献较大的单词的特性。
IDF-Pooling / BM25-Pooling
将TF-IDF和池化层结合进行无监督编码的方法可以分为以下步骤:对文本进行预处理,并使用IDF特征,将每个单词的IDF值乘以到单词词向量上。
SIF-Pooling
备注:
词向量(Word Embedding)是在自然语言处理中的一种广泛使用的技术,用于将文本中的单词映射到一个多维空间中的向量表示。这些向量可以保留单词之间的语义相似性,并且可以在模型训练中被用来作为特征。
一种常见的词向量模型是Word2Vec, 其通过训练神经网络来预测上下文单词,学习单词的向量表示。 另外还有基于Transformer模型的词向量模型, 比如 BERT, RoBERTa, GPT, 通过构建大型语料预训练模型并在其上进行 fine-tune来学习向量表示。
在本次任务中我们将尝试使用词向量来计算文本相似度。无监督句子编码(Unsupervised Sentence Encoding)是自然语言处理中的一种技术,用于将句子转换为向量表示。它和词向量类似,但是它通常不需要大量的标记数据来训练。无监督句子编码可以用来做许多自然语言处理任务,如文本分类,问答系统,对话系统等。句子向量表示可以用来计算句子之间的相似度或者在聚类或者知识图谱等系统中使用。
Word2Vec 是一种将单词表示为向量的方法,它使用了神经网络来学习单词之间的语义关系。有两种常见的计算 word2vec 的方法:
Gensim是一个开源的NLP库,它提供了一系列的工具来训练和使用词向量,包括 word2vec。下面是一个简单的示例,说明如何使用 Gensim 来训练中文词向量:
import jieba
from gensim.models import word2vec
# 预处理文本数据,分词并去掉停用词
sentences = [jieba.lcut(line) for line in open('text.txt')]
# 训练词向量
model = word2vec.Word2Vec(sentences, size=100, window=5, min_count=5, workers=4)
# 保存模型
model.save('word2vec.model')
# 查询词向量
print(model['中国'])
无监督句子编码是指使用无监督学习方法(不需要人为标注的样本)将句子转换为向量的过程。这个过程大致分为以下几步:
通过过池化层,我们可以长度不同的文本转换为相同的维度。基础的池化层包括Mean-Pooling、Max-Pooling,可以直接使用Numpy进行计算。基础的池化层是无监督方法,并没有考虑到单词的重要性。
TF-IDF (term frequency-inverse document frequency)是一种常用的文本特征提取方法,用于衡量单词在文档中的重要性。将TF-IDF和池化层结合进行无监督编码的方法可以分为以下步骤:对文本进行预处理,并使用IDF特征,将每个单词的IDF值乘以到单词词向量上。
SIF (Smooth Inverse Frequency) 不需要训练词向量权重,而是通过简单的参数调整来生成词向量。 SIF计算了句子的嵌入中最重要的元素。然后它减去这些句子嵌入中的主要成分。这就可以删除与频率和句法有关的变量,他们和语义的联系不大。
# https://github.com/PrincetonML/SIF/blob/master/src/SIF_embedding.py
def compute_pc(X,npc=1):
svd = TruncatedSVD(n_components=npc, n_iter=7, random_state=0)
svd.fit(X)
return svd.components_
def remove_pc(X, npc=1):
pc = compute_pc(X, npc)
if npc==1:
XX = X - X.dot(pc.transpose()) * pc
else:
XX = X - X.dot(pc.transpose()).dot(pc)
return XX
通过上述池化方法,我们可以将任意长度的句子转换为相同的长度的向量。然后我们就对向量进行归一化,然后向量之间的相似度,以此来衡量句子之间的相似度。结合数据集中的根据相似度标签,上述哪一个特征最有区分性?
如果对此任务不太了解,可以阅读下面博客:https://zhuanlan.zhihu.com/p/37104535
步骤1:定义孪生网络(嵌入层、LSTM层、全连接层)
参考:https://tianchi.aliyun.com/notebook/409641
https://tianchi.aliyun.com/notebook/409589
步骤2:使用文本匹配数据训练孪生网络
参考:https://tianchi.aliyun.com/notebook/409589
bilstm = keras.layers.Bidirectional(keras.layers.LSTM(
self._params['lstm_units'],
return_sequences=True,
dropout=self._params['dropout_rate']
))
rep_left = bilstm(embed_left)
rep_right = bilstm(embed_right)
#运行cdssm模型
#进入text_match文件夹路劲
python word2vec_static.py #训练词向量,仅在第一次运行本项目的模型时需要
python train.py cdssm
loss, acc = model.evaluate(
x=x_test,
y=y_test,
batch_size=128,
verbose=1
)
print("Test loss:",loss, "Test accuracy:",acc)
参考:
RNN(Recurrent Neural network)是一类具有递归结构的神经网络,它可以处理序列数据,例如文本、音频、时间序列数据等。RNN 的核心思想是在网络的隐藏层中循环地计算当前时间步的输出。
https://pytorch.org/docs/stable/generated/torch.nn.RNN.html
import torch
from torch import nn
rnn = nn.RNN(10, 20, 2)
input = torch.randn(5, 3, 10)
h0 = torch.randn(2, 3, 20)
output, hn = rnn(input, h0)
LSTM(Long Short-Term Memory)是一种特殊的 RNN 模型,它在 RNN 的基础上增加了“记忆门”的机制来控制记忆的编写、读取和遗忘。LSTM 的结构主要由三部分组成:输入门、输出门和遗忘门。它们控制 LSTM 对于当前输入和历史记忆的重视程度。
https://pytorch.org/docs/stable/generated/torch.nn.LSTM.html
import torch
from torch import nn
rnn = nn.LSTM(10, 20, 2)
input = torch.randn(5, 3, 10)
h0 = torch.randn(2, 3, 20)
c0 = torch.randn(2, 3, 20)
output, (hn, cn) = rnn(input, (h0, c0))
RNN/LSTM孪生网络是一种文本匹配模型,网络的输入是两个文本序列,分别对文本进行编码,编码后的向量通过一些运算得到相似度分数,来判断文本之间的相似度。使用RNN/LSTM孪生网络进行文本匹配的网络结构如下:文本嵌入、文本特征提取和文本匹配。
参考资料:
参考:https://github.com/datawhalechina/competition-baseline/blob/master/tutorial/bert/bert-nsp-example.ipynb
步骤0:使用BERT对文本进行编码,计算句子对相似度
步骤1:定义BERT网络
步骤2:使用数据完成BERT-NSP训练
无奈,训练的时候空间不够了。
步骤3:对测试数据进行预测
步骤4(进阶):使用BERT模型完成Sentence-BERT,训练并进行预测。
BERT (Bidirectional Encoder Representations from Transformers) 是一种预训练深度神经网络语言模型。它是由 Google AI Language Team 在 2018 年提出的。BERT 是基于 Transformer 架构,具有双向预训练的能力,能够在大量的文本数据上学习到有效的特征表示。
在预训练中,BERT 模型使用了两种预训练任务来学习语言表示(BERT模型本身可以用来做文本匹配,也就是直接使用NSP的训练思路):
Google AI Language Team在2019年发布了中文版BERT,称作"中文版BERT-Base, Chinese Simplified and Traditional, 12-layer, 768-hidden, 12-heads, 110M parameters" 。此外,还有许多开发者和研究者针对中文语料进行了BERT的预训练,如 BERT-wwm、BERT-wwm-ext、RoBERTa-wwm-ext等,提供了更好的中文语言表示能力。
transformers
是一个由 Hugging Face 团队开发和维护的 Python 库,提供了许多用于自然语言处理的预训练模型。这个库提供了对大量热门模型的支持,如 BERT, GPT-2, RoBERTa, XLM, DistilBert, XLNet, CTRL 等,并且提供了许多有用的工具来简化模型的加载和使用。transformers
库是一个很好的工具,可以帮助开发者轻松的使用预训练的深度学习模型进行自然语言处理任务,并提高开发效率。
学习资料:
步骤1:定义SimCSE网络和损失函数
步骤2:使用比赛数据先进行无监督训练,然后进行有监督训练
步骤3:对测试数据进行预测
SimCSE模型的核心是对比学习,对比学习是通过拉近相似数据的距离,拉远不相似数据的距离为目标,更好地学习数据的表征。使得其在文本匹配任务中产生更好的效果。SimCSE模型是一种简单的对比句向量表征的框架,
SimCSE包含无监督和有监督两种方法。
学习资料:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。