NLP 预训练新技术:Google BERT 文本分类

1131 字 · 2597 阅读 · 2018 年 12 月 11 日

BERT 简介

BERT 是 Google 开源的 NLP 预训练新技术,它的全称是 Bidirectional Encoder Representations from Transformers(Github 仓库)。

BERT 建立在最新的预训练与上下文相关的语言表示的工作之上,包括 Semi-supervised Sequence Learning、Generative Pre-Training、ELMo 和 ULMFit。然而,与以前的模型不同,BERT 是第一个深度、双向、无监督的语言表示模型,仅使用无标签的文本语料库(在本例中为维基百科)进行预训练。

由于模型结构复杂,再加上语料庞大,BERT 在目前最强民用 GPU 上都需要训练长达数个月之久。不过好消息是,Google 发布了支持中文以及多语言的预训练基础模型 BERT-Base。借助于预训练模型,我们就可以很轻松地完成多种 NLP 任务。

BERT 文本分类

文本分类是自然语言处理过程中最为常见的一类任务。那么,我们尝试借助于 BERT 来完成中文文本分类。

BERT 文本分类使用微调的方法,首先需要下载预训练模型:

# 下载预训练模型
wget -nc "https://storage.googleapis.com/bert_models/2018_11_03/chinese_L-12_H-768_A-12.zip"
unzip -o "chinese_L-12_H-768_A-12.zip"

接下来,克隆 BERT 官方仓库,以便于直接利用 Google 提供的模型训练源码:

# 克隆 BERT 官方仓库
git clone https://github.com/google-research/bert.git

Google 提供的模型训练源码中,完成文本分类的代码放在 run_classifier.py 中。其中,Google 提供了在 4 个基准数据集上的测试代码,并对应 XnliProcessor,MnliProcessor,MrpcProcessor 和 ColaProcessor 中。那么,要想完成自己的文本分类任务,我们只需要模仿并改写类即可。

改写 Processor 类

改写 Processor 类的过程非常简单,只需要按照自己的需要传入数据即可。例如,我们这里把自己的文本分类类定为 SelfProcessor,其中包含下面四个函数:

import pandas as pd
from sklearn.model_selection import train_test_split

class SelfProcessor(DataProcessor):
    # 传入训练数据
    def get_train_examples(self, data_dir):
        # 读取训练数据路径
        file_path = os.path.join(data_dir, 'train.csv')
        # 使用 Pandas 读取数据
        df = pd.read_csv(file_path)
        # 将训练数据切分为 80% 训练集和 20% 验证集
        df_train, self.df_dev = train_test_split(df, test_size=0.2)
        examples = []
        # 按 BERT 推荐格式处理数据
        for index, row in df_train.iterrows():
            guid = 'train-%d' % index  # 索引
            text_a = tokenization.convert_to_unicode(str(row[0]))  # 文本 1
            text_b = tokenization.convert_to_unicode(str(row[1]))  # 文本 2
            label = row[2]  # 文本标签
            examples.append(InputExample(guid=guid, text_a=text_a,
                                         text_b=text_b, label=label))
        return examples

    # 传入验证数据
    def get_dev_examples(self, data_dir):
        examples = []
        for index, row in self.df_dev.iterrows():
            guid = 'dev-%d' % index
            text_a = tokenization.convert_to_unicode(str(row[0]))
            text_b = tokenization.convert_to_unicode(str(row[1]))
            label = row[2]
            examples.append(InputExample(guid=guid, text_a=text_a,
                                         text_b=text_b, label=label))
        return examples

    # 传入测试数据(预测)
    def get_test_examples(self, data_dir):
        file_path = os.path.join(data_dir, 'test.csv')
        df_test = pd.read_csv(file_path)
        examples = []
        for index, row in df_test.iterrows():
            guid = 'test-%d' % index
            text_a = tokenization.convert_to_unicode(str(row[0]))
            text_b = tokenization.convert_to_unicode(str(row[1]))
            label = '0'  # 随意指定测试数据标签
            examples.append(InputExample(guid=guid, text_a=text_a,
                                         text_b=text_b, label=label))
        return examples

    def get_labels(self):
        return ['A', 'B', 'C'] # 示例三分类任务对应数据标签

你会发现,传入训练、验证和测试数据的函数基本一致,关键在于按 BERT 要求将数据处理成规定。

接下来,我们还需要修改 main() 函数中的 processors,添加刚刚自定义的 SelfProcessor:

def main(_):
    tf.logging.set_verbosity(tf.logging.INFO)

    processors = {
        "cola": ColaProcessor,
        "mnli": MnliProcessor,
        "mrpc": MrpcProcessor,
        "xnli": XnliProcessor,
        "self": SelfProcessor, # 添加自定义 Processor
    }

执行文本分类

完成上面的工作,就可以开始执行文本分类任务了。按照 BERT 开源仓库示例的执行代码运行即可,例如:

python run_classifier.py \
  --task_name=sim \  # 执行 processor
  --do_train=true \  # 开启训练模型
  --do_eval=true \  # 开启验证模型
  --do_predict=true \  # 开启测试模型
  --data_dir=./dataset \  # 数据路径
  --vocab_file=./chinese_L-12_H-768_A-12/vocab.txt \  # 预训练模型下载文件路径
  --bert_config_file=./chinese_L-12_H-768_A-12/bert_config.json \
  --init_checkpoint=./chinese_L-12_H-768_A-12/bert_model.ckpt \
  --max_seq_length=128 \  # 模型训练参数
  --train_batch_size=32 \
  --learning_rate=5e-5 \
  --num_train_epochs=1.0 \
  --output_dir=./dataset/output  # 输出文件路径

注意,只有定义了相关的函数,才开启相应的选项。例如,Processor 中未定义验证集函数,那么就把 --do_eval=false 即可。

模型训练完成之后,会在输出文件路径下保留相应的模型和测试结果。BERT 优秀的表现在其论文中有所体现,其为 NLP 问题的快速优化解决提供了新的方法和思路。👍