你可能已经听说 GPT-3,但是你也不能不知道 BERT —— 跟我一起用 BERT 跑个小用例

本文目录

一、关于 BERT 的一些背景

2018 年 Google 发布 BERT 后迅速在 NLP 领域引起广泛关注。BERT(Bidirectional Encoder Representations from Transformers)是一种自然语言处理(NLP)的深度学习模型,它可以进行语言模型预测、序列标注和问答等任务。BERT 采用双向的 Transformer 编码器架构,使用了大量的数据和计算资源进行训练,因此具有较强的泛化能力。

BERT 的训练方法是通过让模型对给定的输入文本进行自监督学习,即使用未标记的语料进行训练。BERT 可以在很多 NLP 任务中获得较好的性能,并且由于其双向的编码方式,能够更好地理解语境信息。

BERT 的训练需要大量的计算资源,因此它常常被用来作为解决 NLP 问题的预训练模型,可以用来初始化其他模型的权重,使得这些模型能够更快速地收敛。

二、开始一个 BERT 的动手小试验

为了让 conda 使用 Python 3.7,你可以按照这些步骤来操作。

1、安装 Anaconda 来为部署 BERT 做环境准备

先了解几个概念:Anaconda 是一个软件包管理系统,其中包含了 conda 和许多其他的工具。Conda 是 Anaconda 中的一个组件,用于安装和管理软件包。 我们需要用 conda 创建一个环境,在这个环境里去启用我们想要使用的 BERT 所需要的各种依赖。

更新 conda 到最新版本:

conda update -n base conda

使用 Python 3.7 创建一个新的环境:

conda create -n py37 python=3.7

激活这个新环境:

conda activate py37

验证正在使用的是正确版本的 Python

python --version

另外你可能还会用到的 conda 命令有:

# 你之后一定会需要 deactivate 一个环境,命令如下:
conda deactivate py37

# 查看 conda 当前安装的所有库
conda list

2、安装 BERT 所需要的各种依赖

conda install tensorflow==1.14.0

验证 tensorflow 是否安装正确:

import tensorflow as tf
print(tf.__version__)

3、下载一个预训练(Pre-Train)过的 BERT 模型

官方的模型在这里浏览:https://github.com/google-research/bert#pre-trained-models

也有一些中文的模型,以下是 ChatGPT 推荐的三个:

  • BERT-Base, Chinese:这是 Google 官方提供的中文 BERT 模型,在中文 NLP 任务中表现良好。你可以从 这里下载这个模型。
  • ERNIE:这是由中科院自然语言所提供的中文 BERT 模型,包含了额外的语义信息。你可以从 这里下载这个模型。
  • RoBERTa-wwm-ext:这是由清华大学自然语言处理实验室提供的中文 BERT 模型,在多种中文 NLP 任务中表现良好。你可以从 这里下载这个模型。

4、安装 BERT 的服务端和客户端

这里我们使用 bert-as-service,bert-as-service 是一种将 BERT 模型部署为服务的方式。该工具使用 TensorFlow Serving 来运行 BERT 模型,并允许通过 REST API 进行调用。根据 bert-as-service 的文档,它已经在 TensorFlow 1.14.0 上测试过。

在你激活的环境里,安装 bert-as-service

# 安装服务端和客户端
# 更多关于 bert-serving-server 的信息可以参考:https://bert-serving.readthedocs.io/en/latest/index.html
conda install bert-serving-server bert-serving-client 
验证 bert-as-service 是否安装成功
bert-serving-start -h

5、启动 BERT 服务端

# 命令行下启动BERT服务
# -num_worker 表示启动几个worker服务,即可以处理几个并发请求,超过这个数字的请求将会在LBS(负载均衡器)中排队等待
bert-serving-start -model_dir /模型/的/绝对/路径 -num_worker=4

6、在 PyCharm 中使用 Conda 的环境

在 PyCharm 中启用 Interpreter 为 Anaconda,macOS 上具体地是在「Preference - Project - Python Interpreter - Add Interpreter - Add Local Interpreter - Conda Environment」。

接下来还有一项重要的步骤就是选择该 project 要加载包文件的路径。如果不进行这一步,那该 project 还是从系统环境变量中的路径来搜索你要加载的包,这样在你用 Anaconda 新建的这个环境中所特有的包就会出现无法加载的问题。单击菜单栏 Run 选择 Edit Configuration。在Environment variables中添加一个新的 Path。新的路径为你用 Anaconda 新建的环境的文件夹中的「/Users/captain/opt/anaconda3/bin/python」

配置 PyCharm 这里参考:https://docs.anaconda.com/anaconda/user-guide/tasks/pycharm/

7、编写程序实现 BERT 客户端

这里有一些客户端例子可以参考:https://blog.csdn.net/qq_18256855/article/details/123860126

from bert_serving.client import BertClient
import numpy as np

# 定义类
class BertModel:
    def __init__(self):
        try:
            self.bert_client = BertClient(ip='127.0.0.1', port=5555, port_out=5556)  # 创建客户端对象
            # 注意:可以参考API,查看其它参数的设置
            # 127.0.0.1 表示本机IP,也可以用localhost
        except:
            raise Exception("cannot create BertClient")

    def close_bert(self):
        self.bert_client.close()  # 关闭服务

    def sentence_embedding(self, text):
        '''对输入文本进行embedding
          Args:
            text: str, 输入文本
          Returns:
            text_vector: float, 返回一个列表,包含text的embedding编码值
        '''
        text_vector = self.bert_client.encode([text])[0]
        return text_vector  # 获取输出结果

    def caculate_similarity(self, vec_1, vec_2):
        '''根据两个语句的vector,计算它们的相似性
          Args:
            vec_1: float, 语句1的vector
            vec_2: float, 语句2的vector
          Returns:
            sim_value: float, 返回相似性的计算值
        '''
        # 根据cosine的计算公式
        v1 = np.mat(vec_1)
        v2 = np.mat(vec_2)
        a = float(v1 * v2.T)
        b = np.linalg.norm(v1) * np.linalg.norm(v2)
        cosine = a / b
        return cosine


if __name__ == "__main__":
    # 创建bert对象
    bert = BertModel()
    while True:
        # --- 输入语句 ----
        input_a = input('请输入语句1: ')

        if input_a == "N" or input_a == "n":
            bert.close_bert()  # 关闭服务
            break

        input_b = input('请输入语句2: ')

        # --- 对输入语句进行embedding ---

        a_vec = bert.sentence_embedding(input_a)
        print('a_vec shape : ', a_vec.shape)

        b_vec = bert.sentence_embedding(input_b)
        print('b_vec shape : ', b_vec.shape)

        # 计算两个语句的相似性
        cos = bert.caculate_similarity(a_vec, b_vec)
        print('cosine value : ', cos)

        print('\n\n')

        # 如果相似性值大于0.85,则输出相似,否则,输出不同
        if cos > 0.85:
            print("2个语句的含义相似")
        else:
            print("不相似")

在使用 bert-serving-client 连接 bert-serving-server 时,你需要确保 bert-serving-server 使用的模型和 bert-serving-client 使用的模型是匹配的,否则会出现错误。

程序正常运行后,将要求你输入两句话,然后 BERT 计算两句话的相似性。

请输入语句1: 
请输入语句2: 

两句输入好确认后,得到如下形式的结果:

a_vec shape :  (768,)
b_vec shape :  (768,)
cosine value :  0.8691698561422959

其实这个小试验蛮没意思的,而且准确性也比较令人质疑。

三、BERT 模型的优劣势及其原因

论文地址:《BERT: Pre-Training of Deep Bidirectional Transformers for Language Understanding》

1、BERT 的优势是很明显的

复旦大学的邱锡鹏教授层评价 BERT 的「里程碑意义」在于:

证明了一个非常深的模型可以显著提高 NLP 任务的准确率,而这个模型可以从无标记数据集中预训练得到。

1.1、MLM 和 NSP 预训练能够捕捉到自然语言中的各种复杂细节

因为 BERT 采用了双向的自注意力机制,这里的「双向」意味着 BERT 模型可以同时利用输入文本的前后文信息来预测下一个词是什么、下一句是什么。这样 BERT 模型就可以捕捉到自然语言中的各种隐藏的细节,比如语义关系、语法结构、语义暗示等等。

具体地,BERT 采用了 Masked Language Model(MLM)来做「下一个词是什么」的预训练,采用了 Next Sentence Prediction(NSP)来做「下一句是什么」的预训练。MLM 的方式其实就很像英语考试里的「完形填空」,而 NSP 的方式,就像整句的完形填空。

1.2、识别并专注于较重要的部分进行文本处理

这要得益于因为 BERT 采用了自注意力机制。自注意力机制,通过计算输入单元的权重值,来确定在一个输入序列中哪些输入单元是重要的。具体地,一个输入单元与其他单元的相似性越高,按照我们自然语言的逻辑,那么这部分是在被重复、强调、翻来覆去用不同的方式在解释,那么这部分就是重要的,权重值就更高。

1.3、快速构建针对具体任务的 NLP 系统

因为 BERT 采用了预训练模型,能够在没有监督标注数据的情况下从大量文本中学习语言模型。因为我们认为上下文信息本身就能推测出某个词,所以大量的文本数据本身就是一种「自带标注」的数据,所以 BERT 能够无监督学习。

2、BERT 模型的劣势及其原因

2.1、随机挖 MASK 的完形填空题是有隐患的

对于上面提到的 MLM、NSP 方法做预训练,那么问题也就显而易见了,如果我们挖掉的一组 MASK 完形填空词,是强关联的(非条件独立),那么这一组词的预测就都会出现问题。

2.2、NSP 任务有必要吗?

论文《Crosslingual language model pretraining》中提到 BERT 的 NSP 可能是非必要的,针对这个问题,后续出现的模型都移除了 NSP 任务,比如 RoBERTa、spanBERT、ALBERT。

2.3、针对两个或以上词组成的连续词的词义被丢失

比如 cutting-edge,MLM 的方式可能会割裂这两个子词的相关性,导致模型丢失这个词的词义,针对这个问题 Google 后来发表了 BERT-WWM,WWM 即 Whole Word Masking,从字面就能理解针对的问题。哈尔滨工业大学的科大讯飞联合实验室后来推出了 Chinese-BERT-WWM 专门针对中文解决了这个问题。

2.4、需要的算力高

算力高,自然需要的计算成本运行更高。不过算力成本高这种问题总有办法优化,通常来说不是模型本身所处理问题的局限性和先决条件的局限性(比如依赖大量人工工作)就非常好了。

2.5、需要的模型大

模型大,自然存储成本也就高了。这也类似于上一点,而且算力、存储成本高,可以在大型应用中把成本均摊下来,比如 BERT 如果支持的某个 AGI 应用得到广泛普及。

四、一些关于 BERT 的问题

1、BERT 模型的所谓「双向」与 BiLSTM 的「双向」是啥区别?

BiLSTM 是把句子再倒序一遍,而 BERT 的双向是指在 Encoder 的自注意力机制下编码一个 token 时「同时利用上下文」的 token。

2、为什么 BERT 可以比 RNN 更好地并行化

RNN 因为有时序概念,即后面的特征计算,依赖于前面计算的结果,所以就形成了循环(Recurrent)。而 BERT 采用了自注意力机制则没有时序概念,每个词特征都依赖其上下文独立计算,因此更容易并行化。

Reference

  1. https://arxiv.org/abs/1810.04805
  2. https://github.com/google-research/bert
  3. https://github.com/ymcui/Chinese-BERT-wwm
  4. https://zhuanlan.zhihu.com/p/195723105
  5. https://www.jiqizhixin.com/articles/2018-10-24-13