使用bert模型将可变长度句子映射到固定长度向量(服务器)
bert-serving-server的Python项目详细描述
这个readme.md是pypi的镜像。请访问https://github.com/hanxiao/bert-as-service/blob/master/readme.md" rel="nofollow">https://github.com/hanxiao/bert-as-service/blob/master/readme.md获取最新的readme.md.
作为服务的伯特
将bert模型用作句子编码服务,即将可变长度的句子映射到固定长度的向量。
突出显示•。 它是什么•。 安装. 入门•。 API•。 教程•。 常见问题解答• 基准• 博客
韩晓制作•:环球与子午线:https://han xiao.github.io rel="nofollow">https://hanxiao.github.io
< H2>它是什么bert是一个nlp模型,由google开发用于训练前语言表示。它利用了大量在web上公开的纯文本数据,并以无监督的方式进行培训。对每种语言来说,预先训练bert模型是一个相当昂贵但一次性的过程。幸运的是,google发布了几个预先培训的模型,您可以从这里下载。
句子编码/嵌入是许多nlp应用程序(如情感分析、文本分类)所需的上游任务。目标是将可变长度的句子表示成固定长度的向量,例如hello world
到[0.1,0.3,0.9]
。向量的每个元素都应该"编码"原始句子的一些语义。
最后,bert as service
使用bert作为句子编码器,并通过zeromq将其作为服务宿主,允许您仅用两行代码将句子映射成固定长度的表示形式。
亮点
- :telescope:最新技术:基于google ai发布的预训练12/24层bert模型,该模型被视为nlp社区的里程碑。
- :hatching_chick:易于使用:只需要两行代码即可获得句子/标记级别的编码。
- :zap:快速:在单个特斯拉M40 24GB上每秒900个句子。低延迟,速度优化。请参见基准
- :octopus:可伸缩性:在多个GPU和多个客户机上平滑地伸缩,而不必担心并发性。请参见基准测试。
- :gem:可靠:测试了数十亿个句子;连续几天不间断地运行,没有任何异常。
更多功能:xla&fp16支持;混合GPU-CPU工作负载;优化图形;tf.data
友好;自定义标记器;灵活的池策略;使用BERT作为服务以JSON形式服务HTTP请求" rel="nofollow">buiHTTP服务器和仪表板中的LD;异步编码;多播等
安装
通过pip
安装服务器和客户端。它们可以单独安装,甚至可以安装在不同的机器上:
pip install bert-serving-server # server pip install bert-serving-client # client, independent of `bert-serving-server`
注意,服务器必须在python>;=3.5上运行,且tensorflow>;=1.10(one point ten)。同样,服务器不支持python 2!
:point_up:出于以下考虑,客户机可以在python 2和python 3上运行
开始
<H4>1。下载经过预训练的伯特模型下载下面列出的型号,然后将zip文件解压缩到某个文件夹中,例如/tmp/english-12\u h-768\u a-12/
可选:在下游任务中微调模型。为什么是可选的?
<H4>2。启动BERT服务
安装服务器后,您应该能够使用bert serving start
cli,如下所示:
bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
这将启动一个有四个工人的服务,这意味着它最多可以处理四个并发的请求。更多并发请求将在负载平衡器中排队。有关详细信息,请参见我们的常见问题解答和客户数量基准。
下面显示正确启动时服务器的外观:
<细节>docker build -t bert-as-service -f ./docker/Dockerfile . NUM_WORKER=1PATH_MODEL=/PATH_TO/_YOUR_MODEL/ docker run --runtime nvidia -dit -p 5555:5555 -p 5556:5556 -v $PATH_MODEL:/model -t bert-as-service $NUM_WORKER<详细内容> <H4>3。使用客户端获取句子编码
现在,您可以对句子进行如下编码:
frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])
它将返回一个ndarray
(或者list[list[float]]
如果您愿意的话),其中每一行是表示一个句子的固定长度向量。有成千上万个句子?只需编码!甚至不用批量处理,服务器会处理的。
作为bert的一个特性,您可以通过将一对句子与_
(前后都有空格)连接起来来获得它们的编码,例如
bc.encode(['First do it ||| then do it right'])
下面显示编码时服务器的外观:
远程使用bert服务
您还可以在一台(GPU)计算机上启动服务,并从另一台(CPU)计算机调用它,如下所示:
# on another CPU machinefrombert_serving.clientimportBertClientbc=BertClient(ip='xx.xx.xx.xx')# ip address of the GPU machinebc.encode(['First do it','then do it right','then do it better'])
注意,您只需要pip install-u bert serving client在这种情况下,不需要服务器端。您还可以使用http请求来调用服务。
:bulb:想了解更多信息吗?查看我们的教程:
- 在3分钟内构建一个qa语义搜索引擎。
- 为微调的bert模型提供服务
- 获取类似elmo的上下文单词嵌入
- 使用自己的标记赋予器
- 使用
bertclient
和tf.data
api - 使用bert特性和tf.estimator api训练文本分类器
- 使用tfrecord数据保存和加载
- 异步编码
- 向多个客户端广播
- 在仪表板中监视服务状态
- 使用
bert作为服务来服务json中的http请求
- 从python启动bertserver
服务器和客户端API
返回顶部
学习最新api的最佳方法是阅读文档。
服务器API
请始终参考此处记录的最新服务器端api。,您可以通过:
bert-serving-start --help bert-serving-terminate --help bert-serving-benchmark --help<表><广告>
型号目录
调谐模式目录
ckpt_name
bert_model.ckpt
配置名称
bert_config.json
图形TMP目录
max_seq_len
25
大小写标记化
mask_cls_sep
工人数
1
最大批量大小
256
优先级批量大小
16
端口
5555
端口输出
5556
Http_端口
cors
*
池策略
降低平均值
生成编码向量的池策略,有效值为none
,reduce_mean
,reduce_max
,reduce_mean_max
,cls_token
,first_token
,sep_token
,last_token
。有关这些策略的说明,请参见此处。要获取序列中每个令牌的编码,请将其设置为none
池层
[-2]
-1
表示最后一层,-2
表示最后一层,[-1,-2]
表示连接最后两层的结果,等等。GPU内存分数
0.5
cpu
xla
fp16
设备地图
[]
向客户显示令牌
客户端API
请始终参考此处记录的最新客户端API。客户端提供一个名为bertclient
的Python类,它接受以下参数:
IP
本地主机
端口
5555
端口输出
5556
输出fmt
ndarray
ndarray
/list
)显示服务器配置
false
检查版本
true
标识
无
超时
-1
abertclient
实现以下方法和属性:
.encode()
.encode_async()
.fetch()
一起使用。encode_async()
或一起使用。encode(blocking=false)
。不保留发送顺序。.fetch_all()
一起使用。encode_async()
或一起使用。encode(blocking=false)
。发送订单被保留。.close()
。状态
。服务器状态
:book:教程
返回顶部
示例的完整列表可以在example/
中找到。您可以通过python example/example-k.py
运行每个。大多数示例要求您首先启动bertserver,请按照此处的说明操作。注意,尽管bertclient
在python 2.x和3.x上都通用,但示例只在python 3.6上测试。
- 在3分钟内构建一个qa语义搜索引擎。
- 为微调的bert模型提供服务
- 获取类似elmo的上下文单词嵌入
- 使用自己的标记赋予器
- 使用
bertclient
和tf.data
api - 使用bert特征和tf.estimator-api训练文本分类器
- 使用tfrecord数据保存和加载
- 异步编码
- 向多个客户端广播
- 在仪表板中监视服务状态
- 使用
bert作为服务来服务json中的http请求
- 从python启动bertserver
在3分钟内构建qa语义搜索引擎
< Buff行情>完整的示例可以在example8.py中找到。
作为第一个例子,我们将在三分钟内使用bert as service
实现一个简单的qa搜索引擎。别开玩笑!目标是找到与用户输入类似的问题并返回co回答正确。首先,我们需要一个问答对列表。幸运的是,这个自述文件已经包含了faq列表,所以我将使用它使这个示例完全独立。让我们首先加载所有问题并显示一些统计信息。
prefix_q='##### **Q:** 'withopen('README.md')asfp:questions=[v.replace(prefix_q,'').strip()forvinfpifv.strip()andv.startswith(prefix_q)]print('%d questions loaded, avg. len of %d'%(len(questions),np.mean([len(d.split())fordinquestions])))
这将提供33个加载的问题,平均长度为9。看来我们的问题已经够多了。现在用 接下来,我们需要将问题编码成向量: 最后,我们准备接收新的查询,并对现有问题执行简单的"模糊"搜索。为此,每次有新的查询出现时,我们都将其编码为一个向量,并使用 就这样!现在运行代码并键入查询,查看此搜索引擎如何处理模糊匹配: 经过预训练的bert模型在许多任务中常常表现出相当"好"的性能。然而,要释放bert的真正威力,就必须对下游任务(或特定于域的数据)进行微调。在本例中,我将向您展示如何为经过微调的bert模型服务。 我们遵循"句子(和句子对)分类任务"中的说明,并使用 如果您查看 不要害怕那些神秘的文件,因为对我们来说,唯一重要的文件是model.ckpt-343.data-00000-of-00001(看起来我的培训已经到了343步)。根据总的训练步骤,可以得到 现在,通过将三个部分放在一起来启动bertserver: 服务器启动后,您应该在日志中找到这一行: 这意味着bert参数被过度编码,并成功地从我们微调的 简而言之,找到您微调过的模型路径和检查点名称,然后分别将它们输入到 启动服务器,并将"池策略"设置为"无"。 要使单词嵌入对应于每个标记,只需使用切片索引,如下所示: 请注意,无论原始序列有多长,服务始终会为每个序列返回一个 通常,您希望使用自己的标记器来分段句子,而不是使用bert的默认标记器。硅mply call 这给出了 请注意,不需要启动单独的服务器来处理标记化/未标记化的句子。服务器可以自动判断和处理这两种情况。 有时您希望明确了解在服务器端执行的标记化,以便更好地理解嵌入结果。其中一个例子是要求从服务器嵌入单词(使用未经训练的bert模型启动bertserver
;bert-serving-start -num_worker=1 -model_dir=/data/cips/data/lab/data/model/uncased_L-12_H-768_A-12
pip install bert-serving-server # server
pip install bert-serving-client # client, independent of `bert-serving-server`
0
doc vecs
计算它的点积;对结果进行向下排序;然后返回to p-k类似的问题,如下所示:pip install bert-serving-server # server
pip install bert-serving-client # client, independent of `bert-serving-server`
1
提供微调的伯特模型
run戋classifier.py
微调uncased戋l-12戋h-768戋a-12戋/code>模型MRPC任务。微调后的模型存储在
something./tmp/mrpc_output/
中,可以通过指定run_classifier.py
的--output_dir
来更改。
/tmp/mrpc_output/
,它包含如下内容:pip install bert-serving-server # server
pip install bert-serving-client # client, independent of `bert-serving-server`
2
model.ckpt-123.data-00000-of-00001
或model.ckpt-9876.data-00000-of-00001
。现在,我们已经收集了服务此微调模型所需的全部三条信息:/path/to/bert/uncased\u l-12\u h-768\u a-12
/tmp/mrpc_output/
;pip install bert-serving-server # server
pip install bert-serving-client # client, independent of `bert-serving-server`
3
pip install bert-serving-server # server
pip install bert-serving-client # client, independent of `bert-serving-server`
4
/tmp/mrpc_output/model.ckpt-343
加载。完成!-tuned\u model\u dir
和-ckpt\u name
。嵌入类似elmo的上下文单词
pip install bert-serving-server # server
pip install bert-serving-client # client, independent of `bert-serving-server`
5
pip install bert-serving-server # server
pip install bert-serving-client # client, independent of `bert-serving-server`
6
[max_seq_len,768]
矩阵。当使用切片索引获取单词嵌入时,请注意填充到序列中的特殊标记,即[cls]
,[sep]
,0u pad
使用自己的标记器
encode(is_tokenized=true)
在客户端幻灯片上,如下所示:pip install bert-serving-server # server
pip install bert-serving-client # client, independent of `bert-serving-server`
7
[2,25,768]
张量,其中第一个[1,25,768]
对应于"hello world!"。如果查看它的值,您将发现只有前四个元素(即[1,0:3768]
有值,所有其他元素都是零。这是因为伯特认为"你好,世界!"作为四个标记:[cls]
hello
world!
[sep]
,其余部分是填充符号,在输出前被屏蔽。-pooling_strategy none
),人们想知道哪个单词是标记化的,哪个是不可识别的。您可以通过以下步骤获得这些信息:
encode(…,show_tokens=true)调用服务器
例如,基本用法如
pip install bert-serving-server # server pip install bert-serving-client # client, independent of `bert-serving-server`8
返回一个元组,其中第一个元素是嵌入,第二个是来自服务器的标记化结果:
pip install bert-serving-server # server pip install bert-serving-client # client, independent of `bert-serving-server`9
当使用您自己的标记化时,您可能仍然希望检查服务器是否尊重您的标记。例如,
bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=40
返回:
bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=41
我们可以观察到这个世界!和这在服务器上无法识别,因此它们被设置为
[unk]
最后,请注意,来自google的pretrained bert中文是基于字符的,也就是说,它的词汇表是由单个汉字组成的。因此,如果使用分词算法对数据进行预处理并馈送到该模型中,则没有意义。
非常好奇的读者可能会注意到,尽管标记化结果包括[cls]
(做得好,侦探!)。原因是,无论设置了-mask-cls-sep
,标记化结果都将始终包括[cls]
和[unk]
。当您希望以后对齐标记时,这可能很有用。请记住,-mask_cls_sep
只屏蔽计算之外的[cls]
和[sep]
。它不影响标记化算法。
使用bertclient
和tf.data
api
< Buff行情>完整的示例可以在example4.py中找到。Keras中还有一个示例 api使您能够从简单、可重用的部分构建复杂的输入管道。还可以使用 这里的诀窍是创建一个 完整的示例可以在example5.py中找到。
在上一个例子之后,我们可以使用 完整的示例可以是例如5.pybertclient
动态编码句子,并在下游模型中使用向量。下面是一个示例:bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
2
bertclient
池,并逐个重用它们。这样,我们就可以充分利用num_parallel_调用的能力
使用bert特性和
tf.estimator
api训练文本分类器
< Buff行情>tf.estimator
api轻松地将它扩展到一个完整的分类器。只需对输入函数进行如下小改动:bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
3
使用tfrecord数据保存和加载
< Buff行情>完整的示例可以在example6.py中找到。
tfrecord文件格式是一种简单的面向记录的二进制格式,许多tensorflow应用程序使用它来训练数据。您还可以对所有序列进行预编码,并将其编码存储到tfrecord文件中,然后加载该文件以构建一个tf.dataset
。例如,要将编码写入tfrecord文件:
bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=44
现在我们可以从中加载并构建一个tf.dataset
:
bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=45
要将字/令牌级嵌入保存到tfrecord,首先需要将张量展平到1d数组中,如下所示:
bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=46
然后在加载时重建形状:
bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=47
小心,这将生成一个巨大的tfrecord文件。
异步编码
< Buff行情>完整的示例可以在example2.py中找到。
bertclient.encode()
提供了一种很好的同步方式来获取句子编码。但是,有时我们希望以异步方式执行此操作,首先将所有文本数据提供给服务器,然后再获取编码的结果。这很容易做到:
bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=48
向多个客户端广播
< Buff行情>完整的示例可以在example3.py中找到。
编码的结果根据其标识路由到客户端。如果你有多个相同身份的客户,那么他们都会收到结果!您可以使用这个多播功能来做一些很酷的事情,例如在多个分离的进程中训练多个不同的模型(有些使用scikit learn
有些使用tensorflow
),而只调用bertserver
一次。在下面的示例中,bc
及其两个克隆都将接收编码向量。
bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=49
在仪表板中监视服务状态
< Buff行情>完整的示例可以在plugin/dashboard/中找到。
作为基础设施的一部分,您可能还希望监视服务状态并将其显示在仪表板中。为此,我们可以使用:
docker build -t bert-as-service -f ./docker/Dockerfile . NUM_WORKER=1PATH_MODEL=/PATH_TO/_YOUR_MODEL/ docker run --runtime nvidia -dit -p 5555:5555 -p 5556:5556 -v $PATH_MODEL:/model -t bert-as-service $NUM_WORKER0
这给出了服务器的当前状态,包括json格式的请求数、客户端数等。唯一剩下的事情是启动一个http服务器,将这个json返回到呈现它的前端。
或者,您可以在通过:
启动服务器时简单地公开http端口docker build -t bert-as-service -f ./docker/Dockerfile . NUM_WORKER=1PATH_MODEL=/PATH_TO/_YOUR_MODEL/ docker run --runtime nvidia -dit -p 5555:5555 -p 5556:5556 -v $PATH_MODEL:/model -t bert-as-service $NUM_WORKER1
这将允许用户使用javascript或curl
在端口8001获取服务器状态。
plugin/dashboard/index.html
显示了一个基于bootstrap和vue.js的简单仪表板。
使用bert as service
在json中服务http请求
除了从python调用bert作为服务之外,还可以通过json中的http请求调用它。特别是在禁止低传输层的情况下,它非常有用。在场景后面,
bert as service
在一个单独的进程中生成一个flask服务器,然后将一个bertclient
实例作为代理来与通风机通信。
要启用内置http服务器,我们需要首先(重新)安装具有一些额外python依赖项的服务器:
docker build -t bert-as-service -f ./docker/Dockerfile . NUM_WORKER=1PATH_MODEL=/PATH_TO/_YOUR_MODEL/ docker run --runtime nvidia -dit -p 5555:5555 -p 5556:5556 -v $PATH_MODEL:/model -t bert-as-service $NUM_WORKER2
然后简单地启动服务器H:
docker build -t bert-as-service -f ./docker/Dockerfile . NUM_WORKER=1PATH_MODEL=/PATH_TO/_YOUR_MODEL/ docker run --runtime nvidia -dit -p 5555:5555 -p 5556:5556 -v $PATH_MODEL:/model -t bert-as-service $NUM_WORKER3 完成了!您的服务器现在正在端口
8125
同时监听http和tcp请求!要发送http请求,请首先按以下方式准备json格式的负载:
docker build -t bert-as-service -f ./docker/Dockerfile . NUM_WORKER=1PATH_MODEL=/PATH_TO/_YOUR_MODEL/ docker run --runtime nvidia -dit -p 5555:5555 -p 5556:5556 -v $PATH_MODEL:/model -t bert-as-service $NUM_WORKER4
,其中,id
是一个唯一的标识符,帮助您同步结果;被标记化了
遵循bertclient
api中的含义false
默认设置。
然后通过http post请求在/encode
调用服务器。您可以使用javascript或其他方式,下面是一个使用curl的示例:
docker build -t bert-as-service -f ./docker/Dockerfile . NUM_WORKER=1PATH_MODEL=/PATH_TO/_YOUR_MODEL/ docker run --runtime nvidia -dit -p 5555:5555 -p 5556:5556 -v $PATH_MODEL:/model -t bert-as-service $NUM_WORKER5
,它返回一个json:
docker build -t bert-as-service -f ./docker/Dockerfile . NUM_WORKER=1PATH_MODEL=/PATH_TO/_YOUR_MODEL/ docker run --runtime nvidia -dit -p 5555:5555 -p 5556:5556 -v $PATH_MODEL:/model -t bert-as-service $NUM_WORKER6
要获取服务器状态和客户端状态,可以分别在/status/server
和/status/client
发送get请求。
最后,还可以通过在启动bert service start时指定-cors
来配置cors以限制服务器的公共访问。默认情况下-cors=*
,这意味着服务器是公共访问的。
从python启动bertserver
除了shell,还可以从python启动一个bertserver
。简单地做
docker build -t bert-as-service -f ./docker/Dockerfile . NUM_WORKER=1PATH_MODEL=/PATH_TO/_YOUR_MODEL/ docker run --runtime nvidia -dit -p 5555:5555 -p 5556:5556 -v $PATH_MODEL:/model -t bert-as-service $NUM_WORKER7
请注意,它基本上反映了cli中的arg解析行为,因此该 要关闭服务器,可以通过: 或通过shell cli: 这将终止在5555端口的本地主机上运行的服务器。您也可以使用它来终止远程服务器,有关详细信息,请参见 返回顶部
设计理念和技术细节可以在我的博客中找到。
a:本回购协议的bert代码是从原始bert回购协议中派生出来的,并进行了必要的修改,es.py" rel="nofollow">特别是在extract\u features.py中 一般来说,每个句子都被翻译成768维向量。根据您使用的预先训练的bert, a:是的,需要使用池来获取句子的固定表示。在默认策略中,我取句子中所有标记的第二个到最后一个隐藏层,并进行平均池。 a:是和否。一方面,谷歌预先训练了贝特的维基百科数据,因此应该对语言进行足够的编码。通用电气进入模型。拥有这样的功能并不是一个坏主意。另一方面,这些先验知识并不特定于任何特定领域。如果你在使用它的时候表现得不理想,比如说,对法律案件进行分类,这应该是完全合理的。尽管如此,您始终可以首先在下游任务上微调自己的bert,然后使用 a:当然!调用服务器时,只需使用要连接的层的列表。例子: a:这里有一个表,总结了我实施的所有池策略。通过指定 a:因为预先训练的模型尚未对任何下游任务进行微调。在这种情况下, a:默认情况下,此服务在最后一层的第二层上工作,即 a:在预训练过程中,最后一层与目标函数(即蒙面语言模型和下一句预测)过于接近,因此可能偏向这些目标。如果您对这个参数有疑问,并且仍然想使用最后一个隐藏层,请随意设置 a:视情况而定。请记住,不同的bert层捕获不同的信息。为了更清楚地看到这一点,这里是对uci新闻聚合器数据集(uci news aggregator dataset)的可视化,其中我随机抽取了20k个新闻标题;从不同的层和不同的池策略获取句子编码,最后redu通过主成分分析(pca)把它转换成二维(当然也可以做t-sne,但这不是我的观点)。只有四类数据,用红色、蓝色、黄色和绿色表示。为了重现结果,PLE请运行示例7.py。
直观地说,池层=-1列表中的所有内容都应该是字符串,例如
接近训练输出,因此可能偏向于训练目标。如果不对模型进行微调,则可能导致不正确的表示。['-port','5555']
而不是['-port',5555]
bertserver
类中的static方法
docker build -t bert-as-service -f ./docker/Dockerfile .
NUM_WORKER=1PATH_MODEL=/PATH_TO/_YOUR_MODEL/
docker run --runtime nvidia -dit -p 5555:5555 -p 5556:5556 -v $PATH_MODEL:/model -t bert-as-service $NUM_WORKER
8
docker build -t bert-as-service -f ./docker/Dockerfile .
NUM_WORKER=1PATH_MODEL=/PATH_TO/_YOUR_MODEL/
docker run --runtime nvidia -dit -p 5555:5555 -p 5556:5556 -v $PATH_MODEL:/model -t bert-as-service $NUM_WORKER
9
bert serving terminate--help
。:Speech_气球:常见问题解答
q:您是否有介绍您车型详细信息的书面说明或其他书面说明?
q:伯特码来自哪里?
q:句子向量有多大?
池策略
和池层
输出向量的维数可能不同。q:如何获得固定表示?你是不是在合伙什么的?
q:您是否建议在没有微调的情况下使用bert?
bert as service
高效地提取特征向量。请记住,bert as service
只是一个基于bert的特征提取服务。没有什么能阻止你使用经过微调的伯特。q:我可以得到几层而不是一层的连接吗?
frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])
0
问题:有哪些可用的池策略?
bert-serving start-pooling_策略来选择您最喜欢的策略
< /广告><正文>策略
说明
无
完全没有池,当您想使用单词嵌入而不是句子嵌入时非常有用。这将产生一个序列的编码矩阵。 降低平均值
取时间轴上编码层隐藏状态的平均值 减少最大值
取时间轴上编码层隐藏状态的最大值 降低平均值
将 reduce_mean
和reduce_max
分开,然后在最后一个轴上将它们连接在一起,从而产生1536个dim语句编码CLS U令牌
或第一个令牌
获取与 [cls]
对应的隐藏状态,即第一个标记sep_令牌
或最后一个令牌
获取对应于 [sep]
的隐藏状态,即最后一个标记q:为什么不使用第一个令牌的隐藏状态作为默认策略,即
[cls]
?[cls]
的隐藏状态不是一个好的句子表示。如果以后对模型进行微调,也可以使用[cls]
。q:伯特有12/24层,那么你说的是哪一层?
池层=-2
。您可以通过将池层设置为其他负值来更改它,例如-1对应于最后一层。q:为什么不是最后一个隐藏层?为什么是倒数第二?
pooling-layer=-1
q:那么哪个层和哪个池策略是最好的?
pooling_layer=-12
接近单词嵌入,可以保留非常原始的单词信息(没有花哨的自我注意等)。另一方面,您只需使用单词嵌入就可以获得相同的性能。也就是说,介于[-1,-12]之间的任何东西都是一种权衡。
q:我可以使用其他池技术吗?
要重现结果,请运行bert serving benchmark
q:后端基于什么?
a:zeromq
q:什么是并行过程幕后模特?
q:为什么服务器需要两个端口?
一个端口用于将文本数据推入服务器,另一个端口用于将编码结果发布到客户端。这样,我们就消除了背后的闲聊,也就是说,在每一个层次上,收件人都不会和发件人顶嘴。如上图所示,整个消息流是严格单向的。消除回响对于真正的可伸缩性至关重要,它允许我们以异步方式使用bertclient
。
q:我需要在客户端使用tensorflow吗?
a:否。将bertclient
视为一个通用特征提取程序,其输出可以输入到任何ml模型,例如scikit learn
,pytorch
,tensorflow
。客户机需要的唯一文件是client.py
。将此文件复制到您的项目并导入,然后您就可以开始了。
q:我可以使用google提供的多语言bert模型吗?
a:是。
q:我可以使用自己微调的伯特模型吗?
a:是。事实上,这是建议。确保model\u dir中有以下三项:
- 一个tensorflow检查点(
bert_model.ckpt
)包含预先训练的权重(实际上是3个文件)。 - 一个vocab文件(
vocab.txt
)将wordpiece映射到word id。 - 一个配置文件(
bert_config.json
),指定模型的超参数。
q:我可以在python 2中运行它吗?
a:服务器端否,客户端是。这是基于这样的考虑:Python2.x可能仍然是某些技术堆栈中的一个主要部分。将整个下游堆栈迁移到python 3,以支持将bert作为服务可能需要相当大的努力。另一方面,设置bertserver
只是一次性的事情,甚至可以在docker容器中运行。为了简化集成,我们在客户端支持Python2,以便您可以直接将bertclient
用作Python2项目的一部分,而服务器端应该始终由Python3托管。
q:是否需要对中文进行切分?
不,如果您使用的是由google发布的预先训练过的中文bert,则不需要分词。因为这个中文伯特是基于字符的模型。即使你有意在两个单词之间加上空格,它也不会识别单词/短语。为了更清楚地看到这一点,这是bert模型在标记化之后实际接收到的信息:
frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])1
frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])2
这意味着单词嵌入实际上是中文bert的字符嵌入。
q:为什么我的(英语)单词被标记为something
?
因为你的单词词汇量太大了。来自google的标记器使用贪婪的最长匹配优先算法使用给定的词汇表执行标记化。
例如:
frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])3
q:我可以使用自己的标记器吗?
是的。如果您已经自己标记了句子,只需发送useencode
和list[list[str]]
作为输入并打开是标记化的,即bc.encode(文本,是标记化的=真的)
q:我遇到了zmq.error.zmqerror:在当前状态下无法完成操作
当使用bertclient
时,我应该怎么做?a:这通常是由于误用o多线程/进程环境中的fbertclient
。注意,不能在多个线程/进程之间重用一个bertclient
,必须为每个线程/进程创建单独的实例。例如,以下操作根本不起作用:
frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])4
请改为:
frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])5
q:运行服务器后,我有几个垃圾tmpxxx
文件夹。我怎样才能改变这种行为?
a:ZeroMQ使用这些文件夹存储套接字。您可以通过设置环境变量zeromq_sock_tmp_dir来选择不同的位置:
导出zeromq_sock_tmp_dir=/tmp/
q:两个句子向量的余弦相似度过高(例如总是>;0.8),怎么了?
a:下游任务的恰当表示并不意味着它在余弦距离方面有意义。因为余弦距离是一个线性空间,所有维度的权重相等。如果你想使用余弦距离,那么请关注排名而不是绝对值。也就是说,不要使用:
frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])6
请考虑以下内容:
frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])7
下面的图表说明了从web(char)中随机抽取的3000个中文句子的成对相似性。长度<;25)。我们根据句子向量计算余弦相似度,并根据原始文本计算rouge-l。为了清晰起见,删除了对角线(自相关)。正如我们所见,这两个指标之间存在某种正相关。
q:我的表现不好,我该怎么办?
a:这通常表明,预先训练的bert无法生成下游任务的下降表示。因此,您可以在下游任务上微调模型,然后使用bert as service
来为微调的bert服务。注意,bert as service
只是一个基于bert的特征提取服务。没有什么能阻止你使用经过微调的伯特。
q:我可以在仅限CPU的机器上运行服务器端吗?
a:是,请运行bert serving start-cpu-max_batch_size 16
。注意,CPU在大批量上的扩展性不如GPU,因此服务器端的最大批量大小需要更小,例如16或32。
问题:我如何选择num-worker
?
a:通常,工作线程数应小于或等于您拥有的GPU/CPU数。否则,将为一个GPU/CPU分配多个工作线程,这可能无法很好地扩展(并可能导致GPU上的内存不足)。
q:我可以指定要使用哪个GPU吗?
a:是的,您可以指定-设备映射
如下:
frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])8
这将启动四个工作线程,并将它们分别分配给gpu0、gpu1、gpu4和gpu0。一般来说,如果num-worker
>;设备映射
,则设备将被工作程序重用和共享(可能会缩放到次优或导致OOM);如果num-worker
<;设备映射
,则仅使用设备映射[:num-worker]。
注意,在CPU上运行时,设备映射
被忽略。
:zap:benchmark
返回顶部
基准测试的主要目标是测试该服务的可伸缩性和速度,这对于在dev/prod环境中使用它至关重要。基准测试在tesla m40 24gb上进行,实验重复10次,并报告平均值。
要复制结果,请运行
frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])9
所有实验的共同论点是:
<表><广告>速度wrt。最大顺序长度
max_seq_len
是服务器端的一个参数,它控制bert模型可以处理的序列的最大长度。大于max_seq_len
的序列将在左侧被截断。因此,如果您的客户希望向模型发送长序列,请确保服务器能够正确处理它们。
在性能方面,较长的序列意味着较慢的速度和更多的oom机会,因为多头自我注意(bert的核心单元)需要在序列中的每两个符号之间进行点积和矩阵乘法。
<表><广告>最大长度
1 gpu 2 gpu 4 gpu
速度wrt。客户机批量大小
client_batch_size
是调用encode()
时来自客户端的序列数。出于性能原因,请考虑批量编码序列,而不是逐个编码。
例如,do:
bc.encode(['First do it ||| then do it right'])0
不要:
bc.encode(['First do it ||| then do it right'])1
如果将bertclient()
放入循环中,情况会更糟。不要那样做。
客户机批量大小
速度wrt。num_客户端
num嫒client
表示同时连接到服务器的并发客户端数。
num_客户端
可以看到,1个客户机1 GPU=381 Seqs/s,2个客户机2 GPU 402 Seqs/s,4个客户机4 GPU 413 Seqs/s。这显示了我们并行管道和作业调度的效率,因为随着并发请求的增加,服务可以更充分地利用GPU时间。
速度wrt。最大批量大小
max_batch_size
是服务器端的一个参数,它控制每个工作进程每批的最大样本数。如果来自客户端的传入批大于max_batch_size
,则服务器会将其分成小批,以便在将其发送给工作人员之前,每个小批都小于或等于max_batch_size
。
最大批量大小
速度wrt。池层
池层
确定池操作的编码层。例如,在12层bert模型中,-1
表示接近输出的层,-12
表示接近嵌入层的层。如下所示,池层的深度会影响速度。
速度wrt。-fp16
和-xla
bert as service
支持两个额外的优化:半精度和xla,可以通过分别将-fp16
和-xla
添加到bert service start
来启用。要启用这两个选项,您必须满足以下要求:
- 您的GPU支持FP16指令;
- tensorflow是用xla和
-march=native自行编译的
- 你的CUDA和CUDNN并不太老。
在Tesla V100上,TensorFlow=1.13.0-RC0给出:
与fp32相比,fp16实现了约1.4倍的加速(往返)。要重现结果,请运行python示例/example1.py
引用
返回顶部
如果您在科学出版物中使用bert作为服务,我们希望您能参考以下bibtex条目:
bc.encode(['First do it ||| then do it right'])2