使用bert模型将可变长度句子映射到固定长度向量(客户端)

bert-serving-multilingual-client的Python项目详细描述


>**此readme.md是pypi的镜像。请访问https://github.com/hanxiao/bert-as-service/blob/master/readme.md获取最新的自述文件。md.**
<;h1 align="center">;bert-as-service<;h1>;

<;p align="center">;使用bert模型作为句子编码服务,即将可变长度的句子映射到固定长度矢量。<;/p>;

<;p align="center">;
<;a href="https://github.com/hanxiao/bert as service/stargazers">;
<;img src="https://img.shields.io/github/stars/hanxiao/bert-as-service.svg?colora=orange&;colorb=orange&;logo=github"
a lt="github stars">;
<;/a>;
<;a href="https://pypi.org/search/?q=bert serving">;
<;img src="https://img.shields.io/pypi/v/bert-serving-server.svg?colorb=brightgreen"
a lt="pypi package">;
<;/a>;
<;a href="https://bert as service.readthedocs.io/">;
<;img src="https://readthedocs.org/projects/bert-as-service/badge/?version=latest"
a lt="readthedoc">;
<;/a>;
<;a href="https://github.com/hanxiao/bert as service/releases">;
<;img src="https://img.shields.io/github/release/hanxiao/bert as service.svg"
alt="github release">;
<;/a>;
<;a href="https://github.com/hanxiao/bert as service/issues">;
<;img src="https://img.shields.io/github/issues/hanxiao/bert as service.svg"
alt="github issues">;
<;/a>;
<;a href="https://github.com/hanxiao/bert as service/blob/master/license">;
<;img src="https://img.shields.io/github/license/hanxiao/bert as service.svg"
alt="github license">;
<;/a>;
<;a href="https://twitter.com/intent/tweet?text=wow:&url=https%3a%2f%2fgithub.com%2fhanxiao%2fbert as service">;
<;img src="https://img.shields.io/twitter/url/https/github.com/hanxiao/bert-as-service.svg?
<;p>;

<;p align="Center">;
<;br/>

<;p align="Center">;
<;lt;lt;lt;a;a;a;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;a;lt;lt;lt;lt;lt;gt;lt;lt;lt;lt;lt;lt;a;lt;lt;lt;lt;lt;lt;lt;gt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt A Href="盖蒂"入门">;入门入门<;lt;a>;•
<;<;a
<;lt;lt;lt;lt;a
>;•
<;lt;lt;a
<;lt;lt;lt;lt;lt;lt;lt;lt;a;lt;lt;lt;a;lt;lt;lt;a;lt;lt;lt;lt;lt;lt;a;lt;lt;lt;a;lt;lt;lt;a;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;<;a href="https://hanxiao.github.io/2019/01/02/serving-google-bert-in-production-using-tensorflow-and-zeromq/"target="逖blank">;博客<;/a>;

<;/p>;


<;img src=".github/demo.gif?raw=true"width="700">;
<;/p>;

<;h6 align="center">;由han xiao制作。•:globe戋u with戋meridian:<;a href="https://hanxiao.github.io">;https://hanxiao.github.io<;/a>;



<;h2 align="center">;它是什么<;/h2>;

**bert**是nlp modEL[由谷歌开发](https://github.com/google-research/bert)用于训练前语言表示。它利用了大量在web上公开的纯文本数据,并以无监督的方式进行培训。对每种语言来说,预先训练bert模型是一个相当昂贵但一次性的过程。幸运的是,google发布了几个预先训练的模型,其中[您可以从这里下载](https://github.com/google research/bert pre-trained models)。



**句子编码/嵌入**是许多nlp应用程序(如情感分析、文本分类)所需的上游任务。目标是将可变长度的句子表示为固定长度的向量,例如"hello world"到"0.1,0.3,0.9"。向量的每个元素都应该"编码"原始句子的一些语义。

**最后,`bert as service`**使用bert a是一个句子编码器,通过zeromq将其作为服务托管,允许您将句子映射到两行代码中的固定长度表示。

<;h2 align="center">;突出显示<;/h2>;

-:望远镜:**最新技术**:建立在google ai发布的12/24层bert模型上,该模型被认为是nlp社区的里程碑。
-:孵化器:**易于使用**:只需要两行代码获取句子/标记级别的编码。
-:zap:**快速**:单个tesla m40 24gb上900个句子/秒。低延迟,速度优化。请参阅[Benchmark](Zap Benchmark)。
-:octopus:**scalable**:在多个GPU和多个客户端上平滑地扩展,而不必担心并发性。请参阅[Benchmark]("speed-wrt-num"客户端)。
-:gem:**可靠**:测试了数十亿个句子;运行了几天,没有中断或任何糟糕的异常。

更多功能:[xla&fp16支持]("speed wrt--fp16和--xla);混合GPU-CPU工作负载;优化图形;`tf.data`friendly;自定义标记器;灵活的池策略;[内置http服务器](使用bert作为服务以json方式服务http请求)和仪表板;[异步编码](异步编码);[多播](向多个客户端广播)等。


<;h2 align="center">;安装<;/h2>;

通过"pip"安装服务器和客户端。它们可以单独安装,甚至可以安装在*不同的*机器上:
``bash
pip install bert serving server``server
pip install bert serving client``client,独立于'bert serving server`
```

请注意,服务器必须在**python>;=3.5**上运行,并带有**tensor流量=1.10**(*一点十*)。同样,服务器不支持python 2!

:要点:客户机可以同时在python 2和python 3上运行[出于以下考虑](q-can-i-run-it-in-python-2)。

<;h2 align="center">;入门<;h2>;

下载一个预先训练过的bert模型
下载下面列出的模型,然后将zip文件解压缩到某个文件夹中,例如`/tmp/english_l-12_h-768_a-12/`

<;details>;
<;summary>;已发布的预训练bert模型列表(单击展开…)<;summary>;


<;table&gl t;
<;lt;tr>;lt;td>;<;tr>;lt;td>;lt;a a a a a a a a a-gt;https://storage.googolgleapis.com/bert\u models/2018/2018/U 10/U 18/cased/cased-U L-24-U H-1024 A-A-16.zip">;>;>;bert;a-16.zip">;24个-层,1024个隐藏,16个磁头,340m参数<;/td>;<;/tr>;
<;tr>;<;td>;<;a href="https://storage.googleapis.com/bert廑u models/2018廑u 11廑23/multi廑u cased廑u l-12廑h-768廑a-12.zip">;bert base,multilingual cased(new)<;/a>;<;td>;104种语言,12层,768个隐藏,12-heads,110m参数<;/td>;<;/tr>;
<;tr>;<;td>;<;a href="https://storage.googleapis.com/bert嫒u models/2018嫒u 11嫒03/multilingual嫒u l-12嫒h-768 a-12.zip">;bert base,多语言外壳(旧)<;/a>;<;/td>;<;td>;102种语言,12层,768隐藏,12个heads,110m参数ers<;/td>;<;/tr>;
<;tr>;<;td>;<;a href="https://storage.googleapis.com/bert嫒u models/2018嫒u 11嫒03/chinese嫒l-12嫒u h-768嫒a-12.zip">;bert base,chinese嫒lt;/td>;<;td>;简体中文nd Traditional,12层,768隐藏,12个磁头,110m参数<;/td>;<;/tr>;
<;/table>;


<;/details>;



>;**可选:**在下游任务中微调模型。[为什么是可选的?](q-are-you-suggesting-using-bert-without-fine-tuning)


2。启动bert服务
在安装服务器之后,您应该能够使用"bert-serving start"cli,如下所示:
``bash
bert-serving start-model\dir/tmp/english\u l-12\u h-768\u a-12/-num\worker=4
```
这将启动一个包含四个worker的服务,这意味着它最多可以处理四个**并发**请求。更多并发请求将在负载平衡器中排队。有关详细信息,请参见我们的[FAQ](q-what-is-the-parallel-processing-model-behind-the-scene)和[The benchmark on number of clients](speed-wrt-num-client)。

raw=true"/>;


<;details>;
<;summary>;或者,可以在docker容器中启动bert服务(单击展开…)<;/summary>;

`` bash
docker-build-t-bert as service-f./docker/dockerfile.
num-worker=1
path-model=/path-u-to/\u你的模型/
docker-run-runtime-nvidia-dit-p 5555:5555-p 5556:5556-v$path-model:/model-t-bert-bert as service$num-worker
````
<;详细信息>;






//3。使用client获取句子编码,现在您可以简单地将句子编码如下:
``python
from bert嫒u serving.client import bertclient
bc=bertclient()
bc.encode(['先做','然后做对','然后做得更好')
````
如果您愿意,它将返回一个'ndarray'(或'list[list[float]],其中每一行是表示一个句子的固定长度向量。有成千上万个句子?只是"编码"!*甚至不用批处理*,服务器会处理它。

作为bert的一个特性,您可以通过将一对句子与` `(前后有空格)连接来获得它们的编码,例如,
``python
bc.encode(['先做然后做正确的事情')
````

原始值=真"/>;<;/p>;

"做得好","然后做得更好"]
```

注意,您只需要"pip install-u bert serving client"在这种情况下,不需要服务器端。您还可以[通过http请求调用该服务](使用bert作为服务,以json形式提供http请求)

>;:bulb:**想了解更多信息吗?查看我们的教程:**
>;-[在3分钟内构建一个qa语义搜索引擎]("在3分钟内构建一个qa-qa-semantic-search-engine")
>;-[提供一个经过微调的bert模型]("服务一个经过微调的bert-model")
>;-[获得类似elmo的上下文单词嵌入]("获得类似elmo的上下文单词嵌入")
>;-[使用自己的标记器](使用自己的标记器)
>;-[将"bertclient"与"tf.data"api一起使用](将bertclient与tfdata api一起使用)
>;-[使用bert特征和tf.estimator api训练文本分类器](将"training-a-text-classifier-using-bert-features-and-tfestimator-api)
>;-[使用tfrecord数据保存和加载]("使用tfrecord数据保存和加载")
>;-[异步编码]("异步编码")
>;-[向多个客户端广播]("向多个客户端广播")
>;-[在仪表板中监视服务状态]("监视仪表板中的服务状态")
>;-[使用'bert作为服务'to ser以json格式保存http请求](使用bert作为服务以json格式提供http请求)
>;-[从python启动"bert server"](从python启动bertserver)


<;h2 align="center">;服务器和客户端api<;/h2>;
<;p align="right">;<;a href="bert a s service">;<;s向上>;返回顶部<;/sup>;<;/a>;<;/p>;

[![阅读文档](https://readthedocs.org/projects/bert-as-service/badge/?version=latest&style=for the badge)(http://bert as service.readthedocs.io)


这里有文档。](https://bert as service.readthedocs.io/en/latest/source/server.html服务器端api),您可以通过:
``bash
bert serving start--help
bert serving terminate--help
bert serving benchmark--help
```
ription
----------------——.
`调谐的_model目录str(可选)微调的bert模型的文件夹路径。|检查点文件的文件名。|
`config_name`str `bert_config.json` bert模型的json配置文件的文件名。|
"graph_tmp_dir` str none graph temp file路径
"max_seq_len` int"25序列的最大长度,较长的序列将在右侧修剪。对于动态使用(小)批处理中最长的序列,将其设置为"无"。|
|
|
|
"priority_u batch_u size` int `16`小于此大小的批将被标记为高优先级,并在作业队列中向前跳以更快地获得结果
"port int `5555`端口,用于将数据从客户端推送到服务器
"port u out int"5556"端口,用于发布Resu从服务器到客户端的LTS
'http端口int无接收http请求的服务器端口
'cors` str `设置http请求的"访问控制允许源"
'pooling策略str'reduce_mean`生成编码向量的池策略,有效va值是"none"、"reduce_mean"、"reduce_max"、"reduce_mean_max"、"cls_token"、"first_token"、"sep_token"、"last_token"。对这些策略的解释[可以在这里找到](q-什么是-可用-池策略)。要获取序列中每个令牌的编码,请将其设置为"none"。
"pooling layer list `[-2]`池操作的编码层,其中"-1"表示最后一层,"-2"表示倒数第二层,"[-1,-2"表示连接最后两层的结果,等等。|` GPU内存碎片float `0.5每个GPU应该为每个工作线程分配的总内存碎片
cpu bool false在CPU上运行,而不是在图形优化中的GPU
xla bool false启用[xla编译器](https://www.tensorflow.org/xla/jit)(*实验性!*)|
`fp16 bool false使用float16精度(实验性)的float16精度

`device\u-map list `[]``指定将使用的gpu设备id的列表(id从0开始)
`show\u tokens bool false将令牌化结果发送到客户端



[请始终参考她记录的最新客户端APIe.](https://bert as service.readthedocs.io/en/latest/source/client.html module client)客户端提供一个名为"bertclient"的python类,它接受以下参数:


---——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————g从服务器到客户端的结果,*必须与服务器端配置
'output_fmt` str'ndarray`语句的输出格式编码,可以是numpy数组或python list[list[float]](`ndarray`/`list`)
'show_server_config` bool'false`是否显示服务器在第一次连接时配置是否强制客户端和服务器具有相同版本的uuid,用于多重转换在客户机

a`bertclient`上的r receive操作实现以下方法和属性:

---|
`.fetch()`从服务器获取所有已编码向量并将其返回到生成器中,与`.encode_async()`或`.encode(blocking=false)一起使用。不保留发送顺序。
`.fetch_all()`从服务器获取所有编码向量并将其返回到列表中,与`.encode_async()`或`.encode(blocking=false)`一起使用。保持发送顺序。
`.close()`优雅地关闭客户端和服务器之间的连接
`.status`以json格式获取客户端状态
`.server_u status`以json格式获取服务器状态

<;p align="right">;<;a href="bert as service">;<;sup>;返回顶部<;/sup>;<;/a>;<;/p>;

[![阅读文档](https://readthedocs.org/projects/bert-as-service/badge/?version=latest&;style=for the badge)](https://bert as service.readthedocs.io/en/latest/section/faq.html)

您可以通过"python example/example-k.py"运行每个脚本。大多数示例要求您首先启动bertserver,请遵循[此处的说明](2-start-the-bert-service)。请注意,尽管"bertclient"在python 2.x和3.x上都通用,但示例只在python 3.6上测试。

<;details>;
<;summary>;目录(单击展开…)<;summary>;

>;-[在3分钟内构建一个qa语义搜索引擎](building-a-qa-sEmantic-Search-Engine-in-3-minutes)
>;-[提供微调的Bert模型](提供微调的Bert模型)
>;-[获得类似Elmo的上下文单词嵌入](获得类似Elmo的上下文单词嵌入)
>;-[使用自己的标记赋予器](使用自己的标记赋予器)
>;-[使用g"bertclient"与"tf.data"api一起使用(使用bertclient与tfdata api一起使用)
>;-[使用bert特征和tf.estimator api训练文本分类器]("training-a-text-classifier-use-bert-features-and-tfestimator-api)
>;[使用tfrecord数据保存和加载]("saving and loading with tfr"ecord数据)
>;-[异步编码](异步编码)
>;-[向多个客户端广播](向多个客户端广播)
>;-[在仪表板中监视服务状态](在仪表板中监视服务状态)
>;-[使用"bert作为服务"来服务以json格式保存http请求](使用bert作为服务以json格式提供http请求)
>;-[从python启动'bertserver'(启动rtserver from python)

<;details>;


在3分钟内构建一个qa语义搜索引擎

>;完整的示例可以[找到example8.py](example/example8.py)。

作为第一个示例,我们将在3分钟内使用"bert as service"实现一个简单的qa搜索引擎。别开玩笑!目标是找到与用户输入相似的问题并返回相应的答案。首先,我们需要一个问答对列表。幸运的是,这个自述文件已经包含[常见问题列表](speech_buloon-faq),所以我将使用它使这个示例完全独立。让我们首先加载所有问题并显示一些统计信息。

``python
prefix雒q='雒雒雒q:**'
open('readme.md')作为fp的问题:
questions=[v.replace(prefix雒q',).strip()用于fp中的v,如果v.strip()和v.startswith(prefix雒q)
print('%d questions loaded,avg.len of%d'(len(questions),np.mean([len(d.split())表示问题中的d])
````

看来我们的问题已经够多了。现在使用"uncased_l-12_h-768_a-12"预训练的bert模型启动bertserver:
``bash
bert-serving start-num_worker=1-model_dir=/data/cips/data/lab/data/model/uncased_l-12_h-768_a-12
`````


接下来,我们需要将问题编码成向量:
``python
bc=bertclient(port=4000,port_out=4001)
doc_vecs=bc.encode(questions)
````

为此,每当有新的查询出现时,我们将其编码为一个向量,并使用"doc_vecs"计算其点积;对结果进行向下排序;返回top-k类似的问题如下:
``python
为真:
query=input('your question:')
query-vec=bc.encode([query])[0]
将标准化的点积计算为score
score=np.sum(query-vec*doc-vecs,axis=1)/np.linalg.norm(doc-vecs,axis=1)
topk-idx=np.argsort(score)[::-1][:topk]
topk-idx中的idx:
打印('>;%s\t%s'%(score[idx],questions[idx]))
````

就这样!现在运行代码并键入查询,查看此搜索引擎如何处理模糊匹配:
<;p align="center">;<;img src=".github/qasearch-demo.gif?raw=true"/>;<;/p>;


然而,要释放bert的真正威力,就必须对下游任务(或特定于域的数据)进行微调。在本例中,我将向您展示如何提供微调的bert模型。

微调后的模型存储在`/tmp/mrpc_output/`,可以通过指定`-run_classifier.py`的--output_dir`来更改该模型。

如果查看`/tmp/mrpc_output/`,它包含如下内容:
``bash
checkpoint 128
eval 4.0k
eval\u results.txt 86
eval.tf\u record 219k
events.out.tfevents.1545202214.tencent64.site6.1m
events.out.tfevents.1545203242.tencent64.site 14m
graph.pbtxt 9.0m
model.ckpt-0.data-00000-of-00001 1.3g
model.ckpt-0.index 23k
model.ckpt-0.meta 3.9m
model.ckpt-343.data-00000-of-000011.3g
model.ckpt-343.index 23k
model.ckpt-343.meta 3.9m
train.tfu记录2.0m
```


不要害怕那些神秘的文件,因为对我们来说唯一重要的文件是'model.ckpt-343.data-00000-of-00001'(看起来我的培训在343步就结束了)。根据总的训练步骤,可以得到'model.ckpt-123.data-00000-of-00001'或'model.ckpt-9876.data-00000-of-00001'。现在,我们已经收集了为这个微调模型服务所需的所有三条信息:
-预调整的模型被下载到`/path/to/bert/uncased_l-12_h-768_a-12`
-我们的微调模型存储在`/tmp/mrpc_output/`;
-我们的微调模型检查点命名为` model.ckpt-343"某物"。

应该在日志中找到这一行:
``text
i:graphopt:[gra:opt:50]:检查点(由微调模型覆盖):/tmp/mrpc_output/model.ckpt-343
````
,这意味着bert参数被重写,并从微调的`/tmp/mrpc_output/model.ckpt-343`成功加载。完成!

简而言之,找到经过微调的模型路径和检查点名称,然后分别将它们输入到"优化的模型目录"和"ckpt名称"。

ooling_strategy none-model_dir/tmp/english_l-12_h-768_a-12/
````


要得到每个标记对应的单词嵌入,只需使用切片索引如下:
``python
"怎么了?"])

vec[2,25,25768]
vec[0][1,25,768],hey you`
vec[0][0][0][1,1,768]的句子嵌入,hey you`[hey you`
vec[0][0][0][0]>vec[1,1,768],hey`[br/>vec[0][1,1,768],hey`
vec[0][2][1,1,1768],wec[2,1,1768],wec[2,25,25768],wec[0][0][0][0][1,25,25768],vec[1,2576>vec[0][3][1,1,768],单词embedding for`[sep]`
vec[0][4][1,1,768],word embedding for padding symbol
vec[0][25]error,out of index!
``

请注意,无论原始序列有多长,服务始终会为每个序列返回一个`[max_seq_len,768]`矩阵。使用切片索引获取单词嵌入时,请注意填充到序列中的特殊标记,即`[cls]`,`[sep]`,`0u pad`。

只需在客户端幻灯片上调用"encode(is_tokenized=true)",如下所示:

`` python
texts=['hello world!'","good day"]

。如果查看它的值,您将发现只有前四个元素(即,`[1,0:3768]`有值,其他所有元素都是零。这是因为伯特认为"你好,世界!"作为四个标记:`[cls]``hello``世界!``[sep]`,其余的是填充符号,在输出之前被屏蔽。

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

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

1。在服务器端启用"-show_tokens_to_client";
2。调用通过"encode(…,show_tokens=true)"启动服务器。

,show_tokens=true)
```
返回一个元组,其中第一个元素是嵌入,第二个元素是来自服务器的标记化结果:

``text
(数组([[[0)。,-0。,0。,…,0。,-0。,-0。],
[1.1100919,-0.20474958,0.9895898,…,0.3873255,-1.4093989,-0.47620595],
…,-0.,-0。]],

[[0.,-0。,0。,…,0。,0。,0。],
[0.6293478,-0.4088490.6022662,…,0.41740108,1.214456,1.2532915],
…,0.,0。]]],dtype=float32),

[['[cls]','hello','world','!','[sep]],['[cls]','this','\is','it','[sep]']])
````

例如,
`` python
bc.encode([['hello','world!'],['thisis','it']],show_tokens=true,is_tokenized=true)
````
返回:

```text
(数组([[[0)。,-0。,0。,…,0。,-0。,0。],
[1.1111546,-0.56572634,0.37183186,…,0.02397121,-0.5445367,1.1009651],
…,-0.,0。]],

[[0.,0。,0。,…,0。,-0。,0。],
[0.39262453,0.3782491,0.27096173,…,0.7122045,-0.9874849,0.9318679],
…,-0.,0。]]],dtype=float32),

[['[cls]','hello','[unk]','[sep]',['[cls]','[unk]','it','[sep]]])
````

`服务器上无法识别"this",因此将其设置为"unk"。

因此,如果使用字级切分算法预处理数据并将其馈送到这样的模型中是没有意义的。

。原因是标记化结果将**始终**包括`[cls]`和`[unk]`而不考虑`-mask_cls_sep`的设置。当您希望以后对齐标记时,这可能很有用。记住,`-mask_cls_sep`只屏蔽了`[cls]`和`[sep]`不在计算范围内。它不会影响标记化算法。



还有[Keras中的一个例子](https://github.com/hanxiao/bert as service/issues/29 35; issuecomment-442362241)。

[`tf.data`](https://www.tensorflow.org/guide/datasets)api允许您从简单、可重用的片段构建复杂的输入管道。还可以使用"bertclient"动态编码句子,并在下游模型中使用向量。下面是一个示例:

`` python
批处理大小=256
num廑parallel廑calls=4
num廑clients=num廑parallel廑calls*2应该至少大于'num廑parallel廑calls`

为范围内(num廑client)启动一个客户池
bc廑clients=[bertclient(show廑server廑config=falses)



def get_encodes(x):
x是行的"批处理大小",每个行都是一个json对象
samples=[json.loads(l)for l in x]
text=[s['raw_text']for s in samples]\list[list[str]
labels=[s['label']for s in samples]\list[str]
NT来自可用客户端
bc_client=bc_clients.pop()
features=bc_client.encode(text)
使用后,将其放回
bc_clients.append(bc_client)
返回features,labels


ds=(tf.data.textlinedataset(train_fp).batch(batch_size)
.map(lambda x:tf.py_func(get_encodes,[x],[tf.float32,tf.string]),num_parallel_calls=num_parallel_calls)
.map(lambda x,y:{'feature':x,'label':y})
.make_one_shot_iterator().get_next())
```

这样,我们就可以充分利用"dataset.map()"api的"num_parallel_calls"的能力。


只需对输入函数进行如下的小更改:

`` python
估计器=dnnclassifier(
隐藏单元=[512],
特征列=[tf.feature列.数值列('feature',shape=(768,))],
n个类=len(laws),
配置=运行配置,
标签列ulary=laws_str,
dropout=0.1)


input_fn=lambda fp:(tf.data.textlinedataset(fp)
.apply(tf.contrib.data.shuffle_and_repeat(buffer_size=10000))
.batch(batch_size)
.map(lambda x:tf.py_func(get_encodes,)[x],[tf.float32,tf.string]),num_parallel_calls=num_parallel_calls)
.map(lambda x,y:({'feature':x},y))
.prefetch(20))


train_spec=trainspec(input_fn=lambda:input_fn(train_fp))
eval_spec=evalspec(input_fn=lambda:input_fn(eval_fp),throttle_secs=0)
train_and_evaluate(估计器,train_spec,eval_spec)
`````

完整的例子可以找到example5.py(example/example5.py),其中基于bert特征构建了一个简单的mlp,用于根据定律中的事实描述预测相关文章文件。这个问题是[中国人工智能与法律挑战赛](https://github.com/thunlp/cail/blob/master/readme_n.md)的一部分。



\blob/master/readme_en.md;使用tfrecord数据保存和加载

>;完整的示例可以[参见example6.py](example/example6.py)。

tfrecord文件格式是一种简单的面向记录的二进制格式,许多tensorflow应用程序使用它来训练数据。您还可以对所有序列进行预编码,并将它们的编码存储到tfrecord文件中,然后加载该文件以构建"tf.dataset"。例如,要将编码写入tfrecord文件:

``python
bc=bertclient()
list_vec=bc.encode(lst_str)
list_label=[0 for in lst_str]所有零标签的虚拟列表

def create_float_feature(values):
返回tf.train.feature(float_list=tf.train.floatlist(value=values))

def create_int_feature(values):
返回tf.train.feature(int64_list=tf.train.int64list(value=list(values))

st_label):
features={features':创建浮点特征(vec),"labels":创建浮点特征([label])}
tf_example=tf.train.example(features=tf.train.features(feature=features))
writer.write(tf_example.serializeToString())
````

并构建一个'tf.dataset':
``python
def解码记录(记录):
`将记录解码为tensorflow示例。"
返回tf.parse单个示例(记录,{
'features':tf.fixedlenfeature([768],tf.float32),
'labels':tf.fixedlenfeature([],tf.int64),
})

ds=(tf.data.tfrecorddataset('tmp.tf record').repeat().shuffle(buffer_size=100)。应用(
tf.contrib.data.map_and_batch(lambda record:_decode_record(record),batch_size=64))
。生成一个shot_迭代器().get_next())
`````

g对于tfrecord,首先需要将`[max_seq_len,num_hidden]`张量展平成一维数组,如下所示:
``python
def create_float_feature(values):
return tf.train.feature(float_list=tf.train.floatlist(value=values.reshape(-1)))
````
,然后在loa时重建形状定名:
``python
name庘u-to-u-features={
"feature":tf.fixedlenfeature([max庘seq庘u-length*num庘hidden],tf.float32),
"label庘id":tf.fixedlenfeature([],tf.int64),
}


def庘u-decode庘u-record(record,name庘u-to-u-features):example=tf.parse_single_example(record,name_to_features)
example['feature']=tf.reforme(example['feature'],[max_seq_length,-1])
return example
```
小心,这将生成一个巨大的tfrecord文件。

ete example可以[找到example2.py](example/example2.py)。

`bertclient.encode()`提供了一种很好的同步方式来获取句子编码。但是,有时我们希望以异步方式执行此操作,首先将所有文本数据提供给服务器,然后再获取编码的结果。这很容易做到,方法是:
`` python
一个无止境的数据流,以极快的速度生成数据
def text_gen():
如果是真的:
yield lst_str;生成一批文本行

bc=bertclient()

(text_gen(),max_num_batch=10):
打印(接收到%d x%d'%(j.shape[0],j.shape[1])
````

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

``python
(idx,j.shape[0],j.shape[1]))


bc=bertclient()
启动两个与bc具有相同身份的克隆客户机:j in range(2):
threading.thread(target=client克隆,args=(bc.identity,j)).start()






j in range(3):
bc.encode(lst-str)
``


周一在仪表板中监视服务状态

>;完整的示例可以[在plugin/dashboard/](plugin/dashboard)中找到。

作为基础结构的一部分,您可能还希望监视服务状态并将其显示在仪表板中。为此,我们可以使用:
``python
bc=bertclient(ip='server戋ip')

````

这将以json格式显示服务器的当前状态,包括请求数、客户端数等。唯一剩下的事情是启动一个http服务器,将这个json返回到呈现它的前端。

ne使用javascript或"curl"在端口8001获取服务器状态。

`plugin/dashboard/index.html`显示基于引导和vue.js的简单仪表板。

<;p align="center">;<;img src=".github/dashboard.png?raw=true"/>;<;/p>;

它非常有用,尤其是在低运输层禁止使用ER。在场景背后,"bert as service"在一个单独的进程中生成一个flask服务器,然后将"bertclient"实例作为代理来与通风机通信。

要启用内置http服务器,我们需要首先(重新)使用一些额外的python依赖项来安装服务器:
``bash
pip install-u bert serving server[http]
`````


>然后简单地使用以下命令启动服务器:
``bash
`bert serving start-model\u dir=/your\u model-http-port 8125
````

您的服务器正在同时监听端口"8125"的http和tcp请求!

要发送一个http请求,首先用json编写负载,如下所示:
``json
{
"id":123,
"texts":["你好,世界","你好!"],
"被标记化":false
}
```
,其中"id"是一个唯一的标识符,帮助您同步结果;` is_tokenized`遵循[`bert client`api]中的含义(https://bert as service.readthedocs.io/en/latest/source/client.html client.bertclient.encode_async)和默认的'false'。

然后通过http post请求在`/encode`调用服务器。您可以使用javascript或其他方式,下面是一个使用curl的示例:
``bash
curl-x post http://x x.xx.xx.xx:8125/encode\
-h'内容类型:application/json'\
-d'{"id":123,"texts":["hello world"],"is_tokenized":false}
`````
``,它返回一个json:
``json
{
"id":123,
"results":[[768 float list],[768 float list]],
"status":200
}
````

还可以通过在启动"bert serving start"时指定"-cors"来配置cors以限制服务器的公共访问。默认情况下,-cors=*`,这意味着服务器是公共访问的。



\只需执行
``python
from bert_serving.server.helper import get_args_parser
from bert_serving.server import bertserver
args=get_args_parser().parse_args(['-model_dir','your_model_path_here',
'-port','5555',
'-port_out','5556',
'-max_seq_len'、'none'、
'-mask_cls_sep'、
'-cpu')
server=bertserver(arg s)
server.start()
````

请注意,它基本上反映了cli中的arg解析行为,因此,parse args([])列表中的所有内容都应该是字符串,例如,`['-port','5555']`而不是'['-port',5555'`.

要关闭服务器,您可以通过以下方式调用"bert server"类中的静态方法:
``python
bertserver.shutdown(port=5555)
`````

>或通过shell cli:
``bash
`bert serving terminate-port 5555
````

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


<;h2 align="center">;:Speech_balloon:FAQ<;/h2>;
<;p align="right">;<;a ref="\bert as service">;<;sup>;<;sup>;<;sup>;<;<;/a>;<;&p>;

![阅读文档](https://readthedocs.org/projects/bert-as-service/badge/?version=latest&style=for the badge)(https://bert a s service.readthedocs.io/en/latest/section/faq.html)




设计理念和技术细节可以在[我的博客文章]中找到(https://hanxiao.github.io/2019/01/02/使用TensorFlow和ZeroMQ/在生产中为Google Bert服务)。




**a:**[本回购协议的伯特代码](server/bert_serving/server/bert/)从[原始协议]派生l bert repo](https://github.com/google-research/bert)进行了必要的修改,[特别是在extract_features.py](server/bert_serving/server/bert/extract_features.py)。



一般来说,每个句子都被翻译成768维向量。根据您使用的预先训练的bert,"pooling_strategy"和"pooling_layer",输出向量的维度可能不同。

你是不是在合伙什么的?

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



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



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

`` bash
bert_serving_start-pooling_layer-4-3-2-1-model_dir/tmp/english_l-12_h-768_a-12/
````



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

这将导致一个序列的编码矩阵`[max_seq_len,768]`以时间轴上编码层的隐藏状态的平均值
'reduce_mean``以时间轴上编码层的隐藏状态的最大值
'reduce_mean_do`红色uce-mean和reduce-max分别集中在最后一个轴上,导致1536个dim语句编码
cls_u token或` first_u token`;得到对应于`[cls]`的隐藏状态,即第一个token
sep_u token或` last_u token`;得到相应的隐藏状态。到`[sep]`,即最后一个令牌




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



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


为什么是倒数第二?

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




**A:*取决于记住,不同的bert层捕获不同的信息。为了更清楚地看到这一点,这里有一个在[UCI新闻聚合器数据集](https://www.kaggle.com/uciml/news aggregator dataset)上的可视化,我随机抽取了20k个新闻标题;从不同的层和不同的池策略获取句子编码,最后通过PCA(一个C当然了,我也不这么认为。只有四类数据,用红色、蓝色、黄色和绿色表示。要复制结果,请运行[example7.py](example/example7.py)。

<;p align="center">;<;img src=".github/pool\u mean.png?原始=真">;


<;p align="center">;<;img src=".github/pool_max.png?raw=true">;<;/p>;


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



**a:**当然。但是,如果在图中引入新的"tf.variables",则需要在使用模型之前训练这些变量。您可能还想查看[我在博客中提到的一些池技术](https://hanxiao.github.io/2018/06/24/4-encoding-blocks-you-need-to-know-addition-lstm-rnn-in-tensorflow/"池块"。



不,一点也不。只需"编码"并让服务器处理其余部分。如果批处理太大,服务器将自动进行批处理,而且比自己进行批处理效率更高。不管你有多少句子,10k或100k,只要你能把它保存在客户端的内存中,就把它发送到服务器。另请阅读[关于客户机批处理大小的基准](https://github.com/hanxiao/bert as service speed-wrt-client u batch size)。





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



**a:**最大并发请求数由"bert_serving_start"中的"num_worker"决定。如果同时发送多个"num戥worker"请求,则新请求将临时存储在队列中,直到空闲的worker可用为止。



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


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

**A:**是的。请参阅[benchmark](zap benchmark)。



**a:**[zeromq](http://zeromq.org/)。




<;img src=".github/bert-parallel-pipeline.png?raw=true"width="600">;


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



**a:**no.将"bertclient"视为一个通用的特征提取程序,其输出可以输入*任何*ml模型,例如"scikit learn"、"pytorch"、"tensorflow"。客户机需要的唯一文件是[`client.py`](service/client.py)。将此文件复制到您的项目并导入,然后您就可以开始了。



**a:*是的。



**A:**是的。事实上,这是建议。确保"model_dir"中有以下三项:

-一个包含预先训练的权重(实际上是3个文件)的TensorFlow检查点(`bert_model.ckpt`)。
-一个vocab文件(`vocab.txt`)将wordpiece映射到word id。
-一个配置文件(`bert_config.json`),指定模型的超参数。



**A:**服务器端没有,客户端有。这是基于这样的考虑:Python2.x可能仍然是某些技术堆栈中的一个主要部分。将整个下游堆栈迁移到python 3以支持"bert as service"可能需要相当多的努力。另一方面,设置"bertserver"只是一次性的事情,甚至可以[在docker容器中运行](在nvidia docker上运行bert服务)。为了简化集成,我们在客户端支持Python2,以便您可以直接将"bertclient"用作Python2项目的一部分,而服务器端应该始终由Python3托管。



不,如果你使用的是[谷歌发布的预训练中文伯特](https://github.com/google research/bert pre trained chinese bert released by google)你不需要分词。因为这个中文伯特是基于字符的模型。即使你有意在两个单词之间加上空格,它也不会识别单词/短语。为了更清楚地看到这一点,这就是bert模型在标记化之后实际接收到的信息:

`` python
bc.encode(['hey you','what ts up?'","第二季第三季第二季第二季第三季第二季第三季第三季第])
``` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `
代币:[cls]嘿你[sep]
输入识别号:101 13153 8357 8357 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0代币:[cls]怎么了?[sep]
输入输入:101 9100 8118 8644 8644 136 136 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0中国中文bert的字符嵌入。





因为你的单词词汇量不足(oov)。来自google的tokenizer使用贪婪的最长匹配优先算法来使用给定的词汇表执行tokenization。

例如:
``python
input="unafable"
tokenizer_output=["un","aff","**q:**我可以使用自己的tokenizer吗?

是的。如果你已经在你身上标记了这个句子r own,只需发送use`encode`和`list[list[str]`作为输入,并打开`is掼tokenized`,即'bc.encode(texts,is掼tokenized=true)`.






**a:**这通常是由于在多线程/进程环境中误用了"bertclient"。注意,不能在多个线程/进程中重用一个"bertclient",必须为每个线程/进程创建单独的实例。例如,下面这些根本不起作用:在proc1/thread1/proc1/thread1作用域:
bc.encode(lst_str)




`` python
/>在过程1/线程1中作用域:
bc1=bertclient()
bc1.encode(lst_str)

在proc2/thread2中作用域:
bc2=bertclient()
bc2.encode(lst_str)
````

我怎样才能改变这种行为?

**a:**ZeroMQ使用这些文件夹存储套接字。您可以通过设置环境变量"zeromq_sock_tmp_dir":
"export zeromq_sock_tmp dir=/tmp/`



**a:**下游任务的恰当表示并不意味着它在余弦距离方面有意义。因为余弦距离是一个线性空间,所有维度的权重相等。如果你想使用余弦距离,那么请关注排名而不是绝对值。也就是说,不要使用:
```
如果余弦(a,b)>;0.9,那么a和b是相似的
````
请考虑以下内容:
````
如果余弦(a,b)>;余弦(a,c),那么a比c更相似。
```

随机抽取网络中的句子(char.长度<;25)。我们基于句子向量计算余弦相似度,并基于原始文本计算[rouge-l](https://en.wikipedia.org/wiki/rouge-l(metric))。为了清晰起见,删除了对角线(自相关)。正如我们所看到的,这两个指标之间存在一些正相关关系。

<;p align="center">;<;img src=".github/cosine-vs-rougel.png?raw=true"/>;<;/p>;





**a:*这通常表明预先训练的bert无法生成下游任务的下降表示。因此,可以对下游任务的模型进行微调,然后使用"bert as service"为微调的bert提供服务。注意,"bert as service"只是一个基于bert的特征提取服务。没有什么能阻止您使用经过微调的bert。



**a:**是的,请运行"bert serving start-cpu-max_batch_size 16"。请注意,CPU在大批量上的扩展不如GPU,因此服务器端的"最大批处理大小"需要更小,例如16或32。




**a:**通常,工作线程数应小于或等于您拥有的GPU/CPU数。否则,将为一个GPU/CPU分配多个工作线程,这可能无法很好地扩展(并可能导致GPU内存不足)。




**a:**是的,您可以指定`-device_map`如下:
``bash
bert service start-device_map 0 1 4-num_worker 4-model_dir…
````
这将分别启动四个工作进程并将它们分配给gpu0、gpu1、gpu4和再次分配给gpu0。一般来说,如果"num-worker">;"device-map",则设备将被工人重用和共享(可能会缩放到次优或导致OOM);如果"num-worker"<;`设备地图,只使用设备地图,只使用设备地图[:num工人工人]。



注意,在cpu上运行时,忽略设备地图。




<;h2 align="中"gt;:zap:benchmark<;lt lt lt lt;h2>;
<;p align="right">;
<;p align="right">;<;a href="伯特服务">;lt;sup>;>;自上而下<;lt;lt;lt;lt;sup<;lt;lt;lt;lt;sup<;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt;lt/>[![阅读文档](https://readthedocs.org/projects/bert-as-service/badge/?version=latest&style=for the badge)(https://bert as service.readthedocs.io/en/latest/section/benchmark.html)

benchmarking的主要目标是测试该服务的可伸缩性和速度,这对于在开发/生产环境中使用它至关重要。在Tesla M40 24GB上进行基准测试,重复10次,并报告平均值。

请运行
``bash
bert-serving benchmark--help
`````


>所有实验的共同论点是:




2048
最大批量256
数量客户端|速度wrt。` max_seq_len`

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


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

<;img src=".github/max_seq_len.png?raw=true" width="600">

| `max_seq_len` | 1 GPU | 2 GPU | 4 GPU |
|---------------|-------|-------|-------|
| 20 | 903 | 1774 | 3254 |
| 40 | 473 | 919 | 1687 |
| 80 | 231 | 435 | 768 |
| 160 | 119 | 237 | 464 |
| 320 | 54 | 108 | 212 |

速度wrt。` client_batch_size`

`client_batch_size`是调用"encode()"时来自客户端的序列数。出于性能原因,请考虑批量编码序列,而不是逐个编码。

例如,做好:
``巨蟒
``巨蟒
``巨蟒
``巨蟒
``巨蟒
``巨蟒
``巨蟒
` ` ``巨蟒
` ` ` ``巨蟒
` ` ``巨蟒
`巨蟒
` ``巨蟒
` ` ` ` ` ` ``巨蟒
` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `>vec.append(bc.encode(s))
````

如果将"bertclient()"放入循环中,情况会更糟。不要这样做。

<;img src=".github/client_batch_size.png?raw=true" width="600">

| `client_batch_size` | 1 GPU | 2 GPU | 4 GPU |
|---------------------|-------|-------|-------|
| 1 | 75 | 74 | 72 |
| 4 | 206 | 205 | 201 |
| 8 | 274 | 270 | 267 |
| 16 | 332 | 329 | 330 |
| 64 | 365 | 365 | 365 |
| 256 | 382 | 383 | 383 |
| 512 | 432 | 766 | 762 |
| 1024 | 459 | 862 | 1517 |
| 2048 | 473 | 917 | 1681 |
| 4096 | 481 | 943 | 1809 |



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

<;img src=".github/num嫒clients.png?raw=true"width="600">;


1个gpu 2个gpu 4个gpu
124; 133 267 533
8 67 136 270
16 34 68 136
32 17 34 68

可以观察到,1个客户机1 gpu=381 seqs/s,2个客户机2 gpu 402 seqs/s,4个客户机4 gpu 413 seqs/s。这显示了我们并行p的效率流水线和作业调度,因为随着并发请求的增加,服务可以更充分地利用GPU时间。



speed wrt。` max_batch_size`

`max_batch_size`是服务器端的一个参数,用于控制每个工作进程每批的最大样本数。如果来自客户端的传入批处理大于"最大批处理大小",则服务器会将其分成小批,以便在将其发送给工作人员之前,每个批处理都小于或等于"最大批处理大小"。

<;img src=".github/max\u batch\u size.png?生=真"width="600">;


1 gpu 2 gpu 4 gpu
1 gpu 2 gpu 4 gpu 4 gpu 2 2 124128 473 931 1816
256| 473 919 1688
512 464 866 1483




` pooling_layer`

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

<;img src=".github/pooling_layer.png?raw=row=true"width="600>;

1个gpu 2个gpu 4个gpu 2个gpu 4/>[-3]516 995 1823
[-4]| 1986年
[-5]633 1193 2184
[-6]711 1340 2430
[-7][-7 820
[-8][-8 945 3104<3104
[-9]>[-9
[-10]1392 2542|4241
[-11]1523 2737 4752
[-12]1568 2985 5303



速度WRT。`-fp16和`-xla`

`bert as service`支持另外两个优化:半精度和xla,这两个优化可以分别通过将`-fp16`和`-xla`添加到'bert service start'来启用。要启用这两个选项,您必须满足以下要求:

-您的GPU支持FP16指令;
-您的TensorFlow是用XLA和`-March=Native`自行编译的;
-您的CUDA和CUDNN并不太旧。

/>
<;img src=".github/fp16 xla.svg"width="600">;



<;h2 align="center">;引用<;/h2>;
<;p align="right">;<;a ref="bert as service">;<;sup>;<;返回顶部<;/sup>;<;/a>;

阳离子,我们希望引用以下bibtex条目:

``latex
@misc{xiao2018bertservice,
title={bert as service},
author={xiao,han},
howpublished={\url{https://github.com/hanxiao/bert as service},
year={2018}

````



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

推荐PyPI第三方库


热门话题
swing Java按钮/网格布局   java列出Google日历中的所有事件   java无效:单击API publisher test按钮后连接到后端时出错   带有内部赋值的java While循环导致checkstyle错误   java为什么trimToSize/ensureCapacity方法提供“公共”级访问?   文件输出流的java问题   ListIterator和并发修改异常的java问题   java如何使用两个URL映射   无法识别使用“./../”构造的字符串java相对路径,为什么?   首次写入remotelyclosedsocket不会触发异常,对吗?JAVA   java OneDrive REST API为文件上载提供了400个无效谓词   Java泛型、集合接口和对象类的问题   OpenSSL Java安全提供程序   jmeter java运行jmx禁用操作