[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)

本文主要介绍如果使用huggingface的transformers 2.0 进行NLP的模型训练

除了transformers,其它兼容tf2.0的bert项目还有:

我的博客里有介绍使用方法  [深度学习] 自然语言处理--- 基于Keras Bert使用(上)

  1. keras-bert(Star:1.4k) 支持tf2,但它只支持bert一种预训练模型
  2. bert4keras (Star:692)支持tf2,bert/roberta/albert的预训练权重进行finetune

  3. bert-for-tf2(Star:329)只给了tf2.0 pipeline示例

huggingface的transformers也发布了transformers2.0,开始支持tf.2.0的各个预训练模型,虽然没有对pytorch支持的那么全面但在我们的场景已经足够适用了。

  • transformers github
  • transformers Doc
  • transformers Online Demo
  • Paper: HuggingFace's Transformers: State-of-the-art Natural Language Processing

 

算法流程

[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)_第1张图片

 

一 加载预训练Bert模型

1. 下载Google原始预训练Bert模型

(1)先将原始google预训练的模型文件转换成pytorch格式

这个命令在安装transformers时会回到环境变量中。

python convert_bert_original_tf_checkpoint_to_pytorch.py -h

[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)_第2张图片

python convert_bert_original_tf_checkpoint_to_pytorch.py \
--tf_checkpoint_path Models/chinese_L-12_H-768_A-12/bert_model.ckpt.index \
--bert_config_file Models/chinese_L-12_H-768_A-12/bert_config.json  \
--pytorch_dump_path  Models/chinese_L-12_H-768_A-12/pytorch_model.bin

output:

INFO:transformers.modeling_bert:Converting TensorFlow checkpoint from /home/work/Bert/Models/chinese_L-12_H-768_A-12/bert_model.ckpt.index
Save PyTorch model to Models/chinese_L-12_H-768_A-12/pytorch_model.bin

在开源代码库下面有好多有关转换的py文件

[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)_第3张图片

2 下载transformers预训练模型

在使用transformers的时候,由于Bert、XLNet的文件都在AWS上存储,transformers的默认下载地址指向的是AWS,因此在国内下载速度非常慢。需要我们自己手动下载。

1、下载.txt、.json、.bin文件到本地

以Bert为例,相关的.bin文件(预训练权重)下载地址如下所示:

BERT_PRETRAINED_MODEL_ARCHIVE_MAP = {
    'bert-base-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-pytorch_model.bin",
    'bert-large-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-pytorch_model.bin",
    'bert-base-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased-pytorch_model.bin",
    'bert-large-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-pytorch_model.bin",
    'bert-base-multilingual-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-uncased-pytorch_model.bin",
    'bert-base-multilingual-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-cased-pytorch_model.bin",
    'bert-base-chinese': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-chinese-pytorch_model.bin",
    'bert-base-german-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-german-cased-pytorch_model.bin",
    'bert-large-uncased-whole-word-masking': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-whole-word-masking-pytorch_model.bin",
    'bert-large-cased-whole-word-masking': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-whole-word-masking-pytorch_model.bin",
    'bert-large-uncased-whole-word-masking-finetuned-squad': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-whole-word-masking-finetuned-squad-pytorch_model.bin",
    'bert-large-cased-whole-word-masking-finetuned-squad': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-whole-word-masking-finetuned-squad-pytorch_model.bin",
    'bert-base-cased-finetuned-mrpc': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased-finetuned-mrpc-pytorch_model.bin",
    'bert-base-german-dbmdz-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-german-dbmdz-cased-pytorch_model.bin",
    'bert-base-german-dbmdz-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-german-dbmdz-uncased-pytorch_model.bin",
}

若需要下载.json文件,则下载地址为:

BERT_PRETRAINED_CONFIG_ARCHIVE_MAP = {
    'bert-base-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-config.json",
    'bert-large-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-config.json",
    'bert-base-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased-config.json",
    'bert-large-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-config.json",
    'bert-base-multilingual-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-uncased-config.json",
    'bert-base-multilingual-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-cased-config.json",
    'bert-base-chinese': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-chinese-config.json",
    'bert-base-german-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-german-cased-config.json",
    'bert-large-uncased-whole-word-masking': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-whole-word-masking-config.json",
    'bert-large-cased-whole-word-masking': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-whole-word-masking-config.json",
    'bert-large-uncased-whole-word-masking-finetuned-squad': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-whole-word-masking-finetuned-squad-config.json",
    'bert-large-cased-whole-word-masking-finetuned-squad': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-whole-word-masking-finetuned-squad-config.json",
    'bert-base-cased-finetuned-mrpc': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased-finetuned-mrpc-config.json",
    'bert-base-german-dbmdz-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-german-dbmdz-cased-config.json",
    'bert-base-german-dbmdz-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-german-dbmdz-uncased-config.json",
}

.txt相关文件(词表文件)下载地址如下:

PRETRAINED_VOCAB_FILES_MAP = {
    'vocab_file':
    {
        'bert-base-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-vocab.txt",
        'bert-large-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-vocab.txt",
        'bert-base-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased-vocab.txt",
        'bert-large-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-vocab.txt",
        'bert-base-multilingual-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-uncased-vocab.txt",
        'bert-base-multilingual-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-cased-vocab.txt",
        'bert-base-chinese': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-chinese-vocab.txt",
        'bert-base-german-cased': "https://int-deepset-models-bert.s3.eu-central-1.amazonaws.com/pytorch/bert-base-german-cased-vocab.txt",
        'bert-large-uncased-whole-word-masking': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-whole-word-masking-vocab.txt",
        'bert-large-cased-whole-word-masking': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-whole-word-masking-vocab.txt",
        'bert-large-uncased-whole-word-masking-finetuned-squad': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-whole-word-masking-finetuned-squad-vocab.txt",
        'bert-large-cased-whole-word-masking-finetuned-squad': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-whole-word-masking-finetuned-squad-vocab.txt",
        'bert-base-cased-finetuned-mrpc': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased-finetuned-mrpc-vocab.txt",
        'bert-base-german-dbmdz-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-german-dbmdz-cased-vocab.txt",
        'bert-base-german-dbmdz-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-german-dbmdz-uncased-vocab.txt",
    }
}


 

清华源还支持huggingface hub自动下载。注意:transformers > 3.1.0 的版本支持下面的 mirror 选项。

只需在 from_pretrained 函数调用中添加 mirror 选项,如:

AutoModel.from_pretrained('bert-base-uncased', mirror='tuna')

目前内置的两个来源为 tunabfsu。此外,也可以显式提供镜像地址,如:

AutoModel.from_pretrained('bert-base-uncased', mirror='https://mirrors.tuna.tsinghua.edu.cn/hugging-face-models')

直接在网站里面搜索模型,之后点击List all files in model放在一个文件夹下面导入即可啦~

[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)_第4张图片


3 加载transformers预训练模型

(1)加载转换后的模型

import logging
logging.basicConfig(level=logging.INFO)
import tensorflow as tf
print("Tensorflow Version:", tf.__version__)
import torch
print("Pytorch Version:", torch.__version__)

from transformers import *

import os
pretrained_path = 'Models/chinese_L-12_H-768_A-12'
config_path = os.path.join(pretrained_path, 'bert_config.json')
vocab_path = os.path.join(pretrained_path, 'vocab.txt')

# 加载config
config = BertConfig.from_json_file(config_path)
# 加载torch原始模型
bert_model = BertModel.from_pretrained(pretrained_path, config=config)

# 加载tf原始模型
tfbert_model = TFBertModel.from_pretrained(pretrained_path,from_pt=True, config=config)

发现问题:如果加载为TF2的模型,参数会变少 (请使用 pytorch版本加载转换后的模型)

[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)_第5张图片

 

[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)_第6张图片

首先我们建立一个文件夹,命名为bert-base-uncased,然后将这个三个文件放入这个文件夹,并且对文件进行重命名,重命名时将bert-base-uncased-去除即可。

假设我们训练文件夹名字为 train.py,我们需要将上面的bert-base-uncased文件夹放到与train.py同级的目录下面。
若不改名以及调整文件夹位置将会出现:
vocab.txt not found;pytorch_model.bin not found;Model name 'xxx/pytorch_model.bin ' was not found in model name list等错误。

之后使用下面的代码进行测试即可:

UNCASED = './bert-base-uncased' 
VOCAB = 'vocab.txt' 
tokenizer=BertTokenizer.from_pretrained(os.path.join(UNCASED,VOCAB)) 
bert = BertModel.from_pretrained(UNCASED)

 

二  使用Tokenizer编码

tokenizer是一个将纯文本转换为编码的过程,该过程不涉及将词转换成为词向量,仅仅是对纯文本进行分词,并且添加[MASK]、[SEP]、[CLS]标记,然后将这些词转换为字典索引。

token编码inputs

tokenizer = BertTokenizer.from_pretrained(vocab_path)

第一步 使用 BERT tokenizer 将单词首先分割成 tokens。

第二步 添加句子分类所需的特殊 tokens(在第一个位置是[CLS],在句子的末尾是[SEP])。

[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)_第7张图片

第三步 用嵌入表中的 id 替换每个 token,嵌入表是我们从训练模型中得到的一个组件。

[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)_第8张图片

注意,tokenizer 在一行代码中完成所有这些步骤:

1. encode(text, ...):将文本分词后编码为包含对应 id 的列表

>>> tokenizer.encode('Hello word!')
[101, 8667, 1937, 106, 102]

2. encode_plus(text, ...):将文本分词后创建一个包含对应 id,token 类型及是否遮盖的词典;

tokenizer.encode_plus('Hello world!')
{'input_ids': [101, 8667, 1937, 106, 102], 'token_type_ids': [0, 0, 0, 0, 0], 'attention_mask': [1, 1, 1, 1, 1]}

3. convert_ids_to_tokens(ids, skip_special_tokens):将 id 映射为 token

>>> tokenizer.convert_ids_to_tokens(tokens)
['[CLS]', 'Hello', 'word', '!', '[SEP]']

4. decode(token_ids):将 id 解码

>>> tokenizer.decode(tokens)
'[CLS] Hello word! [SEP]'

5. convert_tokens_to_ids(tokens):将 token 映射为 id

>>> tokenizer.convert_tokens_to_ids(['[CLS]', 'Hello', 'word', '!', '[SEP]'])
[101, 8667, 1937, 106, 102]

 

tokenizer.encode("a visually stunning rumination on love", add_special_tokens=True)

[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)_第9张图片

利用分词器进行编码:

  • encode仅返回input_ids
  • encode_plus返回所有编码信息
    • input_ids:是单词在词典中的编码
    • token_type_ids:区分两个句子的编码(上句全为0,下句全为1)
    • attention_mask:指定对哪些词进行self-Attention操作
print(tokenizer.encode('我不喜欢你'))     #[101, 2769, 679, 1599, 3614, 872, 102]

sen_code = tokenizer.encode_plus('我不喜欢这世界','我只喜欢你')
print(sen_code)
# {'input_ids': [101, 2769, 679, 1599, 3614, 6821, 686, 4518, 102, 2769, 1372, 1599, 3614, 872, 102],
#  'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
#  'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}

将input_ids转化回token:

print(tokenizer.convert_ids_to_tokens(sen_code['input_ids']))
#['[CLS]', '我', '不', '喜', '欢', '这', '世', '界', '[SEP]', '我', '只', '喜', '欢', '你', '[SEP]']

数据集将其作为输入处理之前,我们需要使用 token id 0 填充更短的句子,从而使所有向量具有相同的大小。填充之后,我们有了一个矩阵/张量,准备传给 BERT:

[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)_第10张图片

 

[深度学习] 自然语言处理 --- Bert开发实战 (Transformers)_第11张图片

 

三  使用Bert模型

下载bert-base-chinese的config.json,vocab.txt,pytorch_model.bin三个文件后,放在bert-base-chinese文件夹下,此例中该文件夹放在\home/work\transformers_file\下

根据任务需要,如果不需要为指定任务finetune,可以选择使用BertModel

import numpy as np
import torch
from transformers import BertTokenizer, BertConfig, BertForMaskedLM, BertForNextSentencePrediction
from transformers import BertModel


model_name = 'bert-base-chinese'
MODEL_PATH = '/home/work/transformers_file/bert-base-chinese/'

# a.通过词典导入分词器
tokenizer = BertTokenizer.from_pretrained(model_name)

# b. 导入配置文件
model_config = BertConfig.from_pretrained(model_name)

# 修改配置
model_config.output_hidden_states = True
model_config.output_attentions = True

# 通过配置和路径导入模型
bert_model = BertModel.from_pretrained(MODEL_PATH, config=model_config)

inputs = tokenizer("Hello, my dog is cute", return_tensors="pt")
outputs = model(**inputs)
last_hidden_states = outputs[0]  # The last hidden-state is the first element of the output tuple

参数:

input_ids、attention_mask、token_type_ids、position_ids、head_mask、inputs_embeds、encoder_hidden_states、encoder_attention_mask、output_attentions、output_hidden_states

模型至少需要有1个输入: input_ids 或 input_embeds。

input_ids 就是一连串 token 在字典中的对应id。形状为 (batch_size, sequence_length)。
token_type_ids 可选。就是 token 对应的句子id,值为0或1(0表示对应的token属于第一句,1表示属于第二句)。形状为(batch_size, sequence_length)。

1  input_ids 就是一连串 token 在字典中的对应id。形状为 (batch_size, sequence_length)。Bert 的输入需要用 [CLS] 和 [SEP] 进行标记,开头用 [CLS],句子结尾用 [SEP],各类bert模型对应的输入格式如下所示:

bert:       [CLS] + tokens + [SEP] + padding
roberta:    [CLS] + prefix_space + tokens + [SEP] + padding
distilbert: [CLS] + tokens + [SEP] + padding
xlm:        [CLS] + tokens + [SEP] + padding
xlnet:      padding + tokens + [SEP] + [CLS]

2  token_type_ids 可选。就是 token 对应的句子id,值为0或1(0表示对应的token属于第一句,1表示属于第二句)。形状为(batch_size, sequence_length)。如为None则BertModel会默认全为0(即a句)。

tokens:[CLS] is this jack ##son ##ville ? [SEP] no it is not . [SEP]

token_type_ids:0   0  0    0    0     0       0   0   1  1  1  1   1   1

tokens:[CLS] the dog is hairy . [SEP]

token_type_ids:0   0   0   0  0     0   0

3  attention_mask 可选。各元素的值为 0 或 1 ,避免在 padding 的 token 上计算 attention (1不进行masked,0则masked)。形状为(batch_size, sequence_length)。如为None则BertModel默认全为1。

4  position_ids 可选。表示 token 在句子中的位置id。形状为(batch_size, sequence_length)。形状为(batch_size, sequence_length)。如为None则BertModel会自动生成。

形如[0,1,2,......,seq_length - 1],

5 head_mask 可选。各元素的值为 0 或 1 ,1 表示 head 有效,0无效。形状为(num_heads,)或(num_layers, num_heads)。

6 input_embeds 可选。替代 input_ids,我们可以直接输入 Embedding 后的 Tensor。形状为(batch_size, sequence_length, embedding_dim)。

7 encoder_hidden_states 可选。encoder 最后一层输出的隐藏状态序列,模型配置为 decoder 时使用。形状为(batch_size, sequence_length, hidden_size)。

8 encoder_attention_mask 可选。避免在 padding 的 token 上计算 attention,模型配置为 decoder 时使用。形状为(batch_size, sequence_length)。

 

 

 

构建模型

class BertNerModel(TFBertPreTrainedModel):
    def __init__(self, config, *inputs, **kwargs):
      super(BERT_NER, self).__init__(config, *inputs, **kwargs)
      self.bert_layer = TFBertMainLayer(config, name='bert')
      self.bert_layer.trainable = False
      self.concat_layer = tf.keras.layers.Concatenate(name='concat_bert')
    
    def call(self, inputs):
      outputs = self.bert_layer(inputs)
      #将后n层的结果相连
      tensor = self.concat_layer(list(outputs[2][-4:]))

这里给出的是简要的代码,可以自行根据任务在bert_layer之后加入RNN

自定义模型的写法可以参考官方源码里的TFBertForSequenceClassification, 继承TFBertPreTrainedModel

self.bert_layer(inputs)的返回值为tuple类型:

  1. 最后1层隐藏层的输出值,shape=(batch_size, max_length, hidden_dimention)
  2. [CLS] 对应的输出值,shape=(batch_size, hidden_dimention)
  3. 只有设置了config.output_hidden_states = True,才有该值,所有隐藏层的输出值,返回值类型是list 每个list里的值的shape(batch_size, max_length, hidden_dimention)`

模型的初始化

bert_ner_model = BertNerModel.from_pretrained("bert-base-chinese", output_hidden_states=True)

因为是模型继承的TFBertPreTrainedModel因此这里初始化使用的父类的方式。第一个参数是要加载预训练好的模型参数

注意事项

  1. 通过设置:self.bert.trainable = False, 模型可以更快收敛,减少训练时间
  2. 预测的时候,输出的数据一定要与max_length一致,否则效果完全不可用,猜测可能是我们只给了, 没有给input_mask,有看到transformers的源码,如果不给attention_mask,默认全是1的
  3. 通过设置 output_hidden_states=True, 可以得到隐藏层的结果

 

 

参考文献

  • Huggingface-transformers项目源码剖析及Bert命名实体识别实战
  • HuggingFace-Transformers

  • HuggingFace-Transformers系列的下游应用
  • Tensorflow2.0 基于BERT的开发实战(huggingface-transformers)
  • HuggingFace-Transformers系列的介绍以及在下游任务中的使用
  • 手把手教你用Pytorch-Transformers——部分源码解读及相关说明(一)
  • 关于transformers库中不同模型的Tokenizer

  • NLP学习1 - 使用Huggingface Transformers框架从头训练语言模型

  • Huggingface简介及BERT代码浅析

  • 一起读Bert文本分类代码 (pytorch篇 一)



 

 

 

你可能感兴趣的:(深度学习,NLP,nlp,transformers)