提交 761805a1 编写于 作者: T tink2123

add qps

上级 24cca54f
...@@ -70,38 +70,38 @@ When using PaddleServing for service deployment, you need to convert the saved i ...@@ -70,38 +70,38 @@ When using PaddleServing for service deployment, you need to convert the saved i
Firstly, download the [inference model](https://github.com/PaddlePaddle/PaddleOCR#pp-ocr-20-series-model-listupdate-on-dec-15) of PPOCR Firstly, download the [inference model](https://github.com/PaddlePaddle/PaddleOCR#pp-ocr-20-series-model-listupdate-on-dec-15) of PPOCR
``` ```
# Download and unzip the OCR text detection model # Download and unzip the OCR text detection model
wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_server_v2.0_det_infer.tar && tar xf ch_ppocr_server_v2.0_det_infer.tar wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_det_infer.tar && tar xf ch_ppocr_mobile_v2.0_det_infer.tar
# Download and unzip the OCR text recognition model # Download and unzip the OCR text recognition model
wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_server_v2.0_rec_infer.tar && tar xf ch_ppocr_server_v2.0_rec_infer.tar wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_rec_infer.tar && tar xf ch_ppocr_mobile_v2.0_rec_infer.tar
``` ```
Then, you can use installed paddle_serving_client tool to convert inference model to server model. Then, you can use installed paddle_serving_client tool to convert inference model to mobile model.
``` ```
# Detection model conversion # Detection model conversion
python3 -m paddle_serving_client.convert --dirname ./ch_ppocr_server_v2.0_det_infer/ \ python3 -m paddle_serving_client.convert --dirname ./ch_ppocr_mobile_v2.0_det_infer/ \
--model_filename inference.pdmodel \ --model_filename inference.pdmodel \
--params_filename inference.pdiparams \ --params_filename inference.pdiparams \
--serving_server ./ppocr_det_server_2.0_serving/ \ --serving_server ./ppocr_det_mobile_2.0_serving/ \
--serving_client ./ppocr_det_server_2.0_client/ --serving_client ./ppocr_det_mobile_2.0_client/
# Recognition model conversion # Recognition model conversion
python3 -m paddle_serving_client.convert --dirname ./ch_ppocr_server_v2.0_rec_infer/ \ python3 -m paddle_serving_client.convert --dirname ./ch_ppocr_mobile_v2.0_rec_infer/ \
--model_filename inference.pdmodel \ --model_filename inference.pdmodel \
--params_filename inference.pdiparams \ --params_filename inference.pdiparams \
--serving_server ./ppocr_rec_server_2.0_serving/ \ --serving_server ./ppocr_rec_mobile_2.0_serving/ \
--serving_client ./ppocr_rec_server_2.0_client/ --serving_client ./ppocr_rec_mobile_2.0_client/
``` ```
After the detection model is converted, there will be additional folders of `ppocr_det_server_2.0_serving` and `ppocr_det_server_2.0_client` in the current folder, with the following format: After the detection model is converted, there will be additional folders of `ppocr_det_mobile_2.0_serving` and `ppocr_det_mobile_2.0_client` in the current folder, with the following format:
``` ```
|- ppocr_det_server_2.0_serving/ |- ppocr_det_mobile_2.0_serving/
|- __model__ |- __model__
|- __params__ |- __params__
|- serving_server_conf.prototxt |- serving_server_conf.prototxt
|- serving_server_conf.stream.prototxt |- serving_server_conf.stream.prototxt
|- ppocr_det_server_2.0_client |- ppocr_det_mobile_2.0_client
|- serving_client_conf.prototxt |- serving_client_conf.prototxt
|- serving_client_conf.stream.prototxt |- serving_client_conf.stream.prototxt
...@@ -143,6 +143,58 @@ The recognition model is the same. ...@@ -143,6 +143,58 @@ The recognition model is the same.
After successfully running, the predicted result of the model will be printed in the cmd window. An example of the result is: After successfully running, the predicted result of the model will be printed in the cmd window. An example of the result is:
![](./imgs/results.png) ![](./imgs/results.png)
Adjust the number of concurrency in config.yml to get the largest QPS. Generally, the number of concurrent detection and recognition is 2:1
```
det:
concurrency: 8
...
rec:
concurrency: 4
...
```
Multiple service requests can be sent at the same time if necessary.
The predicted performance data will be automatically written into the `PipelineServingLogs/pipeline.tracer` file:
```
2021-05-12 10:03:24,812 ==================== TRACER ======================
2021-05-12 10:03:24,904 Op(rec):
2021-05-12 10:03:24,904 in[51.5634921875 ms]
2021-05-12 10:03:24,904 prep[215.310859375 ms]
2021-05-12 10:03:24,904 midp[33.1617109375 ms]
2021-05-12 10:03:24,905 postp[10.451234375 ms]
2021-05-12 10:03:24,905 out[9.736765625 ms]
2021-05-12 10:03:24,905 idle[0.1914292677880819]
2021-05-12 10:03:24,905 Op(det):
2021-05-12 10:03:24,905 in[218.63487096774193 ms]
2021-05-12 10:03:24,906 prep[357.35925 ms]
2021-05-12 10:03:24,906 midp[31.47598387096774 ms]
2021-05-12 10:03:24,906 postp[15.274870967741936 ms]
2021-05-12 10:03:24,906 out[16.245693548387095 ms]
2021-05-12 10:03:24,906 idle[0.3675805857279226]
2021-05-12 10:03:24,906 DAGExecutor:
2021-05-12 10:03:24,906 Query count[128]
2021-05-12 10:03:24,906 QPS[12.8 q/s]
2021-05-12 10:03:24,906 Succ[1.0]
2021-05-12 10:03:24,907 Error req[]
2021-05-12 10:03:24,907 Latency:
2021-05-12 10:03:24,907 ave[798.6557734374998 ms]
2021-05-12 10:03:24,907 .50[867.936 ms]
2021-05-12 10:03:24,907 .60[914.507 ms]
2021-05-12 10:03:24,907 .70[961.064 ms]
2021-05-12 10:03:24,907 .80[1043.264 ms]
2021-05-12 10:03:24,907 .90[1117.923 ms]
2021-05-12 10:03:24,907 .95[1207.056 ms]
2021-05-12 10:03:24,908 .99[1325.008 ms]
2021-05-12 10:03:24,908 Channel (server worker num[10]):
2021-05-12 10:03:24,909 chl0(In: ['@DAGExecutor'], Out: ['det']) size[0/0]
2021-05-12 10:03:24,909 chl1(In: ['det'], Out: ['rec']) size[1/0]
2021-05-12 10:03:24,910 chl2(In: ['rec'], Out: ['@DAGExecutor']) size[0/0]
```
<a name="faq"></a> <a name="faq"></a>
## FAQ ## FAQ
**Q1**: No result return after sending the request. **Q1**: No result return after sending the request.
......
...@@ -68,38 +68,38 @@ PaddleOCR提供2种服务部署方式: ...@@ -68,38 +68,38 @@ PaddleOCR提供2种服务部署方式:
首先,下载PPOCR的[inference模型](https://github.com/PaddlePaddle/PaddleOCR#pp-ocr-20-series-model-listupdate-on-dec-15) 首先,下载PPOCR的[inference模型](https://github.com/PaddlePaddle/PaddleOCR#pp-ocr-20-series-model-listupdate-on-dec-15)
``` ```
# 下载并解压 OCR 文本检测模型 # 下载并解压 OCR 文本检测模型
wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_server_v2.0_det_infer.tar && tar xf ch_ppocr_server_v2.0_det_infer.tar wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_det_infer.tar && tar xf ch_ppocr_mobile_v2.0_det_infer.tar
# 下载并解压 OCR 文本识别模型 # 下载并解压 OCR 文本识别模型
wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_server_v2.0_rec_infer.tar && tar xf ch_ppocr_server_v2.0_rec_infer.tar wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_rec_infer.tar && tar xf ch_ppocr_mobile_v2.0_rec_infer.tar
``` ```
接下来,用安装的paddle_serving_client把下载的inference模型转换成易于server部署的模型格式。 接下来,用安装的paddle_serving_client把下载的inference模型转换成易于server部署的模型格式。
``` ```
# 转换检测模型 # 转换检测模型
python3 -m paddle_serving_client.convert --dirname ./ch_ppocr_server_v2.0_det_infer/ \ python3 -m paddle_serving_client.convert --dirname ./ch_ppocr_mobile_v2.0_det_infer/ \
--model_filename inference.pdmodel \ --model_filename inference.pdmodel \
--params_filename inference.pdiparams \ --params_filename inference.pdiparams \
--serving_server ./ppocr_det_server_2.0_serving/ \ --serving_server ./ppocr_det_mobile_2.0_serving/ \
--serving_client ./ppocr_det_server_2.0_client/ --serving_client ./ppocr_det_mobile_2.0_client/
# 转换识别模型 # 转换识别模型
python3 -m paddle_serving_client.convert --dirname ./ch_ppocr_server_v2.0_rec_infer/ \ python3 -m paddle_serving_client.convert --dirname ./ch_ppocr_mobile_v2.0_rec_infer/ \
--model_filename inference.pdmodel \ --model_filename inference.pdmodel \
--params_filename inference.pdiparams \ --params_filename inference.pdiparams \
--serving_server ./ppocr_rec_server_2.0_serving/ \ --serving_server ./ppocr_rec_mobile_2.0_serving/ \
--serving_client ./ppocr_rec_server_2.0_client/ --serving_client ./ppocr_rec_mobile_2.0_client/
``` ```
检测模型转换完成后,会在当前文件夹多出`ppocr_det_server_2.0_serving``ppocr_det_server_2.0_client`的文件夹,具备如下格式: 检测模型转换完成后,会在当前文件夹多出`ppocr_det_mobile_2.0_serving``ppocr_det_mobile_2.0_client`的文件夹,具备如下格式:
``` ```
|- ppocr_det_server_2.0_serving/ |- ppocr_det_mobile_2.0_serving/
|- __model__ |- __model__
|- __params__ |- __params__
|- serving_server_conf.prototxt |- serving_server_conf.prototxt
|- serving_server_conf.stream.prototxt |- serving_server_conf.stream.prototxt
|- ppocr_det_server_2.0_client |- ppocr_det_mobile_2.0_client
|- serving_client_conf.prototxt |- serving_client_conf.prototxt
|- serving_client_conf.stream.prototxt |- serving_client_conf.stream.prototxt
...@@ -140,6 +140,56 @@ python3 -m paddle_serving_client.convert --dirname ./ch_ppocr_server_v2.0_rec_in ...@@ -140,6 +140,56 @@ python3 -m paddle_serving_client.convert --dirname ./ch_ppocr_server_v2.0_rec_in
成功运行后,模型预测的结果会打印在cmd窗口中,结果示例为: 成功运行后,模型预测的结果会打印在cmd窗口中,结果示例为:
![](./imgs/results.png) ![](./imgs/results.png)
调整 config.yml 中的并发个数获得最大的QPS, 一般检测和识别的并发数为2:1
```
det:
#并发数,is_thread_op=True时,为线程并发;否则为进程并发
concurrency: 8
...
rec:
#并发数,is_thread_op=True时,为线程并发;否则为进程并发
concurrency: 4
...
```
有需要的话可以同时发送多个服务请求
预测性能数据会被自动写入 `PipelineServingLogs/pipeline.tracer` 文件中:
```
2021-05-12 10:03:24,812 ==================== TRACER ======================
2021-05-12 10:03:24,904 Op(rec):
2021-05-12 10:03:24,904 in[51.5634921875 ms]
2021-05-12 10:03:24,904 prep[215.310859375 ms]
2021-05-12 10:03:24,904 midp[33.1617109375 ms]
2021-05-12 10:03:24,905 postp[10.451234375 ms]
2021-05-12 10:03:24,905 out[9.736765625 ms]
2021-05-12 10:03:24,905 idle[0.1914292677880819]
2021-05-12 10:03:24,905 Op(det):
2021-05-12 10:03:24,905 in[218.63487096774193 ms]
2021-05-12 10:03:24,906 prep[357.35925 ms]
2021-05-12 10:03:24,906 midp[31.47598387096774 ms]
2021-05-12 10:03:24,906 postp[15.274870967741936 ms]
2021-05-12 10:03:24,906 out[16.245693548387095 ms]
2021-05-12 10:03:24,906 idle[0.3675805857279226]
2021-05-12 10:03:24,906 DAGExecutor:
2021-05-12 10:03:24,906 Query count[128]
2021-05-12 10:03:24,906 QPS[12.8 q/s]
2021-05-12 10:03:24,906 Succ[1.0]
2021-05-12 10:03:24,907 Error req[]
2021-05-12 10:03:24,907 Latency:
2021-05-12 10:03:24,907 ave[798.6557734374998 ms]
2021-05-12 10:03:24,907 .50[867.936 ms]
2021-05-12 10:03:24,907 .60[914.507 ms]
2021-05-12 10:03:24,907 .70[961.064 ms]
2021-05-12 10:03:24,907 .80[1043.264 ms]
2021-05-12 10:03:24,907 .90[1117.923 ms]
2021-05-12 10:03:24,907 .95[1207.056 ms]
2021-05-12 10:03:24,908 .99[1325.008 ms]
2021-05-12 10:03:24,908 Channel (server worker num[10]):
2021-05-12 10:03:24,909 chl0(In: ['@DAGExecutor'], Out: ['det']) size[0/0]
2021-05-12 10:03:24,909 chl1(In: ['det'], Out: ['rec']) size[1/0]
2021-05-12 10:03:24,910 chl2(In: ['rec'], Out: ['@DAGExecutor']) size[0/0]
```
<a name="FAQ"></a> <a name="FAQ"></a>
## FAQ ## FAQ
......
...@@ -26,7 +26,7 @@ dag: ...@@ -26,7 +26,7 @@ dag:
op: op:
det: det:
#并发数,is_thread_op=True时,为线程并发;否则为进程并发 #并发数,is_thread_op=True时,为线程并发;否则为进程并发
concurrency: 2 concurrency: 8
#当op配置没有server_endpoints时,从local_service_conf读取本地服务配置 #当op配置没有server_endpoints时,从local_service_conf读取本地服务配置
local_service_conf: local_service_conf:
...@@ -34,7 +34,7 @@ op: ...@@ -34,7 +34,7 @@ op:
client_type: local_predictor client_type: local_predictor
#det模型路径 #det模型路径
model_config: ./ppocr_det_server_2.0_serving model_config: ./ppocr_det_mobile_2.0_serving
#Fetch结果列表,以client_config中fetch_var的alias_name为准 #Fetch结果列表,以client_config中fetch_var的alias_name为准
fetch_list: ["save_infer_model/scale_0.tmp_1"] fetch_list: ["save_infer_model/scale_0.tmp_1"]
...@@ -45,7 +45,7 @@ op: ...@@ -45,7 +45,7 @@ op:
ir_optim: False ir_optim: False
rec: rec:
#并发数,is_thread_op=True时,为线程并发;否则为进程并发 #并发数,is_thread_op=True时,为线程并发;否则为进程并发
concurrency: 1 concurrency: 4
#超时时间, 单位ms #超时时间, 单位ms
timeout: -1 timeout: -1
...@@ -60,7 +60,7 @@ op: ...@@ -60,7 +60,7 @@ op:
client_type: local_predictor client_type: local_predictor
#rec模型路径 #rec模型路径
model_config: ./ppocr_rec_server_2.0_serving model_config: ./ppocr_rec_mobile_2.0_serving
#Fetch结果列表,以client_config中fetch_var的alias_name为准 #Fetch结果列表,以client_config中fetch_var的alias_name为准
fetch_list: ["save_infer_model/scale_0.tmp_1"] fetch_list: ["save_infer_model/scale_0.tmp_1"]
......
...@@ -33,12 +33,12 @@ class DetResizeForTest(object): ...@@ -33,12 +33,12 @@ class DetResizeForTest(object):
elif 'limit_side_len' in kwargs: elif 'limit_side_len' in kwargs:
self.limit_side_len = kwargs['limit_side_len'] self.limit_side_len = kwargs['limit_side_len']
self.limit_type = kwargs.get('limit_type', 'min') self.limit_type = kwargs.get('limit_type', 'min')
elif 'resize_long' in kwargs: elif 'resize_short' in kwargs:
self.resize_type = 2
self.resize_long = kwargs.get('resize_long', 960)
else:
self.limit_side_len = 736 self.limit_side_len = 736
self.limit_type = 'min' self.limit_type = 'min'
else:
self.resize_type = 2
self.resize_long = kwargs.get('resize_long', 960)
def __call__(self, data): def __call__(self, data):
img = deepcopy(data) img = deepcopy(data)
...@@ -226,8 +226,6 @@ class CTCLabelDecode(BaseRecLabelDecode): ...@@ -226,8 +226,6 @@ class CTCLabelDecode(BaseRecLabelDecode):
super(CTCLabelDecode, self).__init__(config) super(CTCLabelDecode, self).__init__(config)
def __call__(self, preds, label=None, *args, **kwargs): def __call__(self, preds, label=None, *args, **kwargs):
#if isinstance(preds, paddle.Tensor):
# preds = preds.numpy()
preds_idx = preds.argmax(axis=2) preds_idx = preds.argmax(axis=2)
preds_prob = preds.max(axis=2) preds_prob = preds.max(axis=2)
text = self.decode(preds_idx, preds_prob, is_remove_duplicate=True) text = self.decode(preds_idx, preds_prob, is_remove_duplicate=True)
......
...@@ -48,13 +48,12 @@ class DetOp(Op): ...@@ -48,13 +48,12 @@ class DetOp(Op):
def preprocess(self, input_dicts, data_id, log_id): def preprocess(self, input_dicts, data_id, log_id):
(_, input_dict), = input_dicts.items() (_, input_dict), = input_dicts.items()
data = base64.b64decode(input_dict["image"].encode('utf8')) data = base64.b64decode(input_dict["image"].encode('utf8'))
self.raw_im = data
data = np.fromstring(data, np.uint8) data = np.fromstring(data, np.uint8)
# Note: class variables(self.var) can only be used in process op mode # Note: class variables(self.var) can only be used in process op mode
im = cv2.imdecode(data, cv2.IMREAD_COLOR) im = cv2.imdecode(data, cv2.IMREAD_COLOR)
self.im = im
self.ori_h, self.ori_w, _ = im.shape self.ori_h, self.ori_w, _ = im.shape
det_img = self.det_preprocess(im)
det_img = self.det_preprocess(self.im)
_, self.new_h, self.new_w = det_img.shape _, self.new_h, self.new_w = det_img.shape
return {"x": det_img[np.newaxis, :].copy()}, False, None, "" return {"x": det_img[np.newaxis, :].copy()}, False, None, ""
...@@ -65,7 +64,7 @@ class DetOp(Op): ...@@ -65,7 +64,7 @@ class DetOp(Op):
] ]
dt_boxes_list = self.post_func(det_out, [ratio_list]) dt_boxes_list = self.post_func(det_out, [ratio_list])
dt_boxes = self.filter_func(dt_boxes_list[0], [self.ori_h, self.ori_w]) dt_boxes = self.filter_func(dt_boxes_list[0], [self.ori_h, self.ori_w])
out_dict = {"dt_boxes": dt_boxes, "image": self.im} out_dict = {"dt_boxes": dt_boxes, "image": self.raw_im}
return out_dict, None, "" return out_dict, None, ""
...@@ -80,7 +79,9 @@ class RecOp(Op): ...@@ -80,7 +79,9 @@ class RecOp(Op):
def preprocess(self, input_dicts, data_id, log_id): def preprocess(self, input_dicts, data_id, log_id):
(_, input_dict), = input_dicts.items() (_, input_dict), = input_dicts.items()
im = input_dict["image"] raw_im = input_dict["image"]
data = np.frombuffer(raw_im, np.uint8)
im = cv2.imdecode(data, cv2.IMREAD_COLOR)
dt_boxes = input_dict["dt_boxes"] dt_boxes = input_dict["dt_boxes"]
dt_boxes = self.sorted_boxes(dt_boxes) dt_boxes = self.sorted_boxes(dt_boxes)
feed_list = [] feed_list = []
...@@ -95,7 +96,6 @@ class RecOp(Op): ...@@ -95,7 +96,6 @@ class RecOp(Op):
boxes_size = len(dt_boxes) boxes_size = len(dt_boxes)
batch_size = boxes_size // max_batch_size batch_size = boxes_size // max_batch_size
rem = boxes_size % max_batch_size rem = boxes_size % max_batch_size
#_LOGGER.info("max_batch_len:{}, batch_size:{}, rem:{}, boxes_size:{}".format(max_batch_size, batch_size, rem, boxes_size))
for bt_idx in range(0, batch_size + 1): for bt_idx in range(0, batch_size + 1):
imgs = None imgs = None
boxes_num_in_one_batch = 0 boxes_num_in_one_batch = 0
...@@ -131,6 +131,7 @@ class RecOp(Op): ...@@ -131,6 +131,7 @@ class RecOp(Op):
feed_list.append(feed) feed_list.append(feed)
return feed_list, False, None, "" return feed_list, False, None, ""
def postprocess(self, input_dicts, fetch_data, log_id): def postprocess(self, input_dicts, fetch_data, log_id):
res_list = [] res_list = []
if isinstance(fetch_data, dict): if isinstance(fetch_data, dict):
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册