情感分析(五):基于 BERT 实现
本文是 情感分析 系列的第 5 5 5 篇,也是本系列的收官之作。前四篇分别是:
- 【自然语言处理】情感分析(一):基于 NLTK 的 Naive Bayes 实现
- 【自然语言处理】情感分析(二):基于 scikit-learn 的 Naive Bayes 实现
- 【自然语言处理】情感分析(三):基于 Word2Vec 的 LSTM 实现
- 【自然语言处理】情感分析(四):基于 Tokenizer 和 Word2Vec 的 CNN 实现
即使大家没用过 BERT(Bidirectional Encoder Representation from Transformers),相信对它在自然语言处理任务中的优越表现也早已有所耳闻。本篇博客将重点介绍 BERT 是如何助力情感分析的。工欲善其事必先利其器,在正式开始之前,先介绍几个要用到的包。
本文代码已上传至 我的GitHub,需要可自行下载。
1.相关包介绍
1.1 TensorFlow Official Models
TensorFlow Official Models 是使用 TensorFlow 高级 API 的模型集合。比如,它提供了多种 预训练语言模型(Pre-trained Language Model),如下表所示:
模型 | 参考(论文) |
---|---|
ALBERT | ALBERT: A Lite BERT for Self-supervised Learning of Language Representations |
BERT | BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding |
ELECTRA | ELECTRA: Pre-training Text Encoders as Discriminators Rather Than Generators |
安装时,TensorFlow Official Models 需要与 TensorFlow 的版本匹配。例如,TensorFlow-models v2.8.x
与 TensorFlow v2.8.x
兼容。TensorFlow 的版本不低于 2.2,Python 版本 3.7+。更多安装详情请参阅官方文档。
pip install tf-models-official
1.2 TensorFlow Text
TensorFlow Text 为您提供一系列丰富的操作和库,帮助您处理文本形式的输入,例如原始文本字符串或文档。这些库可以执行基于文本的模型经常需要进行的预处理,并且包含对序列建模非常有用的其他功能。
pip install -U tensorflow-text==<version>
使用示例:
import tensorflow as tf
import tensorflow_text as tf_text
def preprocess(vocab_table, example_text):
# Normalize text
tf_text.normalize_utf8(example_text)
# Tokenize into words
word_tokenizer = tf_text.WhitespaceTokenizer()
tokens = word_tokenizer.tokenize(example_text)
# Tokenize into subwords
subword_tokenizer = tf_text.WordpieceTokenizer(lookup_table, token_out_type=tf.int64)
subtokens = subword_tokenizer.tokenize(tokens).merge_dims(1, -1)
# Apply padding
padded_inputs = tf_text.pad_model_inputs(subtokens, max_seq_length=16)
return padded_inputs
1.3 TensorFlow Hub
TensorFlow Hub 是一个包含经过训练的机器学习模型的代码库,这些模型稍作调整便可部署到任何设备上。您只需几行代码即可重复使用经过训练的模型,例如 BERT 和 Faster R-CNN。
pip install --upgrade tensorflow_hub
使用示例:
nnlm-en-dim128
:在英语 Google 新闻 200B 语料库上训练的基于标记的文本嵌入。
import tensorflow_hub as hub
model = hub.KerasLayer("https://tfhub.dev/google/nnlm-en-dim128/2")
embeddings = model(["The rain in Spain.", "falls","mainly", "In the plain!"])
print(embeddings.shape) # (4,128)
2.基于 BERT 的情感分析实战
import os
import shutil
import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_text as text
from official.nlp import optimization # to create AdamW optimizer
数据集:Large Movie Review Dataset
这是一个用于二元情感分类的数据集,包含比以前的基准数据集多得多的数据。我们提供了一组 25000 25000 25000 条极性电影评论用于训练, 25000 25000 25000 条用于测试,还有其他未标记的数据可供使用。提供了原始文本和已经处理过的词袋格式。
url = 'https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz'
dataset = tf.keras.utils.get_file('aclImdb_v1.tar.gz', url, untar=True, cache_dir='.', cache_subdir='')
dataset_dir = os.path.join(os.path.dirname(dataset), 'aclImdb')
train_dir = os.path.join(dataset_dir, 'train')
# remove unused folders to make it easier to load the data
remove_dir = os.path.join(train_dir, 'unsup') # 解压缩后,会有一个 unsup 文件夹。因后续未用到,故删除之
shutil.rmtree(remove_dir) # 递归删除文件夹下的所有子文件夹和子文件
接下来,构造训练、验证、测试数据集。
tf.keras.utils.text_dataset_from_directory
:从目录中的文本文件生成 tf.data.Dataset
。
batch_size
:数据批次的大小,默认值为 32 32 32。如果为None
,则不会对数据进行批处理(数据集将产生单个样本)。validation_split
:介于 0 0 0 和 1 1 1 之间的可选浮点数,保留用于验证的数据部分。subset
:要返回的数据的子集。 “训练”、“验证” 或 “两者” 之一。仅在设置了validation_split
时使用。当subset=“both”
时,程序返回两个数据集(分别是训练和验证数据集)的元组。
AUTOTUNE = tf.data.AUTOTUNE # tf.data 运行时动态调整值
batch_size = 32
seed = 42
raw_train_ds = tf.keras.utils.text_dataset_from_directory('aclImdb/train',
batch_size=batch_size, validation_split=0.2, subset='training', seed=seed)
class_names = raw_train_ds.class_names
train_ds = raw_train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds = tf.keras.utils.text_dataset_from_directory('aclImdb/train',
batch_size=batch_size, validation_split=0.2, subset='validation', seed=seed)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
test_ds = tf.keras.utils.text_dataset_from_directory('aclImdb/test', batch_size=batch_size)
test_ds = test_ds.cache().prefetch(buffer_size=AUTOTUNE)
tfhub_handle_preprocess = 'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3'
tfhub_handle_encoder = 'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-512_A-8/1'
bert_en_uncased_preprocess
:Text preprocessing for BERT.
small_bert/bert_en_uncased_L-4_H-512_A-8
:Smaller BERT model.
def build_classifier_model():
text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text')
preprocessing_layer = hub.KerasLayer(tfhub_handle_preprocess, name='preprocessing')
encoder_inputs = preprocessing_layer(text_input)
encoder = hub.KerasLayer(tfhub_handle_encoder, trainable=True, name='BERT_encoder')
outputs = encoder(encoder_inputs)
net = outputs['pooled_output']
net = tf.keras.layers.Dropout(0.1)(net)
net = tf.keras.layers.Dense(1, activation=None, name='classifier')(net)
return tf.keras.Model(text_input, net)
hub.KerasLayer
:该层包装了一个可调用对象以用作 Keras 层。可调用对象可以直接传递,或者由带有传递给 hub.load()
的句柄的 Python 字符串指定。
classifier_model = build_classifier_model()
tf.keras.utils.plot_model(classifier_model)
tf.keras.losses.BinaryCrossentropy
:计算真实标签和预测标签之间的交叉熵损失。
tf.keras.metrics.BinaryAccuracy
:计算预测匹配二进制标签的频率。
loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)
metrics = tf.metrics.BinaryAccuracy()
tf.data.experimental.cardinality
:返回数据集的基数(如果已知)。
epochs = 5
steps_per_epoch = tf.data.experimental.cardinality(train_ds).numpy()
num_train_steps = steps_per_epoch * epochs
num_warmup_steps = int(0.1*num_train_steps)
init_lr = 3e-5
optimizer = optimization.create_optimizer(init_lr=init_lr,
num_train_steps=num_train_steps,
num_warmup_steps=num_warmup_steps,
optimizer_type='adamw')
classifier_model.compile(optimizer=optimizer, loss=loss, metrics=metrics)
print(f'Training model with {tfhub_handle_encoder}')
history = classifier_model.fit(x=train_ds, validation_data=val_ds, epochs=epochs)
loss, accuracy = classifier_model.evaluate(test_ds)
print(f'Loss: {loss}')
print(f'Accuracy: {accuracy}')
def print_my_examples(inputs, results):
result_for_printing = [f'input: {inputs[i]:<30} : score: {results[i][0]:.6f}' for i in range(len(inputs))]
print(*result_for_printing, sep='\n')
examples = [
'this is such an amazing movie!', # this is the same sentence tried earlier
'The movie was great!',
'The movie was meh.',
'The movie was okish.',
'The movie was terrible...'
]
original_results = tf.sigmoid(classifier_model(tf.constant(examples)))
print('Results from the model in memory:')
print_my_examples(examples, original_results)
模型的准确度是 85 % 85\% 85% 左右,这是通过一个相对较小的 BERT 模型实现的。