前文我们已经简单讲解过ERNIE1.0与2.0的构成与区别
一篇简单易懂的好文
本文小媛带来的是【从0开始快速上手百度 ERNIE】
在使用ERNIE模型之前,用户需要完成如下任务:
!git clone -b dygraph --single-branch https://github.com/PaddlePaddle/ERNIE.git
使用pip方式安装其他依赖文件。
!pip install -r ERNIE/requirements.txt
依赖文件主要包括:
这里以使用情感分析数据集ChnSentiCorp的ERNIE中文预模型为例,展示如何通过简单的三个步骤就可以快速使用ERNIE 1.0中文Base模型实现情感分析场景的推理。
ChnSentiCorp是一个中文情感分析数据集,包含酒店、笔记本电脑和书籍的网购评论。表1对ERNIE1.0/2.0和BERT中文模型在该任务上的效果进行了评测,评测使用的指标为准确率(acc),即在使用验证集或测试集进行推理时,推理正确的数据条目占数据集数据总数的百分比。从表1中可以看到ERNIE具有明显的优势。
使用ERNIE 1.0中文Base模型进行推理分如下三个步骤:
表1 ERNIE1.0/2.0和BERT的测评表
数据集 | ChnSentiCorp | |
---|---|---|
评估指标 | 准确率(acc) | |
验证集(dev) | 测试集(test) | |
BERT Base | 94.6 | 94.3 |
ERNIE 1.0 Base | 95.2 (+0.6) | 95.4 (+1.1) |
ERNIE 2.0 Base | 95.7 (+1.1) | 95.5 (+1.2) |
ERNIE 2.0 Large | 96.1 (+1.5) | 95.8 (+1.5) |
ERNIE在多个中文和英文NLP任务上做过评测,中文任务的数据可以通过下面的命令获取, ChnSentiCorp数据亦包含其中。
!wget https://ernie-github.cdn.bcebos.com/data-chnsenticorp.tar.gz
!tar xvf data-chnsenticorp.tar.gz
在解压出的文件夹“task_data/chnsenticorp”中, 包含了三个文件“train.tsv”、“dev.tsv”、“test.tsv”,分别对应ChnSentiCorp 数据的训练集、验证集和测试集,该任务是一个单句分类任务,数据包含两个字段为“label”和“seg_a”,以“TAB”进行分隔,示例如下:
seg_a label
选择珠江花园的原因就是方便,有电动扶梯直接到达海边,周围餐馆、食廊、商场、超市、摊位一应俱全。酒店装修一般,但还算整洁。泳池在大堂的屋顶,因此很小,不过女儿倒是喜欢。包的早餐是西式的,还算丰富。服务吗,一般 1
15.4寸笔记本的键盘确实爽,基本跟台式机差不多了,蛮喜欢数字小键盘,输数字特方便,样子也很美观,做工也相当不错 1
房间太小。其他的都一般。。。。。。。。。0
1.接电源没有几分钟,电源适配器热的不行. 2.摄像头用不起来. 3.机盖的钢琴漆,手不能摸,一摸一个印. 4.硬盘分区不好办. 0
今天才知道这书还有第6卷,真有点郁闷:为什么同一套书有两种版本呢?当当网是不是该跟出版社商量商量,单独出个第6卷,让我们的孩子不会有所遗憾。1
机器背面似乎被撕了张什么标签,残胶还在。但是又看不出是什么标签不见了,该有的都在,怪 0
呵呵,虽然表皮看上去不错很精致,但是我还是能看得出来是盗的。但是里面的内容真的不错,我妈爱看,我自己也学着找一些穴位。0
这本书实在是太烂了,以前听浙大的老师说这本书怎么怎么不对,哪些地方都是误导的还不相信,终于买了一本看一下,发现真是~~~无语,这种书都写得出来 0
地理位置佳,在市中心。酒店服务好、早餐品种丰富。我住的商务数码房电脑宽带速度满意,房间还算干净,离湖南路小吃街近。1
运行该脚本即可执行Fine-tuning, 脚本会根据你指定的from_pretrained
参数下载预训练模型,运行最大步长max_steps
由样本数
* epoch数
/ 批大小
算出。
!export CUDA_VISIBLE_DEVICES=0
!PYTHONPATH=./ERNIE python ./ERNIE/ernie/finetune_sementic_analysis_dygraph.py \
--from_pretrained ernie-1.0 \
--data_dir ./chnsenticorp/ \
--epoch 10 \
--lr 5e-5 \
--bsz 32 \
--max_steps $((9600*10/32)) \
--save_dir ./tuned_model
执行结束后输出如下的在验证集和测试集上面的测试结果:
training: 250it [01:39, 2.96it/s]2020-05-15 17:52:21,377-DEBUG: train loss 0.00880 lr 3.585e-05
training: 260it [01:43, 3.00it/s]2020-05-15 17:52:24,743-DEBUG: train loss 0.05025 lr 3.568e-05
training: 270it [01:46, 3.00it/s]2020-05-15 17:52:28,108-DEBUG: train loss 0.06813 lr 3.552e-05
training: 280it [01:49, 3.00it/s]2020-05-15 17:52:31,474-DEBUG: train loss 0.12881 lr 3.535e-05
training: 290it [01:53, 3.00it/s]2020-05-15 17:52:34,840-DEBUG: train loss 0.06156 lr 3.518e-05
2020-05-15 17:52:42,877-DEBUG: acc 0.93250
training: 10it [00:08, 1.88it/s]2020-05-15 17:52:46,317-DEBUG: train loss 0.00679 lr 3.485e-05
training: 20it [00:11, 2.84it/s]2020-05-15 17:52:49,817-DEBUG: train loss 0.13993 lr 3.468e-05
training: 30it [00:15, 2.89it/s]2020-05-15 17:52:53,297-DEBUG: train loss 0.02414 lr 3.452e-05
可以看到准确率(acc)达到了0.95左右,与表1中的测评准确率非常接近,说明训练效果达到了良好水平。
Fine-tuning 结束后,如果用户希望使用模型运行推理,可以修改上述命令行,并加入参数--eval
进入推理模式,从而利用保存在某个checkpoint (由--save_dir
指定)的模型执行推理。
!head ./chnsenticorp/dev/part.0|awk -F"\t" '{print $1}'| PYTHONPATH=./ERNIE python ./ERNIE/ernie/finetune_sementic_analysis_dygraph.py \
--from_pretrained ernie-1.0 \
--data_dir ./chnsenticorp/ \
--epoch 10 \
--lr 5e-5 \
--bsz 32 \
--eval \
--max_steps $((9600*10/32)) \
--save_dir ./tuned_model
输入的预测数据由标准输入管道灌入程序。修改完成后请再次运行脚本执行推理。该命令指向的“chnsenticorp/dev/part.0”文件里的前10句话,程序将对这10句话进行推理:
其它分类任务的运行方式类似。同时 ERNIE 还支持阅读理解、语义匹配、序列标注等任务,运行方式可以参考 README 中 Fine-tuning 章节。
开始写代码!
ChnSentiCorp任务运行的shell脚本是 ERNIE/ernie/run_classifier.py
,该文件定义了分类任务Fine-tuning 的详细过程,下面我们将通过如下几个步骤进行详细剖析:
import相关的依赖,解析命令行参数。
import sys
sys.path.append('./ERNIE')
import numpy as np
from sklearn.metrics import f1_score
import paddle as P
import paddle.fluid as F
import paddle.fluid.layers as L
import paddle.fluid.dygraph as D
from ernie.tokenizing_ernie import ErnieTokenizer
from ernie.modeling_ernie import ErnieModelForSequenceClassification
设置好所有的超参数,对于ERNIE任务学习率推荐取 1e-5/2e-5/5e-5, 根据显存大小调节BATCH大小, 最大句子长度不超过512.
BATCH=32
MAX_SEQLEN=300
LR=5e-5
EPOCH=10
D.guard().__enter__() # 为了让Paddle进入动态图模式,需要添加这一行在最前面
ernie = ErnieModelForSequenceClassification.from_pretrained('ernie-1.0', num_labels=3)
optimizer = F.optimizer.Adam(LR, parameter_list=ernie.parameters())
tokenizer = ErnieTokenizer.from_pretrained('ernie-1.0')
(1)定义函数 make_data
,将文本数据读入内存并转换为numpy List存储。
def make_data(path):
data = []
for i, l in enumerate(open(path)):
if i == 0:
continue
l = l.strip().split('\t')
text, label = l[0], int(l[1])
text_id, _ = tokenizer.encode(text) # ErnieTokenizer 会自动添加ERNIE所需要的特殊token,如[CLS], [SEP]
text_id = text_id[:MAX_SEQLEN]
text_id = np.pad(text_id, [0, MAX_SEQLEN-len(text_id)], mode='constant') # 对所有句子都补长至300,这样会比较费显存;
label_id = np.array(label+1)
data.append((text_id, label_id))
return data
train_data = make_data('./chnsenticorp/train/part.0')
test_data = make_data('./chnsenticorp/dev/part.0')
(2)定义函数get_batch_data
,用于获取BATCH
条样本并按照批处理维度stack
到一起。
def get_batch_data(data, i):
d = data[i*BATCH: (i + 1) * BATCH]
feature, label = zip(*d)
feature = np.stack(feature) # 将BATCH行样本整合在一个numpy.array中
label = np.stack(list(label))
feature = D.to_variable(feature) # 使用to_variable将numpy.array转换为paddle tensor
label = D.to_variable(label)
return feature, label
队训练数据重复EPOCH
遍训练循环;每次循环开头都会重新shuffle数据。在训练过程中每间隔100步在验证数据集上进行测试并汇报结果(acc)。
for i in range(EPOCH):
np.random.shuffle(train_data) # 每个epoch都shuffle数据以获得最佳训练效果;
#train
for j in range(len(train_data) // BATCH):
feature, label = get_batch_data(train_data, j)
loss, _ = ernie(feature, labels=label) # ernie模型的返回值包含(loss, logits);其中logits目前暂时不需要使用
loss.backward()
optimizer.minimize(loss)
ernie.clear_gradients()
if j % 10 == 0:
print('train %d: loss %.5f' % (j, loss.numpy()))
# evaluate
if j % 100 == 0:
all_pred, all_label = [], []
with D.base._switch_tracer_mode_guard_(is_train=False): # 在这个with域内ernie不会进行梯度计算;
ernie.eval() # 控制模型进入eval模式,这将会关闭所有的dropout;
for j in range(len(test_data) // BATCH):
feature, label = get_batch_data(test_data, j)
loss, logits = ernie(feature, labels=label)
all_pred.extend(L.argmax(logits, -1).numpy())
all_label.extend(label.numpy())
ernie.train()
f1 = f1_score(all_label, all_pred, average='macro')
acc = (np.array(all_label) == np.array(all_pred)).astype(np.float32).mean()
print('acc %.5f' % acc)
训练过程中单次迭代输出的日志如下所示:
train 0: loss 0.05833
acc 0.91723
train 10: loss 0.03602
train 20: loss 0.00047
train 30: loss 0.02403
train 40: loss 0.01642
train 50: loss 0.12958
train 60: loss 0.04629
train 70: loss 0.00942
train 80: loss 0.00068
train 90: loss 0.05485
train 100: loss 0.01527
acc 0.92821
train 110: loss 0.00927
train 120: loss 0.07236
train 130: loss 0.01391
train 140: loss 0.01612
包含了当前 batch 的训练得到的Loss(ave loss)和每个Epochde 精度(acc)信息。训练完成后用户可以参考快速运行中的方法使用模型体验推理功能。
ERNIE 还提供了混合精度训练、模型蒸馏等高级功能,可以在 README 中获得这些功能的使用方法。
附录:
README:https://github.com/PaddlePaddle/ERNIE/blob/develop/README.zh.md
paddle快速安装:https://www.paddlepaddle.org.cn/install/quick