🧡基于PaddleHUB快速实现对话生成——以相声捧哏生成器为例

转载自AI Studio 项目链接https://aistudio.baidu.com/aistudio/projectdetail/3502884

使用PaddleHub生成军训感想项目中,基于PaddleHub实现了风格文本的生成,基于Paddle2.0+版本进行的风格文本生成。(因版本升级又报错了)


📖0.项目背景

项目情人节反向操作:一句情话一句怼,什么?春联AI被玩成了闲聊?介绍了自然语言处理生成式任务的案例,但缺少对使用自己的数据进行训练的详细介绍及演示。再加上由于版本升级类似的项目以及基本失效了,因此,最新的PaddleHUB示例项目就很有必要。


本项目基于PaddleHUB完成文本对话生成类任务,以相声逗哏生成介绍如何使用自己的数据集完成匹配文本的生成任务。可以帮助大家快速使用PaddleHub实现想法。

项目的实现效果如下:

逗哏输入:吃个糖糕粘牙

参考资料:

  • https://zhuanlan.zhihu.com/p/42568781
  • https://aistudio.baidu.com/aistudio/projectdetail/2341543

📖1.PaddleHub介绍

通过API便捷地获取PaddlePaddle生态下的预训练模型,完成模型的管理和一键预测。配合使用Fine-tune API,可以基于大规模预训练模型快速完成迁移学习,让预训练模型能更好地服务于用户特定场景的应用。

  • 基于优秀的社区生态和持续的投入,飞桨(PaddlePaddle)已经有一批可以直接调用的模型,可以快速实现想法。

  • 这些模型被根据不同方向存在不同的套件中,例如:基于NLP的PaddleNLP,基于GAN的PaddleGAN等.

如果遇到关键点检测,分割,目标识别等任务,不妨先看看PaddleHub模型库

⏲️ 1.1 安装说明

  • PaddlePaddle 安装

    本项目依赖于 PaddlePaddle 2.2 及以上版本,请参考 安装指南 进行安装

  • PaddleNLP 安装

    pip install --upgrade paddlenlp -i https://pypi.org/simple

  • 环境依赖

    Python的版本要求 3.7+

# 需要的paddlenlp升级
!pip install --upgrade pip --user
!pip install --upgrade paddlenlp -i https://pypi.org/simple

# 解压数据集
!unzip  -o work/data_crosstalk.zip -d /home/aistudio/work/

📖2.数据集介绍

收集的多个相声文本来自网络,经处理过后存为TXT,具体详见相声数据集


数据集制作:

(1). 将逗哏作为输入,捧哏作为输出构建匹配数据集。

(2). 修改read函数中的yield返回值,要求返回值为一个包含了firstsecond字典,first的值为输入,second的值为对应的输出。

(3). first/second的值为经过分词后的数据以’\x02’为间隔进行连接,data_path为输入read的参数。

本项目中先将多个txt合成为一个train.txt,然后再使用jieba分词进行处理。


  • 生成数据示例:{‘first’: ‘尤其\x02这次\x02这个\x02病\x02很重\x02,\x02都\x02破\x02了\x02相\x02了’, ‘second’: ‘怎么\x02破相\x02了\x02?’}
# 数据集处理
import glob
path_ ='work/data_crosstalk/*.txt'
txt_list_ = glob.glob(path_)
with open("work/train.txt","w+") as save_f:
    for txt_ in txt_list_:
        f = open(txt_, encoding='gbk')
        lines = f.readlines()
        for i in range(len(lines)-1):
            if '逗:' in lines[i] and '捧:' in lines[i+1]:
                # 逗哏输入 捧哏输出
                text = lines[i].replace('\n','')[2:] + '\t' + lines[i+1].replace('\n','')[2:] + '\n'
                save_f.write(text)
        f.close()
from paddlenlp.datasets import load_dataset
import pandas as pd
import jieba

def read(data_path):
    data_ori = pd.read_csv(data_path, names=['first','second'], header=None,sep='\t')
    temp = {}
    for i in range(len(data_ori)):
        temp_data_0  = jieba.lcut(data_ori.iloc[i,0]) # 0 是 逗
        temp_data_1  = jieba.lcut(data_ori.iloc[i,1]) # 1 是 捧
        yield {'first': '\x02'.join(temp_data_0), 'second': '\x02'.join(temp_data_1)}

# data_path为read()方法的参数
train_ds = load_dataset(read, data_path='work/train.txt',lazy=False)
for idx in range(2):
    print(train_ds[idx])
    print()
{'first': '感谢\x02于\x02老师\x02,\x02带病\x02来\x02参加\x02演出\x02.\x02这\x02段时间\x02您\x02身体\x02不好', 'second': '不\x02舒服'}

{'first': '尤其\x02这次\x02这个\x02病\x02很重\x02,\x02都\x02破\x02了\x02相\x02了', 'second': '怎么\x02破相\x02了\x02?'}
# 查看前五条数据集
data_ori = pd.read_csv('work/train.txt', names=['first','second'], header=None,sep='\t')
data_ori.head()
firstsecond
0感谢于老师,带病来参加演出.这段时间您身体不好不舒服
1尤其这次这个病很重,都破了相了怎么破相了?
2痔疮痔疮破相啊?
3不完整了没听说过
4乍看看不出来仔细看也没有
# 查看数据的长度分布,修改输入的最大长度
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
len_list = []
for i in range(len(data_ori)):
    temp_data  = data_ori.iloc[i,0] # 0 是 逗 1 是捧
    len_list.append(len(temp_data))
        
import seaborn as sns
import matplotlib.pyplot as plt

plt.figure(figsize=(5,5))
sns.distplot(len_list)
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l0aVFbNS-1645801197744)(output_9_0.png)]

📖3.模型实现及训练

import paddlenlp

# 设置模型名称
MODEL_NAME = 'unimo-text-1.0'
tokenizer = paddlenlp.transformers.UNIMOTokenizer.from_pretrained(MODEL_NAME)
[2022-02-20 16:52:01,087] [    INFO] - Already cached /home/aistudio/.paddlenlp/models/unimo-text-1.0/unimo-text-1.0-vocab.txt
from functools import partial
from utils import convert_example

train_trans_func = partial(
    convert_example,
    tokenizer=tokenizer,
    mode='train')

train_ds.map(train_trans_func, lazy=False, num_workers=4)
<paddlenlp.datasets.dataset.MapDataset at 0x7fb262505690>
idx = 1 # 查看输出
print(train_ds[idx]['input_ids'])
print(train_ds[idx]['token_type_ids'])
print(train_ds[idx]['position_ids'])
print(train_ds[idx]['masked_positions'])
print(train_ds[idx]['labels'])
print()
import paddle
from utils import batchify_fn

batch_size = 32

# 定义BatchSampler
train_batch_sampler = paddle.io.DistributedBatchSampler(
        train_ds, batch_size=batch_size, shuffle=True)

# 定义batchify_fn
train_collate_fn = partial(batchify_fn, pad_val=0, mode='train')

# 构造DataLoader
train_data_loader = paddle.io.DataLoader(
    dataset=train_ds,
    batch_sampler=train_batch_sampler,
    collate_fn=train_collate_fn,
    return_list=True)
from paddlenlp.transformers import UNIMOLMHeadModel
model = UNIMOLMHeadModel.from_pretrained(MODEL_NAME)
[2022-02-20 19:14:21,134] [    INFO] - Already cached /home/aistudio/.paddlenlp/models/unimo-text-1.0/unimo-text-1.0.pdparams
# 训练过程中的最大学习率
learning_rate = 1e-4 

# 训练轮次
epochs = 20

# 学习率预热比例
warmup_proportion = 0.02

# 权重衰减系数,类似模型正则项策略,避免模型过拟合
weight_decay = 0.01

num_training_steps = len(train_data_loader) * epochs

# 学习率衰减策略
lr_scheduler = paddlenlp.transformers.LinearDecayWithWarmup(learning_rate, num_training_steps, warmup_proportion)

decay_params = [
    p.name for n, p in model.named_parameters()
    if not any(nd in n for nd in ["bias", "norm"])
]
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)
from utils import evaluation
import paddle.nn.functional as F
from tqdm import tqdm
for epoch in range(0, epochs):
    for batch in tqdm(train_data_loader,desc='epoch:'+str(epoch+1)):
        labels = batch[-1]
        logits = model(*batch[:-1])
        labels = paddle.nn.functional.one_hot(labels, num_classes=logits.shape[-1])
        labels = paddle.nn.functional.label_smooth(labels)
        loss = F.cross_entropy(logits, labels, soft_label=True)

        loss.backward()
        optimizer.step()
        lr_scheduler.step()
        optimizer.clear_grad()

    ppl = paddle.exp(loss)
    print(" epoch: %d, ppl: %.4f, loss: %.5f" % (epoch + 1, ppl, loss))

from utils import post_process_sum

num_return_sequences = 8 #
inputs = '每天起床第一句'

inputs_ids = tokenizer.gen_encode(
        inputs,
        return_tensors=True,
        add_start_token_for_decoding=True,
        return_position_ids=True)

# 调用生成api并指定解码策略为beam_search
outputs, scores = model.generate(**inputs_ids, decode_strategy='beam_search', num_beams=8,num_return_sequences=num_return_sequences)
print("Result:\n" + 100 * '-')
for i in range(num_return_sequences):
='beam_search', num_beams=8,num_return_sequences=num_return_sequences)
print("Result:\n" + 100 * '-')
for i in range(num_return_sequences):
    print(i, '逗::', inputs, '捧::', ''.join(post_process_sum(outputs[i].numpy(), tokenizer)[1]))
Result:
----------------------------------------------------------------------------------------------------
0 逗:: 每天起床第一句 捧:: 嗯
1 逗:: 每天起床第一句 捧:: 第一句
2 逗:: 每天起床第一句 捧:: 啊
3 逗:: 每天起床第一句 捧:: 阿
4 逗:: 每天起床第一句 捧:: 啊?
5 逗:: 每天起床第一句 捧:: 第一句阿
6 逗:: 每天起床第一句 捧:: 哦
7 逗:: 每天起床第一句 捧:: 第一句啊

🐱4 项目总结


特别注意:如果需要修改数据集,按照项目说明修改后即可一键运行。


有任何问题,欢迎评论区留言交流。
Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐