【闯关答题】PaddleHub中文新闻文本标题分类实战

一.引言:

在逛AI Studio时偶然打开学习地图看到了闯关答题赢积分、算力卡和战力的活动,于是选择了其中NLP赛题 试题三:“中文新闻文本标题分类问题”,并基于PaddleHub面向新手们开个简易的Baseline,还没有参加的一起来嫖免费的算力和积分吧!

活动地址:https://aistudio.baidu.com/aistudio/learnmap

闯关简介:

进入学习地图页面https://aistudio.baidu.com/aistudio/learnmap,选择试题三:中文新闻文本标题分类问题后开始答题。

需注意该答题作业环境限制了paddle版本为1.7.2,版本不兼容可能会带来较差的体验!

因此也可以选择像本案例一样直接新建一个新的notebook,训练和预测完成生成提交结果文件后将结果文件result.txt上传到试题答题环境然后提交即可!感兴趣的也可以尝试在本基线模型上进一步优化提升效果。

通关说明:

1.一键运行Baseline进行模型的训练和预测。

2.进入到本项目主目录下载已经生成好的结果文件result.txt

3.进入答题页面环境然后将result.txt文件上传至与work同级目录下后点击生成版本,生成版本时注意添加result.txt文件哦!

4.停止环境后点击提交试题选择要提交的版本及添加结果文件后进行提交即可通关,通关后可到学习地图页面领取通关奖励(算力+积分)哦!

二.数据读取与处理

2.1 数据分析处理与可视化

# 进入存放数据集的目录
%cd /home/aistudio/data/data12701/
/home/aistudio/data/data12701
# 读取数据集
import pandas as pd
train = pd.read_table('Train.txt', sep='\t',header=None)  # 有标签的训练数据文件
test = pd.read_table('Test.txt', sep='\t',header=None)    # 要进行预测的测试数据文件
# 查看训练数据前5条
train.head()
012
00财经上证50ETF净申购突增
10财经交银施罗德保本基金将发行
20财经基金公司不裁员反扩军 走访名校揽人才
30财经基金巨亏30亿 欲打开云天系跌停自救
40财经基金市场周二缩量走低
# 为训练集添加列名便于后续处理
train.columns = ['id',"label","text_a"]
# 查看测试集数据前5条
test.head()
0
0北京君太百货璀璨秋色 满100省353020元
1教育部:小学高年级将开始学习性知识
2专业级单反相机 佳能7D单机售价9280元
3星展银行起诉内地客户 银行强硬客户无奈
4脱离中国的实际 强压人民币大幅升值只能是梦想
# 为测试集添加列名便于处理
test.columns = [["text_a"]]
# 查看测试数据文件信息,共83599条
test.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 83599 entries, 0 to 83598
Data columns (total 1 columns):
 #   Column     Non-Null Count  Dtype 
---  ------     --------------  ----- 
 0   (text_a,)  83599 non-null  object
dtypes: object(1)
memory usage: 653.2+ KB
# 保存处理后的测试集文件
test.to_csv('test.csv', sep='\t', index=False)
# 对训练集的格式进行简单处理,舍弃无用id列,训练集:text_a,label
train = train[['text_a','label']]
# 查看处理后的训练数据前5条
train.head()
text_alabel
0上证50ETF净申购突增财经
1交银施罗德保本基金将发行财经
2基金公司不裁员反扩军 走访名校揽人才财经
3基金巨亏30亿 欲打开云天系跌停自救财经
4基金市场周二缩量走低财经
# 查看训练数据文件信息
# 可以看出共752475条,并不存在空值
train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 752476 entries, 0 to 752475
Data columns (total 2 columns):
 #   Column  Non-Null Count   Dtype 
---  ------  --------------   ----- 
 0   text_a  752475 non-null  object
 1   label   752476 non-null  object
dtypes: object(2)
memory usage: 11.5+ MB
# 训练数据类别标签分布统计
train['label'].value_counts()
科技    146637
股票    138959
体育    118444
娱乐     83369
时政     56778
社会     45765
教育     37743
财经     33389
家居     29328
游戏     21936
房产     18045
时尚     12032
彩票      6830
星座      3221
Name: label, dtype: int64
# 全局设置解决matplotlib中文显示错误的问题,参考:https://aistudio.baidu.com/aistudio/projectdetail/1658980
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt

import matplotlib.font_manager as font_manager

# 设置显示中文
matplotlib.rcParams['font.sans-serif'] = ['FZSongYi-Z13S'] # 指定默认字体
matplotlib.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
# 设置字体大小
matplotlib.rcParams['font.size'] = 16
# 可视化类别标签分布情况
train['label'].value_counts(normalize=True).plot(kind='bar');

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

# 统计数据文本长度,可以看出文本长度普遍较短
print((train['text_a'].astype(str)).map(len).describe())
count    752476.000000
mean         19.471856
std           4.112568
min           1.000000
25%          17.000000
50%          20.000000
75%          23.000000
max          81.000000
Name: text_a, dtype: float64
# 可视化训练集文本长度分布
train['text_a'].astype(str).map(len).plot(kind='kde');

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

2.2 手动划分训练和验证集

# 考虑类别不均衡,此处选择根据label的具体类别按9:1的比例去划分训练和验证集,从而使训练和验证集尽量同分布。
from sklearn.utils import shuffle

new_train = pd.DataFrame()  # 定义新训练集
new_valid = pd.DataFrame()  # 定义新验证集

tags = list(train.label.unique())  # 总类别

# 根据类别进行抽样划分
for tag in tags:
    data = train[(train['label'] == tag)]
    # 抽样取0.1作为验证集
    valid_sample = data.sample(int(0.1 * len(data)))
    valid_index = valid_sample.index
    # 将剩余0.9的数据作为训练集
    all_index = data.index
    residue_index = all_index.difference(valid_index)
    residue = data.loc[residue_index]
    # 对取的数据进行保存
    new_valid = pd.concat([new_valid, valid_sample], ignore_index=True)
    new_train = pd.concat([new_train, residue], ignore_index=True)

# 对划分后的数据进行随机打乱
new_train = shuffle(new_train)
new_valid = shuffle(new_valid)

# 保存训练和验证集文件
new_train.to_csv('train.csv', sep='\t', index=False)   # 训练集
new_valid.to_csv('valid.csv', sep='\t', index=False)   # 验证集

三.基于PaddleHub构建基线模型

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

PaddleHub的github地址(有问题可以提issue):https://github.com/PaddlePaddle/PaddleHub

3.1 PaddleHub预训练模型微调流程:

PaddleHub预训练模型微调流程如下:

1)加载要使用的预训练模型

2)加载并处理数据为模型可接受的格式

3)设置finetune训练与优化策略

4)模型训练与验证

5)模型预测

以上流程每个步骤只需通过很少的代码即可完成,使得我们可以快速构建比赛基线方案。

3.2 前置环境准备

# 下载最新版本的PaddleHub
!pip install -U paddlehub -i https://pypi.tuna.tsinghua.edu.cn/simple
# 导入paddlehub
import paddlehub as hub
# 设置固定随机种子便于结果的复现
# ps: 无法100%复现结果,如需稳定结果建议5folds或多次结果取平均等尝试稳定下
seed = 1024
import random
import numpy as np
import paddle
random.seed(seed)
np.random.seed(seed)
paddle.seed(seed)
<paddle.fluid.core_avx.Generator at 0x7f8c8bf1adb0>

3.3 加载预训练模型及设置微调任务

# 设置要求进行文本分类的14个类别
label_list=list(train.label.unique())
print(label_list)

label_map = { 
    idx: label_text for idx, label_text in enumerate(label_list)
}
print(label_map)
['财经', '彩票', '房产', '股票', '家居', '教育', '科技', '社会', '时尚', '时政', '体育', '星座', '游戏', '娱乐']
{0: '财经', 1: '彩票', 2: '房产', 3: '股票', 4: '家居', 5: '教育', 6: '科技', 7: '社会', 8: '时尚', 9: '时政', 10: '体育', 11: '星座', 12: '游戏', 13: '娱乐'}
# 选择在中文领域效果较优的roberta-wwm-ext-large预训练模型并设置微调任务为14分类任务
model = hub.Module(name="roberta-wwm-ext-large", task='seq-cls', num_classes=14, label_map=label_map)
Download https://bj.bcebos.com/paddlehub/paddlehub_dev/roberta-wwm-ext-large_2.0.2.tar.gz
[##################################################] 100.00%
Decompress /home/aistudio/.paddlehub/tmp/tmp94h07ovo/roberta-wwm-ext-large_2.0.2.tar.gz
[##################################################] 100.00%


[2022-02-22 22:53:02,020] [    INFO] - Successfully installed roberta-wwm-ext-large-2.0.2
[2022-02-22 22:53:02,023] [    INFO] - Downloading https://paddlenlp.bj.bcebos.com/models/transformers/roberta_large/roberta_chn_large.pdparams and saved to /home/aistudio/.paddlenlp/models/roberta-wwm-ext-large
[2022-02-22 22:53:02,025] [    INFO] - Downloading roberta_chn_large.pdparams from https://paddlenlp.bj.bcebos.com/models/transformers/roberta_large/roberta_chn_large.pdparams
100%|██████████| 1271615/1271615 [00:31<00:00, 39982.66it/s]
W0222 22:53:33.922940   150 device_context.cc:447] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 10.1, Runtime API Version: 10.1
W0222 22:53:33.928656   150 device_context.cc:465] device: 0, cuDNN Version: 7.6.

hub.Module的参数用法如下:

  • name:模型名称,可以选择ernieernie_tinybert-base-casedbert-base-chinese, roberta-wwm-extroberta-wwm-ext-large等。
  • task:fine-tune任务。此处为seq-cls,表示文本分类任务。
  • num_classes:表示当前文本分类任务的类别数,根据具体使用的数据集确定,默认为2。

PaddleHub支持的更多模型可以访问:https://www.paddlepaddle.org.cn/hublist

PaddleHub当前支持文本分类任务的模型对应的加载示例如下:

模型名PaddleHub Module
ERNIE, Chinesehub.Module(name='ernie')
ERNIE tiny, Chinesehub.Module(name='ernie_tiny')
ERNIE 2.0 Base, Englishhub.Module(name='ernie_v2_eng_base')
ERNIE 2.0 Large, Englishhub.Module(name='ernie_v2_eng_large')
BERT-Base, English Casedhub.Module(name='bert-base-cased')
BERT-Base, English Uncasedhub.Module(name='bert-base-uncased')
BERT-Large, English Casedhub.Module(name='bert-large-cased')
BERT-Large, English Uncasedhub.Module(name='bert-large-uncased')
BERT-Base, Multilingual Casedhub.Module(nane='bert-base-multilingual-cased')
BERT-Base, Multilingual Uncasedhub.Module(nane='bert-base-multilingual-uncased')
BERT-Base, Chinesehub.Module(name='bert-base-chinese')
BERT-wwm, Chinesehub.Module(name='chinese-bert-wwm')
BERT-wwm-ext, Chinesehub.Module(name='chinese-bert-wwm-ext')
RoBERTa-wwm-ext, Chinesehub.Module(name='roberta-wwm-ext')
RoBERTa-wwm-ext-large, Chinesehub.Module(name='roberta-wwm-ext-large')
RBT3, Chinesehub.Module(name='rbt3')
RBTL3, Chinesehub.Module(name='rbtl3')
ELECTRA-Small, Englishhub.Module(name='electra-small')
ELECTRA-Base, Englishhub.Module(name='electra-base')
ELECTRA-Large, Englishhub.Module(name='electra-large')
ELECTRA-Base, Chinesehub.Module(name='chinese-electra-base')
ELECTRA-Small, Chinesehub.Module(name='chinese-electra-small')

3.4 加载并处理数据为模型可接受的格式

import os, io, csv
from paddlehub.datasets.base_nlp_dataset import InputExample, TextClassificationDataset
# 设置数据集存放位置路径
DATA_DIR="/home/aistudio/data/data12701/"
# 对训练数据(训练集和验证集)进行格式处理,处理为模型可接受的格式
class News(TextClassificationDataset):
    def __init__(self, tokenizer, mode='train', max_seq_len=128):
        if mode == 'train':
            data_file = 'train.csv'
        elif mode == 'dev':
            data_file = 'valid.csv'
        super(News, self).__init__(
            base_path=DATA_DIR,
            data_file=data_file,
            tokenizer=tokenizer,
            max_seq_len=max_seq_len,
            mode=mode,
            is_file_with_header=True,
            label_list=label_list
            )

    # 解析文本文件里的样本
    def _read_file(self, input_file, is_file_with_header: bool = False):
        if not os.path.exists(input_file):
            raise RuntimeError("The file {} is not found.".format(input_file))
        else:
            with io.open(input_file, "r", encoding="UTF-8") as f:
                reader = csv.reader(f, delimiter="\t")  # ‘\t’分隔数据
                examples = []
                seq_id = 0
                header = next(reader) if is_file_with_header else None
                for line in reader:
                    example = InputExample(guid=seq_id, text_a=line[0], label=line[1])
                    seq_id += 1
                    examples.append(example)
                return examples

# 最大序列长度max_seq_len是可调整的重要参数,默认值为128,需要根据具体文本长度调整该值,但最大建议不超过512。
# 由于此次任务为文本标题,文本长度普遍较短,故此处设置为了48
train_dataset = News(model.get_tokenizer(), mode='train', max_seq_len=48)
dev_dataset = News(model.get_tokenizer(), mode='dev', max_seq_len=48)

# 处理完后查看处理后的数据前3条
for e in train_dataset.examples[:3]:
    print(e)
for e in dev_dataset.examples[:3]:
    print(e)
[2022-02-22 22:53:57,261] [    INFO] - Downloading https://paddlenlp.bj.bcebos.com/models/transformers/roberta_large/vocab.txt and saved to /home/aistudio/.paddlenlp/models/roberta-wwm-ext-large
[2022-02-22 22:53:57,263] [    INFO] - Downloading vocab.txt from https://paddlenlp.bj.bcebos.com/models/transformers/roberta_large/vocab.txt
100%|██████████| 107/107 [00:00<00:00, 3921.79it/s]
[2022-02-22 22:55:57,506] [    INFO] - Already cached /home/aistudio/.paddlenlp/models/roberta-wwm-ext-large/vocab.txt


text=商务人士最爱 六款低价便携投影机一览	label=科技
text=九州OL新增双倍经验 享受升级快感	label=游戏
text=英雄联盟7月21日开放测试 明星送祝福	label=游戏
text=杨元庆:乐Pad平板3月底上市 不惧与iPad竞争	label=科技
text=港股挟升近六百点 57只熊仔就此遭殃	label=股票
text=上午期指低见19637后徘徊 收报19730	label=股票

3.5 选择优化策略和运行配置

# 优化器的选择
optimizer = paddle.optimizer.AdamW(learning_rate=2e-5, weight_decay=0.0, parameters=model.parameters())
# 训练运行配置:设置是否使用gpu及训练模型存放路径等
trainer = hub.Trainer(model, optimizer, checkpoint_dir='./ckpt', use_gpu=True, use_vdl=True)      # fine-tune任务的执行者
[2022-02-22 22:56:10,161] [ WARNING] - PaddleHub model checkpoint not found, start from scratch...

3.6 模型训练与验证

trainer.train 主要控制具体的训练过程,包含以下可控制的参数:

  • train_dataset: 训练时所用的数据集;
  • epochs: 训练轮数;
  • batch_size: 训练的批大小,如果使用GPU,请根据实际情况调整batch_size;
  • num_workers: works的数量,默认为0;
  • eval_dataset: 验证集;
  • log_interval: 打印日志的间隔, 单位为执行批训练的次数。
  • save_interval: 保存模型的间隔频次,单位为执行训练的轮数。
# 模型训练:配置训练参数,指定验证集,启动训练
# ps: 需要根据显存大小调整好batch_size值!否则容易爆显存。  此处默认使用了32G显存环境故设置其值较大。
trainer.train(train_dataset, epochs=4, batch_size=256, eval_dataset=dev_dataset, save_interval=1)   

3.7 模型预测并生成结果文件

# 对测试集进行预测:
import numpy as np
# 将输入数据进行格式处理,处理为list格式
data_array = np.array(test)
data_list =data_array.tolist()

# 加载训练好的模型
model = hub.Module(
    name="roberta-wwm-ext-large", 
    task='seq-cls', 
    load_checkpoint='./ckpt/best_model/model.pdparams',  # 加载最优一轮的模型参数
    num_classes=14, 
    label_map=label_map)

# 对测试集数据进行预测:
# ps:由于训练显存未释放,预测时的batchsize注意不要设置过大否则容易出现显存不足的情况
predictions = model.predict(data_list, max_seq_len=48, batch_size=8, use_gpu=True)  
[2022-02-23 04:50:52,582] [    INFO] - Already cached /home/aistudio/.paddlenlp/models/roberta-wwm-ext-large/roberta_chn_large.pdparams
[2022-02-23 04:51:00,264] [    INFO] - Loaded parameters from /home/aistudio/data/data12701/ckpt/best_model/model.pdparams
[2022-02-23 04:51:00,276] [    INFO] - Already cached /home/aistudio/.paddlenlp/models/roberta-wwm-ext-large/vocab.txt
# 预测结果存储为要求格式 txt文件
def write_results(labels, file_path):
    with open(file_path, "w", encoding="utf8") as f:
        f.writelines("\n".join(labels))

write_results(predictions, "./result.txt")
# 移动提交结果文件至主目录下
!cp -r /home/aistudio/data/data12701/result.txt /home/aistudio/

3.8 结果提交

在主目录下找到生成的预测结果文件: result.txt文件后进入答题页面进行提交即可!

a.进入答题页面环境然后将result.txt文件上传至与work同级目录下后点击生成版本,生成版本时注意添加result.txt文件哦!

b.停止环境后点击提交试题选择要提交的版本及添加结果文件后进行提交即可通关,通关后可到学习地图页面领取通关奖励(算力+积分)哦!

感兴趣的也可以尝试在本基线模型上从模型优化、数据处理和结果融合等方向出发,进一步优化提升效果!

四.个人介绍

昵称:炼丹师233

主要方向:搞开发,主攻NLP和数据挖掘相关比赛或项目

https://aistudio.baidu.com/aistudio/personalcenter/thirdview/330406 关注我,下次带来更多精彩项目分享!

Logo

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

更多推荐