★★★ 本文源自AI Studio社区精品项目,【点击此处】查看更多精品内容 >>>
基于PaddleNLP的中文对话文本匹配
一、赛题解析
1.1 赛题背景
文本匹配任务在自然语言处理中是非常重要的基础任务之一,在问答系统、智能对话等诸多应用场景起到关键性的作用,但中文对话中的文本匹配仍然存在很多难点
1.2 赛题任务
根据问题识别出正确的待匹配文本,给定两个问题Q,判定该问题对语义是否匹配。
1.3 评审规则
赛题数据由训练集和测试集组成,训练集数据集读取代码:
import pandas as pd
pd.read_csv('train.csv',sep='\t')
1.3.1 评估指标
本次竞赛的评价标准采用准确率指标,最高分为1。
计算方法参考https://scikit-learn.org/stable/modules/generated/sklearn.metrics.accuracy_score.html
评估代码参考:
from sklearn.metrics import accuracy_score
y_pred = [0, 2, 1, 3]
y_true = [0, 1, 2, 3]
accuracy_score(y_true, y_pred)
1.3.2 作品提交要求
文件格式:预测结果文件按照csv格式提交
文件大小:无要求
提交次数限制:每支队伍每天最多3次
预测结果文件详细说明:
-
以csv格式提交,编码为UTF-8,第一行为表头;
-
标签顺序需要与测试集文本保持一致;
-
提交前请确保预测结果的格式与sample_submit.csv中的格式一致。具体格式如下:
0
0
0
1.4 比赛传送门
二、Baseline思路
赛题是一个典型的文本匹配的任务,因此基于 ERNIE-Gram 模型搭建匹配网络,然后快速进行语义匹配模型的训练、评估和预测。
流程大致如下:
- 对数据进行分析
- 通过同义词和上下文替换的方式进行数据增强
- 将输入的两个文本拼接为一个序列
- 经过多层 transformer 模块编码后,将输出层的字向量取平均位置的特征作为句向量
- 经 softmax 完整最终分类
2.1 模型介绍
在项目效果上,ERNIE模型 对比Bert 等模型有更好的效果
ERNIE 多粒度预训练语义理解技术
作为自然语言处理的基本语义单元,更充分的语言粒度学习能帮助模型实现更强的语义理解能力:
- ERNIE-Gram 提出显式完备的 n-gram 多粒度掩码语言模型,同步建模 n-gram 内部和 n-gram 之间的语义关系,实现同时学习**细粒度(fine-grained)和粗粒度(coarse-grained)**语义信息
- ERNIE-Gram 采用双流结构,在预训练过程中实现了单一位置多语义粒度层次预测,进一步增强了语义知识学习
三、基于PaddleNLP 构造基线
3.1 数据分析
3.1.1 数据读取
#解压数据文件
!unzip -d /home/aistudio/data/data177274/data /home/aistudio/data/data177274/中文对话文本匹配挑战赛数据集.zip
Archive: /home/aistudio/data/data177274/中文对话文本匹配挑战赛数据集.zip
creating: /home/aistudio/data/data177274/data/╓╨╬─╢╘╗░╬─▒╛╞е┼ф╠Ї╒╜╚№╩¤╛▌╝п/
inflating: /home/aistudio/data/data177274/data/╓╨╬─╢╘╗░╬─▒╛╞е┼ф╠Ї╒╜╚№╩¤╛▌╝п/sample_submit.csv
inflating: /home/aistudio/data/data177274/data/╓╨╬─╢╘╗░╬─▒╛╞е┼ф╠Ї╒╜╚№╩¤╛▌╝п/test.csv
inflating: /home/aistudio/data/data177274/data/╓╨╬─╢╘╗░╬─▒╛╞е┼ф╠Ї╒╜╚№╩¤╛▌╝п/train.csv
3.1.2 安装需要的库
# 安装paddlenlp 相关框架,且需先更新pip组件
!pip install --upgrade pip
!pip install paddlenlp==2.4.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
!python -m pip install --upgrade matplotlib
from IPython.display import clear_output
clear_output()
print("环境安装成功!请重启内核!!")
环境安装成功!请重启内核!!
3.1.3 查看样例数据
#查看样例数据
#查看训练数据
import pandas as pd
path='/home/aistudio/data/data177274/data/╓╨╬─╢╘╗░╬─▒╛╞е┼ф╠Ї╒╜╚№╩¤╛▌╝п/train.csv'
data=pd.read_csv(path,encoding='utf-8-sig',sep='\t',header=None,names=['text1', 'text2','label'])
data
text1 | text2 | label | |
---|---|---|---|
0 | 藏獒为什么这么贵 | 藏獒见人不咬为什么 | 0 |
1 | 人生应该怎么才算精彩? | 人生要怎么过才算精彩啊 | 1 |
2 | 为什么打牌老是输 | 为什么我枪神纪进不去了 | 0 |
3 | 现在网上卖什么最赚钱 | 网上卖什么最赚钱 | 1 |
4 | 如何提高气质 | 怎样提高自身气质? | 1 |
... | ... | ... | ... |
49995 | 剑灵拳师破力士不屈 | 剑灵力士能否当T | 0 |
49996 | 冰雪奇缘在哪看 | 冰雪奇缘在哪里看 | 1 |
49997 | 高分子材料与材料科学工程那个专业出路好 | 高分子材料科学与工程专业考研 | 0 |
49998 | 李世民的年号是什么? | 李世民是什么王? | 0 |
49999 | 什么软件可以挂机赚钱的 | 什么挂机软件赚钱 | 1 |
50000 rows × 3 columns
3.1.4 计算最大文本长度
#计算最大的文本长度
def max_text_len(self):
for i, col in enumerate(self):
data[col] = data[col].astype(str)
n_max = data[col].str.len().max()
print(i, col, n_max)
#查看最大的内容和文本长度
print(max_text_len(data.columns.tolist()))
0 text1 49
1 text2 131
2 label 1
None
3.1.5 标签分布探索
data['label'].value_counts(normalize=True)
1 0.58078
0 0.41922
Name: label, dtype: float64
#可视化label 为1 和0 的值的分布情况
import matplotlib.pyplot as plt
import numpy as np
label1= data.loc[data['label']=='1']
label0= data.loc[data['label']=='0']
x = np.array(["label-1", "label-0"])
y = np.array([len(label1), len(label0)])
plt.bar(x, y, color = ["blue","red"],width = 0.3)
plt.show()
能够看到在标签分布中58% 都是为1 的标签,42% 的标签为0
3.1.6 数据增强
#计算标签数据间的差额
label_dif=len(label1)-len(label0)
print(label_dif)
8078
#数据增强
#随机抽取label为0的数据,进行数据增强
import sklearn
label0_df=sklearn.utils.shuffle(label0) #随机打乱
label0_data = label0_df.sample(n=label_dif, random_state=0, axis=0)
label0_data.head(5)
text1 | text2 | label | |
---|---|---|---|
19936 | 时空猎人27w战斗力花多少钱 | 时空猎人战斗力最高是多少 | 0 |
3646 | 欧洲移民哪个国家好 | 欧洲哪个国家好移民 | 0 |
34892 | 去腿毛的好方法 | 怎样去黑头?鼻贴用了没效果啊。 | 0 |
14249 | 许地山以?么作自己的笔名 | 用别人的名字买车 | 0 |
29883 | 你觉得我发的这个动漫头像好看吗? | 这两个头像分别是哪部动漫里面的?好看吗? | 0 |
# 将抽取的数据以文件的方式保存
label0_data.to_csv('label0_data.csv', index=False,encoding='utf-8',sep ='|',header =['text1', 'text2','label'])
#构造文件读取函数
def read_file(filename):
lines = []
with open(filename, 'r', encoding='utf-8') as f:
next(f)
for line in f:
lines.append({"text1":line.split("|")[0].strip(),"text2":line.split("|")[1].strip()})
return lines
data_list=read_file('label0_data.csv')
#通过同义词和上下文替换的方式来进行数据增强
from paddlenlp.dataaug import WordSubstitute
aug = WordSubstitute(['synonym','mlm'], create_n=1, aug_n=1)
Replenish_data=pd.DataFrame()
for line in data_list:
text1=aug.augment(line['text1'])
text2=aug.augment(line['text2'])
result={"text1":text1,"text2":text2}
if len(text1)!=0 and len(text2)!=0:
text1=text1[0]
text2=text2[0]
#对于增强失败的数据,直接调换text1和text2的位置
elif len(text1)!=0 and len(text2)==0:
text1=text1[0]
text2=line['text1']
elif len(text1)!=0 and len(text2)==0:
text2=text2[0]
text1=line['text2']
else:
text1=line['text2']
text2=line['text1']
result={"text1":text1,"text2":text2}
Replenish_data=Replenish_data.append(result,ignore_index=True)
#查看数据增强后的数据
Replenish_data
text1 | text2 | |
---|---|---|
0 | 日子猎人27w战斗力花多少钱 | 时空猎人27w战斗力花多少钱 |
1 | 欧洲哪个国家好移民 | 欧洲移民哪个国家好 |
2 | 怎样去黑头?鼻贴用了没效果啊。 | 去腿毛的好方法 |
3 | 用别人的名字买车 | 许地山以?么作自己的笔名 |
4 | 你觉得我发的这个动漫半身像好看吗? | 这两块头像分别是哪部动漫里面的?好看吗? |
... | ... | ... |
8073 | 牛的母爱读书答案 | 牛的母爱阅读答案 |
8074 | 吃湿核桃藉多了会发胖么? | 请问吃核桃能润肠通便吗?天长日久吃它会发胖吗? |
8075 | 昵图网如何查素材被谁下载了 | 求怎样用手机看种子 |
8076 | 花灰色裤子配什么颜色的袄? | 土灰色的裤子配什么颜色的短装呀? |
8077 | 女孩子说我宣你是什么意思? | 女孩子说我是你的汤是什么意思 |
8078 rows × 2 columns
#进行数据集合并
result = pd.merge(data, Replenish_data, how='outer', on=['text1', 'text2'])
result.fillna('0',inplace=True)
result.loc[(result['label']!='1') & (result['label']!='0')]
text1 | text2 | label |
---|
3.2 构造训练集和测试集
# 构造训练集和测试集
import sklearn
df=sklearn.utils.shuffle(result) #随机打乱
train_data = df.sample(frac=0.8, random_state=0, axis=0)
dev_data = df[~df.index.isin(train_data.index)]
# input_ids:字的编码
# token_type_ids:标识是第一个句子还是第二个句子
# attention_mask:标识是不是填充
#生成训练集和测试集的数据文件
train_data.to_csv('train_data.csv', index=False,encoding='utf-8',sep ='♬',header =['text1', 'text2','label'])
dev_data.to_csv('dev_data.csv', index=False,encoding='utf-8',sep ='♬',header =['text1', 'text2','label'])
from paddlenlp.datasets import load_dataset
def read(data_path):
with open(data_path, 'r', encoding='utf-8') as f:
# 跳过列名
next(f)
for line in f:
text1, text2,label = line.strip('\t').split("♬")[0].strip(),line.strip('\t').split("♬")[1].strip(),line.strip('\t').split("♬")[2].strip()
text1 = line.strip('\t').split("♬")[0].strip()
text2 = line.strip('\t').split("♬")[1].strip()
label=line.strip('\t').split("♬")[2].strip()
yield ({'text1': text1, 'text2': text2,'label':label })
# data_path为read()方法的参数
train_dataset = load_dataset(read, data_path='train_data.csv',lazy=False, split="train")
dev_dataset = load_dataset(read, data_path='dev_data.csv',lazy=False, split="dev")
#查看训练集和测试集的划分情况
print(len(train_dataset))
print(len(dev_dataset))
46462
11616
# 输出训练集的前 3 条样本
for idx, example in enumerate(train_dataset):
if idx <= 3:
print(example)
{'text1': '孕妇可以吃草莓吗?', 'text2': '孕妇能吃草莓么', 'label': '1'}
{'text1': '这句日语是什么意思啊?', 'text2': '日语这句话什么意思', 'label': '1'}
{'text1': '求杉杉来吃', 'text2': '武汉话吃饼子?', 'label': '0'}
{'text1': '有哪些好听的经典钢琴曲', 'text2': '好听的伤感钢琴曲有哪些', 'label': '1'}
3.3 加载预训练模型
# 模型加载
import paddlenlp
MODEL_NAME = "ernie-gram-zh"
tokenizer = paddlenlp.transformers.ErnieGramTokenizer.from_pretrained(MODEL_NAME)
[2022-12-29 00:51:20,152] [ INFO] - Already cached /home/aistudio/.paddlenlp/models/ernie-gram-zh/vocab.txt
[2022-12-29 00:51:20,165] [ INFO] - tokenizer config file saved in /home/aistudio/.paddlenlp/models/ernie-gram-zh/tokenizer_config.json
[2022-12-29 00:51:20,167] [ INFO] - Special tokens file saved in /home/aistudio/.paddlenlp/models/ernie-gram-zh/special_tokens_map.json
3.4 数据格式转换
3.4.1 定义转换器
def convert_example(example, tokenizer, max_seq_length=256, is_test=False):
query, title = example["text1"], example["text2"]
encoded_inputs = tokenizer(
text=query, text_pair=title, max_seq_len=max_seq_length)
input_ids = encoded_inputs["input_ids"]
token_type_ids = encoded_inputs["token_type_ids"]
if not is_test:
label = np.array([example["label"]], dtype="int64")
return input_ids, token_type_ids, label
# 在预测或者评估阶段,不返回 label 字段
else:
return input_ids, token_type_ids
# 对训练集的第 1 条数据进行转换
input_ids, token_type_ids, label = convert_example(train_dataset[0], tokenizer)
#查看转换成的结果
print(input_ids)
print(token_type_ids)
print(label)
[1, 1883, 1176, 48, 22, 943, 688, 3496, 1114, 12045, 2, 1883, 1176, 52, 943, 688, 3496, 356, 2]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]
[1]
3.4.2 构建Dataloader
# 为了后续方便使用,我们使用python偏函数(partial)给 convert_example 赋予一些默认参数
# 预训练模型的最大文本长度为512,当前最大文本长度为131
from functools import partial
# 训练集和验证集的样本转换函数
trans_func = partial(
convert_example,
tokenizer=tokenizer,
max_seq_length=256)
# 我们的训练数据会返回 input_ids, token_type_ids, labels 3 个字段
# 因此针对这 3 个字段需要分别定义 3 个组 batch 操作
from paddlenlp.data import Stack, Pad, Tuple
import paddle
import paddlenlp
import paddle.nn as nn
batchify_fn = lambda samples, fn=Tuple(
Pad(axis=0, pad_val=tokenizer.pad_token_id), # input_ids
Pad(axis=0, pad_val=tokenizer.pad_token_type_id), # token_type_ids
Stack(dtype="int64") # label
): [data for data in fn(samples)]
# # 定义分布式 Sampler: 自动对训练数据进行切分,支持多卡并行训练
# batch_sampler = paddle.io.DistributedBatchSampler(train_dataset, batch_size=32, shuffle=True)
# 使用单卡进行评估,所以采用 paddle.io.BatchSampler 即可
batch_sampler = paddle.io.BatchSampler(dev_dataset, batch_size=32, shuffle=False)
# 基于 train_dataset 定义 train_data_loader
# train_data_loader 会自动对训练数据进行切分
train_data_loader = paddle.io.DataLoader(
dataset=train_dataset.map(trans_func),
batch_sampler=batch_sampler,
collate_fn=batchify_fn,
return_list=True)
# 定义 dev_data_loader
dev_data_loader = paddle.io.DataLoader(
dataset=dev_dataset.map(trans_func),
batch_sampler=batch_sampler,
collate_fn=batchify_fn,
return_list=True)
3.5 搭建语义匹配网络
# 我们基于 ERNIE-Gram 模型结构搭建 Point-wise 语义匹配网络
# 所以此处先定义 ERNIE-Gram 的 pretrained_model
pretrained_model = paddlenlp.transformers.ErnieGramModel.from_pretrained(MODEL_NAME)
class PointwiseMatching(nn.Layer):
# 此处的 pretained_model 在本例中会被 ERNIE-Gram 预训练模型初始化
def __init__(self, pretrained_model, dropout=None):
super().__init__()
self.ptm = pretrained_model
self.dropout = nn.Dropout(dropout if dropout is not None else 0.1)
# 语义匹配任务: 相似、不相似 2 分类任务
self.classifier = nn.Linear(self.ptm.config["hidden_size"], 2)
def forward(self,
input_ids,
token_type_ids=None,
position_ids=None,
attention_mask=None):
# 此处的 Input_ids 由两条文本的 token ids 拼接而成
# token_type_ids 表示两段文本的类型编码
# 返回的 cls_embedding 就表示这两段文本经过模型的计算之后而得到的语义表示向量
_, cls_embedding = self.ptm(input_ids, token_type_ids, position_ids,
attention_mask)
cls_embedding = self.dropout(cls_embedding)
# 基于文本对的语义表示向量进行 2 分类任务
logits = self.classifier(cls_embedding)
probs = F.softmax(logits)
return probs
# 定义 Point-wise 语义匹配网络
model = PointwiseMatching(pretrained_model)
[2022-12-29 00:51:41,914] [ INFO] - Already cached /home/aistudio/.paddlenlp/models/ernie-gram-zh/ernie_gram_zh.pdparams
W1229 00:51:41.920701 26894 gpu_resources.cc:61] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 11.2, Runtime API Version: 11.2
W1229 00:51:41.924329 26894 gpu_resources.cc:91] device: 0, cuDNN Version: 8.2.
e: 0, cuDNN Version: 8.2.
3.6 模型训练和评估
3.6.1 定义训练配置
from paddlenlp.transformers import LinearDecayWithWarmup
import paddle.nn.functional as F
import time
from visualdl import LogWriter
import os
#定义模型训练相关配置
#训练轮数
epochs = 20
#学习率
learning_rate=2e-5
# 每间隔 1000 step 在验证集和测试集上进行评估
eval_steps=1000
# 每间隔 10 step 输出训练指标
log_steps =100
# 训练模型保存路径
output_dir = 'checkpoints'
#训练日志保存路径
log_writer = LogWriter('visualdl_log_dir')
#学习率预热比例
warmup=0.01
#总训练步数
num_training_steps = len(train_data_loader) * epochs
# AdamW优化器参数weight_decay
weight_decay=0.01
# 定义 learning_rate_scheduler,负责在训练过程中对 lr 进行调度
lr_scheduler = LinearDecayWithWarmup(learning_rate, num_training_steps, warmup)
# # LayerNorm参数不参与weight_decay
decay_params = [
p.name for n, p in model.named_parameters()
if not any(nd in n for nd in ["bias", "norm"])
]
# 定义 Optimizer
optimizer = paddle.optimizer.AdamW(
learning_rate=lr_scheduler,
parameters=model.parameters(),
weight_decay=weight_decay,
apply_decay_param_fun=lambda x: x in decay_params)
# 采用交叉熵 损失函数
criterion = paddle.nn.loss.CrossEntropyLoss()
# 评估的时候采用准确率指标
metric = paddle.metric.Accuracy()
3.6.2 构造训练函数
#训练函数
def train(model, train_data_loader):
global_step = 0
tic_train = time.time()
for epoch in range(epochs):
for step, batch in enumerate(train_data_loader):
#正向传播
optimizer.clear_grad()
global_step += 1
input_ids, token_type_ids, label = batch
outputs=model(input_ids=input_ids,token_type_ids=token_type_ids)
loss = criterion(outputs, label)
correct = metric.compute(outputs, label)
metric.update(correct)
acc = metric.accumulate()
## 每间隔 100 step 输出训练指标
if global_step % log_steps == 0:
print(
"global step %d, epoch: %d, batch: %d, loss: %.5f, accu: %.5f, speed: %.2f "
% (global_step, epoch, step, loss, acc,
log_steps / (time.time() - tic_train)))
log_writer.add_scalar("train_loss", loss, global_step)
tic_train = time.time()
## 反向梯度回传,更新参数
loss.backward()
optimizer.step()
lr_scheduler.step()
optimizer.clear_grad()
# 每间隔 1000 step 在验证集和测试集上进行评估
if global_step % eval_steps == 0:
evaluate(model, criterion, metric, dev_data_loader, "dev")
# 训练结束后,存储模型参数
if not os.path.exists(output_dir):
os.makedirs(output_dir)
save_dir = os.path.join(output_dir)
# 保存模型参数
save_param_path = os.path.join(save_dir, 'model_state.pdparams')
paddle.save(model.state_dict(), save_param_path)
# 保存tokenizer的词表等
tokenizer.save_pretrained(output_dir)
3.6.3 构造评估函数
# 因为训练过程中同时要在验证集进行模型评估,因此我们先定义评估函数
@paddle.no_grad()
def evaluate(model, criterion, metric, data_loader, phase="dev"):
model.eval()
total_eval_accuracy = 0
total_eval_loss = 0
losses = []
for batch in data_loader:
#正常传播
input_ids, token_type_ids, label = batch
outputs=model(input_ids=input_ids,token_type_ids=token_type_ids)
loss = criterion(outputs, label)
losses.append(loss.numpy())
correct = metric.compute(outputs, label)
metric.update(correct)
accu = metric.accumulate()
print("eval {} loss: {:.5}, accu: {:.5}".format(phase,
np.mean(losses), accu))
model.train()
metric.reset()
3.7 模型训练
3.7.1 进行训练
# 调用模型训练
train(model, train_data_loader)
3.7.2 可视化训练过程
3.8 模型预测
3.8.1 查看测评集的数据
path='/home/aistudio/data/data177274/data/╓╨╬─╢╘╗░╬─▒╛╞е┼ф╠Ї╒╜╚№╩¤╛▌╝п/test.csv'
#validation_data=pd.read_csv(path,encoding='utf-8-sig',sep='\s+',header=None,names=['text1', 'text2'])
validation_data=pd.read_csv(path,encoding='utf-8-sig',sep='\t',header=None,names=['text1', 'text2'])
validation_data.head(5)
text1 | text2 | |
---|---|---|
0 | 成语中的历史人物 | 成语有关的历史人物 |
1 | 黄财神怎样供奉 | 怎样供奉财神 |
2 | 进门是餐厅好吗 | 进门见餐厅好吗 |
3 | 怎么提高理解力? | 怎样提高理解力 |
4 | 仓鼠用什么磨牙都可以吗 | 用什么可以代替仓鼠的磨牙棒 |
3.8.2 定义预测的data_loader
#定义预测的data_loader
# 预测数据的转换函数
# predict 数据没有 label, 因此 convert_exmaple 的 is_test 参数设为 True
trans_func = partial(
convert_example,
tokenizer=tokenizer,
max_seq_length=512,
is_test=True)
# 预测数据的组 batch 操作
# predict 数据只返回 input_ids 和 token_type_ids,因此只需要 2 个 Pad 对象作为 batchify_fn
batchify_fn = lambda samples, fn=Tuple(
Pad(axis=0, pad_val=tokenizer.pad_token_id), # input_ids
Pad(axis=0, pad_val=tokenizer.pad_token_type_id), # segment_ids
): [data for data in fn(samples)]
# 因为测评集的数据没用label,因此需要做特殊调整
def read_file(data_path):
with open(data_path, 'r', encoding='utf-8') as f:
# 跳过列名
next(f)
for line in f:
text1, text2 = line.strip('\t').split("♬")[0].strip(),line.strip('\t').split("♬")[1].strip()
text1 = line.strip('\t').split("♬")[0].strip()
text2 = line.strip('\t').split("♬")[1].strip()
yield ({'text1': text1, 'text2': text2})
#将测评集的数据进行格式转换
validation_data.to_csv('validation_data.csv', index=False,encoding='utf-8',sep ='♬',header =['text1', 'text2'])
#加载测评集数据
validation_dataset = load_dataset(read_file, data_path='validation_data.csv',lazy=False, split="val")
batch_sampler = paddle.io.BatchSampler(validation_dataset, batch_size=32, shuffle=False)
# 生成预测数据 data_loader
validation_data_loader =paddle.io.DataLoader(
dataset=validation_dataset.map(trans_func),
batch_sampler=batch_sampler,
collate_fn=batchify_fn,
return_list=True)
3.8.3 加载训练好的模型
# 加载训练好的模型
state_dict = paddle.load("./checkpoints/model_state.pdparams")
model.set_dict(state_dict)
3.8.4 构造预测函数
#预测函数
import paddle.nn.functional as F
def prediciton(model, data_loader):
model.eval()
prediction_list = []
label_map = {0: '0', 1: '1'}
with paddle.no_grad():
for batch in data_loader:
input_ids, token_type_ids = batch
input_ids = paddle.to_tensor(input_ids)
token_type_ids = paddle.to_tensor(token_type_ids)
# 获取每个样本的预测概率: [batch_size, 2] 的矩阵
outputs=model(input_ids=input_ids,token_type_ids=token_type_ids)
probs = F.softmax(outputs, axis=-1)
idx = paddle.argmax(probs, axis=1).numpy()
idx = idx.tolist()
labels = [label_map[i] for i in idx]
prediction_list.extend(labels)
return prediction_list
3.8.5 进行预测
# 执行预测函数
test_result=prediciton(model,validation_data_loader)
3.9 生成预测结果文件
pd.DataFrame(test_result).to_csv('submit.csv', header=None, index=None)
四、项目总结
4.1 优化思路
这个项目还有很多值得优化的地方,如:
- 多尝试不同的模型,以及调参;
- 多折交叉验证;
- 融合或者 stacking
4.2 作者介绍
本人是AI达人特训营第二期项目中的一名学员,非常有幸能与大家分享自己的所思所想。
作者:范远展 指导导师:黄灿桦