bert简介_Huggingface简介及BERT代码浅析
本⽂为预训练语⾔模型专题系列第六篇
快速传送门
[萌芽时代],[风起云涌],[⽂本分类通⽤技巧],[GPT家族],[BERT来临]
感谢清华⼤学⾃然语⾔处理实验室对预训练语⾔模型架构
预训练语⾔模型架构的梳理,我们将沿此脉络前⾏,探索预训练语⾔模型的前沿技术,红⾊框为已介绍的⽂章。本期的内容是结合Huggingface的Transformers代码,来进⼀步了解下BERT的pytorch实现,欢迎⼤家留⾔讨论交流。
Hugging face 简介
Hugging face 简介
因为pytorch框架的友好,BERT的强⼤,以及pytorch-pretrained-bert的简单易⽤,使这个repo也是受到⼤家的喜爱,不到10天就突破了1000个star。在2018年11⽉17⽇,repo就实现了BERT的基本功能,发布了版本0.1.2。接下来他们也没闲着,⼜开始将GPT等模型也往repo上搬。在2019年2⽉11⽇relea的 0.5.0版本中,已经添加上了OpenAI GPT模型,以及Google的TransformerXL。
直到2019年7⽉16⽇,在repo上已经有了包括BERT,GPT,GPT-2,Transformer-XL,XLNET,XLM在内六个预训练语⾔模型,这时候名字再叫pytorch-pretrained-bert就不合适了,于是改成了pytorch-transformers,势⼒范围扩⼤了不少。这还没完!2019年6⽉Tensorflow2的beta版发布,Huggingface也闻风⽽动。为了⽴于不败之地,⼜实现了TensorFlow 2.0和PyTorch模型之间的深层互操作性,可以在TF2.0/PyTorch框架之间随意迁移模型。在2019年9⽉也发布了2.0.0版本,同时正式更名为 transformers 。到⽬前为⽌,transformers 提供了超过100种语⾔的,32种预训练语⾔模型,简单,强⼤,⾼性能,是新⼿⼊门的不⼆选择。
Transfromers中BERT简单运⽤
Transfromers中BERT简单运⽤
前⼏期⾥,⼀直在分享论⽂的阅读⼼得,虽然不是第⼀次看,但不知道⼤家是不是和我⼀样⼜有所收获。本期我们⼀起来看看如何使⽤Transformers包实现简单的BERT模型调⽤。
安装过程不再赘述,⽐如安装2.2.0版本 pip install transformers==2.2.0 即可,让我们看看如何调⽤BERT。
import torch
from transformers import BertModel, BertTokenizer
# 这⾥我们调⽤bert-ba模型,同时模型的词典经过⼩写处理
model_name = 'bert-ba-uncad'
# 读取模型对应的tokenizer
tokenizer = BertTokenizer.from_pretrained(model_name)
# 载⼊模型
model = BertModel.from_pretrained(model_name)
# 输⼊⽂本
input_text = "Here is some text to encode"
# 通过tokenizer把⽂本变成 token_id
input_ids = de(input_text, add_special_tokens=True)
# input_ids: [101, 2182, 2003, 2070, 3793, 2000, 4372, 16044, 102]
input_ids = sor([input_ids])
# 获得BERT模型最后⼀个隐层结果
_grad():
last_hidden_states = model(input_ids)[0] # Models outputs are now tuples
""" tensor([[[-0.0549, 0.1053, -0.1065, ..., -0.3550, 0.0686, 0.6506],
[-0.5759, -0.3650, -0.1383, ..., -0.6782, 0.2092, -0.1639],
[-0.1641, -0.5597, 0.0150, ..., -0.1603, -0.1346, 0.6216],
...,
[ 0.2448, 0.1254, 0.1587, ..., -0.2749, -0.1163, 0.8809],
[ 0.0481, 0.4950, -0.2827, ..., -0.6097, -0.1212, 0.2527],
[ 0.9046, 0.2137, -0.5897, ..., 0.3040, -0.6172, -0.1950]]])
shape: (1, 9, 768)
"""
可以看到,包括import在内的不到⼗⾏代码,我们就实现了读取⼀个预训练过的BERT模型,来encode我们指定的⼀个⽂本,对⽂本的每⼀个token⽣成768维的向量。如果是⼆分类任务,我们接下来就可以把第⼀个token也就是[CLS]的768维向量,接⼀个linear层,预测出分类的logits,或者根据标
签进⾏训练。
如果你想在⼀些NLP常⽤数据集上复现BERT的效果,Transformers上也有现成的代码和⽅法,只要把数据配置好,运⾏命令即可,⽽且finetune的任务可以根据你的需要切换,⾮常⽅便。
BERT configuration
BERT configuration
接下来,我们进⼀步看下Transformers的源码,我们⾸先进⼊代码的路径src/transformers 下,其中有很多的python代码⽂件。
configuration 开头的都是各个模型的配置代码,⽐如 configuration_bert.py。在这个⽂件⾥我们能够看到,主要是⼀个继承⾃以 configuration
PretrainedConfig 的类 BertConfig的定义,以及不同BERT模型的config⽂件的下载路径,下⽅显⽰前
三个。
BERT_PRETRAINED_CONFIG_ARCHIVE_MAP = {
"bert-ba-uncad": "//bert/bert-ba-uncad-config.json",
"bert-large-uncad": "//bert/bert-large-uncad-config.json",
"bert-ba-cad": "//bert/bert-ba-cad-config.json",
}
我们打开第⼀个的链接,就能下载到bert-ba-uncad的模型的配置,其中包括dropout, hidden_size, num_hidden_layers, vocab_size 等等。⽐如bert-ba-uncad的配置它是12层的,词典⼤⼩30522等等,甚⾄可以在config⾥利⽤
output_hidden_states配置是否输出所有hidden_state。
{
"architectures": [
"BertForMaskedLM"
],
"attention_probs_dropout_prob": 0.1,
"hidden_act": "gelu",
"hidden_dropout_prob": 0.1,
"hidden_size": 768,
"initializer_range": 0.02,
"intermediate_size": 3072,
"max_position_embeddings": 512,
"num_attention_heads": 12,
"num_hidden_layers": 12,
"type_vocab_size": 2,
"vocab_size": 30522
}
BERT tokenization
BERT tokenization
tokenization开头的都是跟vocab有关的代码,⽐如在 tokenization_bert.py 中有函数如whitespace_tokenize,还有不同的
以tokenization
tokenizer的类。同时也有各个模型对应的。从第⼀个链接进去就是bert-ba-uncad的词典,这⾥⾯有30522个词,对应着config⾥⾯的vocab_size。
其中,第0个token是[pad],第101个token是[CLS],第102个token是[SEP],所以之前我们encode得到的 [101, 2182, 2003, 2070, 3793, 2000, 4372, 16044, 102] ,其实tokenize后convert前的token就是 ['[CLS]', 'here', 'is', 'some', 'text', 'to', 'en',
'##code', '[SEP]'],经过之前BERT论⽂的介绍,⼤家应该都⽐较熟悉了。其中值得⼀提的是,BERT的vocab预留了不少unud token,如果我们会在⽂本中使⽤特殊字符,在vocab中没有,这时候就可以通过替换vacab中的unud token,实现对新的token的embedding 进⾏训练。
PRETRAINED_VOCAB_FILES_MAP = {
"vocab_file": {
"bert-ba-uncad": "//",
"bert-large-uncad": "//",
"bert-ba-cad": "//",
}
}
BERT modeling
BERT modeling
以modeling开头的就是我们最关⼼的模型代码,⽐如 modeling_bert.py。同样的,⽂件中有许多不同的预训练模型以供下载,我们可以按需获取。
代码中我们可以重点关注BertModel类,它就是BERT模型的基本代码。我们可以看到它的类定义中,由embedding,encoder,pooler 组成,forward时顺序经过三个模块,输出output。
class BertModel(BertPreTrainedModel):
def __init__(lf, config):
super().__init__(config)
lf.pooler = BertPooler(config)
lf.init_weights()
def forward(
lf, input_ids=None, attention_mask=None, token_type_ids=None,
position_ids=None, head_mask=None, inputs_embeds=None,
encoder_hidden_states=None, encoder_attention_mask=None,
):
""" 省略部分代码 """
embedding_output = lf.embeddings(
input_ids=input_ids, position_ids=position_ids, token_type_ids=token_type_ids, inputs_embeds=inputs_embeds
)
encoder_outputs = lf.encoder(
embedding_output,
attention_mask=extended_attention_mask,
head_mask=head_mask,
encoder_hidden_states=encoder_hidden_states,
encoder_attention_mask=encoder_extended_attention_mask,
)
quence_output = encoder_outputs[0]
pooled_output = lf.pooler(quence_output)
outputs = (quence_output, pooled_output,) + encoder_outputs[
1:
] # add hidden_states and attentions if they are here
return outputs # quence_output, pooled_output, (hidden_states), (attentions)
BertEmbeddings这个类中可以清楚的看到,embedding由三种embedding相加得到,经过layernorm 和 dropout后输出。
def __init__(lf, config):
super().__init__()
lf.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=0)
lf.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size)
# lf.LayerNorm is not snake-cad to stick with TensorFlow model variable name and be able to load
# any TensorFlow checkpoint file
lf.LayerNorm = BertLayerNorm(config.hidden_size, eps=config.layer_norm_eps)
lf.dropout = nn.Dropout(config.hidden_dropout_prob)
def forward(lf, input_ids=None, token_type_ids=None, position_ids=None, inputs_embeds=None):
""" 省略 embedding⽣成过程 """
embeddings = inputs_embeds + position_embeddings + token_type_embeddings
embeddings = lf.LayerNorm(embeddings)
embeddings = lf.dropout(embeddings)
return embeddings
BertEncoder主要将embedding的输出,逐个经过每⼀层Bertlayer的处理,得到各层hidden_state,再根据config的参数,来决定最后是否所有的hidden_state都要输出,BertLayer的内容展开的话,篇幅过长,读者感兴趣可以⾃⼰⼀探究竟。