diff --git a/demo/README.md b/demo/README.md index 79c4e0b91caf21bac6c47d3c7f9d6d011de8c8a4..97eeecd7331232bd82a65ce44857cf8f50f2c32c 100644 --- a/demo/README.md +++ b/demo/README.md @@ -38,8 +38,8 @@ * [超参优化AutoDL Finetuner使用](./autofinetune) 该样例展示了PaddleHub超参优化AutoDL Finetuner如何使用,给出了自动搜素图像分类/文本分类任务的较佳超参数示例。 -* [服务化部署Hub Serving使用](./serving) - 该样例文件夹下展示了服务化部署Hub Serving如何使用,将PaddleHub支持的可预测Module如何服务化部署。 +* [服务化部署PaddleHub Serving使用](./serving) + 该样例文件夹下展示了服务化部署PaddleHub Serving如何使用,将PaddleHub支持的可预测Module如何服务化部署。 **NOTE:** 以上任务示例均是利用PaddleHub提供的数据集,若您想在自定义数据集上完成相应任务,请查看[PaddleHub适配自定义数据完成FineTune](https://github.com/PaddlePaddle/PaddleHub/wiki/PaddleHub%E9%80%82%E9%85%8D%E8%87%AA%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E5%AE%8C%E6%88%90FineTune) diff --git a/demo/sequence_labeling/predict.py b/demo/sequence_labeling/predict.py index 862e9bfba7675741898e9e42ae32931fde94dd7f..fb189b42b83319bcee2823d71ca25bb94e52ec18 100644 --- a/demo/sequence_labeling/predict.py +++ b/demo/sequence_labeling/predict.py @@ -87,12 +87,13 @@ if __name__ == '__main__': add_crf=True) # Data to be predicted + # If using python 2, prefix "u" is necessary data = [ - ["我们变而以书会友,以书结缘,把欧美、港台流行的食品类图谱、画册、工具书汇集一堂。"], - ["为了跟踪国际最新食品工艺、流行趋势,大量搜集海外专业书刊资料是提高技艺的捷径。"], - ["其中线装古籍逾千册;民国出版物几百种;珍本四册、稀见本四百余册,出版时间跨越三百余年。"], - ["有的古木交柯,春机荣欣,从诗人句中得之,而入画中,观之令人心驰。"], - ["不过重在晋趣,略增明人气息,妙在集古有道、不露痕迹罢了。"], + [u"我们变而以书会友,以书结缘,把欧美、港台流行的食品类图谱、画册、工具书汇集一堂。"], + [u"为了跟踪国际最新食品工艺、流行趋势,大量搜集海外专业书刊资料是提高技艺的捷径。"], + [u"其中线装古籍逾千册;民国出版物几百种;珍本四册、稀见本四百余册,出版时间跨越三百余年。"], + [u"有的古木交柯,春机荣欣,从诗人句中得之,而入画中,观之令人心驰。"], + [u"不过重在晋趣,略增明人气息,妙在集古有道、不露痕迹罢了。"], ] # Add 0x02 between characters to match the format of training data, diff --git a/demo/serving/Classification_vgg11_imagenet/README.md b/demo/serving/Classification_vgg11_imagenet/README.md deleted file mode 100644 index ace0b1a0e58ebe4147f45a04b39606a83e0bb301..0000000000000000000000000000000000000000 --- a/demo/serving/Classification_vgg11_imagenet/README.md +++ /dev/null @@ -1,21 +0,0 @@ -## 数据格式 -input: {files: {"image": [file_1, file_2, ...]}} -output: {"results":[result_1, result_2, ...]} - -## Serving快速启动命令 -```shell -$ hub serving start -m vgg11_imagenet -``` - -## python脚本 -```shell -$ python vgg11_imagenet_serving_demo.py -``` - -## 结果示例 -```python -{ - "results": "[[{'Egyptian cat': 0.540287435054779}], [{'daisy': 0.9976677298545837}]]" -} -``` -结果含有生成图片的base64编码,可提取生成图片,示例python脚本生成图片位置为当前目录下的output文件夹下。 diff --git a/demo/serving/GAN_stgan_celeba/README.md b/demo/serving/GAN_stgan_celeba/README.md deleted file mode 100644 index 411e86ad47202cc36644d6f4e8a5a1b80e2405e6..0000000000000000000000000000000000000000 --- a/demo/serving/GAN_stgan_celeba/README.md +++ /dev/null @@ -1,47 +0,0 @@ -## 数据格式 -#### 模型所需参数可通过嵌套字典形式传递 -input: {files: {"image": [file_1, file_2, ...]}, data: {...}} -output: {"results":[result_1, result_2, ...]} - -## Serving快速启动命令 -```shell -$ hub serving start -m stgan_celeba -``` - -## python脚本 -```shell -$ python yolov3_coco2017_serving_demo.py -``` - -## 结果示例 -```python -[ - { - "path": "cat.jpg", - "data": [ - { - "left": 322.2323, - "right": 1420.4119, - "top": 208.81363, - "bottom": 996.04395, - "label": "cat", - "confidence": 0.9289875 - } - ] - }, - { - "path": "dog.jpg", - "data": [ - { - "left": 204.74722, - "right": 746.02637, - "top": 122.793274, - "bottom": 566.6292, - "label": "dog", - "confidence": 0.86698055 - } - ] - } -] -``` -结果含有生成图片的base64编码,可提取生成图片,示例python脚本生成图片位置为当前目录下的output文件夹下。 diff --git a/demo/serving/Language_Model_lm_lstm/README.md b/demo/serving/Language_Model_lm_lstm/README.md deleted file mode 100644 index c8a4fc1acada6bfeead62211c7dafb085e7284b3..0000000000000000000000000000000000000000 --- a/demo/serving/Language_Model_lm_lstm/README.md +++ /dev/null @@ -1,29 +0,0 @@ -## 数据格式 -input: {"text": [text_1, text_2, ...]} -output: {"results":[result_1, result_2, ...]} - -## Serving快速启动命令 -```shell -$ hub serving start -m lm_lstm -``` - -## python脚本 -```shell -$ python lm_lstm_serving_demo.py -``` - -## 结果示例 -```python -{ - "results": [ - { - "perplexity": 4.584166451916099, - "text": "the plant which is owned by & co. was under contract with to make the cigarette filter" - }, - { - "perplexity": 6.038358983397484, - "text": "more common fibers are and are more easily rejected by the body dr. explained" - } - ] -} -``` diff --git a/demo/serving/Language_Model_lm_lstm/lm_lstm_serving_demo.py b/demo/serving/Language_Model_lm_lstm/lm_lstm_serving_demo.py deleted file mode 100644 index 9142b826e36ce5d8364e82c315a15f446bc2623f..0000000000000000000000000000000000000000 --- a/demo/serving/Language_Model_lm_lstm/lm_lstm_serving_demo.py +++ /dev/null @@ -1,17 +0,0 @@ -# coding: utf8 -import requests -import json - -if __name__ == "__main__": - # 指定用于用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} - text_list = [ - "the plant which is owned by & co. was under contract with to make the cigarette filter", - "more common fibers are and are more easily rejected by the body dr. explained" - ] - text = {"text": text_list} - # 指定预测方法为lm_lstm并发送post请求 - url = "http://127.0.0.1:8866/predict/text/lm_lstm" - r = requests.post(url=url, data=text) - - # 打印预测结果 - print(json.dumps(r.json(), indent=4, ensure_ascii=False)) diff --git a/demo/serving/Lexical_Analysis_lac/README.md b/demo/serving/Lexical_Analysis_lac/README.md deleted file mode 100644 index e1eaf4553ece353c9ffebd952c6ed47f4864c5f9..0000000000000000000000000000000000000000 --- a/demo/serving/Lexical_Analysis_lac/README.md +++ /dev/null @@ -1,57 +0,0 @@ -## 数据格式 -input: {"text": [text_1, text_2, ...]} -output: {"results":[result_1, result_2, ...]} - -## Serving快速启动命令 -```shell -$ hub serving start -m lac -``` - -## python脚本 -#### 不携带用户自定义词典 -```shell -$ python lac_no_dict_serving_demo.py -``` - -#### 携带用户自定义词典 -```shell -$ python lac_with_dict_serving_demo.py -``` - -## 结果示例 -```python -{ - "results": [ - { - "tag": [ - "TIME", - "v", - "q", - "n" - ], - "word": [ - "今天", - "是", - "个", - "好日子" - ] - }, - { - "tag": [ - "n", - "v", - "TIME", - "v", - "v" - ], - "word": [ - "天气预报", - "说", - "今天", - "要", - "下雨" - ] - } - ] -} -``` diff --git a/demo/serving/Object_Detection_yolov3_coco2017/README.md b/demo/serving/Object_Detection_yolov3_coco2017/README.md deleted file mode 100644 index 5bf78622719204b88a44e9b8064b88110350db38..0000000000000000000000000000000000000000 --- a/demo/serving/Object_Detection_yolov3_coco2017/README.md +++ /dev/null @@ -1,46 +0,0 @@ -## 数据格式 -input: {files: {"image": [file_1, file_2, ...]}} -output: {"results":[result_1, result_2, ...]} - -## Serving快速启动命令 -```shell -$ hub serving start -m yolov3_coco2017 -``` - -## python脚本 -```shell -$ python yolov3_coco2017_serving_demo.py -``` - -## 结果示例 -```python -[ - { - "path": "cat.jpg", - "data": [ - { - "left": 322.2323, - "right": 1420.4119, - "top": 208.81363, - "bottom": 996.04395, - "label": "cat", - "confidence": 0.9289875 - } - ] - }, - { - "path": "dog.jpg", - "data": [ - { - "left": 204.74722, - "right": 746.02637, - "top": 122.793274, - "bottom": 566.6292, - "label": "dog", - "confidence": 0.86698055 - } - ] - } -] -``` -结果含有生成图片的base64编码,可提取生成图片,示例python脚本生成图片位置为当前目录下的output文件夹下。 diff --git a/demo/serving/README.md b/demo/serving/README.md index 079b348917a557436c41179c253921c54bac8cfa..42b3dae673f1a24011878a3a6c1481834ec52a58 100644 --- a/demo/serving/README.md +++ b/demo/serving/README.md @@ -1,43 +1,56 @@ -# PaddleHub-Serving -## 1. 简介 -利用PaddleHub-Serving可以完成模型服务化部署,主要包括利用Bert as Service实现embedding服务化,利用预测模型实现预测服务化。 +# PaddleHub Serving +## 简介 +### 背景 +使用PaddleHub能够完成预训练模型的管理和预测,但开发者还经常面临将模型部署上线以对外提供服务的需求,而利用PaddleHub Serving可便捷的将模型部署上线,开发者只需要关注如何处理输入数据和输出结果即可。 +### 主要功能 +PaddleHub Serving是基于PaddleHub的一键模型服务部署工具,能够通过简单的Hub命令行工具轻松启动一个模型预测在线服务。 -## 2. Bert as Service -* [Bert as Service介绍与示例](bert_service) +PaddleHub Serving主要包括利用Bert Service实现embedding服务化,以及利用预测模型实现预训练模型预测服务化两大功能,未来还将支持开发者使用PaddleHub Fine-tune API的模型服务化。 -该示例展示了利用Bert as Service进行远程embedding服务化部署和在线预测,获取文本embedding结果。 +## Bert Service +`Bert Service`是基于[Paddle Serving](https://github.com/PaddlePaddle/Serving)框架的快速部署模型远程计算服务方案,可将embedding过程通过调用API接口的方式实现,减少了对机器资源的依赖。使用PaddleHub可在服务器上一键部署`Bert Service`服务,在另外的普通机器上通过客户端接口即可轻松的获取文本对应的embedding数据。 -## 3. Serving -模型预测服务化有以下示例: +关于其具体信息和demo请参见[Bert Service](../../tutorial/bert_service.md) -* [图像分类-基于vgg11_imagent](./Classification_vgg11_imagenet) +该示例展示了利用`Bert Service`进行远程embedding服务化部署和在线预测,并获取文本embedding结果。 -该示例展示了利用vgg11_imagent完成图像分类服务化部署和在线预测,获取图像分类结果。 +## 预训练模型一键服务部署 +预训练模型一键服务部署是基于PaddleHub的预训练模型快速部署的服务化方案,能够将模型预测以API接口的方式实现。 -* [图像生成-基于stgan_celeba](./GAN_stgan_celeba) +关于预训练模型一键服务部署的具体信息请参见[PaddleHub Serving](../../tutorial/serving.md) -该示例展示了利用stgan_celeba生成图像服务化部署和在线预测,获取指定风格的生成图像。 +预训练模型一键服务部署包括以下示例: -* [英文词法分析-基于lm_lstm](./Language_Model_lm_lstm) +* [图像分类-基于vgg11_imagent](module_serving/classification_vgg11_imagenet) -该示例展示了利用lm_lstm完成英文语法分析服务化部署和在线预测,获取文本的流利程度。 +  该示例展示了利用vgg11_imagent完成图像分类服务化部署和在线预测,获取图像分类结果。 -* [中文词法分析-基于lac](./Lexical_Analysis_lac) +* [图像生成-基于stgan_celeba](module_serving/GAN_stgan_celeba) -该示例展示了利用lac完成中文文本分词服务化部署和在线预测,获取文本的分词结果,并可通过用户自定义词典干预分词结果。 +  该示例展示了利用stgan_celeba生成图像服务化部署和在线预测,获取指定风格的生成图像。 -* [目标检测-基于yolov3_coco2017](./Object_Detection_yolov3_coco2017) +* [文本审核-基于porn_detection_lstm](module_serving/text_censorship_porn_detection_lstm) -该示例展示了利用yolov3_coco2017完成目标检测服务化部署和在线预测,获取检测结果和覆盖识别框的图片。 +  该示例展示了利用porn_detection_lstm完成中文文本黄色敏感信息鉴定的服务化部署和在线预测,获取文本是否敏感及其置信度。 -* [中文语义分析-基于simnet_bow](./Semantic_Model_simnet_bow) +* [中文词法分析-基于lac](module_serving/lexical_analysis_lac) -该示例展示了利用simnet_bow完成中文文本相似度检测服务化部署和在线预测,获取文本的相似程度。 +  该示例展示了利用lac完成中文文本分词服务化部署和在线预测,获取文本的分词结果,并可通过用户自定义词典干预分词结果。 -* [图像分割-基于deeplabv3p_xception65_humanseg](./Semantic_Segmentation_deeplabv3p_xception65_humanseg) +* [目标检测-基于yolov3_darknet53_coco2017](module_serving/object_detection_yolov3_darknet53_coco2017) -该示例展示了利用deeplabv3p_xception65_humanseg完成图像分割服务化部署和在线预测,获取识别结果和分割后的图像。 +  该示例展示了利用yolov3_darknet53_coco2017完成目标检测服务化部署和在线预测,获取检测结果和覆盖识别框的图片。 -* [中文情感分析-基于senta_lstm](./Sentiment_Analysis_senta_lstm) +* [中文语义分析-基于simnet_bow](module_serving/semantic_model_simnet_bow) -该示例展示了利用senta_lstm完成中文文本情感分析服务化部署和在线预测,获取文本的情感分析结果。 +  该示例展示了利用simnet_bow完成中文文本相似度检测服务化部署和在线预测,获取文本的相似程度。 + +* [图像分割-基于deeplabv3p_xception65_humanseg](module_serving/semantic_segmentation_deeplabv3p_xception65_humanseg) + +  该示例展示了利用deeplabv3p_xception65_humanseg完成图像分割服务化部署和在线预测,获取识别结果和分割后的图像。 + +* [中文情感分析-基于simnet_bow](module_serving/semantic_model_simnet_bow) + +  该示例展示了利用senta_lstm完成中文文本情感分析服务化部署和在线预测,获取文本的情感分析结果。 + +关于Paddle Serving预训练模型一键服务部署功能的具体信息请参见[Module Serving](module_serving)。 diff --git a/demo/serving/Semantic_Model_simnet_bow/README.md b/demo/serving/Semantic_Model_simnet_bow/README.md deleted file mode 100644 index 7a85028bbb3a00ef726ccd996476eb5c2552f83c..0000000000000000000000000000000000000000 --- a/demo/serving/Semantic_Model_simnet_bow/README.md +++ /dev/null @@ -1,36 +0,0 @@ -## 数据格式 -input: {"text1": [text_a1, text_a2, ...], "text2": [text_b1, text_b2, ...]} -output: {"results":[result_1, result_2, ...]} - -## Serving快速启动命令 -```shell -$ hub serving start -m simnet_bow -``` - -## python脚本 -```shell -$ python simnet_bow_serving_demo.py -``` - -## 结果示例 -```python -{ - "results": [ - { - "similarity": 0.8445, - "text_1": "这道题太难了", - "text_2": "这道题是上一年的考题" - }, - { - "similarity": 0.9275, - "text_1": "这道题太难了", - "text_2": "这道题不简单" - }, - { - "similarity": 0.9083, - "text_1": "这道题太难了", - "text_2": "这道题很有意思" - } - ] -} -``` diff --git a/demo/serving/Semantic_Segmentation_deeplabv3p_xception65_humanseg/README.md b/demo/serving/Semantic_Segmentation_deeplabv3p_xception65_humanseg/README.md deleted file mode 100644 index 5f5ad302366e09acd52847606f576e5cba3dcad0..0000000000000000000000000000000000000000 --- a/demo/serving/Semantic_Segmentation_deeplabv3p_xception65_humanseg/README.md +++ /dev/null @@ -1,24 +0,0 @@ -## 数据格式 -input: {files: {"image": [file_1, file_2, ...]}} -output: {"results":[result_1, result_2, ...]} - -## Serving快速启动命令 -```shell -$ hub serving start -m deeplabv3p_xception65_humanseg -``` - -## python脚本 -```shell -$ python deeplabv3p_xception65_humanseg_serving_demo.py -``` - -## 结果示例 -```python -[ - { - "origin": "girl.jpg", - "processed": "humanseg_output/girl_2.png" - } -] -``` -结果含有生成图片的base64编码,可提取生成图片,示例python脚本生成图片位置为当前目录下的output文件夹中。 diff --git a/demo/serving/Sentiment_Analysis_senta_lstm/README.md b/demo/serving/Sentiment_Analysis_senta_lstm/README.md deleted file mode 100644 index 6eadf2f2c3827bc4dfd042b8977f7590a898c275..0000000000000000000000000000000000000000 --- a/demo/serving/Sentiment_Analysis_senta_lstm/README.md +++ /dev/null @@ -1,35 +0,0 @@ -## 数据格式 -input: {"text": [text_1, text_2, ...]} -output: {"results":[result_1, result_2, ...]} - -## Serving快速启动命令 -```shell -$ hub serving start -m senta_lstm -``` - -## python脚本 -``` shell -$ python senta_lstm_serving_demo.py -``` - -## 结果示例 -```python -{ - "results": [ - { - "negative_probs": 0.7079, - "positive_probs": 0.2921, - "sentiment_key": "negative", - "sentiment_label": 0, - "text": "我不爱吃甜食" - }, - { - "negative_probs": 0.0149, - "positive_probs": 0.9851, - "sentiment_key": "positive", - "sentiment_label": 1, - "text": "我喜欢躺在床上看电影" - } - ] -} -``` diff --git a/demo/serving/bert_service/README.md b/demo/serving/bert_service/README.md index 57c769e1bd159245dc55c023dffd968ec4f70335..7f318e5c9ac99390a75de8addf80d6d13c911cad 100644 --- a/demo/serving/bert_service/README.md +++ b/demo/serving/bert_service/README.md @@ -1,180 +1,26 @@ # Bert Service -## 1. 简介 -### 1.1 什么是Bert Service -`Bert Service`是基于Paddle Serving框架的快速部署模型远程计算服务方案,可将embedding过程通过调用API接口的方式实现,减少了对机器资源的依赖。使用PaddleHub可在服务器上一键部署`Bert Service`服务,在另外的普通机器上通过客户端接口即可轻松的获取文本对应的embedding数据。 +## 简介 +### 什么是Bert Service +`Bert Service`是基于[Paddle Serving](https://github.com/PaddlePaddle/Serving)框架的快速部署模型远程计算服务方案,可将embedding过程通过调用API接口的方式实现,减少了对机器资源的依赖。使用PaddleHub可在服务器上一键部署`Bert Service`服务,在另外的普通机器上通过客户端接口即可轻松的获取文本对应的embedding数据。 -整体流程图如下: +**NOTE:** 关于`Bert Service`的更多信息请参见[Bert Serving](../../../tutorial/bert_service.md)。 -
- -BS流程图 - -
- -### 1.2 为什么使用Bert Service -* 算力有限的集群环境中,可利用一台或几台高性能机器部署`Bert Service`服务端,为全部机器提供在线embedding功能。 - -* 实际的生产服务器不适宜承担大批量embedding工作,通过API接口可减少资源占用。 - -* 专注下游深度学习任务,可利用PaddleHub的`Bert Service`大幅减少embedding代码。 - -`Bert as Service`具有几个突出的优点: - -* 代码精短,易于使用。简单的pip安装方式,服务端仅需一行命令即可启动,客户端仅需一行代码即可获取embedding结果。 - -* 更高性能,更高效率。通过Paddle AnalysisPredictor API对模型的计算图进行优化,提升了计算速度并减小了显存占用。 - -* 随"机"应变,灵活扩展。可根据机器资源选择不同数量的服务端,并根据实际需求快速、灵活地进行增减,同时支持各张显卡执行不同的模型计算任务。 - -* 删繁就简,专注任务。`Bert Service`基于PaddlePaddle和PaddleHub开发,将模型的下载和安装等管理工作交由PaddleHub,开发者可以专注于主要任务,还可以无缝对接PaddleHub继续进行文本分类、序列标注等下游任务。 - - -## 2. 环境准备 -### 2.1 环境要求 -下表是使用`Bert Service`的环境要求,带有*号标志项为非必需依赖,可根据实际使用需求选择安装。 - -|项目|版本|说明| -|:-:|:-:|:-:| -|操作系统|Linux|目前仅支持Linux操作系统| -|PaddleHub|>=1.4.0|无| -|PaddlePaddle|>=1.6.1|若使用GPU计算,则对应使用PaddlePaddle-gpu版本| -|GCC|>=4.8|无| -|CUDA*|>=8|若使用GPU,需使用CUDA8以上版本| -|paddle-gpu-serving*|>=0.8.0|在`Bert Service`服务端需依赖此包| -|ujson*|>=1.35|在`Bert Service`客户端需依赖此包| - -### 2.2 安装步骤 -a) 安装PaddlePaddle,利用pip下载CPU版本命令如下。GPU版本、Docker方式安装等其他更具体的安装过程见[开始使用PaddlePaddle](https://paddlepaddle.org.cn/install/quick) -```shell -$ # 安装paddlepaddle的CPU版本 -$ pip install paddlepaddle -``` -b) 安装PaddleHub -```shell -$ pip install paddlehub -``` -c) server端,需另外安装`paddle-gpu-serving`,以获取快速部署服务的能力 -```shell -$ pip install paddle-gpu-serving -``` -d) client端,需另外安装ujson -```shell -$ pip install ujson -``` - - -## 3. 支持模型 -目前`Bert Service`支持的语义模型如下表,可根据需要选择模型进行部署embedding服务,未来还将支持更多模型。 - -|模型|网络| -|:-|:-:| -|[ernie](https://paddlepaddle.org.cn/hubdetail?name=ERNIE&en_category=SemanticModel)|ERNIE| -|[ernie_tiny](https://paddlepaddle.org.cn/hubdetail?name=ernie_tiny&en_category=SemanticModel)|ERNIE| -|[ernie_v2_eng_large](https://paddlepaddle.org.cn/hubdetail?name=ernie_v2_eng_large&en_category=SemanticModel)|ERNIE| -|[ernie_v2_eng_base](https://paddlepaddle.org.cn/hubdetail?name=ernie_v2_eng_base&en_category=SemanticModel)|ERNIE| -|[roberta_wwm_ext_chinese_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=roberta_wwm_ext_chinese_L-12_H-768_A-12&en_category=SemanticModel)|BERT| -|[roberta_wwm_ext_chinese_L-24_H-1024_A-16](https://paddlepaddle.org.cn/hubdetail?name=roberta_wwm_ext_chinese_L-24_H-1024_A-16&en_category=SemanticModel)|BERT| -|[bert_wwm_ext_chinese_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=bert_wwm_ext_chinese_L-12_H-768_A-12&en_category=SemanticModel)|BERT| -|[bert_uncased_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=bert_uncased_L-12_H-768_A-12&en_category=SemanticModel)|BERT| -|[bert_uncased_L-24_H-1024_A-16](https://paddlepaddle.org.cn/hubdetail?name=bert_uncased_L-24_H-1024_A-16&en_category=SemanticModel)|BERT| -|[bert_cased_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=bert_cased_L-12_H-768_A-12&en_category=SemanticModel)|BERT| -|[bert_cased_L-24_H-1024_A-16](https://paddlepaddle.org.cn/hubdetail?name=bert_cased_L-24_H-1024_A-16&en_category=SemanticModel)|BERT| -|[bert_multi_cased_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=bert_multi_cased_L-12_H-768_A-12&en_category=SemanticModel)|BERT| -|[bert_chinese_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=bert_chinese_L-12_H-768_A-12&en_category=SemanticModel)|BERT| - - -## 4. 服务端(server) -### 4.1 简介 -server端接收client端发送的数据,执行模型计算过程并将计算结果返回给client端。 - -server端启动时会按照指定的模型名称从PaddleHub获取对应的模型文件进行加载,无需提前下载模型或指定模型路径,对模型的管理工作由PaddleHub负责。在加载模型后在指定的端口启动`BRPC`服务,保持端口监听,当接收到数据后便执行模型计算,并将计算结果通过`BRPC`返回并发送至client端。 - -### 4.2 启动 -使用PaddleHub的命令行工具可一键启动`Bert Service`,命令如下: -```shell -$ hub serving start bert_service -m ernie_tiny -p 8866 --use_gpu --gpu 0 -``` -启动成功则显示 -```shell -Server[baidu::paddle_serving::predictor::bert_service::BertServiceImpl] is serving on port=8866. -``` -整个启动过程如下图: - - -
- -  启动BS - -
- - -其中各参数说明如下表: - -
- -|参数|说明|是否必填| -|:--:|:--:|:----:| -|hub serving start bert_service|启动`Bert Service`服务端。|必填项| -|--module/-m|指定启动的模型,如果指定的模型不存在,则自动通过PaddleHub下载指定模型。|必填项| -|--port/-p|指定启动的端口,每个端口对应一个模型,可基于不同端口进行多次启动,以实现多个模型的服务部署。|必填项| -|--use_gpu|若指定此项则使用GPU进行工作,反之仅使用CPU。注意需安装GPU版本的PaddlePaddle。|非必填项,默认为不指定| -|--gpu|指定使用的GPU卡号,如未指定use_gpu则填写此项无效,每个服务对应一张卡,部署多个服务时需指定不同卡|非必填项,默认为0号显卡| - -
- -### 4.3 关闭 -通过在启动服务端的命令行页面使用Ctrl+C终止`Bert Service`运行,关闭成功则显示: -```shell -Paddle Inference Server exit successfully! -``` - - -## 5.客户端(client) -### 5.1 简介 -client端接收文本数据,并获取server端返回的模型计算的embedding结果。 - -client端利用PaddleHub的语义理解任务将原始文本按照不同模型的数据预处理方案将文本ID化,并生成对应的sentence type、position、input masks数据,将这些信息封装成json数据,通过http协议按照指定的IP端口信息发送至server端,等待并获取模型生成结果。 -### 5.2 启动 -连接服务端方法原型为: -```python -def connect(input_text, - model_name, - max_seq_len=128, - show_ids=False, - do_lower_case=True, - server="127.0.0.1:8866", - retry=3) -``` - -其中各参数说明如下表: - -|参数|说明|类型|样例| -|:--:|:--:|:--:|:--:| -|input_text|输入文本,要获取embedding的原始文本|二维list类型,内部元素为string类型的文本|[['样例1'],['样例2']]| -|model_name|指定使用的模型名称|string|"ernie"| -|max_seq_len|计算时的样例长度,样例长度不足时采用补零策略,超出此参数则超出部分会被截断|int|128| -|show_ids|是否展现数据预处理后的样例信息,指定为True则显示样例信息,反之则不显示|bool|False| -|do_lower_case|是否将英文字母转换成小写,指定为True则将所有英文字母转换为小写,反之则保持原状|bool|True| -|server|要访问的server地址,包括ip地址及端口号|string|"127.0.0.1:8866"| -|retry|连接失败后的最大重试次数|int|3| - - -## 6. Demo +## Demo——利用Bert Service部署ernie_tiny在线embedding服务 在这里,我们将展示一个实际场景中可能使用的demo,我们利用PaddleHub在一台GPU机器上部署`ernie_tiny`模型服务,并在另一台CPU机器上尝试访问,获取一首七言绝句的embedding。 -### 6.1 安装环境依赖 +### Step1:安装环境依赖 首先需要安装环境依赖,根据第2节内容分别在两台机器上安装相应依赖。 -### 6.2 启动Bert Service服务端 +### Step2:启动Bert Service服务端 确保环境依赖安装正确后,在要部署服务的GPU机器上使用PaddleHub命令行工具启动`Bert Service`服务端,命令如下: ```shell $ hub serving start bert_service -m ernie_tiny --use_gpu --gpu 0 --port 8866 ``` -启动成功后打印 +启动成功后打印: ```shell Server[baidu::paddle_serving::predictor::bert_service::BertServiceImpl] is serving on port=8866. ``` 这样就启动了`ernie_tiny`的在线服务,监听8866端口,并在0号GPU上进行任务。 -### 6.3 使用Bert Service客户端进行远程调用 +### Step3:使用Bert Service客户端进行远程调用 部署好服务端后,就可以用普通机器作为客户端测试在线embedding功能。 首先导入客户端依赖。 @@ -182,7 +28,7 @@ Server[baidu::paddle_serving::predictor::bert_service::BertServiceImpl] is servi from paddlehub.serving.bert_serving import bs_client ``` -接着启动并初始化`bert service`客户端`BSClient`(这里的server为虚拟地址,需根据自己实际ip设置) +接着启动并初始化`bert service`客户端`BSClient`(这里的server为虚拟地址,需根据自己实际ip设置)。 ```python bc = bs_client.BSClient(module_name="ernie_tiny", server="127.0.0.1:8866") ``` @@ -200,7 +46,7 @@ result = bc.get_result(input_text=input_text) ```python [[0.9993321895599361, 0.9994612336158751, 0.9999646544456481, 0.732795298099517, -0.34387934207916204, ... ]] ``` -客户端代码demo文件见[示例](bert_service_client.py)。 +客户端代码demo文件见[示例](../paddlehub/serving/bert_serving/bert_service.py)。 运行命令如下: ```shell $ python bert_service_client.py @@ -214,30 +60,12 @@ $ python bert_service_client.py -### 6.4 关闭Bert Service服务端 +### Step4:关闭Bert Service服务端 如要停止`Bert Service`服务端程序,可在其启动命令行页面使用Ctrl+C方式关闭,关闭成功会打印如下日志: ```shell Paddle Inference Server exit successfully! ``` 这样,我们就利用一台GPU机器就完成了`Bert Service`的部署,并利用另一台普通机器进行了测试,可见通过`Bert Service`能够方便地进行在线embedding服务的快速部署。 -## 7. FAQ -> Q : 如何在一台服务器部署多个模型? -> A : 可通过多次启动`Bert Service`,分配不同端口实现。如果使用GPU,需要指定不同的显卡。如同时部署`ernie`和`bert_chinese_L-12_H-768_A-12`,分别执行命令如下: -> ```shell -> $ hub serving start bert_service -m ernie -p 8866 -> $ hub serving start bert_service -m bert_chinese_L-12_H-768_A-12 -p 8867 -> ``` - -> Q : 启动时显示"Check out http://yq01-gpu-255-129-12-00.epc.baidu.com:8887 in web - browser.",这个页面有什么作用。 -> A : 这是`BRPC`的内置服务,主要用于查看请求数、资源占用等信息,可对server端性能有大致了解,具体信息可查看[BRPC内置服务](https://github.com/apache/incubator-brpc/blob/master/docs/cn/builtin_service.md)。 - -> Q : 为什么输入文本的格式为[["文本1"], ["文本2"], ],而不是["文本1", "文本2", ]? -> A : 因为Bert模型可以对一轮对话生成向量表示,例如[["问题1","回答1"],["问题2","回答2"]],为了防止使用时混乱,每个样本使用一个list表示,一个样本list内部可以是1条string或2条string,如下面的文本: -> ```python -> input_text = [ -> ["你今天吃饭了吗","我已经吃过饭了"], -> ["今天天气怎么样","今天天气不错"], -> ] -> ``` +## 预训练模型一键服务部署 +除了`Bert Service`外,PaddleHub Serving还具有预训练模型一键服务部署功能,能够将预训练模型快捷部署上线,对外提供可靠的在线预测服务,具体信息请参见[Module Serving](../../../tutorial/serving.md)。 diff --git a/demo/serving/module_serving/GAN_stgan_celeba/README.md b/demo/serving/module_serving/GAN_stgan_celeba/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e8a2af1fd0cf37eeee577f8a85cdb81d7de23082 --- /dev/null +++ b/demo/serving/module_serving/GAN_stgan_celeba/README.md @@ -0,0 +1,70 @@ +# 部署图像生成服务-以stgan_celeba为例 +## 简介 +图像生成是指根据预先设置的标签,生成对应图像的过程。stgan_celeba通过在GAN中加入encoder-decoder,可实现人脸属性的转换。关于stgan_celeba的具体信息请参见[stgan_celeba](https://paddlepaddle.org.cn/hubdetail?name=stgan_celeba&en_category=GANs)。 + +使用PaddleHub Serving可以轻松部署一个在线图像生成服务API,可将此API接入自己的web网站,也可接入应用程序,如美图类应用,实现传照片修饰脸的功能。 + +下面就带领大家使用PaddleHub Serving,通过简单几步部署一个图像生成服务。 + +## Step1:启动PaddleHub Serving +启动命令如下: +```shell +$ hub serving start -m stgan_celeba +``` +启动时会显示加载模型过程,启动成功后显示: +```shell +Loading stgan_celeba successful. +``` +这样就完成了一个图像生成服务化API的部署,默认端口号为8866。 + +## Step2:测试图像生成在线API +我们用来测试的样例图片为: + +

+ +

+ +根据stgan_celeba所需信息,准备的数据包括图像文件和生成图像风格,格式为: +```python +files = [("image", file_a), ("image", file_b)] +data = {"info": ["info_a_1, info_a_2", "info_b_1, info_b_2"], "style": ["style_a", "style_b"]} +``` + +**NOTE:** 文件列表每个元素第一个参数为"image"。 + +info为图像描述,根据示例图像信息,info应为"Male,Black_Hair,Eyeglasses,No_Beard",即"男性,黑发,戴眼镜,没有胡子"。 + +image为要生成的图像风格,我们选取"Bald"(秃顶的)作为生成图像的风格。 + +代码如下: +```python +>>> # 指定要使用的图片文件并生成列表[("image", img_1), ("image", img_2), ... ] +>>> file_list = ["../img/man.png"] +>>> files = [("image", (open(item, "rb"))) for item in file_list] +>>> # 为每张图片对应指定info和style +>>> data = {"info": ["Male,Black_Hair,Eyeglasses,No_Beard"], "style": ["Bald"]} +``` + +## Step3:获取并验证结果 +然后就可以发送请求到图像生成服务API,并得到结果,代码如下: + +```python +>>> url = "http://127.0.0.1:8866/predict/image/stgan_celeba" +>>> r = requests.post(url=url, data=data, files=files) +``` +stgan_celeba返回的结果包括生成图像的base64编码格式,经过转换可以得到生成图像,代码如下: +```python +>>> for item in results: +... with open(output_path, "wb") as fp: +... fp.write(base64.b64decode(item["base64"].split(',')[-1])) +``` +查看指定输出文件夹,就能看到生成图像了,如图: + +

+ +

+ + +这样我们就完成了对图像生成服务化的部署和测试。 + +完整的测试代码见[stgan_celeba_serving_demo.py](stgan_celeba_serving_demo.py)。 diff --git a/demo/serving/GAN_stgan_celeba/stgan_celeba_serving_demo.py b/demo/serving/module_serving/GAN_stgan_celeba/stgan_celeba_serving_demo.py similarity index 75% rename from demo/serving/GAN_stgan_celeba/stgan_celeba_serving_demo.py rename to demo/serving/module_serving/GAN_stgan_celeba/stgan_celeba_serving_demo.py index c6b5129c32d639889319a9a4a6c3fc9721fd5dcf..f345ed272f781c5f11c81ea65cc95e18a10f9859 100644 --- a/demo/serving/GAN_stgan_celeba/stgan_celeba_serving_demo.py +++ b/demo/serving/module_serving/GAN_stgan_celeba/stgan_celeba_serving_demo.py @@ -6,20 +6,21 @@ import os if __name__ == "__main__": # 指定要使用的图片文件并生成列表[("image", img_1), ("image", img_2), ... ] - file_list = ["../img/woman.png"] + file_list = ["../img/man.png"] files = [("image", (open(item, "rb"))) for item in file_list] # 为每张图片对应指定info和style - data = {"info": ["Female,Brown_Hair"], "style": ["Aged"]} + data = {"info": ["Male,Black_Hair"], "style": ["Bald"]} # 指定图片生成方法为stgan_celeba并发送post请求 url = "http://127.0.0.1:8866/predict/image/stgan_celeba" r = requests.post(url=url, data=data, files=files) + print(r.text) results = eval(r.json()["results"]) # 保存生成的图片到output文件夹,打印模型输出结果 - if not os.path.exists("output"): - os.mkdir("output") + if not os.path.exists("stgan_output"): + os.mkdir("stgan_output") for item in results: - output_path = os.path.join("output", item["path"].split("/")[-1]) + output_path = os.path.join("stgan_output", item["path"].split("/")[-1]) with open(output_path, "wb") as fp: fp.write(base64.b64decode(item["base64"].split(',')[-1])) item.pop("base64") diff --git a/demo/serving/module_serving/GAN_stgan_celeba/stgan_output/Bald_man.png b/demo/serving/module_serving/GAN_stgan_celeba/stgan_output/Bald_man.png new file mode 100644 index 0000000000000000000000000000000000000000..e5b17a741668a5e1a3d1df7a4fb519b447a36e31 Binary files /dev/null and b/demo/serving/module_serving/GAN_stgan_celeba/stgan_output/Bald_man.png differ diff --git a/demo/serving/module_serving/README.md b/demo/serving/module_serving/README.md new file mode 100644 index 0000000000000000000000000000000000000000..cca1f084bf01b754123c3d7664b908e253c9dcf6 --- /dev/null +++ b/demo/serving/module_serving/README.md @@ -0,0 +1,50 @@ +# PaddleHub Serving模型一键服务部署 +## 简介 +### 为什么使用一键服务部署 +使用PaddleHub能够快速进行模型预测,但开发者常面临本地预测过程迁移线上的需求。无论是对外开放服务端口,还是在局域网中搭建预测服务,都需要PaddleHub具有快速部署模型预测服务的能力。在这个背景下,模型一键服务部署工具——PaddleHub Serving应运而生。开发者通过一行命令即可快速启动一个模型预测在线服务,而无需关注网络框架选择和实现。 +### 什么是一键服务部署 +PaddleHub Serving是基于PaddleHub的一键模型服务部署工具,能够通过简单的Hub命令行工具轻松启动一个模型预测在线服务,前端通过Flask和Gunicorn完成网络请求的处理,后端直接调用PaddleHub预测接口,同时支持使用多进程方式利用多核提高并发能力,保证预测服务的性能。 + +### 支持模型 +目前PaddleHub Serving支持PaddleHub所有可直接用于预测的模型进行服务部署,包括`lac`、`senta_bilstm`等NLP类模型,以及`yolov3_darknett53_coco2017`、`vgg16_imagenet`等CV类模型,未来还将支持开发者使用PaddleHub Fine-tune API得到的模型用于快捷服务部署。 + +**NOTE:** 关于PaddleHub Serving一键服务部署的具体信息请参见[PaddleHub Serving](../../../tutorial/serving.md)。 + +## Demo + +获取PaddleHub Serving的一键服务部署场景示例,可参见下列demo: + +* [图像分类-基于vgg11_imagent](../module_serving/classification_vgg11_imagenet) + +  该示例展示了利用vgg11_imagent完成图像分类服务化部署和在线预测,获取图像分类结果。 + +* [图像生成-基于stgan_celeba](../module_serving/GAN_stgan_celeba) + +  该示例展示了利用stgan_celeba生成图像服务化部署和在线预测,获取指定风格的生成图像。 + +* [文本审核-基于porn_detection_lstm](../module_serving/text_censorship_porn_detection_lstm) + +  该示例展示了利用porn_detection_lstm完成中文文本黄色敏感信息鉴定的服务化部署和在线预测,获取文本是否敏感及其置信度。 + +* [中文词法分析-基于lac](../module_serving/lexical_analysis_lac) + +  该示例展示了利用lac完成中文文本分词服务化部署和在线预测,获取文本的分词结果,并可通过用户自定义词典干预分词结果。 + +* [目标检测-基于yolov3_darknet53_coco2017](../module_serving/object_detection_yolov3_darknet53_coco2017) + +  该示例展示了利用yolov3_darknet53_coco2017完成目标检测服务化部署和在线预测,获取检测结果和覆盖识别框的图片。 + +* [中文语义分析-基于simnet_bow](../module_serving/semantic_model_simnet_bow) + +  该示例展示了利用simnet_bow完成中文文本相似度检测服务化部署和在线预测,获取文本的相似程度。 + +* [图像分割-基于deeplabv3p_xception65_humanseg](../module_serving/semantic_segmentation_deeplabv3p_xception65_humanseg) + +  该示例展示了利用deeplabv3p_xception65_humanseg完成图像分割服务化部署和在线预测,获取识别结果和分割后的图像。 + +* [中文情感分析-基于simnet_bow](../module_serving/semantic_model_simnet_bow) + +  该示例展示了利用senta_lstm完成中文文本情感分析服务化部署和在线预测,获取文本的情感分析结果。 + +## Bert Service +除了预训练模型一键服务部署功能之外,PaddleHub Serving还具有`Bert Service`功能,支持ernie_tiny、bert等模型快速部署,对外提供可靠的在线embedding服务,具体信息请参见[Bert Service](../../../tutorial/bert_service.md)。 diff --git a/demo/serving/module_serving/classification_vgg11_imagenet/README.md b/demo/serving/module_serving/classification_vgg11_imagenet/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5d035c496a9e45310f62736c36976c140b77136d --- /dev/null +++ b/demo/serving/module_serving/classification_vgg11_imagenet/README.md @@ -0,0 +1,61 @@ +# 部署图像分类服务-以vgg11_imagenent为例 +## 简介 +图像分类是指通过模型,预测给定的图片所属类别,vgg11_imagenent就是一种有效的图像分类模型。关于vgg11_imagenent的具体信息请参见[vgg11_imagenent](https://paddlepaddle.org.cn/hubdetail?name=vgg11_imagenet&en_category=ImageClassification)。 + +使用PaddleHub Serving可以部署一个在线图片分类服务,既可以对用户暴露直接预测接口,也可以利用此接口实现一个web网站,甚至可以集成到移动端应用程序中实现拍照识别功能。 + +这里就带领大家使用PaddleHub Serving,通过简单几步部署一个图像分类服务。 + +## Step1:启动PaddleHub Serving +启动命令如下: +```shell +$ hub serving start -m vgg11_imagenet +``` +启动时会显示加载模型过程,启动成功后显示: +```shell +Loading vgg11_imagenet successful. +``` +这样就完成了一个图像分类服务化API的部署,默认端口号为8866。 + +## Step2:测试图像分类在线API +我们用来测试的样例图片为: + +

+ +

+ +

+ +

+ +准备的数据格式为: +```python +files = [("image", file_1), ("image", file_2)] +``` +**NOTE:** 每个元素第一个参数为"image"。 + +代码如下: +```python +>>> file_list = ["../img/cat.jpg", "../img/flower.jpg"] +>>> files = [("image", (open(item, "rb"))) for item in file_list] +``` + +## Step3:获取并验证结果 +然后就可以发送请求到图像分类服务API,并得到结果了,代码如下: +```python +>>> # 指定检测方法为vgg11_imagenet并发送post请求 +>>> url = "http://127.0.0.1:8866/predict/image/vgg11_imagenet" +>>> r = requests.post(url=url, files=files) +``` +vgg11_imagenent返回的结果为图像分类结果及其对应的概率,我们尝试打印接口返回结果: +```python + +>>> print(json.dumps(r.json(), indent=4, ensure_ascii=False)) +{ + "results": "[[{'Egyptian cat': 0.540287435054779}], [{'daisy': 0.9976677298545837}]]" +} +``` + +这样我们就完成了对图像分类预测服务化部署和测试。 + +完整的测试代码见[vgg11_imagenent_serving_demo.py](vgg11_imagenet_serving_demo.py)。 diff --git a/demo/serving/Classification_vgg11_imagenet/vgg11_imagenet_serving_demo.py b/demo/serving/module_serving/classification_vgg11_imagenet/vgg11_imagenet_serving_demo.py similarity index 100% rename from demo/serving/Classification_vgg11_imagenet/vgg11_imagenet_serving_demo.py rename to demo/serving/module_serving/classification_vgg11_imagenet/vgg11_imagenet_serving_demo.py diff --git a/demo/serving/module_serving/lexical_analysis_lac/README.md b/demo/serving/module_serving/lexical_analysis_lac/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9181ba0a9e8a6548b4e9a3fdf827bbd5fc57eb38 --- /dev/null +++ b/demo/serving/module_serving/lexical_analysis_lac/README.md @@ -0,0 +1,101 @@ +# 部署词法分析服务-以lac为例 +## 简介 +`Lexical Analysis of Chinese`,简称`LAC`,是一个联合的词法分析模型,能整体性地完成中文分词、词性标注、专名识别任务。关于`LAC`的具体信息请参见[LAC](https://paddlepaddle.org.cn/hubdetail?name=lac&en_category=LexicalAnalysis)。 + +使用PaddleHub Serving可以部署一个在线词法分析服务,可以将此接口用于词法分析、在线分词等在线web应用。 + +这里就带领大家使用PaddleHub Serving,通过简单几步部署一个词法分析在线服务。 + +## Step1:启动PaddleHub Serving +启动命令如下 +```shell +$ hub serving start -m lac +``` +启动时会显示加载模型过程,启动成功后显示 +```shell +Loading lac successful. +``` +这样就完成了一个词法分析服务化API的部署,默认端口号为8866。 + +## Step2:测试语言模型在线API +### 不使用自定义词典 +在服务部署好之后,我们可以进行测试,用来测试的文本为`今天是个好日子`和`天气预报说今天要下雨`。 + +准备的数据格式为: +```python +{"text": [text_1, text_2, ...]} +``` +**NOTE:** 字典的key为"text"。 + +根据文本和数据格式,代码如下: +```python +>>> # 指定用于用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} +>>> text_list = ["今天是个好日子", "天气预报说今天要下雨"] +>>> text = {"text": text_list} +``` + +## Step3:获取并验证结果 +接下来发送请求到词法分析API,并得到结果,代码如下: +```python +# 指定预测方法为lac并发送post请求 +>>> url = "http://127.0.0.1:8866/predict/text/lac" +>>> r = requests.post(url=url, data=text) +``` +`LAC`模型返回的结果为每个文本分词后的结果,我们尝试打印接口返回结果: +```python +# 打印预测结果 +>>> print(json.dumps(r.json(), indent=4, ensure_ascii=False)) +{ + "results": [ + { + "tag": [ + "TIME", + "v", + "q", + "n" + ], + "word": [ + "今天", + "是", + "个", + "好日子" + ] + }, + { + "tag": [ + "n", + "v", + "TIME", + "v", + "v" + ], + "word": [ + "天气预报", + "说", + "今天", + "要", + "下雨" + ] + } + ] +} +``` +这样我们就完成了对词法分析的预测服务化部署和测试。 + +完整的测试代码见[lac_serving_demo.py](lac_serving_demo.py)。 + +### 使用自定义词典 +`LAC`模型在预测时还可以使用自定义词典干预默认分词结果,这种情况只需要将自定义词典以文件的形式附加到request请求即可,数据格式如下: +```python +{"user_dict": user_dict.txt} +``` +根据数据格式,具体代码如下: +```python +>>> # 指定自定义词典{"user_dict": dict.txt} +>>> file = {"user_dict": open("dict.txt", "rb")} +>>> # 请求接口时以文件的形式附加自定义词典,其余和不使用自定义词典的请求方式相同,此处不再赘述 +>>> url = "http://127.0.0.1:8866/predict/text/lac" +>>> r = requests.post(url=url, files=file, data=text) +``` + +完整的测试代码见[lac_with_dict_serving_demo.py](lac_with_dict_serving_demo.py)。 diff --git a/demo/serving/Lexical_Analysis_lac/dict.txt b/demo/serving/module_serving/lexical_analysis_lac/dict.txt similarity index 100% rename from demo/serving/Lexical_Analysis_lac/dict.txt rename to demo/serving/module_serving/lexical_analysis_lac/dict.txt diff --git a/demo/serving/Lexical_Analysis_lac/lac_serving_demo.py b/demo/serving/module_serving/lexical_analysis_lac/lac_serving_demo.py similarity index 82% rename from demo/serving/Lexical_Analysis_lac/lac_serving_demo.py rename to demo/serving/module_serving/lexical_analysis_lac/lac_serving_demo.py index 404cd86769b35d7c4b7cb019e2b70ee1a729e8bd..58d696bc3142966264735ee2f02a66a1ae22839c 100644 --- a/demo/serving/Lexical_Analysis_lac/lac_serving_demo.py +++ b/demo/serving/module_serving/lexical_analysis_lac/lac_serving_demo.py @@ -3,7 +3,7 @@ import requests import json if __name__ == "__main__": - # 指定用于用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} + # 指定用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} text_list = ["今天是个好日子", "天气预报说今天要下雨"] text = {"text": text_list} # 指定预测方法为lac并发送post请求 diff --git a/demo/serving/Lexical_Analysis_lac/lac_with_dict_serving_demo.py b/demo/serving/module_serving/lexical_analysis_lac/lac_with_dict_serving_demo.py similarity index 85% rename from demo/serving/Lexical_Analysis_lac/lac_with_dict_serving_demo.py rename to demo/serving/module_serving/lexical_analysis_lac/lac_with_dict_serving_demo.py index 03c0a2dc79bf419047a735261360da6a3f126fea..944463883d3277ef3a72cf7b691a10d2e16c2952 100644 --- a/demo/serving/Lexical_Analysis_lac/lac_with_dict_serving_demo.py +++ b/demo/serving/module_serving/lexical_analysis_lac/lac_with_dict_serving_demo.py @@ -3,7 +3,7 @@ import requests import json if __name__ == "__main__": - # 指定用于用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} + # 指定用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} text_list = ["今天是个好日子", "天气预报说今天要下雨"] text = {"text": text_list} # 指定自定义词典{"user_dict": dict.txt} diff --git a/demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/README.md b/demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/README.md new file mode 100644 index 0000000000000000000000000000000000000000..68d8a7bbe481f98f4cf6d8a12e7d8c08aa363498 --- /dev/null +++ b/demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/README.md @@ -0,0 +1,121 @@ +# 部署图像分类服务-以yolov3_darknet53_coco2017为例 +## 简介 +目标检测作为深度学习常见任务,在各种场景下都有所使用。使用`yolov3_darknet53_coco2017`模型可以进行目标检测任务,关于`yolov3_darknet53_coco2017`的具体信息请参见[yolov3_darknet53_coco2017](https://paddlepaddle.org.cn/hubdetail?name=yolov3_darknet53_coco2017&en_category=ObjectDetection)。 + +使用PaddleHub Serving可以轻松部署一个在线目标检测服务API,可将此API接入自己的web网站进行在线目标检测,也可接入移动端应用程序,实现识图、圈人等功能。 + +下面就带领大家使用PaddleHub Serving,通过简单几步部署一个目标检测服务。 + +## Step1:启动PaddleHub Serving +启动命令如下: +```shell +$ hub serving start -m yolov3_darknet53_coco2017 +``` +启动时会显示加载模型过程,启动成功后显示: +```shell +Loading yolov3_darknet53_coco2017 successful. +``` +这样就完成了一个图像生成服务化API的部署,默认端口号为8866。 + +## Step2:测试图像生成在线API +我们用来测试的样例图片为: + +

+ + + +

+ +

+ + + +

+ +准备的数据格式为: +```python +files = [("image", file_1), ("image", file_2)] +``` +**NOTE:** 文件列表每个元素第一个参数为"image"。 + +代码如下: +```python +>>> # 指定要检测的图片并生成列表[("image", img_1), ("image", img_2), ... ] +>>> file_list = ["../img/cat.jpg", "../img/dog.jpg"] +>>> files = [("image", (open(item, "rb"))) for item in file_list] +``` + +## Step3:获取并验证结果 +然后就可以发送请求到目标检测服务API,并得到结果,代码如下: +```python +>>> # 指定检测方法为yolov3_darknet53_coco2017并发送post请求 +>>> url = "http://127.0.0.1:8866/predict/image/yolov3_darknet53_coco2017" +>>> r = requests.post(url=url, files=files) +``` +我们可以打印接口返回结果: +```python +>>> results = eval(r.json()["results"]) +>>> print(json.dumps(results, indent=4, ensure_ascii=False)) +[ + { + "path": "cat.jpg", + "data": [ + { + "left": 319.489, + "right": 1422.8364, + "top": 208.94229, + "bottom": 993.8552, + "label": "cat", + "confidence": 0.9174191 + } + ] + }, + { + "path": "dog.jpg", + "data": [ + { + "left": 200.6918, + "right": 748.96204, + "top": 122.74927, + "bottom": 566.2066, + "label": "dog", + "confidence": 0.83619183 + }, + { + "left": 506.8462, + "right": 623.2322, + "top": 378.0084, + "bottom": 416.116, + "label": "tie", + "confidence": 0.5082839 + } + ] + } +] +``` +根据结果可以看出准确识别了请求的图片。 + +yolov3_darknet53_coco2017返回的结果还包括标注检测框的图像的base64编码格式,经过转换可以得到生成图像,代码如下: +```python +>>> for item in results: +... with open(output_path, "wb") as fp: +... fp.write(base64.b64decode(item["base64"].split(',')[-1])) +``` +查看指定输出文件夹,就能看到生成图像了,如图: + +

+ + + +

+ +

+ + + +

+ + +这样我们就完成了对目标检测服务化的部署和测试。 + +完整的测试代码见[yolov3_darknet53_coco2017_serving_demo.py](yolov3_darknet53_coco2017_serving_demo.py)。 diff --git a/demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/output/cat.jpg b/demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/output/cat.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d71260a8c364349bc0f07ef9938220eef893d857 Binary files /dev/null and b/demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/output/cat.jpg differ diff --git a/demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/output/dog.jpg b/demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/output/dog.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b29ca3439b1e1f462ebf2a97ba732a7826ac834f Binary files /dev/null and b/demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/output/dog.jpg differ diff --git a/demo/serving/Object_Detection_yolov3_coco2017/yolov3_coco2017_serving_demo.py b/demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/yolov3_darknet53_coco2017_serving_demo.py similarity index 91% rename from demo/serving/Object_Detection_yolov3_coco2017/yolov3_coco2017_serving_demo.py rename to demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/yolov3_darknet53_coco2017_serving_demo.py index e8f9409370a9cd550bb76313978ffd787c71c436..8b85f223ada1116ddefa8000fadb42b55eb02369 100644 --- a/demo/serving/Object_Detection_yolov3_coco2017/yolov3_coco2017_serving_demo.py +++ b/demo/serving/module_serving/object_detection_yolov3_darknet53_coco2017/yolov3_darknet53_coco2017_serving_demo.py @@ -9,7 +9,7 @@ if __name__ == "__main__": file_list = ["../img/cat.jpg", "../img/dog.jpg"] files = [("image", (open(item, "rb"))) for item in file_list] # 指定检测方法为yolov3_coco2017并发送post请求 - url = "http://127.0.0.1:8866/predict/image/yolov3_coco2017" + url = "http://127.0.0.1:8866/predict/image/yolov3_darknet53_coco2017" r = requests.post(url=url, files=files) results = eval(r.json()["results"]) diff --git a/demo/serving/module_serving/semantic_model_simnet_bow/README.md b/demo/serving/module_serving/semantic_model_simnet_bow/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c55111c84f531731e312cf7c97173192744061d3 --- /dev/null +++ b/demo/serving/module_serving/semantic_model_simnet_bow/README.md @@ -0,0 +1,72 @@ +# 部署语义模型服务-以simnet_bow为例 +## 简介 +`simnet_bow`是一个计算短文本相似度的模型,可以根据用户输入的两个文本,计算出相似度得分。关于`simnet_bow`的具体信息请参见[simnet_bow](https://paddlepaddle.org.cn/hubdetail?name=simnet_bow&en_category=SemanticModel)。 + +使用PaddleHub Serving可以部署一个在线语义模型服务,可以将此接口用于在线文本相似度分析、智能问答检索等应用。 + +这里就带领大家使用PaddleHub Serving,通过简单几步部署一个语义模型在线服务。 + +## Step1:启动PaddleHub Serving +启动命令如下: +```shell +$ hub serving start -m simnet_bow +``` +启动时会显示加载模型过程,启动成功后显示: +```shell +Loading lac successful. +``` +这样就完成了一个语义模型服务化API的部署,默认端口号为8866。 + +## Step2:测试语义模型在线API +在服务部署好之后,我们可以进行测试,用来测试的文本对分别为`[这道题太难了:这道题是上一年的考题], [这道题太难了:这道题不简单], [这道题太难了:这道题很有意思]`。 + +准备的数据格式为: +```python +{"text_1": [text_a1, text_a2, ... ], "text_2": [text_b1, text_b2, ... ]} +``` +**NOTE:** 字典的key分别为"text_1"和"text_2",与`simnet_bow`模型使用的输入数据一致。 + +根据文本和数据格式,代码如下: +```python +>>> # 指定用于用于匹配的文本并生成字典{"text_1": [text_a1, text_a2, ... ] +>>> # "text_2": [text_b1, text_b2, ... ]} +>>> text = { +>>> "text_1": ["这道题太难了", "这道题太难了", "这道题太难了"], +>>> "text_2": ["这道题是上一年的考题", "这道题不简单", "这道题很有意思"] +>>> } +``` + +## Step3:获取并验证结果 +接下来发送请求到语义模型API,并得到结果,代码如下: +```python +>>> # 指定匹配方法为simnet_bow并发送post请求 +>>> url = "http://127.0.0.1:8866/predict/text/simnet_bow" +>>> r = requests.post(url=url, data=text) +``` +`simnet_bow`模型返回的结果为每对文本对比后的相似度,我们尝试打印接口返回结果: +```python +# 打印预测结果 +>>> print(json.dumps(r.json(), indent=4, ensure_ascii=False)) +{ + "results": [ + { + "similarity": 0.8445, + "text_1": "这道题太难了", + "text_2": "这道题是上一年的考题" + }, + { + "similarity": 0.9275, + "text_1": "这道题太难了", + "text_2": "这道题不简单" + }, + { + "similarity": 0.9083, + "text_1": "这道题太难了", + "text_2": "这道题很有意思" + } + ] +} +``` +这样我们就完成了对语义模型simnet_bow的预测服务化部署和测试。 + +完整的测试代码见[simnet_bow_serving_demo.py](simnet_bow_serving_demo.py)。 diff --git a/demo/serving/Semantic_Model_simnet_bow/simnet_bow_serving_demo.py b/demo/serving/module_serving/semantic_model_simnet_bow/simnet_bow_serving_demo.py similarity index 86% rename from demo/serving/Semantic_Model_simnet_bow/simnet_bow_serving_demo.py rename to demo/serving/module_serving/semantic_model_simnet_bow/simnet_bow_serving_demo.py index 7fa7b087083d714d5ba4400e23c2d6e0e419cfff..73d38e812b6b6ebe270db5037f0142a828e99251 100644 --- a/demo/serving/Semantic_Model_simnet_bow/simnet_bow_serving_demo.py +++ b/demo/serving/module_serving/semantic_model_simnet_bow/simnet_bow_serving_demo.py @@ -3,7 +3,7 @@ import requests import json if __name__ == "__main__": - # 指定用于用于匹配的文本并生成字典{"text_1": [text_a1, text_a2, ... ] + # 指定用于匹配的文本并生成字典{"text_1": [text_a1, text_a2, ... ] # "text_2": [text_b1, text_b2, ... ]} text = { "text_1": ["这道题太难了", "这道题太难了", "这道题太难了"], diff --git a/demo/serving/module_serving/semantic_segmentation_deeplabv3p_xception65_humanseg/README.md b/demo/serving/module_serving/semantic_segmentation_deeplabv3p_xception65_humanseg/README.md new file mode 100644 index 0000000000000000000000000000000000000000..21294670b59cfd15fa4d761c932e2e23d5ecdd62 --- /dev/null +++ b/demo/serving/module_serving/semantic_segmentation_deeplabv3p_xception65_humanseg/README.md @@ -0,0 +1,77 @@ +# 部署图像分割服务-以deeplabv3p_xception65_humanseg为例 +## 简介 +图像分割是深度学习的常见任务。使用`deeplabv3p_xception65_humanseg`模型可以进行人像分割任务,关于`deeplabv3p_xception65_humanseg`的具体信息请参见[deeplabv3p_xception65_humanseg](https://paddlepaddle.org.cn/hubdetail?name=deeplabv3p_xception65_humanseg&en_category=ImageSegmentation)。 + +使用PaddleHub Serving可以轻松部署一个在线图像分割服务API,可将此API接入自己的web网站进行在线图像分割,也可接入移动端应用程序,实现拍照分割等功能。 + +下面就带领大家使用PaddleHub Serving,通过简单几步部署一个目标检测服务。 + +## Step1:启动PaddleHub Serving +启动命令如下 +```shell +$ hub serving start -m deeplabv3p_xception65_humanseg +``` +启动时会显示加载模型过程,启动成功后显示: +```shell +Loading deeplabv3p_xception65_humanseg successful. +``` +这样就完成了一个图像分割服务化API的部署,默认端口号为8866。 + +## Step2:测试图像分割在线API +我们用来测试的样例图片为: + +

+ + + +

+ +准备的数据格式为: +```python +files = [("image", file_1), ("image", file_2)] +``` +**NOTE:** 文件列表每个元素第一个参数为"image"。 + +代码如下 +```python +>>> # 指定要检测的图片并生成列表[("image", img_1), ("image", img_2), ... ] +>>> file_list = ["../../../../docs/imgs/girl.jpg"] +>>> files = [("image", (open(item, "rb"))) for item in file_list] +``` + +## Step3:获取并验证结果 +然后就可以发送请求到图像分割服务API,并得到结果,代码如下: +```python +>>> # 指定检测方法为deeplabv3p_xception65_humanseg并发送post请求 +>>> url = "http://127.0.0.1:8866/predict/image/deeplabv3p_xception65_humanseg" +>>> r = requests.post(url=url, files=files) +``` +我们可以打印接口返回结果: +```python +>>> results = eval(r.json()["results"]) +>>> print(json.dumps(results, indent=4, ensure_ascii=False)) +[ + { + "origin": "girl.jpg", + "processed": "humanseg_output/girl.png" + } +] +``` + +deeplabv3p_xception65_humanseg返回的结果还包括人像分割后的图像的base64编码格式,经过转换可以得到生成图像,代码如下: +```python +>>> for item in results: +... with open(output_path, "wb") as fp: +... fp.write(base64.b64decode(item["base64"].split(',')[-1])) +``` +查看指定输出文件夹,就能看到生成图像了,如图: + +

+ + + +

+ +这样我们就完成了对图像分割模型deeplabv3p_xception65_humanseg服务化的部署和测试。 + +完整的测试代码见[deeplabv3p_xception65_humanseg_serving_demo.py](deeplabv3p_xception65_humanseg_serving_demo.py)。 diff --git a/demo/serving/Semantic_Segmentation_deeplabv3p_xception65_humanseg/deeplabv3p_xception65_humanseg_serving_demo.py b/demo/serving/module_serving/semantic_segmentation_deeplabv3p_xception65_humanseg/deeplabv3p_xception65_humanseg_serving_demo.py similarity index 100% rename from demo/serving/Semantic_Segmentation_deeplabv3p_xception65_humanseg/deeplabv3p_xception65_humanseg_serving_demo.py rename to demo/serving/module_serving/semantic_segmentation_deeplabv3p_xception65_humanseg/deeplabv3p_xception65_humanseg_serving_demo.py diff --git a/demo/serving/module_serving/semantic_segmentation_deeplabv3p_xception65_humanseg/output/girl.png b/demo/serving/module_serving/semantic_segmentation_deeplabv3p_xception65_humanseg/output/girl.png new file mode 100644 index 0000000000000000000000000000000000000000..667e9e70e5e8f0cf84cc5db7728eada13bf14607 Binary files /dev/null and b/demo/serving/module_serving/semantic_segmentation_deeplabv3p_xception65_humanseg/output/girl.png differ diff --git a/demo/serving/module_serving/sentiment_analysis_senta_lstm/README.md b/demo/serving/module_serving/sentiment_analysis_senta_lstm/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ceb1634a06080514789ae37c979cd8ab742fc737 --- /dev/null +++ b/demo/serving/module_serving/sentiment_analysis_senta_lstm/README.md @@ -0,0 +1,85 @@ +# 部署情感分析服务-以senta_lstm为例 +## 简介 +情感分析针对带有主观描述的中文文本,可自动判断该文本的情感极性类别并给出相应的置信度。利用`senta_lstm`模型可以完成中文情感分析任务,关于`senta_lstm`的具体信息请参见[senta_lstm] +(https://paddlepaddle.org.cn/hubdetail?name=senta_lstm&en_category=SentimentAnalysis)。 + +使用PaddleHub Serving可以部署一个在线情感分析服务,可以将此接口用于分析评论、智能客服等应用。 + +这里就带领大家使用PaddleHub Serving,通过简单几步部署一个情感分析在线服务。 + +## Step1:启动PaddleHub Serving +启动命令如下 +```shell +$ hub serving start -m senta_lstm +``` +启动时会显示加载模型过程,启动成功后显示 +```shell +Loading senta_lstm successful. +``` +这样就完成了一个词法分析服务化API的部署,默认端口号为8866。 + +## Step2:测试词法分析在线API +在服务部署好之后,我们可以进行测试,用来测试的文本为`我不爱吃甜食`和`我喜欢躺在床上看电影`。 + +准备的数据格式为: +```python +{"text": [text_1, text_2, ...]} +``` +**NOTE:** 字典的key为"text"。 + +根据文本和数据格式,代码如下: +```python +>>> # 指定用于用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} +>>> text_list = ["我不爱吃甜食", "我喜欢躺在床上看电影"] +>>> text = {"text": text_list} +``` + +## Step3:获取并验证结果 +接下来发送请求到词法分析API,并得到结果,代码如下: +```python +# 指定预测方法为lac并发送post请求 +>>> url = "http://127.0.0.1:8866/predict/text/senta_lstm" +>>> r = requests.post(url=url, data=text) +``` +`LAC`模型返回的结果为每个文本分词后的结果,我们尝试打印接口返回结果: +```python +# 打印预测结果 +>>> print(json.dumps(r.json(), indent=4, ensure_ascii=False)) +{ + "results": [ + { + "tag": [ + "TIME", + "v", + "q", + "n" + ], + "word": [ + "今天", + "是", + "个", + "好日子" + ] + }, + { + "tag": [ + "n", + "v", + "TIME", + "v", + "v" + ], + "word": [ + "天气预报", + "说", + "今天", + "要", + "下雨" + ] + } + ] +} +``` +这样我们就完成了对词法分析的预测服务化部署和测试。 + +完整的测试代码见[senta_lstm_serving_demo.py](senta_lstm_serving_demo.py)。 diff --git a/demo/serving/Sentiment_Analysis_senta_lstm/senta_lstm_serving_demo.py b/demo/serving/module_serving/sentiment_analysis_senta_lstm/senta_lstm_serving_demo.py similarity index 82% rename from demo/serving/Sentiment_Analysis_senta_lstm/senta_lstm_serving_demo.py rename to demo/serving/module_serving/sentiment_analysis_senta_lstm/senta_lstm_serving_demo.py index bcd070e0c0a0da2aafc701b5abfb7e0b410f8976..9ff7f49c25fea7e734d7c27f0638ba6fc641a730 100644 --- a/demo/serving/Sentiment_Analysis_senta_lstm/senta_lstm_serving_demo.py +++ b/demo/serving/module_serving/sentiment_analysis_senta_lstm/senta_lstm_serving_demo.py @@ -3,7 +3,7 @@ import requests import json if __name__ == "__main__": - # 指定用于用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} + # 指定用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} text_list = ["我不爱吃甜食", "我喜欢躺在床上看电影"] text = {"text": text_list} # 指定预测方法为senta_lstm并发送post请求 diff --git a/demo/serving/module_serving/text_censorship_porn_detection_lstm/README.md b/demo/serving/module_serving/text_censorship_porn_detection_lstm/README.md new file mode 100644 index 0000000000000000000000000000000000000000..acc4d144b79c2195c084d1c0cbcb81e6259dd5c6 --- /dev/null +++ b/demo/serving/module_serving/text_censorship_porn_detection_lstm/README.md @@ -0,0 +1,70 @@ +# 部署文本审核服务-以porn_detection_lstm为例 +## 简介 +在网站建设等场景中经常需要对敏感信息进行鉴定和过滤,采用文本审核模型`porn_detection_lstm`可自动判别文本是否涉黄并给出相应的置信度,关于`porn_detection_lstm`的具体信息请参见[porn_detection_lstm](https://paddlepaddle.org +.cn/hubdetail?name=porn_detection_lstm&en_category=TextCensorship) + +使用PaddleHub Serving可以部署一个在线文本审核服务,可以将此接口用于防止低俗交友、色情文本等应用。 + +这里就带领大家使用PaddleHub Serving,通过简单几步部署一个文本审核在线服务。 + +## Step1:启动PaddleHub Serving +启动命令如下: +```shell +$ hub serving start -m porn_detection_lstm +``` +启动时会显示加载模型过程,启动成功后显示: +```shell +Loading porn_detection_lstm successful. +``` +这样就完成了一个文本审核服务化API的部署,默认端口号为8866。 + +## Step2:测试文本审核在线API +在服务部署好之后,我们可以进行测试,用来测试的文本为`黄片下载`和`中国黄页`。 + +准备的数据格式为: +```python +{"text": [text_1, text_2, ...]} +``` +**NOTE:** 字典的key为"text"。 + +根据文本和数据格式,代码如下: +```python +>>> # 指定用于用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} +>>> text_list = ["黄片下载", "中国黄页"] +>>> text = {"text": text_list} +``` +## Step3:获取并验证结果 +接下来发送请求到文本审核API,并得到结果,代码如下: +```python +# 指定预测方法为lac并发送post请求 +>>> url = "http://127.0.0.1:8866/predict/text/porn_detection_lstm" +>>> r = requests.post(url=url, data=text) +``` +`porn_detection_lstm`模型返回的结果为每个文本鉴定后的结果,我们尝试打印接口返回结果: +```python +# 打印预测结果 +>>> print(json.dumps(r.json(), indent=4, ensure_ascii=False)) +{ + "results": [ + { + "not_porn_probs": 0.0121, + "porn_detection_key": "porn", + "porn_detection_label": 1, + "porn_probs": 0.9879, + "text": "黄片下载" + }, + { + "not_porn_probs": 0.9954, + "porn_detection_key": "not_porn", + "porn_detection_label": 0, + "porn_probs": 0.0046, + "text": "中国黄页" + } + ] +} +``` +可以看出正确得到了两个文本的预测结果。 + +这样我们就完成了对文本审核模型的预测服务化部署和测试。 + +完整的测试代码见[porn_detection_lstm_serving_demo.py](porn_detection_lstm_serving_demo.py)。 diff --git a/demo/serving/module_serving/text_censorship_porn_detection_lstm/porn_detection_lstm_serving_demo.py b/demo/serving/module_serving/text_censorship_porn_detection_lstm/porn_detection_lstm_serving_demo.py new file mode 100644 index 0000000000000000000000000000000000000000..627f3d9be4fb2cde20e65f9b5216e75ecf1d1788 --- /dev/null +++ b/demo/serving/module_serving/text_censorship_porn_detection_lstm/porn_detection_lstm_serving_demo.py @@ -0,0 +1,14 @@ +# coding: utf8 +import requests +import json + +if __name__ == "__main__": + # 指定用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} + text_list = ["黄片下载", "中国黄页"] + text = {"text": text_list} + # 指定预测方法为lac并发送post请求 + url = "http://127.0.0.1:8866/predict/text/porn_detection_lstm" + r = requests.post(url=url, data=text) + + # 打印预测结果 + print(json.dumps(r.json(), indent=4, ensure_ascii=False)) diff --git a/demo/serving/bert_service/img/bs.png b/docs/imgs/bs.png similarity index 100% rename from demo/serving/bert_service/img/bs.png rename to docs/imgs/bs.png diff --git a/demo/serving/img/cat.jpg b/docs/imgs/cat.jpg similarity index 100% rename from demo/serving/img/cat.jpg rename to docs/imgs/cat.jpg diff --git a/demo/serving/img/dog.jpg b/docs/imgs/dog.jpg similarity index 100% rename from demo/serving/img/dog.jpg rename to docs/imgs/dog.jpg diff --git a/demo/serving/img/flower.jpg b/docs/imgs/flower.jpg similarity index 100% rename from demo/serving/img/flower.jpg rename to docs/imgs/flower.jpg diff --git a/demo/serving/img/girl.jpg b/docs/imgs/girl.jpg similarity index 100% rename from demo/serving/img/girl.jpg rename to docs/imgs/girl.jpg diff --git a/docs/imgs/man.png b/docs/imgs/man.png new file mode 100644 index 0000000000000000000000000000000000000000..2a6b9755d77b7a986a7e2144034772ace2af91ff Binary files /dev/null and b/docs/imgs/man.png differ diff --git a/docs/imgs/start_serving_lac.png b/docs/imgs/start_serving_lac.png new file mode 100644 index 0000000000000000000000000000000000000000..67b1ae337549305c95bceb810fd448c3697e0dd5 Binary files /dev/null and b/docs/imgs/start_serving_lac.png differ diff --git a/docs/imgs/web_demo.png b/docs/imgs/web_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..169f426251d243327bbbb05bf056cc27a7a1d7b5 Binary files /dev/null and b/docs/imgs/web_demo.png differ diff --git a/demo/serving/img/woman.png b/docs/imgs/woman.png similarity index 100% rename from demo/serving/img/woman.png rename to docs/imgs/woman.png diff --git a/paddlehub/autofinetune/mpi_helper.py b/paddlehub/autofinetune/mpi_helper.py index 9608363bfa888f208e20faaf7c9ac9d2278b33d3..5aa67cce018ab2edb5b93555c609074b1ce9e3a6 100755 --- a/paddlehub/autofinetune/mpi_helper.py +++ b/paddlehub/autofinetune/mpi_helper.py @@ -69,7 +69,7 @@ class MPIHelper(object): def split_range(self, array_length): if self._size == 1: return 0, array_length - average_count = array_length / self._size + average_count = array_length // self._size if array_length % self._size == 0: return average_count * self._rank, average_count * (self._rank + 1) else: diff --git a/paddlehub/commands/autofinetune.py b/paddlehub/commands/autofinetune.py index d8ad287eddfea2695f67fc6086544390b1a7a2e8..73538ae6c359b45839c5021ebfcb6a4bba2cffd4 100644 --- a/paddlehub/commands/autofinetune.py +++ b/paddlehub/commands/autofinetune.py @@ -223,7 +223,7 @@ class AutoFineTuneCommand(BaseCommand): "\tsaved_params_dir\n") print( - "The related infomation about hyperparamemters searched are saved as %s/log_file.txt ." + "The related information about hyperparamemters searched are saved as %s/log_file.txt ." % autoft._output_dir) for solution, modeldir in solutions_modeldirs.items(): param = evaluator.convert_params(solution) diff --git a/paddlehub/commands/serving.py b/paddlehub/commands/serving.py index 2c221e01bc18280dc023e57fa3373804c23288a0..836414c782517c3d5fd38fe98978450f60b4a5c0 100644 --- a/paddlehub/commands/serving.py +++ b/paddlehub/commands/serving.py @@ -31,6 +31,7 @@ import multiprocessing import time import signal + if platform.system() == "Windows": class StandaloneApplication(object): diff --git a/paddlehub/common/dir.py b/paddlehub/common/dir.py index 070c8002cb947dc75af1b2388b51fa5a95578133..2c7601df0373046909fe077b1ee143b154d446d3 100644 --- a/paddlehub/common/dir.py +++ b/paddlehub/common/dir.py @@ -43,7 +43,7 @@ THIRD_PARTY_HOME = os.path.join(gen_hub_home(), "thirdparty") TMP_HOME = os.path.join(gen_hub_home(), "tmp") if not os.path.exists(TMP_HOME): - os.mkdir(TMP_HOME) + os.makedirs(TMP_HOME) @contextlib.contextmanager diff --git a/paddlehub/dataset/dataset.py b/paddlehub/dataset/dataset.py index c7633cf7c2b1b03c67db193f40b34f11a6e0c54c..7d8713bf94687d54954ad1372aa37409e41135c4 100644 --- a/paddlehub/dataset/dataset.py +++ b/paddlehub/dataset/dataset.py @@ -128,7 +128,7 @@ class BaseDataset(object): def num_labels(self): return len(self.label_list) - # To compatibility with the usage of ImageClassificationDataset + # To be compatible with ImageClassificationDataset def label_dict(self): return {index: key for index, key in enumerate(self.label_list)} @@ -167,7 +167,7 @@ class BaseDataset(object): def _load_label_data(self): with open(os.path.join(self.base_path, self.label_file), "r") as file: - return file.read().split("\n") + return file.read().strip().split("\n") def __str__(self): return "Dataset: %s with %i train examples, %i dev examples and %i test examples" % ( diff --git a/paddlehub/finetune/task/base_task.py b/paddlehub/finetune/task/base_task.py index 4035705291a6fe2955e88396985309c58c4e85eb..51351555635c47bc68b808af85669ffdc0decfed 100644 --- a/paddlehub/finetune/task/base_task.py +++ b/paddlehub/finetune/task/base_task.py @@ -24,7 +24,11 @@ import copy import logging import inspect from functools import partial - +import six +if six.PY2: + from inspect import getargspec as get_args +else: + from inspect import getfullargspec as get_args import numpy as np import paddle.fluid as fluid from tb_paddle import SummaryWriter @@ -129,7 +133,7 @@ class TaskHooks(): "name: %s has existed in hook_type:%s, use modify method to modify it" % (name, hook_type)) else: - args_num = len(inspect.getfullargspec(func).args) + args_num = len(get_args(func).args) if args_num != self._hook_params_num[hook_type]: raise ValueError( "The number of parameters to the hook hook_type:%s should be %i" @@ -249,16 +253,11 @@ class BaseTask(object): self.exe = fluid.Executor(place=self.place) self.build_strategy = fluid.BuildStrategy() - # log item - if not os.path.exists(self.config.checkpoint_dir): - mkdir(self.config.checkpoint_dir) - tb_log_dir = os.path.join(self.config.checkpoint_dir, "visualization") - self.tb_writer = SummaryWriter(tb_log_dir) - # run environment self._phases = [] self._envs = {} self._predict_data = None + self._tb_writer = None # event hooks self._hooks = TaskHooks() @@ -559,6 +558,15 @@ class BaseTask(object): return [metric.name for metric in self.metrics] + [self.loss.name] return [output.name for output in self.outputs] + @property + def tb_writer(self): + if not os.path.exists(self.config.checkpoint_dir): + mkdir(self.config.checkpoint_dir) + tb_log_dir = os.path.join(self.config.checkpoint_dir, "visualization") + if not self._tb_writer: + self._tb_writer = SummaryWriter(tb_log_dir) + return self._tb_writer + def create_event_function(self, hook_type): def hook_function(self, *args): for name, func in self._hooks[hook_type].items(): diff --git a/paddlehub/finetune/task/reading_comprehension_task.py b/paddlehub/finetune/task/reading_comprehension_task.py index ccc590ee36906611d33b52c5a4b03cfcfba2c3b3..fe73fceb795064836f1a7a41f2493b3e5a06effd 100644 --- a/paddlehub/finetune/task/reading_comprehension_task.py +++ b/paddlehub/finetune/task/reading_comprehension_task.py @@ -26,6 +26,7 @@ import json from collections import OrderedDict +import io import numpy as np import paddle.fluid as fluid from .base_task import BaseTask @@ -517,13 +518,13 @@ class ReadingComprehensionTask(BaseTask): null_score_diff_threshold=self.null_score_diff_threshold, is_english=self.is_english) if self.phase == 'val' or self.phase == 'dev': - with open( + with io.open( self.data_reader.dataset.dev_path, 'r', encoding="utf8") as dataset_file: dataset_json = json.load(dataset_file) dataset = dataset_json['data'] elif self.phase == 'test': - with open( + with io.open( self.data_reader.dataset.test_path, 'r', encoding="utf8") as dataset_file: dataset_json = json.load(dataset_file) diff --git a/paddlehub/finetune/task/sequence_task.py b/paddlehub/finetune/task/sequence_task.py index c524468f63721efeb31fc90771a7bf4dca53dc34..05d0393c459810267e89f9cc44b7a43d4a35b239 100644 --- a/paddlehub/finetune/task/sequence_task.py +++ b/paddlehub/finetune/task/sequence_task.py @@ -232,6 +232,6 @@ class SequenceLabelTask(BaseTask): for length in seq_lens: seq_infers = batch_infers[current_id:current_id + length] seq_result = list(map(id2label.get, seq_infers[1:-1])) - current_id += self.max_seq_len + current_id += length if self.add_crf else self.max_seq_len results.append(seq_result) return results diff --git a/paddlehub/module/manager.py b/paddlehub/module/manager.py index 62cc7e17d669ae6b1dd5bbcc01c1d2c05bb327be..a65c53646f351453fe2d16f319a08e040f26aac3 100644 --- a/paddlehub/module/manager.py +++ b/paddlehub/module/manager.py @@ -23,13 +23,14 @@ import shutil from functools import cmp_to_key import tarfile +import paddlehub as hub from paddlehub.common import utils from paddlehub.common.downloader import default_downloader from paddlehub.common.dir import MODULE_HOME from paddlehub.common.cml_utils import TablePrinter -from paddlehub.module import module_desc_pb2 -import paddlehub as hub from paddlehub.common.logger import logger +from paddlehub.common import tmp_dir +from paddlehub.module import module_desc_pb2 class LocalModuleManager(object): @@ -87,12 +88,96 @@ class LocalModuleManager(object): extra=None): md5_value = installed_module_version = None from_user_dir = True if module_dir else False - if module_name: - self.all_modules(update=True) - module_info = self.modules_dict.get(module_name, None) - if module_info: - if not module_version or module_version == self.modules_dict[ - module_name][1]: + with tmp_dir() as _dir: + if module_name: + self.all_modules(update=True) + module_info = self.modules_dict.get(module_name, None) + if module_info: + if not module_version or module_version == self.modules_dict[ + module_name][1]: + module_dir = self.modules_dict[module_name][0] + module_tag = module_name if not module_version else '%s-%s' % ( + module_name, module_version) + tips = "Module %s already installed in %s" % ( + module_tag, module_dir) + return True, tips, self.modules_dict[module_name] + + search_result = hub.HubServer().get_module_url( + module_name, version=module_version, extra=extra) + name = search_result.get('name', None) + url = search_result.get('url', None) + md5_value = search_result.get('md5', None) + installed_module_version = search_result.get('version', None) + if not url or (module_version is not None + and installed_module_version != module_version + ) or (name != module_name): + if hub.HubServer()._server_check() is False: + tips = "Request Hub-Server unsuccessfully, please check your network." + return False, tips, None + module_versions_info = hub.HubServer().search_module_info( + module_name) + if module_versions_info is not None and len( + module_versions_info) > 0: + + if utils.is_windows(): + placeholders = [20, 8, 14, 14] + else: + placeholders = [30, 8, 16, 16] + tp = TablePrinter( + titles=[ + "ResourceName", "Version", "PaddlePaddle", + "PaddleHub" + ], + placeholders=placeholders) + module_versions_info.sort( + key=cmp_to_key(utils.sort_version_key)) + for resource_name, resource_version, paddle_version, \ + hub_version in module_versions_info: + colors = ["yellow", None, None, None] + + tp.add_line( + contents=[ + resource_name, resource_version, + utils.strflist_version(paddle_version), + utils.strflist_version(hub_version) + ], + colors=colors) + tips = "The version of PaddlePaddle or PaddleHub " \ + "can not match module, please upgrade your " \ + "PaddlePaddle or PaddleHub according to the form " \ + "below." + tp.get_text() + else: + tips = "Can't find module %s" % module_name + if module_version: + tips += " with version %s" % module_version + return False, tips, None + + result, tips, module_zip_file = default_downloader.download_file( + url=url, + save_path=_dir, + save_name=module_name, + replace=True, + print_progress=True) + result, tips, module_dir = default_downloader.uncompress( + file=module_zip_file, + dirname=MODULE_HOME, + delete_file=True, + print_progress=True) + + if module_package: + with tarfile.open(module_package, "r:gz") as tar: + file_names = tar.getnames() + size = len(file_names) - 1 + module_dir = os.path.join(_dir, file_names[0]) + for index, file_name in enumerate(file_names): + tar.extract(file_name, _dir) + + if module_dir: + if not module_name: + module_name = hub.Module(directory=module_dir).name + self.all_modules(update=False) + module_info = self.modules_dict.get(module_name, None) + if module_info: module_dir = self.modules_dict[module_name][0] module_tag = module_name if not module_version else '%s-%s' % ( module_name, module_version) @@ -100,114 +185,27 @@ class LocalModuleManager(object): module_dir) return True, tips, self.modules_dict[module_name] - search_result = hub.HubServer().get_module_url( - module_name, version=module_version, extra=extra) - name = search_result.get('name', None) - url = search_result.get('url', None) - md5_value = search_result.get('md5', None) - installed_module_version = search_result.get('version', None) - if not url or (module_version is not None - and installed_module_version != module_version) or ( - name != module_name): - if hub.HubServer()._server_check() is False: - tips = "Request Hub-Server unsuccessfully, please check your network." - return False, tips, None - module_versions_info = hub.HubServer().search_module_info( - module_name) - if module_versions_info is not None and len( - module_versions_info) > 0: - - if utils.is_windows(): - placeholders = [20, 8, 14, 14] - else: - placeholders = [30, 8, 16, 16] - tp = TablePrinter( - titles=[ - "ResourceName", "Version", "PaddlePaddle", - "PaddleHub" - ], - placeholders=placeholders) - module_versions_info.sort( - key=cmp_to_key(utils.sort_version_key)) - for resource_name, resource_version, paddle_version, \ - hub_version in module_versions_info: - colors = ["yellow", None, None, None] - - tp.add_line( - contents=[ - resource_name, resource_version, - utils.strflist_version(paddle_version), - utils.strflist_version(hub_version) - ], - colors=colors) - tips = "The version of PaddlePaddle or PaddleHub " \ - "can not match module, please upgrade your " \ - "PaddlePaddle or PaddleHub according to the form " \ - "below." + tp.get_text() + if module_dir: + if md5_value: + with open( + os.path.join(MODULE_HOME, module_dir, "md5.txt"), + "w") as fp: + fp.write(md5_value) + + save_path = os.path.join(MODULE_HOME, module_name) + if os.path.exists(save_path): + shutil.rmtree(save_path) + if from_user_dir: + shutil.copytree(module_dir, save_path) else: - tips = "Can't find module %s" % module_name - if module_version: - tips += " with version %s" % module_version - return False, tips, None - - result, tips, module_zip_file = default_downloader.download_file( - url=url, - save_path=hub.CACHE_HOME, - save_name=module_name, - replace=True, - print_progress=True) - result, tips, module_dir = default_downloader.uncompress( - file=module_zip_file, - dirname=MODULE_HOME, - delete_file=True, - print_progress=True) - - if module_package: - with tarfile.open(module_package, "r:gz") as tar: - file_names = tar.getnames() - size = len(file_names) - 1 - module_dir = os.path.split(file_names[0])[1] - module_dir = os.path.join(hub.CACHE_HOME, module_dir) - # remove cache - if os.path.exists(module_dir): - shutil.rmtree(module_dir) - for index, file_name in enumerate(file_names): - tar.extract(file_name, hub.CACHE_HOME) - - if module_dir: - if not module_name: - module_name = hub.Module(directory=module_dir).name - self.all_modules(update=False) - module_info = self.modules_dict.get(module_name, None) - if module_info: - module_dir = self.modules_dict[module_name][0] - module_tag = module_name if not module_version else '%s-%s' % ( - module_name, module_version) - tips = "Module %s already installed in %s" % (module_tag, - module_dir) - return True, tips, self.modules_dict[module_name] - - if module_dir: - if md5_value: - with open( - os.path.join(MODULE_HOME, module_dir, "md5.txt"), - "w") as fp: - fp.write(md5_value) - - save_path = os.path.join(MODULE_HOME, module_name) - if os.path.exists(save_path): - shutil.move(save_path) - if from_user_dir: - shutil.copytree(module_dir, save_path) - else: - shutil.move(module_dir, save_path) - module_dir = save_path - tips = "Successfully installed %s" % module_name - if installed_module_version: - tips += "-%s" % installed_module_version - return True, tips, (module_dir, installed_module_version) - tips = "Download %s-%s failed" % (module_name, module_version) - return False, tips, module_dir + shutil.move(module_dir, save_path) + module_dir = save_path + tips = "Successfully installed %s" % module_name + if installed_module_version: + tips += "-%s" % installed_module_version + return True, tips, (module_dir, installed_module_version) + tips = "Download %s-%s failed" % (module_name, module_version) + return False, tips, module_dir def uninstall_module(self, module_name, module_version=None): self.all_modules(update=True) diff --git a/paddlehub/module/module.py b/paddlehub/module/module.py index 41651c2bb013688a6d9c0955945a91b3e688bc02..d0566e4a3c92f380c80884e1c6c050c2b65eb4e2 100644 --- a/paddlehub/module/module.py +++ b/paddlehub/module/module.py @@ -120,9 +120,10 @@ def runable(func): class Module(object): - def __new__(cls, name=None, directory=None, module_dir=None, version=None): - module = None + _record = {} + + def __new__(cls, name=None, directory=None, module_dir=None, version=None): if cls.__name__ == "Module": if name: module = cls.init_with_name(name=name, version=version) @@ -139,17 +140,18 @@ class Module(object): else: directory = module_dir module = cls.init_with_directory(directory=directory) - - if not module: - module = object.__new__(cls) - else: CacheUpdater("update_cache", module.name, module.version).start() + else: + module = object.__new__(cls) + return module def __init__(self, name=None, directory=None, module_dir=None, version=None): - if not directory: + # Avoid module being initialized multiple times + if not directory or id(self) in Module._record: return + Module._record[id(self)] = True mod = self.__class__.__module__ + "." + self.__class__.__name__ if mod in _module_runable_func: diff --git a/paddlehub/reader/tokenization.py b/paddlehub/reader/tokenization.py index ef49ed76fd82d0a0b58cfe2b3bc7122eb9e8acac..bde0ed43cd5140d2d926b5e43d53e0f55ed91205 100644 --- a/paddlehub/reader/tokenization.py +++ b/paddlehub/reader/tokenization.py @@ -170,7 +170,7 @@ class WSSPTokenizer(object): self.inv_vocab = {v: k for k, v in self.vocab.items()} self.ws = ws self.lower = lower - self.dict = pickle.load(open(word_dict, 'rb'), encoding='utf8') + self.dict = pickle.load(open(word_dict, 'rb')) self.sp_model = spm.SentencePieceProcessor() self.window_size = 5 self.sp_model.Load(sp_model_dir) diff --git a/paddlehub/serving/app_single.py b/paddlehub/serving/app_single.py index 24a44dab4f9687cca09b1f0e95eae125bae0e058..96fd56f6cd5fcf45a22239c690e309b74b87608f 100644 --- a/paddlehub/serving/app_single.py +++ b/paddlehub/serving/app_single.py @@ -133,7 +133,7 @@ def predict_gan(module, input_img, id, batch_size, extra={}): def predict_object_detection(module, input_img, id, batch_size, extra={}): - output_folder = "output" + output_folder = "detection_result" global use_gpu method_name = module.desc.attr.map.data['default_signature'].s predict_method = getattr(module, method_name) diff --git a/paddlehub/serving/templates/main.html b/paddlehub/serving/templates/main.html index 56208ea1ab83f14005ef09e613c9ad357f9e57b0..d272aae7f642fa359deddddf298ab2f1a57b3d76 100644 --- a/paddlehub/serving/templates/main.html +++ b/paddlehub/serving/templates/main.html @@ -115,7 +115,7 @@ + 'id="file_text"' + 'onblur="blur_input_text()"' + 'onfocus="focus_input_text()"' - + 'name="input_text">' + + 'name="text">' + '在此键入文本或上传文本文件' + '' + '' @@ -237,14 +237,15 @@ alert("Connection error:"+request.error); }, success: function(data) { - html = get_result_html(data["result"]); + console.log(data); + html = get_result_html(data["results"]); document.getElementById("result_text").value = html; } }); } function sub_img() { var formParam = { - "input_img": document.getElementById("file_img").src, + "image": document.getElementById("file_img").src, "input_file":document.getElementById("file").value }; to_url = "/predict/image/" + document.getElementById("inputGroupSelect02").value; @@ -258,9 +259,13 @@ alert("Connection error:"+request.error); }, success: function(data) { - data = data["result"]; - document.getElementById("result_text").value = data["desc"]; - document.getElementById("result_img").src = data["output_img"]; + data = data.results; + data = data.replace(/'/g, '"'); + data = JSON.parse(data); + data = data[0]; + + document.getElementById("result_text").value = JSON.stringify(data.data[0]); + document.getElementById("result_img").src = data.base64; } }); } diff --git a/paddlehub/serving/templates/serving_config.json b/paddlehub/serving/templates/serving_config.json index 1d4196dd7ee2cebec1b0d1daf96b14be1454a435..cd761decf67696a052f86e2a7ba2359ef6264fe5 100644 --- a/paddlehub/serving/templates/serving_config.json +++ b/paddlehub/serving/templates/serving_config.json @@ -3,30 +3,22 @@ { "module": "lac", "version": "1.0.0", - "batch_size": 200, - "queue_size": 200, - "category": "NLP" + "batch_size": 200 }, { "module": "senta_lstm", "version": "1.0.0", - "batch_size": 1, - "queue_size": 200, - "category": "NLP" + "batch_size": 1 }, { "module": "yolov3_darknet53_coco2017", "version": "1.0.0", - "batch_size": 1, - "queue_size": 10, - "category": "CV" + "batch_size": 1 }, { "module": "faster_rcnn_coco2017", "version": "1.0.0", - "batch_size": 1, - "queue_size": 10, - "category": "CV" + "batch_size": 1 } ], "use_gpu": false, diff --git a/requirements.txt b/requirements.txt index a43890071f74995fcd8db4b50f10aca67c6fe963..1bde5c3a9d8d88ce3eae1420aec7bb49f19b1369 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,4 +25,7 @@ pandas ; python_version >= "3" pandas < 0.25.0 ; python_version < "3" # gunicorn not support windows -gunicorn >= 19.10.0; sys_platform == "darwin" or sys_platform == "linux" +gunicorn >= 19.10.0; sys_platform != "win32" + +# moviepy 1.0.1 not support imageio>2.5 +moviepy < 1.0.0 ; python_version < "3" diff --git a/tutorial/bert_service.md b/tutorial/bert_service.md new file mode 100644 index 0000000000000000000000000000000000000000..d841cf45aaaab57426d88d85dc9b5f37e8be0a36 --- /dev/null +++ b/tutorial/bert_service.md @@ -0,0 +1,247 @@ +# Bert Service +## 简介 +### 什么是Bert Service +`Bert Service`是基于[Paddle Serving](https://github.com/PaddlePaddle/Serving)框架的快速部署模型远程计算服务方案,可将embedding过程通过调用API接口的方式实现,减少了对机器资源的依赖。使用PaddleHub可在服务器上一键部署`Bert Service`服务,在另外的普通机器上通过客户端接口即可轻松的获取文本对应的embedding数据。 + +整体流程图如下: + +
+ +BS流程图 + +
+ +### 为什么使用Bert Service +* 算力有限的集群环境中,可利用一台或几台高性能机器部署`Bert Service`服务端,为全部机器提供在线embedding功能。 + +* 实际的生产服务器不适宜承担大批量embedding工作,通过API接口可减少资源占用。 + +* 专注下游深度学习任务,可利用PaddleHub的`Bert Service`大幅减少embedding代码。 + +`Bert Service`具有几个突出的优点: + +* 代码精短,易于使用。简单的pip安装方式,服务端仅需一行命令即可启动,客户端仅需一行代码即可获取embedding结果。 + +* 更高性能,更高效率。通过Paddle AnalysisPredictor API对模型的计算图进行优化,提升了计算速度并减小了显存占用。 + +* 随"机"应变,灵活扩展。可根据机器资源选择不同数量的服务端,并根据实际需求快速、灵活地进行增减,同时支持各张显卡执行不同的模型计算任务。 + +* 删繁就简,专注任务。`Bert Service`基于PaddlePaddle和PaddleHub开发,将模型的下载和安装等管理工作交由PaddleHub,开发者可以专注于主要任务,还可以无缝对接PaddleHub继续进行文本分类、序列标注等下游任务。 + +使用Bert Service搭建服务主要分为下面三个步骤: + +## Step1:准备环境 +### 环境要求 +下表是使用`Bert Service`的环境要求,带有*号标志项为非必需依赖,可根据实际使用需求选择安装。 + +|项目|版本|说明| +|:-:|:-:|:-:| +|操作系统|Linux|目前仅支持Linux操作系统| +|PaddleHub|>=1.4.0|无| +|PaddlePaddle|>=1.6.1|若使用GPU计算,则对应使用PaddlePaddle-gpu版本| +|GCC|>=4.8|无| +|CUDA*|>=8|若使用GPU,需使用CUDA8以上版本| +|paddle-gpu-serving*|>=0.8.2|在`Bert Service`服务端需依赖此包| +|ujson*|>=1.35|在`Bert Service`客户端需依赖此包| + +### 安装步骤 +a) 安装PaddlePaddle,利用pip下载CPU版本命令如下。GPU版本、Docker方式安装等其他更具体的安装过程见[开始使用PaddlePaddle](https://paddlepaddle.org.cn/install/quick) +```shell +$ # 安装paddlepaddle的CPU版本 +$ pip install paddlepaddle +``` +b) 安装PaddleHub +```shell +$ pip install paddlehub +``` +c) server端,需另外安装`paddle-gpu-serving`,以获取快速部署服务的能力 +```shell +$ pip install paddle-gpu-serving +``` +d) client端,需另外安装ujson +```shell +$ pip install ujson +``` + + +### 支持模型 +目前`Bert Service`支持的语义模型如下表,可根据需要选择模型进行部署embedding服务,未来还将支持更多模型。 + +|模型|网络| +|:-|:-:| +|[ernie](https://paddlepaddle.org.cn/hubdetail?name=ERNIE&en_category=SemanticModel)|ERNIE| +|[ernie_tiny](https://paddlepaddle.org.cn/hubdetail?name=ernie_tiny&en_category=SemanticModel)|ERNIE| +|[ernie_v2_eng_large](https://paddlepaddle.org.cn/hubdetail?name=ernie_v2_eng_large&en_category=SemanticModel)|ERNIE| +|[ernie_v2_eng_base](https://paddlepaddle.org.cn/hubdetail?name=ernie_v2_eng_base&en_category=SemanticModel)|ERNIE| +|[roberta_wwm_ext_chinese_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=roberta_wwm_ext_chinese_L-12_H-768_A-12&en_category=SemanticModel)|BERT| +|[roberta_wwm_ext_chinese_L-24_H-1024_A-16](https://paddlepaddle.org.cn/hubdetail?name=roberta_wwm_ext_chinese_L-24_H-1024_A-16&en_category=SemanticModel)|BERT| +|[bert_wwm_ext_chinese_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=bert_wwm_ext_chinese_L-12_H-768_A-12&en_category=SemanticModel)|BERT| +|[bert_uncased_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=bert_uncased_L-12_H-768_A-12&en_category=SemanticModel)|BERT| +|[bert_uncased_L-24_H-1024_A-16](https://paddlepaddle.org.cn/hubdetail?name=bert_uncased_L-24_H-1024_A-16&en_category=SemanticModel)|BERT| +|[bert_cased_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=bert_cased_L-12_H-768_A-12&en_category=SemanticModel)|BERT| +|[bert_cased_L-24_H-1024_A-16](https://paddlepaddle.org.cn/hubdetail?name=bert_cased_L-24_H-1024_A-16&en_category=SemanticModel)|BERT| +|[bert_multi_cased_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=bert_multi_cased_L-12_H-768_A-12&en_category=SemanticModel)|BERT| +|[bert_chinese_L-12_H-768_A-12](https://paddlepaddle.org.cn/hubdetail?name=bert_chinese_L-12_H-768_A-12&en_category=SemanticModel)|BERT| + + +## Step2:启动服务端(server) +### 简介 +server端接收client端发送的数据,执行模型计算过程并将计算结果返回给client端。 + +server端启动时会按照指定的模型名称从PaddleHub获取对应的模型文件进行加载,无需提前下载模型或指定模型路径,对模型的管理工作由PaddleHub负责。在加载模型后在指定的端口启动`BRPC`服务,保持端口监听,当接收到数据后便执行模型计算,并将计算结果通过`BRPC`返回并发送至client端。 + +### 启动 +使用PaddleHub的命令行工具可一键启动`Bert Service`,命令如下: +```shell +$ hub serving start bert_service -m ernie_tiny -p 8866 --use_gpu --gpu 0 +``` +启动成功则显示 +```shell +Server[baidu::paddle_serving::predictor::bert_service::BertServiceImpl] is serving on port=8866. +``` +整个启动过程如下图: + + +
+ +  启动BS + +
+ + +其中各参数说明如下表: + +
+ +|参数|说明|是否必填| +|:--:|:--:|:----:| +|hub serving start bert_service|启动`Bert Service`服务端。|必填项| +|--module/-m|指定启动的模型,如果指定的模型不存在,则自动通过PaddleHub下载指定模型。|必填项| +|--port/-p|指定启动的端口,每个端口对应一个模型,可基于不同端口进行多次启动,以实现多个模型的服务部署。|必填项| +|--use_gpu|若指定此项则使用GPU进行工作,反之仅使用CPU。注意需安装GPU版本的PaddlePaddle。|非必填项,默认为不指定| +|--gpu|指定使用的GPU卡号,如未指定use_gpu则填写此项无效,每个服务对应一张卡,部署多个服务时需指定不同卡|非必填项,默认为0号显卡| + +
+ +### 关闭 +通过在启动服务端的命令行页面使用Ctrl+C终止`Bert Service`运行,关闭成功则显示: +```shell +Paddle Inference Server exit successfully! +``` + + +## Step3:启动客户端(client) +### 简介 +client端接收文本数据,并获取server端返回的模型计算的embedding结果。 + +client端利用PaddleHub的语义理解任务将原始文本按照不同模型的数据预处理方案将文本ID化,并生成对应的sentence type、position、input masks数据,将这些信息封装成json数据,通过http协议按照指定的IP端口信息发送至server端,等待并获取模型生成结果。 +### 启动 +服务端类BSClient初始化方法原型为: +```python +BSClient.__init__(self, + module_name, + server, + max_seq_len=20, + show_ids=False, + do_lower_case=True, + retry=3) +# 获取embedding方法原型为 +BSClient.get_result(self, input_text) +``` +其中各参数说明如下表: + +|参数|说明|类型|样例| +|:--:|:--:|:--:|:--:| +|module_name|指定使用的模型名称|string|"ernie"| +|server|要访问的server地址,包括ip地址及端口号|string|"127.0.0.1:8866"| +|max_seq_len|计算时的样例长度,样例长度不足时采用补零策略,超出此参数则超出部分会被截断|int|128| +|show_ids|是否展现数据预处理后的样例信息,指定为True则显示样例信息,反之则不显示|bool|False| +|do_lower_case|是否将英文字母转换成小写,指定为True则将所有英文字母转换为小写,反之则保持原状|bool|True| +|retry|连接失败后的最大重试次数|int|3| +|input_text|输入文本,要获取embedding的原始文本|二维list类型,内部元素为string类型的文本|[['样例1'],['样例2']]| + +## Demo——利用Bert Service部署ernie_tiny在线embedding服务 +在这里,我们将展示一个实际场景中可能使用的demo,我们利用PaddleHub在一台GPU机器上部署`ernie_tiny`模型服务,并在另一台CPU机器上尝试访问,获取一首七言绝句的embedding。 +### Step1:安装环境依赖 +首先需要安装环境依赖,根据第2节内容分别在两台机器上安装相应依赖。 + +### Step2:启动Bert Service服务端 +确保环境依赖安装正确后,在要部署服务的GPU机器上使用PaddleHub命令行工具启动`Bert Service`服务端,命令如下: +```shell +$ hub serving start bert_service -m ernie_tiny --use_gpu --gpu 0 --port 8866 +``` +启动成功后打印 +```shell +Server[baidu::paddle_serving::predictor::bert_service::BertServiceImpl] is serving on port=8866. +``` +这样就启动了`ernie_tiny`的在线服务,监听8866端口,并在0号GPU上进行任务。 +### Step3:使用Bert Service客户端进行远程调用 +部署好服务端后,就可以用普通机器作为客户端测试在线embedding功能。 + +首先导入客户端依赖。 +```python +from paddlehub.serving.bert_serving import bs_client +``` + +接着启动并初始化`bert service`客户端`BSClient`(这里的server为虚拟地址,需根据自己实际ip设置) +```python +bc = bs_client.BSClient(module_name="ernie_tiny", server="127.0.0.1:8866") +``` + +然后输入文本信息。 +```python +input_text = [["西风吹老洞庭波"], ["一夜湘君白发多"], ["醉后不知天在水"], ["满船清梦压星河"], ] +``` + +最后利用客户端接口`get_result`发送文本到服务端,以获取embedding结果。 +```python +result = bc.get_result(input_text=input_text) +``` +这样,就得到了embedding结果(此处只展示部分结果)。 +```python +[[0.9993321895599361, 0.9994612336158751, 0.9999646544456481, 0.732795298099517, -0.34387934207916204, ... ]] +``` +客户端代码demo文件见[示例](../paddlehub/serving/bert_serving/bert_service.py)。 +运行命令如下: +```shell +$ python bert_service_client.py +``` + +运行过程如下图: + +
+ +  启动BS + +
+ +### Step4:关闭Bert Service服务端 +如要停止`Bert Service`服务端程序,可在其启动命令行页面使用Ctrl+C方式关闭,关闭成功会打印如下日志: +```shell +Paddle Inference Server exit successfully! +``` +这样,我们就利用一台GPU机器就完成了`Bert Service`的部署,并利用另一台普通机器进行了测试,可见通过`Bert Service`能够方便地进行在线embedding服务的快速部署。 + +## 预训练模型一键服务部署 +除了`Bert Service`外,PaddleHub Serving还具有预训练模型一键服务部署功能,能够将预训练模型快捷部署上线,对外提供可靠的在线预测服务,具体信息请参见[Module Serving](./serving.md)。 + +## FAQ +Q : 如何在一台服务器部署多个模型? +A : 可通过多次启动`Bert Service`,分配不同端口实现。如果使用GPU,需要指定不同的显卡。如同时部署`ernie`和`bert_chinese_L-12_H-768_A-12`,分别执行命令如下: +```shell +$ hub serving start bert_service -m ernie -p 8866 +$ hub serving start bert_service -m bert_chinese_L-12_H-768_A-12 -p 8867 +``` + +Q : 启动时显示"Check out http://yq01-gpu-255-129-12-00.epc.baidu.com:8887 in web + browser.",这个页面有什么作用。 +A : 这是`BRPC`的内置服务,主要用于查看请求数、资源占用等信息,可对server端性能有大致了解,具体信息可查看[BRPC内置服务](https://github.com/apache/incubator-brpc/blob/master/docs/cn/builtin_service.md)。 + +Q : 为什么输入文本的格式为[["文本1"], ["文本2"], ],而不是["文本1", "文本2", ]? +A : 因为Bert模型可以对一轮对话生成向量表示,例如[["问题1","回答1"],["问题2","回答2"]],为了防止使用时混乱,每个样本使用一个list表示,一个样本list内部可以是1条string或2条string,如下面的文本: +```python +input_text = [ + ["你今天吃饭了吗","我已经吃过饭了"], + ["今天天气怎么样","今天天气不错"], +] +``` diff --git a/tutorial/serving.md b/tutorial/serving.md new file mode 100644 index 0000000000000000000000000000000000000000..a3ff4d00bcbe26b120caf48d6979bf8927df8384 --- /dev/null +++ b/tutorial/serving.md @@ -0,0 +1,222 @@ +# PaddleHub Serving模型一键服务部署 +## 简介 +### 为什么使用一键服务部署 +使用PaddleHub能够快速进行模型预测,但开发者常面临本地预测过程迁移线上的需求。无论是对外开放服务端口,还是在局域网中搭建预测服务,都需要PaddleHub具有快速部署模型预测服务的能力。在这个背景下,模型一键服务部署工具——PaddleHub Serving应运而生。开发者通过一行命令即可快速启动一个模型预测在线服务,而无需关注网络框架选择和实现。 +### 什么是一键服务部署 +PaddleHub Serving是基于PaddleHub的一键模型服务部署工具,能够通过简单的Hub命令行工具轻松启动一个模型预测在线服务,前端通过Flask和Gunicorn完成网络请求的处理,后端直接调用PaddleHub预测接口,同时支持使用多进程方式利用多核提高并发能力,保证预测服务的性能。 + +### 支持模型 +目前PaddleHub Serving支持对PaddleHub所有可直接预测的模型进行服务部署,包括`lac`、`senta_bilstm`等NLP类模型,以及`yolov3_darknet53_coco2017`、`vgg16_imagenet`等CV类模型,更多模型请参见[PaddleHub支持模型列表](https://paddlepaddle.org.cn/hublist)。未来还将支持开发者使用PaddleHub Fine-tune API得到的模型用于快捷服务部署。 + +### 所需环境 +下表是使用PaddleHub Serving的环境要求及注意事项。 + +|项目|建议版本|说明| +|:-:|:-:|:-:| +|操作系统|Linux/Darwin/Windows|建议使用Linux或Darwin,对多线程启动方式支持性较好| +|PaddleHub|>=1.4.0|无| +|PaddlePaddle|>=1.6.1|若使用GPU计算,则对应使用PaddlePaddle-gpu版本| + +## 使用 +### Step1:启动服务端部署 +PaddleHub Serving有两种启动方式,分别是使用命令行启动,以及使用配置文件启动。 + +#### 命令行命令启动 +启动命令 +```shell +$ hub serving start --modules [Module1==Version1, Module2==Version2, ...] \ + --port XXXX \ + --use_gpu \ + --use_multiprocess +``` + +**参数**: + +|参数|用途| +|-|-| +|--modules/-m|PaddleHub Serving预安装模型,以多个Module==Version键值对的形式列出
*`当不指定Version时,默认选择最新版本`*| +|--port/-p|服务端口,默认为8866| +|--use_gpu|使用GPU进行预测,必须安装paddlepaddle-gpu| +|--use_multiprocess|是否启用并发方式,默认为单进程方式| + +#### 配置文件启动 +启动命令 +```shell +$ hub serving start --config config.json +``` +`config.json`格式如下: + +```json +{ + "modules_info": [ + { + "module": "MODULE_NAME_1", + "version": "MODULE_VERSION_1", + "batch_size": "BATCH_SIZE_1" + }, + { + "module": "MODULE_NAME_2", + "version": "MODULE_VERSION_2", + "batch_size": "BATCH_SIZE_2" + } + ], + "use_gpu": false, + "port": 8866, + "use_multiprocess": false +} +``` + +**参数**: + +|参数|用途| +|-|-| +|--modules_info|PaddleHub Serving预安装模型,以字典列表形式列出,其中:
`module`为预测服务使用的模型名
`version`为预测模型的版本
`batch_size`为预测批次大小 +|--use_gpu|使用GPU进行预测,必须安装paddlepaddle-gpu| +|--port/-p|服务端口,默认为8866| +|--use_multiprocess|是否启用并发方式,默认为单进程方式,推荐多核CPU机器使用此方式| + +### Step2:访问服务端 + +在使用PaddleHub Serving部署服务端的模型预测服务后,就可以在客户端访问预测接口以获取结果了,接口url格式为: + +http://0.0.0.0:8866/predict//\ + +其中,\为text或image,与模型种类对应,\为模型名。 + +通过发送一个POST请求,即可获取预测结果,下面我们将展示一个具体的demo,以说明使用PaddleHub Serving部署和使用流程。 + +### Step3:利用PaddleHub Serving进行个性化开发 +使用PaddleHub Serving进行模型服务部署后,可以利用得到的接口进行开发,如对外提供web服务,或接入到应用程序中,以降低客户端预测压力,提高性能,下面展示了一个web页面demo: + +

+ + + +

+ +## Demo——部署一个在线lac分词服务 + +### Step1:部署lac在线服务 +现在,我们要部署一个lac在线服务,以通过接口获取文本的分词结果。 + +首先,任意选择一种启动方式,两种方式分别为: +```shell +$ hub serving start -m lac +``` +或 +```shell +$ hub serving start -c serving_config.json +``` +其中`serving_config.json`的内容如下: +```json +{ + "modules_info": [ + { + "module": "lac", + "version": "1.0.0", + "batch_size": 1 + } +], + "use_gpu": false, + "port": 8866, + "use_multiprocess": false +} +``` +启动成功界面如图: + +

+ + + +

+ +这样我们就在8866端口部署了lac的在线分词服务。 +*此处warning为Flask提示,不影响使用* + +### Step2:访问lac预测接口 + +在服务部署好之后,我们可以进行测试,用来测试的文本为`今天是个好日子`和`天气预报说今天要下雨`。 + +客户端代码如下 +```python +# coding: utf8 +import requests +import json + +if __name__ == "__main__": + # 指定用于用于预测的文本并生成字典{"text": [text_1, text_2, ... ]} + text_list = ["今天是个好日子", "天气预报说今天要下雨"] + text = {"text": text_list} + # 指定预测方法为lac并发送post请求 + url = "http://0.0.0.0:8866/predict/text/lac" + r = requests.post(url=url, data=text) + + # 打印预测结果 + print(json.dumps(r.json(), indent=4, ensure_ascii=False)) +``` +运行后得到结果 + + +```python +{ + "results": [ + { + "tag": [ + "TIME", "v", "q", "n" + ], + "word": [ + "今天", "是", "个", "好日子" + ] + }, + { + "tag": [ + "n", "v", "TIME", "v", "v" + ], + "word": [ + "天气预报", "说", "今天", "要", "下雨" + ] + } + ] +} +``` + +此Demo的具体信息和代码请参见[LAC Serving](../demo/serving/module_serving/lexical_analysis_lac)。另外,下面展示了一些其他的一键服务部署Demo。 + +## Demo——其他模型的一键部署服务 + +获取其他PaddleHub Serving的一键服务部署场景示例,可参见下列demo + +* [图像分类-基于vgg11_imagent](../demo/serving/module_serving/classification_vgg11_imagenet) + +  该示例展示了利用vgg11_imagent完成图像分类服务化部署和在线预测,获取图像分类结果。 + +* [图像生成-基于stgan_celeba](../demo/serving/module_serving/GAN_stgan_celeba) + +  该示例展示了利用stgan_celeba生成图像服务化部署和在线预测,获取指定风格的生成图像。 + +* [文本审核-基于porn_detection_lstm](../demo/serving/module_serving/text_censorship_porn_detection_lstm) + +  该示例展示了利用porn_detection_lstm完成中文文本黄色敏感信息鉴定的服务化部署和在线预测,获取文本是否敏感及其置信度。 + +* [中文词法分析-基于lac](../demo/serving/module_serving/lexical_analysis_lac) + +  该示例展示了利用lac完成中文文本分词服务化部署和在线预测,获取文本的分词结果,并可通过用户自定义词典干预分词结果。 + +* [目标检测-基于yolov3_darknet53_coco2017](.../demo/serving/serving/object_detection_yolov3_darknet53_coco2017) + +  该示例展示了利用yolov3_darknet53_coco2017完成目标检测服务化部署和在线预测,获取检测结果和覆盖识别框的图片。 + +* [中文语义分析-基于simnet_bow](../demo/serving/module_serving/semantic_model_simnet_bow) + +  该示例展示了利用simnet_bow完成中文文本相似度检测服务化部署和在线预测,获取文本的相似程度。 + +* [图像分割-基于deeplabv3p_xception65_humanseg](../demo/serving/module_serving/semantic_segmentation_deeplabv3p_xception65_humanseg) + +  该示例展示了利用deeplabv3p_xception65_humanseg完成图像分割服务化部署和在线预测,获取识别结果和分割后的图像。 + +* [中文情感分析-基于simnet_bow](../demo/serving/module_serving/semantic_model_simnet_bow) + +  该示例展示了利用senta_lstm完成中文文本情感分析服务化部署和在线预测,获取文本的情感分析结果。 + +## Bert Service +除了预训练模型一键服务部署功能之外,PaddleHub Serving还具有`Bert Service`功能,支持ernie_tiny、bert等模型快速部署,对外提供可靠的在线embedding服务,具体信息请参见[Bert Service](./bert_service.md)。