T5(Transfer Text-to-Text Transfo
论文链接:https://arxiv.org/abs/1910.10683
代码链接:https://github.com/google-research/text-to-text-transfer-transformer
T5多任务概述图导言:一个统一框架,靠着大力出奇迹,将所有 NLP 任务都转化成 Text-to-Text(文本到文本)任务
比如英德翻译任务,只需将训练数据集的输入部分前加上
translate English to German
即可。假设需要翻译That is good
,那么先转换成translate English to German:That is good.
输入模型,之后就可以直接输出德语翻译Das ist gut.
。对于需要输出连续值的STS(文本语义相似度任务),也是直接输出文本。通过这样的方式就能将NLP任务都转换成Text-to-Text形式,也就可以用同样的模型,同样的损失函数,同样的训练过程,同样的解码过程来完成所有NLP任务
1.1 模型结构
T5模型采用Transformer的encoder-decoder结构,GPT采用的是Transformer的decoder结构,BERT采用的是Transformer的encoder结构
T5模型图T5模型和原始的Transformer结构基本一致,具体的做了如下几点改动:
-
编码器和解码器仍保留在模型中。编码器和解码器层成为块(block),子层为包含自注意层和前馈网络的子组件(subcomponent)。像乐高一样,可以组装块和子组件来建立模型,例如图2中的ENCODER #1
-
Layer normalization层归一化应用于每个子组件的输入,使用一个简化版本的Layer normalization,其中激活只是重新调整,没有附加偏差被应用
-
常规的的计算公式为:,这里为期望,为方差。
-
而此处的的计算公式为,这里对应的参数初始化的时候全为
-
-
在Layer normalization之后,将一个residual的skip connection将每个子组件的输入添加到其输出中
- 此处使用了简化的相对位置embedding,即每个位置对应一个数值而不是向量,原来的Transformer采用
sin/cos
习得position embeddings,而T5将(key和query)相对位置的数值加在attention softmax之前的logits上,每个head的有自己的position embeddings,所有的层共享一套position embeddings,每一层都计算一次,让模型对位置更加敏感
1.2 数据来源:Colossal Clean Crawled Corpus (C4)
T5作者选取了Common Crawl数据集,这个数据集每周大约爬取20TB的WEB数据。虽然数据集已经抽取了文本,但实际上并不干净,里面还包含了很多非自然语言的东西,比如错误消息、菜单、重复文本,于是对数据进行了比较细致的处理:
-
只取结尾有标点的句子
-
去掉包含脏话的网页
-
有很多页面包含
"enable Javascript"
的提示,去掉包含Javascript
的句子 -
"lorem ipsum"
是一个测试网页排版的拉丁文,去掉包含这个占位符的网页 -
去掉包含代码片段的网页
-
以三句为一个片段进行去重
-
去掉非英文的网页
经过上述处理后最终生成了750GB的数据集C4,并且在TensorFlow Datasets进行开源
1.3 任务以及训练格式
1.3.1 输入输出模型
-
输入:参考GPT2,直接把任务名称当作prefix和输入拼在一起,例如:
"translate English to German: That is good."
-
输出:分类任务(如推断),需要输出"entailment", "neutral", "contradiction"这三种文本,否则都算错;回归任务输出str(序列)类型的浮点数。还有其他任务,可前往附录A参考
-
这里每个任务前缀的选择可以认为是一种超参,即通过人为设计前缀样式。作者发现不同的前缀对模型的影响有限,因此没有做大量实验比较选择不同前缀的结果。
1.3.2 预训练过程
之后是对预训练目标的大范围探索,具体做了哪些实验,下面这张图就能一目了然
预训练模型的预训练范式总览总共从四个层面来进行比较:
-
第一个方面,高层次方法(自监督的预训练方法)对比,总共三种方法:
-
语言模型式,就是 GPT-2 那种方式,从左到右预测
-
BERT-style式,就是像BERT一样将一部分给破坏掉,然后还原出来,其效果最好
-
Deshuffling(顺序还原)式,就是将文本打乱,然后还原出来
-
-
第二方面,对文本一部分进行破坏时的策略,也分三种方法:
-
Mask法,如现在大多模型的做法,将被破坏token换成特殊符如
[M]
-
Replace span法,可以当作是把上面 Mask 法中相邻
[M]
都合成了一个特殊符,每一小段替换一个特殊符,提高计算效率,其效果最好 -
Drop法,没有替换操作,直接随机丢弃一些字符
-
-
第三方面,对文本进行多大程度的破坏,挑了 4 个值:
10%,15%,25%,50%
,最后发现 BERT 的15%
效果最好 -
第四方面,Replace Span需要决定对大概多长的小段进行破坏,于是对不同长度进行探索:
2,3,5,10
这四个值,最后发现3
效果最好
1.3.3 模型参数量
最后就是结合上面所有实验结果,训练了不同规模几个模型,由小到大:
T5模型参数量表1.3.4 训练结论
-
Architectures
-
原始的Transformer结构表现最好
-
encoder-decoder结构和BERT、GPT的计算量差不多
-
共享encoder和decoder的参数没有使效果差太多
-
-
Unsupervised objectives
-
自编码和自回归的效果差不多
-
推荐选择更短目标序列的目标函数,提高计算效率
-
-
Datasets
-
在领域内进行无监督训练可以提升一些任务的效果,但在一个小领域数据上重复训练会降低效果
-
Large、diverse的数据集效果最好
-
-
Training strategies
-
精调时更新所有参数 > 更新部分参数
-
在多个任务上预训练之后微调 = 无监督预训练
-
-
Scaling
-
在小模型上训练更多数据 < 用少量步数训练更大的模型
-
从一个预训练模型上微调多个模型后集成 < 分开预训练+微调后集成
-
2.1 T5模型的使用
- 首先安装需要的包:
pip install transformers==4.2.0
pip install sentencepiece==0.1.94
- 下载并加载模型和分词器:
model = T5ForConditionalGeneration.from_pretrained('t5-large')
tokenizer = T5Tokenizer.from_pretrained('t5-large')</pre>
- T5模型参数打印:
# print(model.config)
T5Config {
"_name_or_path": "t5-large",
"architectures": [
"T5WithLMHeadModel"
],
"d_ff": 4096, # 每个T5Block中feed forward layer的size
"d_kv": 64, # 每个attention head的key、query、value的size。d_kv必须等于d_model/num_heads
"d_model": 1024, # 每个encoder layers和pooler layer的大小
"decoder_start_token_id": 0, # 模型根据标签自动创建decoder_start_token_id,方法是将标签向右移动一个位置,并加上前缀
"dropout_rate": 0.1, # 所有dropout layers的比例
"eos_token_id": 1, # eos_token的id
"feed_forward_proj": "relu", # 所有要使用的feed_forward类型
"initializer_factor": 1.0, # 初始化所有权重矩阵的系数
"is_encoder_decoder": true, # 同时使用encoder与decoder
"layer_norm_epsilon": 1e-06, # layer_norm使用的epsilon
"model_type": "t5", # 模型类型
"n_positions": 512, # positions数量
"num_decoder_layers": 24, # decoder中的隐藏层数。如果未设置,将使用与num_layers相同的值
"num_heads": 16, # 每个attention layer的attention heads
"num_layers": 24, # 每个encoder中的hidden layers
"relative_attention_max_distance": 128, # bucket separation较长序列的最大距离
"relative_attention_num_buckets": 32, # 每个attention layers要使用的bucket数
"task_specific_params": {
"summarization": {
"early_stopping": true,
"length_penalty": 2.0,
"max_length": 200,
"min_length": 30,
"no_repeat_ngram_size": 3,
"num_beams": 4,
"prefix": "summarize: "
},
"translation_en_to_de": {
"early_stopping": true,
"max_length": 300,
"num_beams": 4,
"prefix": "translate English to German: "
},
"translation_en_to_fr": {
"early_stopping": true,
"max_length": 300,
"num_beams": 4,
"prefix": "translate English to French: "
},
"translation_en_to_ro": {
"early_stopping": true,
"max_length": 300,
"num_beams": 4,
"prefix": "translate English to Romanian: "
}
},
"transformers_version": "4.19.2", # tranformers包支持版本
"use_cache": true, # 模型是否应返回最后一个key/values attentions
"vocab_size": 32128 # T5模型的词汇表
}
- T5模型结构打印(第N个block):
T5Block(
(layer): ModuleList(
(0): T5LayerSelfAttention(
(SelfAttention): T5Attention(
(q): Linear(in_features=1024, out_features=1024, bias=False)
(k): Linear(in_features=1024, out_features=1024, bias=False)
(v): Linear(in_features=1024, out_features=1024, bias=False)
(o): Linear(in_features=1024, out_features=1024, bias=False)
)
(layer_norm): T5LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(1): T5LayerCrossAttention(
(EncDecAttention): T5Attention(
(q): Linear(in_features=1024, out_features=1024, bias=False)
(k): Linear(in_features=1024, out_features=1024, bias=False)
(v): Linear(in_features=1024, out_features=1024, bias=False)
(o): Linear(in_features=1024, out_features=1024, bias=False)
)
(layer_norm): T5LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
(2): T5LayerFF(
(DenseReluDense): T5DenseReluDense(
(wi): Linear(in_features=1024, out_features=4096, bias=False)
(wo): Linear(in_features=4096, out_features=1024, bias=False)
(dropout): Dropout(p=0.1, inplace=False)
(relu_act): ReLU()
)
(layer_norm): T5LayerNorm()
(dropout): Dropout(p=0.1, inplace=False)
)
)
)
- 使用T5模型并创建
prefix
实现摘要任务:
def summarize(text, max_length):
'''
text: 要生成摘要的文本
max_length: 摘要的最大长度
'''
# 去掉多余的空格和换行符
preprocess_text = text.strip().replace('\n','')
# 准备前缀+文本
t5_prepared_text = 'summarize: ' + preprocess_text
print("Preprocessed and prepared text: \n", t5_prepared_text)
# 分词
tokenized_text = tokenizer.encode(t5_prepared_text, return_tensors="pt").to(device)
# 进行文本摘要
summary_ids = model.generate(tokenized_text,
num_beams=4,
no_repeat_ngram_size=2,
min_length=30,
max_length=max_length,
early_stopping=True)
# 将id转换为输出 summary_ids.shape = [1, 50]
output = tokenizer.decode(summary_ids[0], skip_special_tokens=True)
return output