前言
之前的教程中,有同学要求将讲解的代码开源,以方便使用。本文将会用最精简的框架去介绍来自顶级公司的pytorch模型的整个框架及流程,并整理开源为通用的模型框架,供研究使用。(如果你还没有阅读过之前的基础知识,建议通过传送门大概了解一下。)
本文的模型框架主要来源于谷歌的BERT开源代码、大名鼎鼎的Huggingface、AllenNlp以及Albert中文版等,通读完这些代码,你会发现有很多相似的部分,本文主要依照它们的设计思想及具体代码,构建出一个常见的、完整的pytorch训练流程,你只需要完成模型的设计、语料的准备、以及主程序中相应代码的修改,就可以完成一个非常成熟的深度学习过程,包括完整的日志记录系统、评估系统、预处理系统等。
本文的模型框架经过亲自实践证明是有用的,并会在未来进行改进和扩充。目前主要针对与自然语言处理(NLP)基于预训练模型的相关分类实验。我们接下来一一讲解其框架的各个组成部分,部分需要补课的同学可以传送至之前的文章:《转战pytorch(1)——清点装备》,《转战pytorch(2)——“简单”训练》,《转战pytorch(3)——跟上脚步(以Albert为例)》,《转战pytorch——实现自己的任务(4)》。
整体框架
整个模型框架如上图所示,主要包含analysis
,dataset
,metrics
,model
,module
,outputs
,prev_trained_model
,processors
几个部分,由于callback
和tools
不是我们实验的主体部分,也没什么变化,因此我们这里忽略讲解,详情可以看一看代码。下面以一个关系识别的任务为背景,对于框架的每个部分进行一个详细的介绍。
语料准备
语料主要以任务名为文件夹,放置在dataset中,分别以train.tsv
,dev.tsv
,test.tsv
为例子完成初始数据的准备。如果你的原始数据不是这样的,建议增加一个create_tsv.py
文件完成语料格式的转换。
由于语料的不同以及版权问题,我们在上传的资源里移除了真实的语料文件,需要自己准备。
处理器
当处理数据送到模型前的操作,都在processors
文件夹里,这个文件夹主要有两个部分,一个是与任务相关的glue
,这里规定了不同任务的处理器的具体流程、标签的数目以及输出的类型;另一个是工具类utils
,主要规定了输入的样例的类和特征的类。
评估指标
对于实验结果的评估,我们主要放置在metrics
文件夹中,其中有两个文件,一个用于真正的计算方法的实现(custom_metrics.py
),另一个则是将任务预评估方法对应起来(glue_custom_metrics.py
).
模型构建
我们构建的模型主要在model
文件夹中,对于现在常见的预训练模型,我们主要构建config
、modeling
、tokenization
三个部分,你也可以根据自己的模型构建属于自己的文件。
另一个与模型相关的文件夹为module
,这个文件夹主要放模型的子模块,可以将常用的模块先封装至module
中,然后再在model
中构建自己的模型中调用,增强模型的复用性。
如果你使用的是huggingface的一些内置模型的话,这里可以是空的。
结果的输出
结果一般输出到outputs
中,以任务为文件夹名分开存放。
预训练模型
预训练模型都存放于prev_trained_model
文件夹中。很多与训练模型都可以在网上找到。这里提供albert中文版和bert中文版的来源,以及Huggingface的model cards。
实验主过程
实验主过程就如之前教程中讲的那样,是独立的一个Py文件,这里为run_classfication_relation.py
,大致分为以下4步:
- 前期准备(参数、数据、模型等初始化)
- 训练过程
- 评估过程
- 测试过程
以上四个内容,在本文的代码中,都集中在main
函数中。在其中,我们只对于config,model和tokenizer部分进行了bert和albert的替换,我们也可以替换成自己的模型。
如果你只是专注一个简单的分类任务,或者是回归任务,那么这个过程就完全的够用了,但如果你是做parsing或者其他需要模型作为一个决策者参与到整个系统中时,则需要额外构建一个系统来进行实验。
分析过程
对于实验结果,我们需要有针对性的进行分析,因此,在这里强烈建议将实验结果完整的保存下来,这样以便于我们回溯的时候可以更加方便地进行分析。所有地分析代码都在analysis
文件夹中。
运行脚本
运行的脚本我们可以存放于一个.sh文件中,一方面,我们可以直接执行,另一方面,我们也可以方便的修改。就算最笨的办法也相当于一个粘贴板,需要的时候复制里面的代码粘贴到命令行中即可。
# bert model
CUDA_VISIBLE_DEVICES=0 python3 run_classifier_relation.py \
--model_type=bert \
--model_name_or_path=./prev_trained_model/bert-base-chinese/pytorch_model.bin \
--vocab_file=./prev_trained_model/bert-base-chinese/bert-base-chinese-vocab.txt \
--config_name=./prev_trained_model/bert-base-chinese/bert_config.json \
--task_name=relation \
--do_train \
--do_eval \
--do_predict \
--predict_all_checkpoints \
--do_lower_case \
--data_dir=./dataset/relation/ \
--max_seq_length=512 \
--per_gpu_train_batch_size=2 \
--per_gpu_eval_batch_size=2 \
--learning_rate=1e-5 \
--num_train_epochs=5.0 \
--logging_steps=1192 \
--save_steps=1192 \
--output_dir=./outputs/relation_output/ \
--overwrite_output_dir \
--seed=42
额外收获(Albert 和BERT的差异)
在亲自实验的任务(篇章关系识别)中,我们发现,正如论文中所讲的那样,尽管Albert和BERT在同一任务、同一语料下,同等参数设置下,性能大致相当(BERT 58.58F1,Albert 57.5F1),但是BERT能够识别出更多的类型标签(BERT 识别 6-7个类别,但是Albert只能识别最多的3个类别,总计15个类型)。对于预训练模型评测的8个任务,其标签的类型数都是2-3个,两者的直观性能没有太多的差距,但是显然更大的BERT模型能够捕获到更加多样的特征表示。
小结
通过一个关系分类的实例,我们对于这个最简易但非常完整的框架有了进一步的认识,它具有很多优点,例如训练过程全程可控、操作日志全记录、以及无需配置,容易扩展等。在将来,我们将会对此进行一些实用的功能扩展。