1. 基本用法
Hugging face提供的transformers库主要用于预训练模型的载入,需要载入三个基本对象
from transformers import BertConfig
from transformers import BertModel
from transformers import BertTokenizer
BertConfig
是该库中模型配置的class。
BertModel
模型的class(还有其它的继承BertPreTrainedModel
的派生类,对应不同的Bert任务,BertForNextSentencePrediction
以及BertForSequenceClassification
)。
BertTokenizer
分词的class(这个分词对象比BERT官方代码的好用,输入文本中的[SEP]
等特殊字符不会被切开,而是作为一个整体保留下来)。
推荐使用科大讯飞的pytorch版预训练模型,中文BERT-wwm系列模型,也可以自己手动把tensorflow的checkpoint文件转化为pytorch模型。以讯飞下载的模型为例:
tokenizer = BertTokenizer.from_pretrained('chinese_roberta_wwm_ext_pytorch') # 默认回去读取文件下的vocab.txt文件
model = BertModel.from_pretrained('chinese_roberta_wwm_ext_pytorch') # 应该会报错, 默认读取config.json, 需要重命名bert_config.json
# 或者这样改, 手动指定config对象
config = BertConfig.from_json_file('model_path/bert_config.json')
model = BertModel.from_pretrained('model_path', config=config)
至此,预训练模型和分词工具已经载入完成,下面是一个简单的实例。
s_a, s_b = "李白拿了个锤子", "锤子?"
# 分词是tokenizer.tokenize, 分词并转化为id是tokenier.encode
# 简单调用一下, 不作任何处理经过transformer
input_id = tokenizer.encode(s_a)
input_id = torch.tensor([input_id]) # 输入数据是tensor且batch形式的
sequence_output, pooled_output = model(input_id) # 输出形状分别是[1, 9, 768], [1, 768]
# 但是输入BertModel的还需要指示前后句子的信息的token type, 以及遮掉PAD部分的attention mask
inputs = tokenizer.encode_plus(s_a, text_pair=s_b, return_tensors="pt") # 还有些常用的可选参数max_length, pad_to_max_length等
print(inputs.keys()) # 返回的是一个包含id, mask信息的字典
# dict_keys(['input_ids', 'token_type_ids', 'attention_mask']
sequence_output, pooled_output = model(**inputs)
2. 关于Fine-tuning
不推荐自己从头开始写下游任务的fine-tuning流程,数据预处理加上训练流程巨烦人。如果再加上warm up,weight decay,甚至多卡训练,简直爆炸。推荐在transformers提供的栗子上面修改,比如官方给的xnli数据集上的样例。
主要需要修改run_xnli.py文件import的几个数据结构
from transformers import xnli_compute_metric as compute_metric
from transformers import xnli_output_modes as output_modes
from transformers import xnli_processors as processors
# 注释掉以上三行, 我们重新写这些模块
# compute_metric是evaluation阶段的评估函数, xnli是文本分类任务
def compute_metric(task_name, preds, labels):
assert len(preds) == len(labels)
if task_name == "your_task":
return {"acc": (preds == labels).mean()}
else:
raise KeyError(task_name)
# output_modes任务输出形式
output_modes = {"your_task": "classification"} # 回归任务则是"regression"
processor
类似于google Bert代码中的写法,参考此处TextProcessor的写法。另外有个地方和google不一样。
# DataProcessor基类多了一个方法, 用于载入tensorflow Dataset的数据,
# 一般没有这个需求, 就不用去管它。
def get_example_from_tensor_dict(self, tensor_dict):
pass
processors = {"your_task": YourProcessor}
采用这里的Processor和数据形式,给出一个运行脚本的样例:
#!/bin/bash
export DATA_DIR=/data_path # 数据路径
export MODEL_PATH=/model_path # 与训练模型路径
CUDA_VISIBLE_DEVICES=0 python run_xnli.py \
--model_type bert \
--model_name_or_path ${MODEL_PATH} \
--task_name your_task \
--do_train \
--do_eval \
--do_lower_case \
--data_dir ${DATA_DIR} \
--max_seq_length 64 \
--per_gpu_train_batch_size 32 \
--per_gpu_eval_batch_size 32 \
--warmup_steps 100 \
--learning_rate 5e-5 \
--num_train_epochs 5.0 \
--output_dir /model_output_dir \
--overwrite_output_dir