TensorRT
前言
TensorRT是nvidia官方开源的加速推理框架,适用于流行的深度学习框架:pytorch、tensorflow、Caffe等。TensorRT(下面简称trt)需要与nvidia提供的显卡一起使用,没有nvidia的cuda无法使用。
提高部署推理的方法有2种,一种是训练过程中需要进行优化加速的,比如模型压缩、模型剪枝、量化、知识蒸馏,另外一种是训练完成后通过优化计算图结构,比如torch script、tf lite、trt、onnx等。
本章根据上一章的内容:MobileNet物体分类(https://blog.csdn.net/kui9702/article/details/123463075),对结果模型进行优化。如果有疑问可以评论区询问或者查看上一篇链接。
# 20220328 begin
Pytorch Jit的原理
1.Jit将动态图变为静态图推理达到加速的原理。pytorch 采用动态图机制,即每次进行前向推理时会自动构建反向传播计算图,
之后会在反向传播的时候使用这个计算图,并且用完之后进行销毁。这虽然为我们提供调试debug的方便,
但是每一次的调用,会将结果输送到python进程中。而图执行是直接在进程外添加编译步骤,将计算图的方式推送到内核级别
,在执行完毕之后不会返回到python进程(静态图),这样不方便调试。但是graph execution(静态图执行)
会比eager execution(动态图执行)快。
2.Jit拥有c++的api,利于部署,利于移植。
TensorRT的加速原理
1.TRT支持模型参数量化,支持INT8和FP16。由于模型在推理中,不一定需要很细的精度,比如FP32,所以可以通过量化参数为FP16甚至
INT8减少计算开销达到加速的效果。
2.对网络结构进行重构,包括运算合并,cuda加速,网络结构层合并的方式较少对cudnn api的调用,可以将部分结构层次合并为一个层次:
比如conv bn relu层进行合并达到加速。
# 20220328 end
Trt安装
参考我的写的文章进行安装:(https://blog.csdn.net/kui9702/article/details/123563150)
Trt加速步骤
- 根据自己的需求训练模型。
- 获得训练后的模型、模型需要保存网络结构与网络权重参数
- 将模型转化为onnx中间件(不知道onnx的可以百度,后期会出)
- 将onnx转化为trt
- 代码部署trt
pytorch推理
# 自定义数据集加载类 class PredictImageDataset(Dataset): def __init__(self, path, classes, iftrain=False): super(PredictImageDataset, self).__init__() self.transform = Compose([ Resize((224, 224)), ToTensor(), ]) self.iftrain = iftrain self.files = glob.glob(os.path.join(path, "*", "*.jpg")) self.classes = classes def __len__(self): return len(self.files) def __getitem__(self, idx): image_path = self.files[idx] image_name = os.path.basename(image_path) label_id = self.classes.index(image_name.split("_")[0]) image = Image.open(image_path) img = cv2.cvtColor(np.asarray(image), cv2.COLOR_RGB2BGR) img = cv2.resize(img, (224, 224)) image = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) image = self.transform(image) return image, label_id, image_path @timespend # 之前写的时间装饰器 def predict(model, predict_path=r"./dataset/test_data"): # 加载测试集 test_loader = DataLoader(PredictImageDataset(predict_path, classes=classes, iftrain=False), batch_size=1) total = 0 for images, labels, images_path in tqdm(test_loader): images, labels = images.cuda(), labels.cuda() with torch.no_grad(): # 模型预测 outputs = model(images) print(outputs) _, pred = torch.max(outputs, 1) correct = torch.sum(labels == pred) total += correct print(f"correct percent : {float(total) / (len(test_loader.dataset))}") return float(total) / (len(test_loader.dataset)) def pytorch_python_predict(testpath): # MobileNet 训练cifar10 的模型 model_path = r"/data/kile/other/Inception/model/40_0.8137084944659287.pth" # 加载模型 model = torch.load(model_path).eval().cuda() # 开始预测 predict(model, testpath)
很常规的模型推理,具体可以看代码描述
pytorch转onnx
import onnx def torch2onnx(tensor_input=None): # 加载魔心 model_path = r"/data/kile/other/Inception/model/40_0.8137084944659287.pth" model = torch.load(model_path).eval().cuda() # onnx模型输入 if tensor_input is None: tensor_input = torch.randn(1, 3, 224, 224) onnx_save_path = r"onnx_/mobilev2.onnx" # 模型转化 torch.onnx.export(model, tensor_input, onnx_save_path, training=TrainingMode.EVAL) # 加载onnx模型 检查模型转化结果 onnx_model = onnx.load(onnx_save_path) onnx.checker.check_model(onnx_model)
onnx模型推理
import onnxruntime def python_onnx_predict(testpath): model_path = r"./onnx_/mobilev2.onnx" # 加载模型 model = onnxruntime.InferenceSession(model_path, providers=(['CUDAExecutionProvider'])) # onnx模型预测 onnx_predict(model, testpath) @timespend def onnx_predict(model, predict_path=r"./dataset/test_data"): # 获取onnx模型输入 input_name = model.get_inputs()[0].name # 获取onnx,模型输出 output_name = model.get_outputs()[0].name test_loader = DataLoader(PredictImageDataset(predict_path, classes=classes, iftrain=False), batch_size=1) total = 0 for images, labels, images_path in tqdm(test_loader): images, labels = images.cpu(), labels.cpu() outputs = model.run([output_name], {input_name: np.asarray(images.cpu(), dtype=np.float32)})[0] print(outputs) _, pred = torch.max(torch.tensor(outputs), 1) correct = torch.sum(labels == pred) total += correct print(f"correct percent : {float(total) / (len(test_loader.dataset))}") return float(total) / (len(test_loader.dataset))
onnx转trt
代码实现比较复杂但是Trt提供了命令转化,本次不改变模型的精度,用命令转化很方便,根据代码改变路径即可
# onnx2trt 代码比较复杂 利用官方提供的可执行文件 model_path = r"./onnx_/mobilev2.onnx" trt_path = r"./onnx_/mobilev2_onnx1.trt" import os val = os.system("/data/TensorRT-8.2.0.6/bin/trtexec --onnx="+model_path+" --saveEngine="+trt_path) print(val)
trt预测
def trt_pre(batch, context): output = np.empty(len(classes), dtype=np.float32) # 模型预测推理结果 batch = batch.reshape(-1) print(batch.nbytes) d_input = driver.mem_alloc(1 * batch.nbytes) d_output = driver.mem_alloc(1 * output.nbytes) bindings = [int(d_input), int(d_output)] stream = driver.Stream() # 将图片数据送到cuda显存中 driver.memcpy_htod_async(d_input, batch, stream) # 模型预测 context.execute_async_v2(bindings, stream.handle, None) # 将结果送回内存中 driver.memcpy_dtoh_async(output, d_output, stream) # 异步等待结果 stream.synchronize() return output @timespend def trt_predict(model, predict_path=r"/data/kile/other/Inception/mobile_net/dataset/test_data"): test_loader = DataLoader(PredictImageDataset(predict_path, classes=classes, iftrain=False), batch_size=1) total = 0 for images, labels, images_path in tqdm(test_loader): images, labels = images.cpu(), labels.cpu() outputs = trt_pre(np.asarray(images.cpu(), dtype=np.float32), model) # print(outputs) for i in outputs: print(i) _, pred = torch.max(torch.tensor(outputs).unsqueeze(0), 1) # print(pred) correct = torch.sum(labels == pred) total += correct print(f"correct percent : {float(total) / (len(test_loader.dataset))}") return float(total) / (len(test_loader.dataset)) def python_tensorrt_predict(testpath): model_path = r"./onnx_/mobilev2_onnx1.trt" # 加载runtime,记录log runtime = tensorrt.Runtime(tensorrt.Logger(tensorrt.Logger.WARNING)) # 反序列化模型 engine = runtime.deserialize_cuda_engine(open(model_path, "rb").read()) # 推理上下文 context = engine.create_execution_context() # 开始预测 trt_predict(context, testpath)
总结:
实在是快的多。在pth转化为onnx再转化为trt过程,精度会发生变化,由于我只单纯将模型转化,并没有对模型本身的精度做出改变,所以不影响我的分类结果,我这边对onnx、trt测试结果显示,基本不影响分类结果,精度损失控制在小数点第5、6位之后,影响很小。但是速度有大的提升。
结果: