Modifique la capa de detección de yolov5 para mejorar el rendimiento del servicio de inferencia Triton
En el modo Inferir, la salida de datos de la capa de detección predeterminada de yolov5 es un [batches, 25200, 85]
tensor con forma. Si se implementa en Nvidia Triton
, el tamaño del tensor de la capa de salida es demasiado grande y el tiempo para procesar la salida será más largo, lo que provocará una acumulación de colas. Especialmente cuando no está en Triton Server
la Client
misma máquina y no se puede utilizar shared memory
, el tiempo para transmitir datos al cliente a través de la red será más largo, lo que afectará el rendimiento del servicio de inferencia. Enlaces de código relacionados
1. Método de prueba
Convierta el modelo al motor tensorrt e impleméntelo en Triton Inference Server. El número de grupo de instancias es 1 y el tipo es GPU. Realice pruebas de rendimiento en otras máquinas a través de la herramienta perf_analyzer proporcionada por Triton.
- Convertir yolov5s.pt al formato onnx
-
Convertir onnx a motor tensorrt
/usr/src/tensorrt/bin/trtexec \ --onnx=yolov5s.onnx \ --minShapes=images:1x3x640x640 \ --optShapes=images:8x3x640x640 \ --maxShapes=images:32x3x640x640 \ --workspace=4096 \ --saveEngine=yolov5s.engine \ --shapes=images:1x3x640x640 \ --verbose \ --fp16 \ > result-FP16.txt
-
Implementado en el servidor de inferencia Triton
Cargue el modelo en la ruta del repositorio de modelos establecida por el servidor Triton y escriba la configuración del servicio del modelo.
-
python generate_input.py --input_images <image_path> ----output_file <real_data>.json
-
Pruebas de rendimiento utilizando datos reales
perf_analyzer -m <triton_model_name> -b 1 --input-data <real_data>.json --concurrency-range 1:10 --measurement-interval 10000 -u <triton server endpoint> -i gRPC -f <triton_model_name>.csv
2. Indicadores de desempeño antes de la modificación.
El siguiente es el resultado de la prueba de rendimiento del motor yolov5 trt usando la capa de detección predeterminada, implementada en triton. Se puede ver que al usar la capa de detección predeterminada, se consume mucho tiempo en la acumulación de cola () y el procesamiento de datos de salida ( Server Queue
) Server Compute Output
, y el rendimiento ni siquiera llega a llegar1 infer/sec
Excepto por el rendimiento, las unidades de otros indicadores somos nosotros, donde Client Send y Client Recv son el tiempo para la serialización y deserialización de datos de gRPC, respectivamente.
concurrencia | Inferencias/Segundo | Envío de cliente | Red+Servidor Envío/Recepción | Cola del servidor | Entrada de cálculo del servidor | Inferir cálculo del servidor | Salida de cálculo del servidor | latencia p90 |
---|---|---|---|---|---|---|---|---|
1 | 0,7 | 1683 | 1517232 | 466 | 8003 | 4412 | 9311 | 1592936 |
2 | 0,8 | 1464 | 1514475 | 393 | 10659 | 4616 | 956736 | 2583025 |
3 | 0,7 | 2613 | 1485868 | 1013992 | 7370 | 4396 | 1268070 | 3879331 |
4 | 0,7 | 2268 | 1463386 | 2230040 | 9933 | 5734 | 1250245 | 4983687 |
5 | 0,6 | 2064 | 1540583 | 3512025 | 11057 | 4843 | 1226058 | 6512305 |
6 | 0,6 | 2819 | 1573869 | 4802885 | 10134 | 4320 | 1234644 | 7888080 |
7 | 0,5 | 1664 | 1507386 | 6007235 | 11197 | 4899 | 1244482 | 8854777 |
Por lo tanto, una solución para la transformación es simplificar la capa de datos, filtrar aproximadamente el bbox según conf antes de enviarlo a nms y, finalmente, hacer referencia al procesamiento de la capa de detección en tensorrtx para transformar la salida en un vector de forma, [batches, num_bboxes, 6]
dondenum_bboxes=1000
6 = [cx,cy,w,h,conf,cls_id]
, enconf = obj_conf * cls_prob
3. Pasos específicos
3.1 repositorio clon ultralítico yolov5
git clone -b v6.1 https://github.com/ultralytics/yolov5.git
3.2 Transformar la capa de detección
Modifique la función directa de detección para
def forward(self, x):
z = [] # inference output
for i in range(self.nl):
x[i] = self.m[i](x[i]) # conv
bs, _, ny, nx = x[i].shape # x(bs,255,20,20) to x(bs,3,20,20,85)
x[i] = x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous()
if not self.training: # inference
if self.onnx_dynamic or self.grid[i].shape[2:4] != x[i].shape[2:4]:
self.grid[i], self.anchor_grid[i] = self._make_grid(nx, ny, i)
y = x[i].sigmoid()
if self.inplace:
y[..., 0:2] = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i] # xy
y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
else: # for YOLOv5 on AWS Inferentia https://github.com/ultralytics/yolov5/pull/2953
xy = (y[..., 0:2] * 2 - 0.5 + self.grid[i]) * self.stride[i] # xy
wh = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
y = torch.cat((xy, wh, y[..., 4:]), -1)
z.append(y.view(bs, -1, self.no))
# custom output >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# [bs, 25200, 85]
origin_output = torch.cat(z, 1)
output_bboxes_nums = 1000
# operator argsort to ONNX opset version 12 is not supported.
# top_conf_index = origin_output[..., 4].argsort(descending=True)[:,:output_bboxes_nums]
# [bs, 1000]
top_conf_index =origin_output[..., 4].topk(k=output_bboxes_nums)[1]
# torch.Size([bs, 1000, 85])
filter_output = origin_output.gather(1, top_conf_index.unsqueeze(-1).expand(-1, -1, 85))
filter_output[...,5:] *= filter_output[..., 4].unsqueeze(-1) # conf = obj_conf * cls_conf
bboxes = filter_output[..., :4]
conf, cls_id = filter_output[..., 5:].max(2, keepdim=True)
# [bs, 1000, 6]
filter_output = torch.cat((bboxes, conf, cls_id.float()), 2)
return x if self.training else filter_output
# custom output >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
# return x if self.training else (torch.cat(z, 1), x)
3.3 Exportar onnx
onnx simplify
Cuando, debe comentar el siguiente código ; de lo contrario, el modelo onnx exportado seguirá siendostatic shape
model_onnx, check = onnxsim.simplify(
model_onnx,
dynamic_input_shape=dynamic
# 必须注释
#input_shapes={'images': list(im.shape)} if dynamic else None
)
Ejecute python export.py --weight yolov5s.pt --dynamic --simplify --include onnx
el modelo export onnx. La estructura onnx exportada es la siguiente:
3.4 Exportar motor tensorrt
4. Rendimiento modificado
-
tamaño del lote = 1
El rendimiento se ha incrementado más de 25 veces
Server Queue
yServer Compute Output
el tiempo de suma también se ha reducido significativamente.concurrencia Inferencias/Segundo Envío de cliente Red+Servidor Envío/Recepción Cola del servidor Entrada de cálculo del servidor Inferir cálculo del servidor Salida de cálculo del servidor Recepción de cliente latencia p90 1 11.9 1245 69472 286 7359 5022 340 3 93457 2 19.2 1376 89804 341 7538 4997 161 3 118114 3 20.2 1406 131265 1500 8240 4881 500 3 171370 4 20 1382 180621 2769 9051 5184 496 3 235043 5 20,5 1362 226046 2404 8112 5068 622 3 286810 6 20.8 1487 271714 2034 8331 5076 506 3 406248 7 20.1 1535 328144 2626 8444 5122 405 3 430850 8 19.9 1512 384690 3511 8168 5018 581 5 465658 9 20.2 1433 420893 3499 9034 5180 389 3 522285 10 20,5 1476 469029 3369 8280 5165 442 3 622745 -
tamaño del lote = 8
En relación con el tamaño del lote = 1,
Server Compute Input、Server Compute Infer, Server Compute Output
la velocidad aumenta aproximadamente 1,4 veces, 2 veces y 4 veces respectivamente. El precio es que a medida que aumenta el tamaño del lote, aumenta el tiempo de transmisión de datos.concurrencia Inferencias/Segundo Envío de cliente Red+Servidor Envío/Recepción Cola del servidor Entrada de cálculo del servidor Inferir cálculo del servidor Salida de cálculo del servidor Recepción de cliente latencia p90 1 15.2 11202 527075 360 5386 2488 43 5 570189 2 18.4 10424 829927 124 5780 2491 33 4 901743 3 20 10203 1178111 2290 5640 2570 20 4 1267145 4 20 10097 1595614 4843 5998 2454 104 5 1716309 5 19.2 9117 1971608 2397 5376 2480 203 4 2518530 6 20 8728 2338066 2914 6304 2496 96 4 2706257 7 20 14785 2708292 6581 5556 2489 160 5 3170047 8 20 13035 3052707 5067 6353 2492 62 4 3235293 9 17.6 10870 3535601 7037 6307 2480 136 5 3856391 10 18.4 9357 3953830 8044 5629 2520 64 3 4531638