『行远见大』 LCQMC 信息检索文本相似度 Baseline

news/2025/3/15 14:30:33/

『行远见大』 LCQMC 信息检索文本相似度 Baseline

项目简介

LCQMC 中文问题匹配相似度计算,根据两段信息检索文本在语义上是否相似进行二分类,相似判断为1,不相似判断为0。本项目为各位同学提供一个 Baseline:acc = 0.89751,各位同学可参考本项目并在此基础上调优。

数据集介绍

LCQMC(A Large-scale Chinese Question Matching Corpus), 百度知道领域的中文问题匹配数据集,目的是为了解决在中文领域大规模问题匹配数据集的缺失。该数据集从百度知道不同领域的用户问题中抽取构建数据。

数据集名称训练集大小验证集大小测试集大小
LCQMC238,7668,80212,500

数据集链接:https://aistudio.baidu.com/aistudio/datasetdetail/78992

比赛报名

报名链接:https://aistudio.baidu.com/aistudio/competition/detail/45

致敬开源

大家好,我是行远见大。欢迎你与我一同建设飞桨开源社区,知识分享是一种美德,让我们向开源致敬!

前置基础知识

文本语义匹配

文本语义匹配是自然语言处理中一个重要的基础问题,NLP 领域的很多任务都可以抽象为文本匹配任务。例如,信息检索可以归结为查询项和文档的匹配,问答系统可以归结为问题和候选答案的匹配,对话系统可以归结为对话和回复的匹配。语义匹配在搜索优化、推荐系统、快速检索排序、智能客服上都有广泛的应用。如何提升文本匹配的准确度,是自然语言处理领域的一个重要挑战。

  • 信息检索:在信息检索领域的很多应用中,都需要根据原文本来检索与其相似的其他文本,使用场景非常普遍。
  • 新闻推荐:通过用户刚刚浏览过的新闻标题,自动检索出其他的相似新闻,个性化地为用户做推荐,从而增强用户粘性,提升产品体验。
  • 智能客服:用户输入一个问题后,自动为用户检索出相似的问题和答案,节约人工客服的成本,提高效率。

让我们来看一个简单的例子,比较各候选句子哪句和原句语义更相近:

原句:“车头如何放置车牌”

  • 比较句1:“前牌照怎么装”
  • 比较句2:“如何办理北京车牌”
  • 比较句3:“后牌照怎么装”

(1)比较句1与原句,虽然句式和语序等存在较大差异,但是所表述的含义几乎相同

(2)比较句2与原句,虽然存在“如何” 、“车牌”等共现词,但是所表述的含义完全不同

(3)比较句3与原句,二者讨论的都是如何放置车牌的问题,只不过一个是前牌照,另一个是后牌照。二者间存在一定的语义相关性

所以语义相关性,句1大于句3,句3大于句2,这就是语义匹配。

短文本语义匹配网络

短文本语义匹配(SimilarityNet, SimNet)是一个计算短文本相似度的框架,可以根据用户输入的两个文本,计算出相似度得分。主要包括 BOW、CNN、RNN、MMDNN 等核心网络结构形式,提供语义相似度计算训练和预测框架,适用于信息检索、新闻推荐、智能客服等多个应用场景,帮助企业解决语义匹配问题。

SimNet 模型结构如图所示,包括输入层、表示层以及匹配层。

SimilarityNet 框架

SimilarityNet模型框架结构图

模型框架结构图如下图所示,其中 query 和 title 是数据集经过处理后的待匹配的文本,然后经过分词处理,编码成 id,经过 SimilarityNet 处理,得到输出,训练的损失函数使用的是交叉熵损失。

SimilarityNet 模型框架结构图

LCQMC 信息检索文本相似度计算

环境配置

# 导入必要的库
import math
import numpy as np
import os
import collections
from functools import partial
import random
import time
import inspect
import importlib
from tqdm import tqdmimport paddle
import paddle.nn as nn
import paddle.nn.functional as F
from paddle.io import IterableDataset
from paddle.utils.download import get_path_from_urlprint("本项目基于Paddle的版本号为:"+ paddle.__version__)
本项目基于Paddle的版本号为:2.1.0
# AI Studio上的PaddleNLP版本过低,所以需要首先升级PaddleNLP
!pip install paddlenlp --upgrade
# 导入PaddleNLP相关的包
import paddlenlp as ppnlp
from paddlenlp.data import JiebaTokenizer, Pad, Stack, Tuple, Vocab
# from utils import convert_example
from paddlenlp.datasets import MapDataset
from paddle.dataset.common import md5file
from paddlenlp.datasets import DatasetBuilderprint("本项目基于PaddleNLP的版本号为:"+ ppnlp.__version__)
本项目基于PaddleNLP的版本号为:2.0.2

加载预训练模型 ERNIE

# 若运行失败,请重启项目
MODEL_NAME = "ernie-1.0"
ernie_model = ppnlp.transformers.ErnieModel.from_pretrained(MODEL_NAME)
model = ppnlp.transformers.ErnieForSequenceClassification.from_pretrained(MODEL_NAME, num_classes=2)
# 定义ERNIE模型对应的 tokenizer,并查看效果
tokenizer = ppnlp.transformers.ErnieTokenizer.from_pretrained(MODEL_NAME)
[2021-06-08 23:52:00,862] [    INFO] - Downloading vocab.txt from https://paddlenlp.bj.bcebos.com/models/transformers/ernie/vocab.txt
100%|██████████| 90/90 [00:00<00:00, 20643.52it/s]
tokens = tokenizer._tokenize("行远见大荣誉出品")
print("Tokens: {}".format(tokens))# token映射为对应token id
tokens_ids = tokenizer.convert_tokens_to_ids(tokens)
print("Tokens id: {}".format(tokens_ids))# 拼接上预训练模型对应的特殊token ,如[CLS]、[SEP]
tokens_ids = tokenizer.build_inputs_with_special_tokens(tokens_ids)
print("Tokens id: {}".format(tokens_ids))
# 转化成paddle框架数据格式
tokens_pd = paddle.to_tensor([tokens_ids])
print("Tokens : {}".format(tokens_pd))# 此时即可输入ERNIE模型中得到相应输出
sequence_output, pooled_output = ernie_model(tokens_pd)
print("Token wise output: {}, Pooled output: {}".format(sequence_output.shape, pooled_output.shape))
Tokens: ['行', '远', '见', '大', '荣', '誉', '出', '品']
Tokens id: [40, 629, 373, 19, 838, 1054, 39, 100]
Tokens id: [1, 40, 629, 373, 19, 838, 1054, 39, 100, 2]
Tokens : Tensor(shape=[1, 10], dtype=int64, place=CUDAPlace(0), stop_gradient=True,[[1  , 40 , 629, 373, 19 , 838, 1054, 39 , 100, 2  ]])
Token wise output: [1, 10, 768], Pooled output: [1, 768]/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/tensor/creation.py:125: DeprecationWarning: `np.object` is a deprecated alias for the builtin `object`. To silence this warning, use `object` by itself. Doing this will not modify any behavior and is safe. 
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecationsif data.dtype == np.object:
encoded_text = tokenizer(text="行远见大荣誉出品",  max_seq_len=20)
for key, value in encoded_text.items():print("{}:\n\t{}".format(key, value))# 转化成paddle框架数据格式
input_ids = paddle.to_tensor([encoded_text['input_ids']])
print("input_ids : {}".format(input_ids))
segment_ids = paddle.to_tensor([encoded_text['token_type_ids']])
print("token_type_ids : {}".format(segment_ids))# 此时即可输入 ERNIE 模型中得到相应输出
sequence_output, pooled_output = ernie_model(input_ids, segment_ids)
print("Token wise output: {}, Pooled output: {}".format(sequence_output.shape, pooled_output.shape))
input_ids:[1, 40, 629, 373, 19, 838, 1054, 39, 100, 2]
token_type_ids:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
input_ids : Tensor(shape=[1, 10], dtype=int64, place=CUDAPlace(0), stop_gradient=True,[[1  , 40 , 629, 373, 19 , 838, 1054, 39 , 100, 2  ]])
token_type_ids : Tensor(shape=[1, 10], dtype=int64, place=CUDAPlace(0), stop_gradient=True,[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
Token wise output: [1, 10, 768], Pooled output: [1, 768]

加载数据集

# 首次运行需要把注释(#)去掉
# !unzip -oq /home/aistudio/data/data78992/lcqmc.zip
# 删除解压后的无用文件
!rm -r __MACOSX

查看数据

import pandas as pdtrain_data = "./lcqmc/train.tsv"
train_data = pd.read_csv(train_data, header=None, sep='\t')
p='\t')
train_data.head(10)
012
0喜欢打篮球的男生喜欢什么样的女生爱打篮球的男生喜欢什么样的女生1
1我手机丢了,我想换个手机我想买个新手机,求推荐1
2大家觉得她好看吗大家觉得跑男好看吗?0
3求秋色之空漫画全集求秋色之空全集漫画1
4晚上睡觉带着耳机听音乐有什么害处吗?孕妇可以戴耳机听音乐吗?0
5学日语软件手机上的手机学日语的软件1
6打印机和电脑怎样连接,该如何设置如何把带无线的电脑连接到打印机上0
7侠盗飞车罪恶都市怎样改车侠盗飞车罪恶都市怎么改车1
8什么花一年四季都开什么花一年四季都是开的1
9看图猜一电影名看图猜电影!1

读取数据

class lcqmcfile(DatasetBuilder):SPLITS = {'train': 'lcqmc/train.tsv','dev': 'lcqmc/dev.tsv',}def _get_data(self, mode, **kwargs):filename = self.SPLITS[mode]return filenamedef _read(self, filename):with open(filename, 'r', encoding='utf-8') as f:head = Nonefor line in f:data = line.strip().split("\t")if not head:head = dataelse:query, title, label = datayield {"query": query, "title": title, "label": label}def get_labels(self):return ["0", "1"]
def load_dataset(name=None,data_files=None,splits=None,lazy=None,**kwargs):reader_cls = lcqmcfileprint(reader_cls)if not name:reader_instance = reader_cls(lazy=lazy, **kwargs)else:reader_instance = reader_cls(lazy=lazy, name=name, **kwargs)datasets = reader_instance.read_datasets(data_files=data_files, splits=splits)return datasets
train_ds, dev_ds = load_dataset(splits=["train", "dev"])
<class '__main__.lcqmcfile'>

模型构建

from functools import partial
from paddlenlp.data import Stack, Tuple, Pad
from utils import  convert_example, create_dataloaderbatch_size = 64
max_seq_length = 128trans_func = partial(convert_example,tokenizer=tokenizer,max_seq_length=max_seq_length)
batchify_fn = lambda samples, fn=Tuple(Pad(axis=0, pad_val=tokenizer.pad_token_id),       # inputPad(axis=0, pad_val=tokenizer.pad_token_type_id),  # segmentStack(dtype="int64")                               # label
): [data for data in fn(samples)]
train_data_loader = create_dataloader(train_ds,mode='train',batch_size=batch_size,batchify_fn=batchify_fn,trans_fn=trans_func)
dev_data_loader = create_dataloader(dev_ds,mode='dev',batch_size=batch_size,batchify_fn=batchify_fn,trans_fn=trans_func)

训练配置

from paddlenlp.transformers import LinearDecayWithWarmup# 训练过程中的最大学习率
learning_rate = 5e-5
# 训练轮次
epochs = 3
# 学习率预热比例
warmup_proportion = 0.1
# 权重衰减系数,类似模型正则项策略,避免模型过拟合
weight_decay = 0.01num_training_steps = len(train_data_loader) * epochs
lr_scheduler = LinearDecayWithWarmup(learning_rate, num_training_steps, warmup_proportion)
optimizer = paddle.optimizer.AdamW(learning_rate=lr_scheduler,parameters=model.parameters(),weight_decay=weight_decay,apply_decay_param_fun=lambda x: x in [p.name for n, p in model.named_parameters()if not any(nd in n for nd in ["bias", "norm"])])criterion = paddle.nn.loss.CrossEntropyLoss()
metric = paddle.metric.Accuracy()

模型训练

import paddle.nn.functional as F
from utils import evaluateglobal_step = 0
for epoch in range(1, epochs + 1):for step, batch in enumerate(train_data_loader, start=1):input_ids, segment_ids, labels = batchlogits = model(input_ids, segment_ids)loss = criterion(logits, labels)probs = F.softmax(logits, axis=1)correct = metric.compute(probs, labels)metric.update(correct)acc = metric.accumulate()global_step += 1if global_step % 10 == 0 :print("global step %d, epoch: %d, batch: %d, loss: %.5f, acc: %.5f" % (global_step, epoch, step, loss, acc))loss.backward()optimizer.step()lr_scheduler.step()optimizer.clear_grad()evaluate(model, criterion, metric, dev_data_loader)
global step 11130, epoch: 3, batch: 3668, loss: 0.03892, acc: 0.95436
global step 11140, epoch: 3, batch: 3678, loss: 0.10784, acc: 0.95436
global step 11150, epoch: 3, batch: 3688, loss: 0.13684, acc: 0.95438
global step 11160, epoch: 3, batch: 3698, loss: 0.10246, acc: 0.95437
global step 11170, epoch: 3, batch: 3708, loss: 0.10320, acc: 0.95436
global step 11180, epoch: 3, batch: 3718, loss: 0.05931, acc: 0.95435
global step 11190, epoch: 3, batch: 3728, loss: 0.01982, acc: 0.95436
eval loss: 0.31526, accu: 0.89751

Baseline 运行了3个 epoch,用时约45分钟。

  • epoch1:eval loss: 0.32242, accu: 0.86558
  • epoch2:eval loss: 0.30539, accu: 0.88910
  • epoch3:eval loss: 0.31526, accu: 0.89751

保存模型

model.save_pretrained('xyjd')
tokenizer.save_pretrained('xyjd')

预测模型

测试结果

from utils import predict
import pandas as pdlabel_map = {0:'0', 1:'1'}def preprocess_prediction_data(data):examples = []for query, title in data:examples.append({"query": query, "title": title})# print(len(examples),': ',query,"---", title)return examples
test_file = 'lcqmc/test.tsv'
data = pd.read_csv(test_file, sep='\t')
# print(data.shape)
data1 = list(data.values)
examples = preprocess_prediction_data(data1)

输出 tsv 文件

results = predict(model, examples, tokenizer, label_map, batch_size=batch_size)for idx, text in enumerate(examples):print('Data: {} \t Label: {}'.format(text, results[idx]))data2 = []
for i in range(len(data1)):data2.extend(results[i])data['label'] = data2
print(data.shape)
data.to_csv('lcqmc.tsv',sep='\t')

在测试集上的部分预测结果展示:

作者简介

  • 飞桨主页:行远见大
  • 个人经历:上海开源信息技术协会成员
  • 我的口号:向开源致敬,一同建设飞桨开源社区
  • 常住地址:常年混迹在 AI Studio 平台和各类 PaddlePaddle 群
  • QQ:1206313185 添加时请备注添加原因和 AI Studio 的 ID
  • 感谢小伙伴们一键三连(喜欢♡、fork〧、关注+)支持,点 ♡ 数越多,更新越快~
文章来源:https://blog.csdn.net/m0_63642362/article/details/121135031
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ppmy.cn/news/907946.html

相关文章

几个经典的游戏

仙剑奇侠传2 游戏简介&#xff1a; 《仙剑奇侠传》这款华人的经典游戏&#xff0c;在1995年七月发行后&#xff0c;即在各种游戏杂志的排行榜中名列前茅&#xff0c;并连续蝉连海峡两岸各大杂志排行榜第一名近二年时间&#xff0c;同时取得日本SEGA Saturn游戏主机的开发授权&a…

微服务系列文章之 Springboot应用在k8s集群中配置的使用

Docker部署其实也可以再docker run或者dockerfile里面&#xff0c;将配置文件目录映射到宿主机&#xff0c;然后通过宿主机配置文件修改参数。 FROM docker.io/python:3.6MAINTAINER tianye # 设置容器时间 RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&am…

PaddleNLP/ examples / semantic_indexing

召回库转换成向量存入hnswlib&#xff0c;然后查询的也转换成向量进行查询即可转换成向量的方式有很多种方式 https://blog.csdn.net/CHIERYU/article/details/81989920 语义索引模型的目标是: 给定输入文本&#xff0c;模型可以从海量候选召回库中快速、准确地召回一批语义相…

双塔模型-语义索引策略 [In-batch Negatives]

背景介绍 语义索引&#xff08;可通俗理解为向量索引&#xff09;技术是搜索引擎、推荐系统、广告系统在召回阶段的核心技术之一。语义索引模型的目标是&#xff1a;给定输入文本&#xff0c;模型可以从海量候选召回库中快速、准确地召回一批语义相关文本。语义索引模型的效果…

指针进阶(万字深层次指针解析)

❤️ 作者简介 &#xff1a;对纯音乐情有独钟的阿甘 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识&#xff0c;对纯音乐有独特的喜爱 &#x1f4d7; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;如果你也感兴趣的话欢迎关注博主&#xff0c;期待更新 指针进阶 …

Tik Tok运营干货:如何在Tik Tok上做出爆款视频?

在TikTok这个流量风口的形势下&#xff0c;我们又该如何顺应发展&#xff0c;在TikTok平台的创造更多的内容去涨粉? 目前&#xff0c;TikTok即将全面进入商业化阶段&#xff0c;印尼和英国小店功能功能也逐渐齐全稳定&#xff0c;东南亚等国也在前不久上线&#xff0c;后续将…

计算机科学硕士学位的英文,中国计算机专业研究生英文学术论文摘要中元话语的使用与特征研究...

摘要&#xff1a; 元话语是构建语篇,引导读者理解语篇内容并表明作者态度,实现作者与读者互动的重要语言形式.近年来,元话语研究受到广泛的关注.国外学界对元话语进行了大量的理论和实证研究,并取得了丰富的成果.这些实证研究主要以英语为母语者使用元话语的情况作为基准,研究来…

用ChatGPT创作小说,根据不同情境设定做多线推进

小说根据不同情境设定做多线推进 我们在读小说、看影视剧时&#xff0c;经常会有这样那样的遗憾&#xff1a;这里主角怎么没有吻上去呢&#xff1f;为什么不能给个大团圆结局呢&#xff1f;再仔细找找就能发现宝藏了啊&#xff01;等等等等……在网剧领域&#xff0c;已经开始…