BERT(Bidirectional Encoder Representations from Transformers)是自然语言处理(NLP)领域的一个重要里程碑,极大地增强了在NLP中的转移学习能力,并为解决各种NLP任务带来了巨大希望。阅读本文,将获得丰富的BERT知识,并能够通过实践深入了解BERT的应用。
BERT是一种能够理解文本表示的模型。当给它一个序列作为输入时,它会多次向左和向右查看,并为每个单词生成一个向量表示作为输出。2018年底,谷歌AI语言研究团队开源了BERT这一新的NLP技术,因其出色的性能在深度学习社区引起了轰动。
NLP领域面临的一个最大挑战是缺乏足够的训练数据。虽然总体上有很多文本数据可用,但如果想要创建特定任务的数据集,就需要将这些数据分成许多不同的领域。这样做,最终只剩下几千或几十万个人工标注的训练样本。不幸的是,基于深度学习的NLP模型要表现良好,需要更多的数据——它们在训练时使用数百万或数十亿的标注训练样本时会看到显著的改进。
为了弥补这一数据差距,研究人员开发了各种技术,使用网络上大量的未标注文本进行通用语言表示模型的训练(这被称为预训练)。然后,这些通用预训练模型可以在较小的任务特定数据集上进行微调,例如在处理问答和情感分析等问题时。
这种方法与从头开始在较小的任务特定数据集上训练相比,可以显著提高准确性。BERT是这些NLP预训练技术的最新补充;它在深度学习社区引起了轰动,因为它在各种NLP任务中展示了最先进的结果,如问答。
BERT依赖于Transformer(一种学习文本中单词之间上下文关系的注意力机制)。一个基本的Transformer由一个编码器组成,用于读取文本输入,以及一个解码器用于产生任务的预测。由于BERT的目标是生成语言表示模型,它只需要编码器部分。BERT的编码器输入是一个令牌序列,这些令牌首先被转换为向量,然后在神经网络中进行处理。但在处理开始之前,BERT需要输入被处理和装饰一些额外的元数据:
以下是使用BERT进行文本分类任务的步骤。首先,需要加载所需的包,并构建一个BERT层。然后,需要理解问题陈述并导入数据集。接下来,将对原始文本进行编码。最后,将构建模型并运行模型。
import numpy as np
import pandas as pd
import tensorflow as tf
import tensorflow_hub as hub
import logging
logging.basicConfig(level=logging.INFO)
import tensorflow_hub as hub
import tokenization
module_url = 'https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/2'
bert_layer = hub.KerasLayer(module_url, trainable=True)
使用的是GitHub错误预测数据集,该数据集可在MachineHack平台上找到。目标是基于GitHub标题和文本主体预测错误、功能和问题。
train=pd.read_json("embold_train.json").reset_index(drop=True)
test=pd.read_json("embold_test.json").reset_index(drop=True)
令牌化是将原始文本分割成令牌的过程,这些令牌是表示单词的数值数据。BERT语言模型是使用WordPiece词汇预训练的,不仅使用令牌嵌入,还使用段落嵌入来区分成对的序列,例如问答示例。位置嵌入也是必需的,以便在BERT模型中注入位置感知,因为注意力机制在上下文评估中不考虑位置。
需要注意的BERT的一个重要限制是,BERT的序列最大长度为512个令牌。对于短于最大允许输入大小的序列输入,需要添加填充令牌[PAD]。另一方面,如果序列更长,需要切割序列。这个BERT对序列最大长度的限制是需要为长文本段注意的。
同样重要的是所谓的特殊令牌,例如[CLS]令牌和[SEP]令牌。[CLS]令牌将被插入到序列的开头,[SEP]令牌在末尾。如果处理序列对,将在最后一个[SEP]令牌的末尾添加额外的[SEP]令牌。
vocab_file = bert_layer.resolved_object.vocab_file.asset_path.numpy()
do_lower_case = bert_layer.resolved_object.do_lower_case.numpy()
tokenizer = tokenization.FullTokenizer(vocab_file, do_lower_case)
以下是构建模型的代码。模型使用BERT层的输出,并添加了一些全连接层和Dropout层。最后,使用Softmax激活函数进行多类别分类。
def build_model(bert_layer, max_len=512):
input_word_ids = tf.keras.Input(shape=(max_len,), dtype=tf.int32, name="input_word_ids")
input_mask = tf.keras.Input(shape=(max_len,), dtype=tf.int32, name="input_mask")
segment_ids = tf.keras.Input(shape=(max_len,), dtype=tf.int32, name="segment_ids")
pooled_output, sequence_output = bert_layer([input_word_ids, input_mask, segment_ids])
clf_output = sequence_output[:, 0, :]
net = tf.keras.layers.Dense(64, activation='relu')(clf_output)
net = tf.keras.layers.Dropout(0.2)(net)
net = tf.keras.layers.Dense(32, activation='relu')(net)
net = tf.keras.layers.Dropout(0.2)(net)
out = tf.keras.layers.Dense(5, activation='softmax')(net)
model = tf.keras.models.Model(inputs=[input_word_ids, input_mask, segment_ids], outputs=out)
model.compile(tf.keras.optimizers.Adam(lr=1e-5), loss='categorical_crossentropy', metrics=['accuracy'])
return model
checkpoint = tf.keras.callbacks.ModelCheckpoint('model.h5', monitor='val_accuracy', save_best_only=True, verbose=1)
earlystopping = tf.keras.callbacks.EarlyStopping(monitor='val_accuracy', patience=5, verbose=1)
train_history = model.fit(
train_input, train_labels,
validation_split=0.2,
epochs=3,
callbacks=[checkpoint, earlystopping],
batch_size=32,
verbose=1
)