前言

  在Bert刚出来的时候就想要尝试一下,但是由于时间的原因,也只是看了一下Google的Bert论文,因为水平有限,看的也只是模模糊糊的,对transformer也没有更深入的了解。随着时间的流逝,Google在近期也开源了自己的代码。上周看到了一个dalao写一个可以简单调用的bert框架bert-as-server(CS架构),由此可以进行WordEmbedding步骤。感觉很好玩,就尝试了一下并用其去计算语句相似度。

Bert简介

以下是本人在看完论文之后的理解,并不保证理解的一定是对的,欢迎指正错误!

  首先在刚拿到这篇论文的时候,发现好多单词看不懂,比如Transformer,都不知道应该怎么去翻译,然后通过上网查一些资料,知道了它是谷歌创造的一种学习文本中单词之间的上下文关系的注意力机制。BERT模型中,该模型是基于其周围环境(双向)来学习单词的上下文的,它的顺序是双向的。在预训练中,该模型有两个训练任务。
  第一个任务是MLM(掩蔽语言模型),它是从完形填空中获得的灵感,在向BERT输入单词序列之前,每个序列中有随机15%的单词被[MASK]令牌替换。然后,该模型试图根据序列中其他non-masked提供的上下文来预测掩蔽词的原始值。它的执行过程是80%的时间,用[MASK]标记单词;10%的时间,用随机词替换单词;10%的时间,保持单词不变。
  第二个任务是NSP(下一句话预测)具体地,当为每个预训练示例选择句子A和B时,50%的时间B是跟随A的实际下一句子,并且50%的时间是来自语料库的随机句子。

两个步骤

  • 第一步骤就是以上所说的预训练,通过预训练获得预训练模型,有意思的是,预训练过程是无监督的,也就是说语料库可以说是近乎无限的了,在这一步骤中,google也指出了需要消耗庞大的计算资源,按照google的TPU租赁价格,预训练一次可能会花费几万刀。_(:з」∠)_(我真是个穷人)
  • 第二步是根据我们需要的功能进行微调,这一步需要花费的资源很小。

PS:从近期的google的动作可以看出,预训练已经是越来越流行了。

Bert-as-service

  hanxiao大佬开源出来的bert-as-service框架很适合初学者,因为可以很简单的配置,然后使用上最先进的Bert模型。

服务器上下载该框架

1
2
git clone https://github.com/hanxiao/bert-as-service
cd bert-as-service

下载预训练模型

  在这次实验中,我使用的是

服务器端启动服务

1
nohup python app.py -model_dir uncased_L-12_H-768_A-12/ -num_worker=4 > run.log 2>&1 &

在此介绍一下nohub命令,他可以将进程静默运行,并且不会死掉,而且可以生成日志,很方便,训练过程需要挂载服务器上的时候,亲测很好用!

客户端使用服务

  我们所下载到的bert-as-service中的service/,我们还是需要用在客户端上的,因为需要它来构成通信。

1
2
3
4
5
6
7
>>> from service.client import BertClient
>>> bc = BertClient(ip="XXXX")
>>> bc.encode(['First do it', 'then do it right'])
array([[ 0.06414033, 0.59846574, -0.1504644 , ..., 0.24284995,
-0.93878424, 0.35514495],
[ 0.22535315, 0.16440475, 0.13435377, ..., -0.23758584,
-0.7612017 , 0.21298395]], dtype=float32)

  可以看出我们的句子已经转化为向量了,不过这是句子向量,如果需要更加细致的token信息或者词向量需要根据作者思路进行修改了。

cosine相似度计算

  在我们获得了两个句子的向量之后,我们就可以进行相似度计算的操作了。在此我们使用的cos相似度。

余弦相似度,又称为余弦相似性,是通过计算两个向量的夹角余弦值来评估他们的相似度。余弦相似度将向量根据坐标值,绘制到向量空间中,如最常见的二维空间。

cos相似度的数学推倒

两个向量间的余弦值可以通过使用欧几里得点积公式求出:$$a\bullet b=||a|| ||b||cos\theta$$
给定两个属性向量,A和B,其余弦相似性θ由点积和向量长度给出,如下所示:
cos相似度的计算公式

cos相似度的实现

实例

1
2
3
4
5
6
7
8
9
10
from service.client import BertClient
import numpy as np
bc = BertClient(ip="XXX")

def cosine(a,b):
return a.dot(b)/(np.linalg.norm(a)*np.linalg.norm(b))

emb=np.array(bc.encode(['First do it', 'then do it right']))

print(['First do it', 'then do it right'],":",cosine(emb[0],emb[1]))

结果

1
['First do it', 'then do it right'] : 0.92645866