使用bert模型将可变长度句子映射到固定长度向量(服务器)

bert-serving-server的Python项目详细描述


< Buff行情>

这个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模型用作句子编码服务,即将可变长度的句子映射到固定长度的向量。

GitHub starspypi packagereadthedocgithub releasegithub issuesGithub许可证Twitter

突出显示•。 它是什么•。 安装. 入门•。 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.10one point ten)。同样,服务器不支持python 2!

:point_up:出于以下考虑,客户机可以在python 2和python 3上运行

开始

<H4>1。下载经过预训练的伯特模型

下载下面列出的型号,然后将zip文件解压缩到某个文件夹中,例如/tmp/english-12\u h-768\u a-12/

<细节>已发布的预训练伯特模型列表(单击展开…)<表>bert base,uncased12层,768隐藏,12头,110m参数bert large,uncased24层,1024隐藏,16头,340m参数bert base,cased12层,768隐藏,12头,110m参数bert large,cased24层,1024隐藏,16头,340m参数bert base,多语言cased(new)104种语言,12层,768隐藏,12头,110m参数bert base,多语种大小写(旧)102种语言,12层,768隐藏,12头,110m参数bert base,中文简体中文和繁体中文,12层,768隐藏,12个磁头,110m参数<表><详细内容>< Buff行情>

可选:在下游任务中微调模型。为什么是可选的?

<H4>2。启动BERT服务

安装服务器后,您应该能够使用bert serving startcli,如下所示:

bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4

这将启动一个有四个工人的服务,这意味着它最多可以处理四个并发的请求。更多并发请求将在负载平衡器中排队。有关详细信息,请参见我们的常见问题解答和客户数量基准。

下面显示正确启动时服务器的外观:

<细节>或者,可以在docker容器中启动bert服务(单击展开…)
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请求来调用服务。

< Buff行情>

:bulb:想了解更多信息吗?查看我们的教程:

服务器和客户端API

返回顶部

readthedoc

学习最新api的最佳方法是阅读文档。

服务器API

请始终参考此处记录的最新服务器端api。,您可以通过:

bert-serving-start --help
bert-serving-terminate --help
bert-serving-benchmark --help
<表><广告>参数 键入默认值 说明 < /广告><正文>型号目录 STR 必需的预先训练的bert模型的文件夹路径。调谐模式目录 STR (可选)微调的bert模型的文件夹路径。ckpt_name STR bert_model.ckpt检查点文件的文件名。配置名称 STR bert_config.jsonbert模型的json配置文件的文件名。图形TMP目录 STR 无图形临时文件的路径max_seq_len INT/TDT>25序列的最大长度,较长的序列将在右侧修剪。对于动态使用(小)批次中最长的序列,将其设置为"无"。大小写标记化布尔错误标记器是否应跳过默认的小写和重音删除。例如,应用于多语言预训练的bert模型。mask_cls_sep布尔错误用零屏蔽[CLS]和[SEP]上的嵌入。工人数 INT/TDT>1运行bert模型的(gpu/cpu)工作进程的数量,每个进程在一个单独的进程中工作。最大批量大小 INT/TDT>256每个工人处理的最大序列数,较大的批将被划分为小批。优先级批量大小 INT/TDT>16小于此大小的批将被标记为高优先级,并在作业队列中向前跳转以更快地获得结果端口 INT/TDT>5555用于将数据从客户端推送到服务器的端口端口输出 INT/TDT>5556发布端口从服务器到客户端的ING结果Http_端口 INT/TDT>无用于接收http请求的服务器端口cors STR *为http请求设置"access control allow origin"池策略 STR 降低平均值 生成编码向量的池策略,有效值为nonereduce_meanreduce_maxreduce_mean_maxcls_tokenfirst_tokensep_tokenlast_token。有关这些策略的说明,请参见此处。要获取序列中每个令牌的编码,请将其设置为none池层列表[-2]池操作的编码层,其中-1表示最后一层,-2表示最后一层,[-1,-2]表示连接最后两层的结果,等等。GPU内存分数浮动0.5每个GPU应为每个工作线程分配的总内存量的一部分cpu布尔错误使用CPU而不是GPU运行xla布尔错误启用xla编译器以进行图形优化(实验!)/td>fp16布尔错误使用浮点16精度(实验性)设备地图列表[]指定要使用的GPU设备ID列表(ID从0开始)向客户显示令牌布尔错误向客户端发送标记化结果

客户端API

请始终参考此处记录的最新客户端API。客户端提供一个名为bertclient的Python类,它接受以下参数:

<表><广告>参数 键入默认值 说明 < /广告><正文>IP STR 本地主机服务器的IP地址端口 INT/TDT>5555用于将数据从客户端推送到服务器的端口,必须与服务器端配置一致 端口输出 INT/TDT>5556用于将结果从服务器发布到客户端的端口,必须与服务器端配置一致 输出fmt STR ndarray语句的输出格式以numpy数组或python list[list[float]](ndarray/list显示服务器配置布尔false首次连接时是否显示服务器配置检查版本布尔true是否强制客户端和服务器具有相同的版本标识 STR 用于标识客户机的uuid,在多类型转换中很有用超时 INT/TDT>-1设置客户端接收操作的超时(毫秒)

abertclient实现以下方法和属性:

<表><广告>方法 说明 < /广告><正文>.encode()将字符串列表编码为向量列表.encode_async()从生成器异步编码批处理.fetch()从服务器获取所有编码向量并将其返回到生成器中,与一起使用。encode_async()一起使用。encode(blocking=false)。不保留发送顺序。.fetch_all()从服务器获取所有已编码向量并将其返回到列表中,与一起使用。encode_async()一起使用。encode(blocking=false)。发送订单被保留。.close()优雅地关闭客户端和服务器之间的连接。状态以json格式获取客户端状态。服务器状态以json格式获取服务器状态

:book:教程

返回顶部

readthedoc

示例的完整列表可以在example/中找到。您可以通过python example/example-k.py运行每个。大多数示例要求您首先启动bertserver,请按照此处的说明操作。注意,尽管bertclient在python 2.x和3.x上都通用,但示例只在python 3.6上测试。

<细节>目录(单击展开…)< Buff行情><详细内容>

在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模型启动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

就这样!现在运行代码并键入查询,查看此搜索引擎如何处理模糊匹配:

提供微调的伯特模型

经过预训练的bert模型在许多任务中常常表现出相当"好"的性能。然而,要释放bert的真正威力,就必须对下游任务(或特定于域的数据)进行微调。在本例中,我将向您展示如何为经过微调的bert模型服务。

我们遵循"句子(和句子对)分类任务"中的说明,并使用run戋classifier.py微调uncased戋l-12戋h-768戋a-12戋/code>模型MRPC任务。微调后的模型存储在/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-343.data-00000-of-00001(看起来我的培训已经到了343步)。根据总的训练步骤,可以得到model.ckpt-123.data-00000-of-00001model.ckpt-9876.data-00000-of-00001。现在,我们已经收集了服务此微调模型所需的全部三条信息:

  • 预训练模型下载到/path/to/bert/uncased\u l-12\u h-768\u a-12
  • 我们的微调模型存储在/tmp/mrpc_output/
  • 我们精心调整的模型检查点命名为model.ckpt-343something.

现在,通过将三个部分放在一起来启动bertserver:

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

这意味着bert参数被过度编码,并成功地从我们微调的/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

使用自己的标记器

通常,您希望使用自己的标记器来分段句子,而不是使用bert的默认标记器。硅mply callencode(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]helloworld![sep],其余部分是填充符号,在输出前被屏蔽。

请注意,不需要启动单独的服务器来处理标记化/未标记化的句子。服务器可以自动判断和处理这两种情况。

有时您希望明确了解在服务器端执行的标记化,以便更好地理解嵌入结果。其中一个例子是要求从服务器嵌入单词(使用-pooling_strategy none),人们想知道哪个单词是标记化的,哪个是不可识别的。您可以通过以下步骤获得这些信息:

  1. 启用服务器端的"向客户端显示令牌"
  2. 通过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=4
0

返回:

bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
1

我们可以观察到这个世界!和这在服务器上无法识别,因此它们被设置为[unk]

最后,请注意,来自google的pretrained bert中文是基于字符的,也就是说,它的词汇表是由单个汉字组成的。因此,如果使用分词算法对数据进行预处理并馈送到该模型中,则没有意义。

非常好奇的读者可能会注意到,尽管标记化结果包括[cls](做得好,侦探!)。原因是,无论设置了-mask-cls-sep,标记化结果都将始终包括[cls][unk]。当您希望以后对齐标记时,这可能很有用。请记住,-mask_cls_sep只屏蔽计算之外的[cls][sep]。它不影响标记化算法。

使用bertclienttf.dataapi

< Buff行情>

完整的示例可以在example4.py中找到。Keras中还有一个示例

api使您能够从简单、可重用的部分构建复杂的输入管道。还可以使用bertclient动态编码句子,并在下游模型中使用向量。下面是一个示例:

bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
2

这里的诀窍是创建一个bertclient池,并逐个重用它们。这样,我们就可以充分利用num_parallel_调用的能力

使用bert特性和tf.estimatorapi训练文本分类器 < Buff行情>

完整的示例可以在example5.py中找到。

在上一个例子之后,我们可以使用tf.estimatorapi轻松地将它扩展到一个完整的分类器。只需对输入函数进行如下小改动:

bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
3

完整的示例可以是例如5.py,其中基于bert特征构建了一个简单的mlp,用于根据法律文件中的事实描述预测相关文章。这个问题是中国人工智能和法律挑战赛的一部分。

使用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=4
4

现在我们可以从中加载并构建一个tf.dataset

bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
5

要将字/令牌级嵌入保存到tfrecord,首先需要将张量展平到1d数组中,如下所示:

bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
6

然后在加载时重建形状:

bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
7

小心,这将生成一个巨大的tfrecord文件。

异步编码

< Buff行情>

完整的示例可以在example2.py中找到。

bertclient.encode()提供了一种很好的同步方式来获取句子编码。但是,有时我们希望以异步方式执行此操作,首先将所有文本数据提供给服务器,然后再获取编码的结果。这很容易做到:

bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
8

向多个客户端广播

< Buff行情>

完整的示例可以在example3.py中找到。

编码的结果根据其标识路由到客户端。如果你有多个相同身份的客户,那么他们都会收到结果!您可以使用这个多播功能来做一些很酷的事情,例如在多个分离的进程中训练多个不同的模型(有些使用scikit learn有些使用tensorflow),而只调用bertserver一次。在下面的示例中,bc及其两个克隆都将接收编码向量。

bert-serving-start -model_dir /tmp/english_L-12_H-768_A-12/ -num_worker=4
9

在仪表板中监视服务状态

< 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_WORKER
0

这给出了服务器的当前状态,包括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_WORKER
1

这将允许用户使用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_WORKER
2

然后简单地启动服务器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_WORKER
3 完成了!您的服务器现在正在端口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_WORKER
4

,其中,id是一个唯一的标识符,帮助您同步结果;被标记化了遵循bertclientapi中的含义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_WORKER
5

,它返回一个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_WORKER
6

要获取服务器状态和客户端状态,可以分别在/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_WORKER
7

请注意,它基本上反映了cli中的arg解析行为,因此该列表中的所有内容都应该是字符串,例如['-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

或通过shell cli:

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

这将终止在5555端口的本地主机上运行的服务器。您也可以使用它来终止远程服务器,有关详细信息,请参见bert serving terminate--help

:Speech_气球:常见问题解答

返回顶部

readthedoc

q:您是否有介绍您车型详细信息的书面说明或其他书面说明?

设计理念和技术细节可以在我的博客中找到。

q:伯特码来自哪里?

a:本回购协议的bert代码是从原始bert回购协议中派生出来的,并进行了必要的修改,es.py" rel="nofollow">特别是在extract\u features.py中

q:句子向量有多大?

一般来说,每个句子都被翻译成768维向量。根据您使用的预先训练的bert,池策略池层输出向量的维数可能不同。

q:如何获得固定表示?你是不是在合伙什么的?

a:是的,需要使用池来获取句子的固定表示。在默认策略中,我取句子中所有标记的第二个到最后一个隐藏层,并进行平均池。

q:您是否建议在没有微调的情况下使用bert?

a:是和否。一方面,谷歌预先训练了贝特的维基百科数据,因此应该对语言进行足够的编码。通用电气进入模型。拥有这样的功能并不是一个坏主意。另一方面,这些先验知识并不特定于任何特定领域。如果你在使用它的时候表现得不理想,比如说,对法律案件进行分类,这应该是完全合理的。尽管如此,您始终可以首先在下游任务上微调自己的bert,然后使用bert as service高效地提取特征向量。请记住,bert as service只是一个基于bert的特征提取服务。没有什么能阻止你使用经过微调的伯特。

q:我可以得到几层而不是一层的连接吗?

a:当然!调用服务器时,只需使用要连接的层的列表。例子:

frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])
0
问题:有哪些可用的池策略?

a:这里有一个表,总结了我实施的所有池策略。通过指定bert-serving start-pooling_策略来选择您最喜欢的策略

<表><广告>策略 说明 < /广告><正文>完全没有池,当您想使用单词嵌入而不是句子嵌入时非常有用。这将产生一个序列的编码矩阵。降低平均值 取时间轴上编码层隐藏状态的平均值减少最大值取时间轴上编码层隐藏状态的最大值降低平均值reduce_meanreduce_max分开,然后在最后一个轴上将它们连接在一起,从而产生1536个dim语句编码CLS U令牌第一个令牌获取与[cls]对应的隐藏状态,即第一个标记sep_令牌最后一个令牌获取对应于[sep]的隐藏状态,即最后一个标记
q:为什么不使用第一个令牌的隐藏状态作为默认策略,即[cls]

a:因为预先训练的模型尚未对任何下游任务进行微调。在这种情况下,[cls]的隐藏状态不是一个好的句子表示。如果以后对模型进行微调,也可以使用[cls]

q:伯特有12/24层,那么你说的是哪一层?

a:默认情况下,此服务在最后一层的第二层上工作,即池层=-2。您可以通过将池层设置为其他负值来更改它,例如-1对应于最后一层。

q:为什么不是最后一个隐藏层?为什么是倒数第二?

a:在预训练过程中,最后一层与目标函数(即蒙面语言模型和下一句预测)过于接近,因此可能偏向这些目标。如果您对这个参数有疑问,并且仍然想使用最后一个隐藏层,请随意设置pooling-layer=-1

q:那么哪个层和哪个池策略是最好的?

a:视情况而定。请记住,不同的bert层捕获不同的信息。为了更清楚地看到这一点,这里是对uci新闻聚合器数据集(uci news aggregator dataset)的可视化,其中我随机抽取了20k个新闻标题;从不同的层和不同的池策略获取句子编码,最后redu通过主成分分析(pca)把它转换成二维(当然也可以做t-sne,但这不是我的观点)。只有四类数据,用红色、蓝色、黄色和绿色表示。为了重现结果,PLE请运行示例7.py。

直观地说,池层=-1接近训练输出,因此可能偏向于训练目标。如果不对模型进行微调,则可能导致不正确的表示。pooling_layer=-12接近单词嵌入,可以保留非常原始的单词信息(没有花哨的自我注意等)。另一方面,您只需使用单词嵌入就可以获得相同的性能。也就是说,介于[-1,-12]之间的任何东西都是一种权衡。

q:我可以使用其他池技术吗?

a:当然。但是如果在图中引入新的tf.variables,那么在使用模型之前需要训练这些变量。您可能还需要检查我在博客中提到的一些池技术

q:我是否需要在encode()之前对数据进行批处理?

不,一点也不。只需执行encode并让服务器处理其余部分。如果批处理太大,服务器将自动进行批处理,而且比自己进行批处理效率更高。不管你有多少句子,10k或100k,只要你能把它保存在客户端的内存中,就把它发送到服务器。另请阅读客户批量大小的基准。

q:我是否可以启动多个客户端并同时向一台服务器发送请求?

a:是的!这就是回购的目的。事实上,你可以随心所欲地创建更多的客户。一台服务器可以处理所有这些问题(只要有足够的时间)。

q:一个服务可以同时处理多少个请求?

a:最大并发请求数由bert service start中的num_worker决定。如果同时发送超过num_worker的请求,新请求将临时存储在队列中,直到有空闲的worker可用。

q:所以一个请求意味着一个句子?

a:没有。一个请求表示从客户端发送的句子列表。将请求的大小视为批处理大小。请求可以包含256、512或1024个句子。请求的最佳大小通常是根据经验确定的。一个大的请求当然可以提高gpu的利用率,但同时也增加了传输开销。您可以运行python example/example1.py来获得一个简单的基准测试。

q:速度如何?生产速度够快吗?

a:这在很大程度上取决于max_seq_len和请求的大小。在一个max_seq_len=40的tesla m40 24gb上,使用12层bert每秒应该可以获得470个样本。一般来说,我建议使用较小的max seq\u len(25)和较大的请求大小(512/1024)。

q:您是否对效率进行了基准测试?

a:是。请参见基准

要重现结果,请运行bert serving benchmark

q:后端基于什么?

a:zeromq

q:什么是并行过程幕后模特?
q:为什么服务器需要两个端口?

一个端口用于将文本数据推入服务器,另一个端口用于将编码结果发布到客户端。这样,我们就消除了背后的闲聊,也就是说,在每一个层次上,收件人都不会和发件人顶嘴。如上图所示,整个消息流是严格单向的。消除回响对于真正的可伸缩性至关重要,它允许我们以异步方式使用bertclient

q:我需要在客户端使用tensorflow吗?

a:否。将bertclient视为一个通用特征提取程序,其输出可以输入到任何ml模型,例如scikit learnpytorchtensorflow。客户机需要的唯一文件是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:我可以使用自己的标记器吗?
是的。如果您已经自己标记了句子,只需发送useencodelist[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

返回顶部

readthedoc

基准测试的主要目标是测试该服务的可伸缩性和速度,这对于在dev/prod环境中使用它至关重要。基准测试在tesla m40 24gb上进行,实验重复10次,并报告平均值。

要复制结果,请运行

frombert_serving.clientimportBertClientbc=BertClient()bc.encode(['First do it','then do it right','then do it better'])
9

所有实验的共同论点是:

<表><广告>参数 值< /广告><正文>工人人数1,2,4最大长度 客户批量大小2048年最大批量大小num_客户端

速度wrt。最大顺序长度

max_seq_len是服务器端的一个参数,它控制bert模型可以处理的序列的最大长度。大于max_seq_len的序列将在左侧被截断。因此,如果您的客户希望向模型发送长序列,请确保服务器能够正确处理它们。

在性能方面,较长的序列意味着较慢的速度和更多的oom机会,因为多头自我注意(bert的核心单元)需要在序列中的每两个符号之间进行点积和矩阵乘法。

<表><广告>最大长度 1 gpu2 gpu4 gpu< /广告><正文>1774年32541687

速度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()放入循环中,情况会更糟。不要那样做。

<表><广告>客户机批量大小1 gpu2 gpu4 gpu< /广告><正文>< 332 >< > >< 766 >< > >10241517年2048年168140961809年

速度wrt。num_客户端

num嫒client表示同时连接到服务器的并发客户端数。

<表><广告>num_客户端1 gpu2 gpu4 gpu< /广告><正文>1759年 261<Td>1028

可以看到,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

<表><广告>最大批量大小1 gpu2 gpu4 gpu< /广告><正文>1726年1759年1816年16881483

速度wrt。池层

池层确定池操作的编码层。例如,在12层bert模型中,-1表示接近输出的层,-12表示接近嵌入层的层。如下所示,池层的深度会影响速度。

<表><广告> 池层 1 gpu2 gpu4 gpu< /广告><正文>[-1]1568年[-2]1686[-3]1823年[-4]10761986年[-5]11932184[-6]13402430[-7]1528年2729[-8]1772年3104[-9]11282047年3622[-10]139225424241[-11]1523年27374752[-12]1568年29855303

速度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

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java WebClient请求级别超时抛出名为default onErrorDropped的运算符   java JAXB外部绑定文件错误   Freemarker中的java转义宏参数值   java正在同步来自不同对象{已编辑}的线程   java如何在另一个类上更新活动中的元素   java Hibernate连接查询   java可以使用Apache Crunch创建类似于图形的数据结构吗?   java在JLabel的开头加上3个点   java 安卓应用程序显示线程错误   java@RequestBody在Spring中总是空的   java Android异步任务永远不会结束   具有多个属性的java Jaxws枚举   java中的安卓 Stripe InvalidRequestException   多线程java。util。非多线程程序中的ConcurrentModificationException   Minecraft Java插件如何删除HashMap中存储的所有块   空Java字符串的大小   从AJP连接器请求检索Shibboleth属性的java   oracle11g将Java类文件加载到Oracle数据库