Ubuntu 18.04 下 TensorRT 6.0 安装与推理引擎部署实战指南
1. 为什么在 2026 年还要折腾 Ubuntu 18.04 与 TensorRT 6.0先把话说在前头如果你现在手里有一台能跑 Ubuntu 22.04 甚至 24.04 的机器并且推理框架可以自由选型那这篇文章对你来说参考价值有限直接上更新的版本组合会更省心。但现实情况是很多工业现场、边缘计算盒子、老旧的产线视觉设备它们的系统镜像被厂商锁死在 Ubuntu 18.04驱动版本、CUDA 版本、推理引擎版本全都被一套出厂固件绑死。你没法升级系统因为一升级厂商的采集卡驱动、相机 SDK、加密狗授权全废。这时候TensorRT 6.0 就成了一个绕不开的选项。TensorRT 是 NVIDIA 推出的高性能推理优化库核心工作是把训练好的模型ONNX、Caffe、UFF 等格式做图层融合、精度校准、内核自动调优最终生成一个针对特定 GPU 架构高度优化的推理引擎。TensorRT 6.0 这个版本发布于 2019 年末对应 CUDA 10.2 和 cuDNN 7.6.5是 Ubuntu 18.04 生命周期内兼容性最成熟的一代组合之一。它支持 INT8 量化校准、动态形状有限支持、多流并发推理对于当年主流的 ResNet、YOLOv3、SSD 这类检测网络加速比能做到 CPU 推理的十几倍甚至几十倍。这篇文章适合谁看三类人。第一类是被老旧设备绑住、不得不在 Ubuntu 18.04 上部署推理服务的算法工程师第二类是刚接触 TensorRT、想搞明白从零安装到跑通第一个引擎完整链路的初学者第三类是手里有 TensorRT 7 或 8 的经验但需要回头维护老项目的开发者。我会把安装配置的每一步、每个参数背后的原因、以及我实际踩过的坑都摊开讲让你少走弯路。需要提前说明的是TensorRT 6.0 的安装方式和后续版本差异很大。7.x 之后官方主推 tar 包加 pip 的组合而 6.0 时代 deb 包是主流且对 CUDA、cuDNN 的版本匹配要求极其严格差一个小版本就可能出现符号未定义或者段错误。所以下面的内容会围绕 deb 包安装这条主线展开同时补充 tar 包方式的适用场景。2. 安装前的环境盘点与依赖梳理2.1 硬件与驱动的前置检查动手之前先把机器的底子摸清楚。TensorRT 6.0 只支持 NVIDIA 的 GPU且计算能力Compute Capability需要不低于 3.0。计算能力这个参数你可以理解为 GPU 的“指令集代次”它决定了 TensorRT 能针对哪些硬件特性做优化。比如 Tesla P4 是 6.1Tesla T4 是 7.5Jetson 系列则各有不同。查看方式很简单nvidia-smi这条命令会输出驱动版本、CUDA 版本注意这里显示的是驱动支持的最高 CUDA 版本不是你实际安装的版本以及 GPU 型号。TensorRT 6.0 要求驱动版本不低于 440如果低于这个数先升级驱动。升级驱动在 Ubuntu 18.04 上有两种常见做法一是通过apt从官方软件源安装nvidia-driver-440二是用官方提供的.run文件手动安装。我个人的经验是如果机器没有图形界面需求纯计算节点用.run文件更干净不会和系统自带的 nouveau 驱动纠缠如果有图形界面走apt更稳妥避免装完驱动进不了桌面。注意升级驱动前务必确认没有正在运行的训练或推理任务驱动重装会导致 GPU 上下文丢失正在跑的任务会直接崩掉。2.2 CUDA 10.2 与 cuDNN 7.6.5 的版本锁定逻辑TensorRT 6.0 官方明确要求 CUDA 10.2这不是随便定的。TensorRT 的每个版本在编译时都链接了特定版本的 CUDA 运行时库如果系统里的 CUDA 版本不匹配轻则加载引擎时报错重则直接段错误。CUDA 10.2 的安装包在官方归档页面还能找到选择cuda_10.2.89_440.33.01_linux.run这个版本它自带的驱动版本是 440.33.01正好满足 TensorRT 6.0 的最低要求。安装 CUDA 时有个经典陷阱安装程序默认会勾选安装驱动如果你已经装好了驱动一定要在安装界面里把 Driver 那一项的勾去掉否则它会把你的驱动覆盖成它自带的版本可能和你现有的环境冲突。另外CUDA 安装完成后需要配置环境变量在~/.bashrc里加上export PATH/usr/local/cuda-10.2/bin:$PATH export LD_LIBRARY_PATH/usr/local/cuda-10.2/lib64:$LD_LIBRARY_PATHcuDNN 7.6.5 的安装相对简单下载对应 CUDA 10.2 的压缩包解压后把头文件和库文件拷贝到 CUDA 目录即可sudo cp cuda/include/cudnn.h /usr/local/cuda-10.2/include/ sudo cp cuda/lib64/libcudnn* /usr/local/cuda-10.2/lib64/ sudo chmod ar /usr/local/cuda-10.2/include/cudnn.h /usr/local/cuda-10.2/lib64/libcudnn*这里有个细节值得说cuDNN 的库文件有多个包括libcudnn.so、libcudnn.so.7、libcudnn.so.7.6.5它们之间是软链接关系。拷贝时用*通配符能一次性全拷过去但要注意权限否则运行时会出现“找不到库”的报错。我见过不止一个同事因为忘了chmod这一步排查了半天以为是 TensorRT 的问题。2.3 Python 环境的选择与隔离TensorRT 6.0 提供了 Python 接口支持 Python 2.7 和 Python 3.6。Ubuntu 18.04 默认自带 Python 3.6这正好是官方支持的版本。但我不建议直接用系统 Python因为 TensorRT 的 Python 包安装后会往site-packages里塞东西和系统包管理器管理的包混在一起后期维护很痛苦。用virtualenv或者conda建一个独立环境是更明智的选择。如果你用 conda注意 conda 环境里的 Python 版本要和 TensorRT 的 Python 包版本对应。TensorRT 6.0 的 Python 包命名规则是tensorrt-6.0.x.x-cp36-none-linux_x86_64.whl其中cp36表示 CPython 3.6。如果你建的是 Python 3.7 的环境这个 whl 包装不上会提示版本不兼容。所以建环境时明确指定 Python 3.6conda create -n trt6 python3.6 conda activate trt6这一步看似简单但实际项目中我遇到过因为 Python 版本不对导致import tensorrt直接报ImportError的情况排查起来很费时间。提前锁定版本能省掉很多麻烦。3. TensorRT 6.0 的安装方式对比与实操3.1 deb 包安装最省心但限制最多的路径deb 包是 TensorRT 6.0 最推荐的安装方式官方提供了nv-tensorrt-repo-ubuntu1804-cuda10.2-trt6.0.x.x-ga-yyyymmdd_1-1_amd64.deb这样的仓库包。安装流程分三步先装仓库包再更新源最后装 TensorRT 本体。sudo dpkg -i nv-tensorrt-repo-ubuntu1804-cuda10.2-trt6.0.1.5-ga-20191106_1-1_amd64.deb sudo apt-key add /var/nv-tensorrt-repo-cuda10.2-trt6.0.1.5-ga-20191106/7fa2af80.pub sudo apt-get update sudo apt-get install tensorrt这三步里第二步的apt-key add容易被忽略。如果不加这个公钥apt-get update会报“仓库没有签名”的错误导致后续安装失败。公钥文件的路径在仓库包安装后会打印在终端上注意看输出信息。deb 包安装的好处是它会自动处理依赖关系把 TensorRT 的运行时库、开发头文件、Python 包如果你装了python3-libnvinfer都装好省去手动配置LD_LIBRARY_PATH的麻烦。但它的限制也很明显deb 包只包含 TensorRT 的核心库不包含一些可选的插件和工具比如uff转换工具、graphsurgeon等这些需要单独安装sudo apt-get install uff-converter-tf sudo apt-get install graphsurgeon-tf另外deb 包安装的 TensorRT 是全局的如果你有多个项目需要不同版本的 TensorRTdeb 方式就不太合适了因为版本切换很麻烦。3.2 tar 包安装灵活但需要手动配置tar 包方式适合需要多版本共存或者没有 root 权限的场景。下载TensorRT-6.0.1.5.Ubuntu-18.04.x86_64-gnu.cuda-10.2.cudnn7.6.tar.gz解压到任意目录比如/opt/TensorRT-6.0.1.5。然后配置环境变量export LD_LIBRARY_PATH/opt/TensorRT-6.0.1.5/lib:$LD_LIBRARY_PATH export PATH/opt/TensorRT-6.0.1.5/bin:$PATHPython 包在 tar 包的python/目录下用 pip 安装cd /opt/TensorRT-6.0.1.5/python pip install tensorrt-6.0.1.5-cp36-none-linux_x86_64.whltar 包方式的一个隐藏坑是它自带的libnvinfer.so等库文件可能和系统里已有的 CUDA 库产生符号冲突。如果同时装了 deb 版和 tar 版LD_LIBRARY_PATH的顺序决定了优先加载哪个。我建议在.bashrc里把 TensorRT 的路径放在 CUDA 路径之前确保优先使用 tar 包里的库。3.3 两种方式的对比与选型建议对比维度deb 包安装tar 包安装依赖处理自动手动多版本共存困难容易权限要求需要 root普通用户即可可选组件需单独安装全部包含升级/卸载apt 管理干净手动删除目录适用场景单一版本、生产环境开发调试、多版本选型逻辑很直接如果你是给一台固定用途的机器部署选 deb如果你是在开发机上需要同时维护多个项目选 tar。我个人的习惯是开发机用 tar因为可以随时切换版本部署到现场设备时用 deb因为运维同事用apt管理更顺手。4. 验证安装与第一个推理引擎的生成4.1 安装验证的三种手段装完之后别急着跑模型先做三层验证。第一层检查库文件是否能被正确加载ldconfig -p | grep nvinfer如果输出里有libnvinfer.so.6和libnvinfer_plugin.so.6说明库路径没问题。第二层Python 导入测试import tensorrt as trt print(trt.__version__)正常应该输出6.0.1.5。如果报ImportError: libnvinfer.so.6: cannot open shared object file说明LD_LIBRARY_PATH没配好或者库文件权限不对。第三层用trtexec工具做端到端测试。trtexec是 TensorRT 自带的命令行工具可以加载 ONNX 模型并生成引擎trtexec --onnxmodel.onnx --saveEnginemodel.engine --fp16这条命令会加载model.onnx以 FP16 精度生成引擎并保存。如果能看到“Engine built successfully”之类的输出说明整个链路是通的。4.2 从 ONNX 到 engine 的完整代码示例trtexec适合快速验证但实际项目里通常需要用 Python API 做更精细的控制比如设置动态形状、INT8 校准、多优化配置文件等。下面是一个完整的 Python 脚本展示从 ONNX 加载到引擎序列化的全过程import tensorrt as trt TRT_LOGGER trt.Logger(trt.Logger.WARNING) EXPLICIT_BATCH 1 (int)(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH) def build_engine(onnx_path, engine_path, fp16_modeTrue): with trt.Builder(TRT_LOGGER) as builder, \ builder.create_network(EXPLICIT_BATCH) as network, \ trt.OnnxParser(network, TRT_LOGGER) as parser: builder.max_workspace_size 1 30 # 1GB builder.max_batch_size 1 if fp16_mode: builder.fp16_mode True with open(onnx_path, rb) as f: if not parser.parse(f.read()): for error in range(parser.num_errors): print(parser.get_error(error)) return None engine builder.build_cuda_engine(network) if engine is None: print(Engine build failed) return None with open(engine_path, wb) as f: f.write(engine.serialize()) return engine if __name__ __main__: build_engine(model.onnx, model.engine)这段代码里有几个关键点需要解释。EXPLICIT_BATCH这个标志在 TensorRT 6.0 里很重要它决定了网络是否使用显式批次维度。如果你用的是 ONNX 模型且模型输入的第一个维度是批次维度就必须加这个标志否则解析会出错。max_workspace_size是 TensorRT 在构建引擎时可以使用的最大显存设得太小会导致某些层无法选择最优内核设得太大则浪费显存。1GB 对于大多数检测网络够用了如果是大型分割网络可能需要 2GB 以上。fp16_mode开启后TensorRT 会优先选择 FP16 精度的内核在支持 FP16 的 GPU 上计算能力 6.0 以上能显著提升速度精度损失通常在可接受范围内。但要注意不是所有层都支持 FP16TensorRT 会自动回退到 FP32所以最终引擎是混合精度的。4.3 引擎反序列化与推理执行引擎生成后实际推理时不需要再解析 ONNX直接反序列化 engine 文件即可速度很快import tensorrt as trt import pycuda.driver as cuda import pycuda.autoinit import numpy as np def load_engine(engine_path): with open(engine_path, rb) as f, trt.Runtime(TRT_LOGGER) as runtime: return runtime.deserialize_cuda_engine(f.read()) def infer(engine, input_data): with engine.create_execution_context() as context: input_idx engine.get_binding_index(input) output_idx engine.get_binding_index(output) input_shape engine.get_binding_shape(input_idx) output_shape engine.get_binding_shape(output_idx) d_input cuda.mem_alloc(input_data.nbytes) d_output cuda.mem_alloc(np.prod(output_shape) * 4) stream cuda.Stream() cuda.memcpy_htod_async(d_input, input_data, stream) context.execute_async_v2( bindings[int(d_input), int(d_output)], stream_handlestream.handle ) output np.empty(output_shape, dtypenp.float32) cuda.memcpy_dtoh_async(output, d_output, stream) stream.synchronize() return output这里用到了pycuda做显存分配和数据拷贝。execute_async_v2是 TensorRT 6.0 的异步执行接口配合 CUDA 流可以实现多流并发。注意bindings列表的顺序要和引擎的绑定顺序一致用get_binding_index获取索引更稳妥不要硬编码 0 和 1。5. 常见报错与排查技巧实录5.1 库加载类错误最常见的报错是libnvinfer.so.6: cannot open shared object file。这个问题的根源通常是LD_LIBRARY_PATH没有包含 TensorRT 的库目录或者库文件权限不对。排查步骤先用find / -name libnvinfer.so*找到库文件的实际位置然后确认该目录在LD_LIBRARY_PATH里。如果是 deb 安装库文件通常在/usr/lib/x86_64-linux-gnu/这个路径默认就在系统库搜索路径里一般不会出问题。如果是 tar 安装库文件在解压目录的lib/下必须手动加到LD_LIBRARY_PATH。另一个变种是undefined symbol错误比如undefined symbol: _ZN10nvinfer1...。这通常是因为 TensorRT 的库和 CUDA 库版本不匹配或者同时加载了两个不同版本的 TensorRT 库。用ldd命令检查库的依赖关系ldd /usr/lib/x86_64-linux-gnu/libnvinfer.so.6 | grep cuda如果输出的 CUDA 库路径指向的不是你安装的 CUDA 10.2说明系统里有多个 CUDA 版本需要调整LD_LIBRARY_PATH的顺序。5.2 引擎构建失败类错误引擎构建阶段的报错信息通常比较详细parser.get_error(i)会给出具体原因。常见的几类一是 ONNX 模型里有 TensorRT 6.0 不支持的算子比如某些自定义的NonMaxSuppression变体。这时候需要用graphsurgeon或者onnx-simplifier做图修改把不支持的算子替换成支持的组合。二是输入形状不匹配比如模型期望的是NCHW格式但 ONNX 里导出的是NHWC。三是显存不足max_workspace_size设得太大而 GPU 显存不够构建过程中会报out of memory。实操心得构建引擎时如果报算子不支持先用trtexec --onnxmodel.onnx --verbose跑一遍verbose 模式会打印每一层的解析情况能快速定位到具体是哪一层出了问题。5.3 推理结果异常类问题引擎能跑通但结果不对这类问题最隐蔽。常见原因有三个。第一输入数据的预处理和训练时不一致比如归一化参数不同、通道顺序不同。TensorRT 不会帮你做预处理输入什么就是什么。第二INT8 校准不充分导致量化误差过大。INT8 校准需要一批有代表性的校准数据通常 500 到 1000 张图片如果校准集和实际推理数据分布差异大精度会明显下降。第三输出层的解析方式不对比如检测网络有多个输出头绑定顺序搞错了。排查这类问题的技巧是先用 FP32 模式构建引擎确认结果正确再逐步切换到 FP16 和 INT8对比每一步的精度变化。如果 FP32 就不对那问题在预处理或后处理不在 TensorRT 本身。5.4 常见问题速查表报错现象可能原因排查手段解决方式libnvinfer.so.6 找不到LD_LIBRARY_PATH 未配置ldconfig -p | grep nvinfer添加库路径到环境变量undefined symbol库版本冲突ldd检查依赖调整库加载顺序算子不支持ONNX 含 TRT6 不支持的算子trtexec --verbose用 graphsurgeon 替换算子构建时 OOMworkspace 过大查看 GPU 显存占用减小 max_workspace_size推理结果错误预处理不一致对比 FP32 与 CPU 结果统一预处理逻辑INT8 精度下降大校准集不具代表性对比 FP16 与 INT8 输出更换校准数据6. 性能调优与生产部署的几点经验6.1 workspace 与 batch size 的权衡max_workspace_size和max_batch_size是构建引擎时两个最影响性能和显存的参数。workspace 越大TensorRT 在层融合和内核选择时的自由度越高生成的引擎可能更快但构建时的显存峰值也越高。我的经验是对于 1080Ti 或 T4 这类 8GB 到 16GB 显存的卡workspace 设 1GB 到 2GB 是甜点区间。batch size 则要根据实际吞吐需求来定batch 越大吞吐越高但单次推理延迟也越大。如果是对延迟敏感的在线服务batch 设 1 到 4如果是离线批处理可以设到 16 甚至 32。6.2 多流并发与显存复用TensorRT 6.0 支持多 CUDA 流并发执行能显著提升 GPU 利用率。实现方式是为每个流创建一个独立的执行上下文然后用execute_async_v2提交任务。但要注意每个执行上下文都会占用一份显存如果并发数太多显存会不够。一个折中方案是复用输入输出显存用 CUDA 的cudaMalloc一次性分配好多个流轮流使用。另外TensorRT 6.0 的ICudaEngine是线程安全的但IExecutionContext不是多线程环境下每个线程需要自己的上下文。6.3 引擎序列化与版本兼容性引擎文件.engine是和 GPU 架构、TensorRT 版本、CUDA 版本强绑定的。在一台机器上生成的引擎换到另一台不同 GPU 的机器上可能无法加载报serialization version mismatch或device mismatch。所以生产部署时引擎应该在目标设备上生成或者至少确保目标设备的 GPU 架构和构建时一致。如果做不到就保留 ONNX 模型在部署时现场构建引擎虽然多花几分钟但能保证兼容性。注意TensorRT 6.0 生成的引擎不能跨大版本使用比如用 6.0 生成的引擎在 7.x 上加载会失败。反过来也一样。所以升级 TensorRT 版本时所有引擎都需要重新构建。6.4 实际项目中的部署架构建议在一个典型的边缘推理服务里我通常会把架构分成三层最底层是 TensorRT 引擎和 CUDA 运行时中间层是推理服务进程用 Python 或 C 写最上层是业务逻辑。推理服务进程启动时加载引擎常驻显存通过共享内存或消息队列接收请求。这样做的原因是引擎加载和反序列化比较耗时几秒到几十秒不能每次请求都重新加载。另外推理服务进程最好设置成开机自启用 systemd 管理崩溃后自动重启。如果业务对延迟要求极高可以考虑用 C 写推理服务直接调用 TensorRT 的 C API省去 Python 解释器的开销。TensorRT 6.0 的 C API 和 Python API 功能基本对等但 C 版本在内存控制和线程管理上更精细。不过开发效率上 Python 明显更高所以我的建议是原型验证用 Python性能压榨阶段再考虑迁移到 C。7. 一些零散但重要的补充TensorRT 6.0 的 INT8 校准器接口在 Python 里用起来有点绕需要继承trt.IInt8EntropyCalibrator2并实现get_batch、read_calibration_cache、write_calibration_cache三个方法。校准缓存文件.cache建议保留下次构建同模型时可以直接复用省去重新校准的时间。校准数据最好从实际业务数据里采样不要用公开数据集凑数否则量化后的精度可能不达预期。另外TensorRT 6.0 对动态形状的支持比较有限只支持在构建时指定一个形状范围运行时在这个范围内变化。如果你的模型输入尺寸经常变比如不同分辨率的图片建议在预处理阶段统一 resize 到固定尺寸这样引擎可以针对固定形状做最优优化性能更好。如果实在需要动态形状用builder.create_optimization_profile()创建优化配置文件指定最小、最优、最大三个形状。最后说一个我踩过的坑Ubuntu 18.04 默认的 GCC 版本是 7.5而 TensorRT 6.0 的某些头文件在 GCC 7.5 下编译 C 代码时会有警告虽然不影响运行但看着烦。如果追求干净编译可以装 GCC 6 并切换过去。不过这只是洁癖问题不影响功能。这个组合虽然老但在特定场景下依然能打。把环境配好、引擎跑通之后它的稳定性是经过时间检验的。后续如果条件允许可以逐步往 TensorRT 8.x 迁移API 变化不小但核心概念是相通的。