ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

多任务YOLOv8实战:检测、可行驶区域与车道线分割一网打尽

多任务YOLOv8实战:检测、可行驶区域与车道线分割一网打尽 简介面向自动驾驶实时感知场景这套解决方案将YOLOv8目标检测、可行驶区域分割与车道线分割集成于一个轻量级多任务模型中。模型采用统一设计的轻量级分割头和统一损失函数无需针对特定子任务定制模块仅由卷积层堆叠构成兼顾精度与推理效率特别适合实时性要求高的车载或边缘平台。包内共1313个文件以Python源码、Markdown文档、YAML配置和模型权重为主另有少量可执行文件、环境脚本、Dockerfile、虚拟环境依赖等辅助素材覆盖从模型定义、环境配置到部署依赖的完整链路。压缩包整体约28.54MB目录规模适中便于按需检索。目前已有1348人学习适合自动驾驶、图像分割方向的研究生、算法工程师及竞赛学习者可作为多任务感知模型设计、轻量分割头开发以及统一损失函数调优的参考范例。1. 多任务 YOLOv8 不是把三个模型塞进一个网先搞清楚要解决什么问题在车载感知、机器人导航或者园区安防里同一个摄像头画面往往要同时回答很多问题哪里有车哪有人、哪些物理区域可以开过去、车道线在哪。如果按最省事的做法检测跑一个 YOLOv8分割跑一个 YOLOv8两路模型同时部署一帧图像两次前向推理显卡和功耗立刻告急。把目标检测、可行驶区域分割、车道线分割压进同一个 YOLOv8 结构里共享主干网络和特征层一次 forward 同时输出检测框、区域掩码和车道线掩码这样算力账才吃得下。这篇文章写给打算用 YOLOv8 做多任务感知的人新手能照着准备数据、调通训练熟手也能绕开我当年在损失函数和后处理上踩过的几个坑。后面先讲结构选型再给数据集方案和可复现的训练命令最后一节放一个能直接抄的推理校验技巧。2. 任务组合与网络结构为什么检测头和分割头能共享一个骨架2.1 共享骨架与多分支 Head一张 YOLOv8 网络结构图背后的算账逻辑YOLOv8 网络结构图可以从下往上拆成三块backbone 用 C2f 模块提取基础特征neck 用 PAFPN 把不同尺度的特征做融合最后接头部输出。多任务版本没有改变前两块的架构只在 neck 后面多挂几个分支头。常见做法是保留一个检测头再并列加两个分割头三个头共享同一组特征金字塔输出。这样做最大的好处是参数增量很小检测头本身只占整个模型尾部一小部分分割头也只是一两个卷积层加一个上采样层。以 YOLOv8s 这个量级为例单独检测模型大约 11M 参数加一个分割头一般增加 1.5M 到 2.5M 参数两个分割头加起来也不会让模型体积翻倍。相比各自独立模型多任务模型参数总量大概能省三分之一到二分之一推理延迟省得更多因为只有一次主干计算。但共享特征不是没有代价。检测任务希望特征有平移不变性和较高的感受野用来框出完整物体车道线分割任务希望特征保留精细的边缘信息和连续走向可行驶区域分割则更像大块语义分类对边缘精度要求略低。这三个任务放在一起理论上存在负迁移某些特征对检测有效但对车道线有害。实际经验是只要数据量足够、损失权重调匀正向迁移远大于负迁移因为三个任务都建立在「场景整体理解」这个共同基座上。我在做多任务头时也遇到过分割头把所有电线杆都认成车道线的情况这是因为共享特征把「长条状物体」这个模式过度放大了后来靠把车道线分支的输入特征从多个尺度拼接、并加上上下文注意力才把误检压下去。总的原则是同一份特征对三个任务的适配程度不会完全一样所以各分支头最好保留一点独立卷积去矫正特征。2.2 可行驶区域和车道线为什么选分割头而不是检测头有人会问车道线能不能用目标检测的旋转框来标理论上可以但实际很别扭。车道线是细长曲线一个旋转框会把大量背景包进来回归目标不稳定且弯曲车道线用矩形框表达本身就信息丢失。分割头输出的是像素级概率图每一个像素回答「这是不是车道线」天然适合曲线和遮挡场景。可行驶区域也一样它本质上是二分类语义分割边界往往延伸到图像边缘用检测框会框进大量不可行驶的杂物只有逐像素掩码能准确描述「能开到哪里」。那为什么不干脆去掉检测头只做两个分割头因为目标检测的输出并不仅仅用于画框。框经过 NMS 之后能直接接跟踪、计数和测距这些下游逻辑依赖一个紧凑的矩形描述。分割掩码要得到同类效果还得额外做连通域分析或轮廓拟合稳定性反而不如检测头。多任务 YOLOv8 保留了检测头正是为了把「目标是什么、在哪」和「哪里可走、线在哪」分离表达。在自动驾驶典型场景里检测头负责车辆、行人、骑行者的位置和置信度可行驶区域分割头负责可通行边界车道线分割头负责结构化道路信息三个输出互不冲突最后可以在后处理里互相校验。2.3 一个模型还是三个模型参数账、显存账和工程账很多人在项目评估阶段会纠结与其把 YOLOv8 改成多任务不如分别部署三个模型每个模型单独调到最优。这个思路没有错但要看部署条件。先算显存三个独立模型在推理时即便可以分时复用同一块显存峰值也要放下不止一个模型的工作集因为中间特征图不一定能及时释放。在 Jetson Orin、RK3588 这类嵌入式板子上显存和带宽都紧多任务单模型明显更划算。再算延迟三个模型串行推理假设每个模型跑 20ms总延迟约 60ms多任务模型的主干计算只有一次检测头和分割头并行分支总延迟一般能压到 30ms 左右终端体验差距明显。不过工程上独立模型也有优点可以分别打不同版本的补丁某一任务数据集更新了不用重训全部模型某个任务可以换成效果更好的候选模型改动不牵连其他任务。多任务模型一旦训出来就是一坨耦合的黑匣子改一个损失函数可能导致所有任务指标波动。我的建议是如果算力紧张、任务间共享语义明显直接上多任务如果你有团队长期迭代、不同任务由不同人维护三个独立模型未必是坏事。对于文章场景多任务模型在数据上有天然优势它让同一个摄像头画面产生三类监督信号等于一次标注喂给三个任务这一点是独立模型做不到的。评估收益最直观的方法是先把三个独立模型跑通记录总参数量、显存峰值和帧率再用多任务版本替换同一份数据训练看端到端帧率和 mIoU 是否在可接受的范围内。方案参数总量推理方式峰值显存维护复杂度三个独立 YOLOv8约 3 倍单模型串行 3 次前向高低可单独替换一个多任务 YOLOv8约 1.3~1.6 倍单模型单次前向低高调参互相影响这个表格里的倍数是我在多种任务组合下得到的经验范围不是某个特定版本的精确实测。你可以拿自己的模型跑一下把数字填进去作为方案会审依据。3. 准备数据集与标注一张图要同时标三类标签3.1 标注格式检测用 YOLO 框分割用 PNG maskYOLOv8 检测训练常见格式是每个 txt 文件里写若干行每行五个数类别编号、归一化中心 x、归一化中心 y、归一化宽、归一化高。这个格式成熟稳定直接复用。但两个分割任务没有现成的 YOLO 格式需要单独准备。可行驶区域分割和车道线分割本质上都是单通道语义图我一般把它们保存成 PNG 掩码文件背景像素值 0目标像素值 1。注意是单通道灰度 PNG不是三通道彩色图否则读取时可能被 resize 出奇怪结果。整个数据集目录我建议这样组织dataset/ images/ train/ img_001.jpg img_002.jpg val/ img_001.jpg labels/ train/ img_001.txt img_002.txt val/ img_001.txt seg_drive/ train/ img_001.png img_002.png val/ img_001.png seg_lane/ train/ img_001.png img_002.png val/ img_001.png这个目录结构里images 放原始图像labels 放目标检测的 YOLO 格式标签seg_drive 和 seg_lane 分别放两个分割任务的掩码。三个标注文件与图像文件名一一对应要求同名且格式不同训练脚本加载时按文件名匹配。图像和掩码的尺寸必须保持一致不然训练时 tensor 对齐会出现维度错误。这里注意一个容易忽略的点目标检测标签中的类别和分割掩码中的类别是两套体系。检测框里的「车辆」「行人」类别编码有十几个分割掩码里「可行驶区域」和「车道线」各自只有一类。千万不要把分割掩码里的类别编号直接放进检测标签也不要反过来。它们只是在同一张图片上同时存在的两种监督信息互不替代。3.2 用 LabelMe 标可行驶区域和车道线标注流程与导出脚本目标检测常用标注工具很多LabelMe 是其中比较适合同时画框和多边形的一种。它可以把检测框和分割多边形放在同一个 JSON 文件里但 YOLO 和 PNG mask 需要分别导出所以我的工作流是分两轮第一轮先标检测框导出 YOLO 格式 txt第二轮再画可行驶区域和车道线的多边形导出 PNG mask。两轮标注共用同一个图片目录可以明显降低返工成本。LabelMe 原始标注是 JSON里面每一笔多边形是一条 shape。实际转换时不一定装完整图形界面直接用 Python 写脚本就能把 JSON 转成 maskimport json import numpy as np import cv2 from labelme import utils with open(img_001.json) as f: data json.load(f) img utils.img_b64_to_array(data[imageData]) label_name_to_value {_background_: 0, _objects_: 1} mask, _ utils.shapes_to_label( img.shape[:2], data[shapes], label_name_to_value, ) mask_png (mask 0).astype(np.uint8) cv2.imwrite(seg_drive/img_001.png, mask_png * 255)这段代码先把 LabelMe 的 JSON 读进来用 img_b64_to_array 还原原始图像尺寸再通过 shapes_to_label 把多边形填充成掩码。最后乘以 255 是因为掩码要写进 PNG 可视化训练时读到再除以 255 转回 0/1。注意 shapes_to_label 的 label_name_to_value 映射如果同一个 JSON 里同时存在「可行驶区域」和「车道线」两类你需要分成两个不同映射分别导出或者先按名称拆分 shapes 再导出。更稳妥的做法是可行驶区域和车道线分开标注成两个 JSON避免一边画错影响另一边。导出之后必须做一次校验把 PNG mask 缩放到原始图大小再用半透明色叠加到原图上肉眼确认边界有没有错位。常见问题是 LabelMe 的坐标是整数像素而图片 resize 后 mask 也跟着变如果训练脚本对图像做了 letterbox那么 mask 要做同样参数的 letterbox否则对不齐。3.3 数据增强的坑翻转、裁剪会破坏空间语义多任务训练里数据增强是最容易让模型学会错误规律的环节。先看水平翻转。目标检测框水平翻转之后坐标同步变换类别不变没问题。分割掩码水平翻转也同步变但这里有一个潜规则如果车道线分割任务要区分左车道线和右车道线那么翻转后左变右、右变左标签必须交换左右类别。如果你只是单类「车道线」那就没事翻转只会让模型学到更对称的特征。可行驶区域同理翻转后仍是可行驶区域。随机裁剪和缩放也要谨慎。检测框裁剪后可能不完整但 YOLO 训练本来就能适应部分遮挡车道线被裁掉一段后掩码会出现断裂模型会被迫学习「车道线可以中间断开」的错误先验。所以我通常对分割任务不做随机裁剪只做整图缩放和轻微旋转旋转角度控制在正负 10 度以内过大会让远处车道线扭曲到不合理的几何形状。色彩增强方面亮度对比度调整会影响白色车道线和浅色路面的对比度极端参数会把两者混成一片。建议把 hue 偏移量设为 0saturation 只在 0.8 到 1.2 之间浮动。我自己曾经把 brightness 调到 0.6结果可行驶区域的 mIoU 掉了三个点当时还没意识到是颜色增强搞的鬼。数据增强策略本质上是给模型加先验越强的增强会让模型更鲁棒但也会侵蚀结构化任务的几何约束。车道线和可行驶区域都是空间结构信息增强时优先保证结构的连续性其次再考虑多样性。4. 搭建多任务训练环境从 YOLOv8 源码到第一个模型跑通4.1 在 Ubuntu 上搭 YOLOv8 CPU 环境最小安装命令很多人拿着 YOLOv8 代码第一件事就是配置环境这里给一个在 Ubuntu 20.04 上能跑通的最小方案。先建一个独立的 conda 环境避免把系统 Python 环境改乱。然后是安装 PyTorch 的 CPU 版本这个可以直接从 CPU 索引装不需要碰 GPU 驱动conda create -n yolomulti python3.8 conda activate yolomulti pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install ultralytics opencv-python labelme安装完成之后用python -c import torch; print(torch.__version__)验证。如果输出正常说明基础环境没问题。这里重点说明一下为什么指定python3.8YOLOv8 系列在 3.8 到 3.10 上都跑得很稳3.8 对很多算法库兼容性更好避免后面装编译型依赖时报错。CPU 环境下训练多任务模型确实辛苦但并不是不能跑。关键是把输入分辨率调小、batch 调小、epoch 数减半。后续训练命令里我会给出一组能直接用的参数。如果你后续要到 GPU 或 RK3588 上部署环境也不需要推倒重来只需要重新安装对应平台版本的 PyTorch模型代码保持统一。4.2 自定义多任务头修改配置还是重写损失Ultralytics 官方 YOLOv8 没有直接提供一个「检测 两个分割头」的现成配置。常见做法是改源码在模型定义文件里把 Detect 头替换成一个自定义的多任务头并在训练脚本里同时计算三种损失。不要试图通过改 yaml 配置文件来凭空生成一个不存在的 head那样只会得到一堆被忽略的未知字段。下面是一个极简的多任务头定义突出三个分支的挂载方式import torch.nn as nn class MultiTaskHead(nn.Module): def __init__(self, det_head, in_channels): super().__init__() self.det det_head self.drive_head nn.Sequential( nn.Conv2d(in_channels, 64, 3, padding1), nn.BatchNorm2d(64), nn.ReLU(inplaceTrue), nn.Conv2d(64, 1, 1), nn.Upsample(scale_factor4, modebilinear, align_cornersFalse), ) self.lane_head nn.Sequential( nn.Conv2d(in_channels, 64, 3, padding1), nn.BatchNorm2d(64), nn.ReLU(inplaceTrue), nn.Conv2d(64, 1, 1), nn.Upsample(scale_factor4, modebilinear, align_cornersFalse), ) def forward(self, features, targetsNone): det_out self.det(features) drive_out self.drive_head(features[-1]) lane_out self.lane_head(features[-1]) return det_out, drive_out, lane_out这段代码里det_out 沿用原检测头的输出格式drive_head 和 lane_head 分别从特征金字塔的最后一层接出来通过一个卷积和上采样还原到原图 1/4 分辨率。in_channels 要取 neck 最后一层的输出通道数例如 YOLOv8s 是 256。注意这里分割头只用了最高层的特征精度可能不够实际工程里可以把三层金字塔特征都上采样后 concat 再卷积但那样代码会明显变长。训练时损失函数要拆开算def multi_task_loss(preds, det_targets, drive_masks, lane_masks, weight_det1.0, weight_drive0.5, weight_lane0.5): det_out, drive_out, lane_out preds loss_det det_loss(det_out, det_targets) loss_drive dice_loss(drive_out, drive_masks) loss_lane dice_loss(lane_out, lane_masks) return weight_det * loss_det weight_drive * loss_drive weight_lane * loss_lane建议先分别用 1.0、1.0、1.0 跑一个短 epoch打印出三种损失的数值范围然后再调权重。我在项目里的经验是分割损失普遍比检测损失小如果不提高权重分割头基本学不到东西。4.3 训练命令与四个必调参数一旦模型头改好训练循环和官方流程差别不大。如果你沿用 ultralytics 的训练器需要把损失函数替换成上面那个多任务版本。为了方便排错我更喜欢直接用 PyTorch 写一个简洁的训练循环for epoch in range(epochs): model.train() for images, det_labels, drive_masks, lane_masks in dataloader: images images.to(device) drive_masks drive_masks.to(device) lane_masks lane_masks.to(device) preds model(images) loss multi_task_loss( preds, det_labels, drive_masks, lane_masks, weight_det1.0, weight_drive0.8, weight_lane0.8, ) optimizer.zero_grad() loss.backward() optimizer.step() torch.cuda.synchronize()这一段里的权重是我调试后比较稳的一组但不同数据集差异很大。如果你的检测框很多、分割目标很大权重还要调整。训练时除了损失权重第一个必调参数是输入分辨率。官方默认 640x640CPU 上跑 640 会非常慢我建议在模型文件里把输入降成 416x416检测数量多的小目标场景再恢复到 640。第二个必调参数是 batch size。CPU 环境下 batch 设置 4 比较稳妥显卡 16G 显存可以到 16。注意 batch 过小时BatchNorm 统计量波动很大分割头的输出会出现闪烁这时可以把 BN 换成 GroupNorm。第三个必调参数是学习率。官方默认优化器是 SGD 加很大初始学习率GPU 上没问题CPU 环境收敛慢我更推荐用 AdamW初始学习率 0.001配合CosineAnnealing。第四个必调参数是 epoch 数。多任务比单任务往往需要更多 epoch 才稳定但受限于 CPU 算力建议先设 60 个 epoch每 5 个 epoch 保存一次权重观察损失曲线是否单调下降。这里牵扯到一个用户常搜的问题yolov8 模型训练参数含义。如果你打算用官方训练器--epochs、--batch、--imgsz这些参数名和这里是一致的但多任务损失权重官方没有暴露接口还是需要改源码。所以我的建议很直接多任务项目别依赖官方命令老老实实写一个自己的训练脚本调试起来会舒服得多。5. 多任务训练避坑指南我在这类项目里踩过的 4 个坑5.1 坑一检测损失和分割损失数量级不对等分割任务被带崩现象训练十几轮后目标检测的 mAP 已经在稳步上涨但可行驶区域的 mIoU 一直在 0.15 以下徘徊而且每次验证时掩码区域都在缩小最后只剩画面中央一块。原因检测损失由分类损失和回归损失组成数值普遍比分割损失大一个到两个数量级。当两个损失直接相加时反向传播梯度被检测损失主导分割头虽然也在降损失但更新的方向被反复撕扯很难收敛到有效区域。这是多任务训练最经典的失衡问题。解决先在训练脚本里打印每个损失项确认它们的平均值和量级。比如检测损失均值是 6.5分割损失均值是 1.2那么就把分割权重调到 3.0 左右。我习惯把权重初始化成loss_det / loss_drive的比例再做一轮微调。另外可以把分割损失换成 Dice 损失它的数值比较稳定不会因为目标像素少而剧烈波动。我在换了 Dice 之后mIoU 从 0.15 提到了 0.3效果立竿见影。5.2 坑二可行驶区域 mask 没有边缘软化分割结果锯齿严重现象训练收敛后把分割概率图阈值化成 0/1发现可行驶区域边缘有一圈毛刺尤其在路面和路肩交界处锯齿每隔几个像素就跳变。视觉上像低分辨率贴图放大后的效果。原因标注 mask 是硬标签边界像素被强行分成 0 和 1模型在边缘附近会输出很陡峭的置信度变化。语义分割里这种硬边界对 BCE 损失不友好因为模型无法正确估计边界的梯度方向被迫在几个像素之间摇摆。解决训练前对 mask 做一次高斯模糊把边缘从硬 0/1 变成 0 到 1 的连续过渡。常见做法是用cv2.GaussianBlur(mask, (5, 5), 3)对 GT mask 做平滑这样模型学到的是带模糊带的概率图推理时再取 0.5 阈值边缘反而更稳定。注意模糊半径不要太大否则车道线这种细线会被抹掉。车道线 mask 我一般只做3x3模糊甚至不用模糊因为车道线本身只有几个像素宽过度平滑会直接把目标融进背景。可行驶区域可以做稍大尺寸的模糊因为它面积大边缘过渡不影响整体结构。5.3 坑三三个头的输出尺寸和通道数不一样后处理写错索引现象推理时拿到模型输出直接取pred[0]做 NMS结果发现得到的是一个四维张量里面的数值根本不是检测框而是分割头的概率图。屏幕上一片花或者直接报维度不匹配。原因多任务模型的 forward 返回是一个元组检测头返回的是多个尺度特征级联后的列表分割头返回的是单张掩码。不同源码实现的排列顺序不同有人习惯把检测结果放第一位我自己的项目里也遇到过把分割头放前面的时候。后处理代码写死了索引一旦输出顺序变化就会踩坑。解决在拿到模型第一版权重后先跑一次确定性推理把三个返回值的 shape 和数值范围都打印出来。增长期维护时给这个推理函数写一个简单的单元测试。det_out, drive_out, lane_out model(images) print(det_out.shape, drive_out.shape, lane_out.shape)推理时定义一个输出打包函数只允许按指定名称读取results { det: det_out, drive: drive_out, lane: lane_out, }这样后处理什么索引都不会写错。另外还要确认检测头的输出到底是一个 list 还是张量YOLOv8 官方在训练时输出列表在部署时导出模型又是另一个形式千万别用同一个索引策略同时处理两种模式。很多模型转换到 RK3588 或 TensorRT 后输出顺序会乱提前用字典约束住后面会省很多事。5.4 坑四CPU 环境训练半天不收敛问题不在模型而在学习率和 batch现象在 CPU 环境跑多任务模型设了 200 个 epoch跑了两个小时损失曲线一直横着走偶尔上下跳动看不出下降趋势。检查模型定义没发现错数据集也检查过没问题。原因CPU 训练时 batch 被迫设得很小比如 2 到 4。小 batch 导致 BatchNorm 统计量不稳定梯度方向噪声极大。同时很多教程直接沿用 GPU 训练的大学习率比如 0.01 用于随机梯度下降。这两个因素叠加模型就在原地打转。解决把 batch 尽量设到 8 以上内存不够就降低输入分辨率到 416甚至 320。然后把优化器换成 AdamW学习率调到 0.001并做一个 5 个 epoch 的 warmup让学习率从 0.0001 慢慢爬升。另一个有效手段是把 BatchNorm 换成 GroupNorm但需要改模型定义成本更高。我的建议是优先优化学习率和 batch。这套组合在几个项目里都让损失在 20 个 epoch 内开始下降被称为「后悔药」一点也不夸张。6. 一个实用技巧用多任务输出的互相校验提高结果可用性6.1 把三个结果叠加到一张图上快速判断任务间的空间一致性多任务模型的调试难点在于三个输出之间的相对位置是否正确。我建议每次验证时都把检测框、可行驶区域和车道线用不同颜色叠到原图上一眼就能看出问题。import cv2 def visualize(images, det_out, drive_out, lane_out): drive_mask (drive_out.squeeze(1).sigmoid() 0.5).cpu().numpy() lane_mask (lane_out.squeeze(1).sigmoid() 0.5).cpu().numpy() drive_color (0, 255, 0) lane_color (0, 0, 255) for i in range(images.shape[0]): vis images[i].permute(1, 2, 0).cpu().numpy().copy() vis (vis * 255).astype(uint8) vis cv2.addWeighted(vis, 0.7, drive_color, 0.3, 0) vis cv2.addWeighted(vis, 0.7, lane_color, 0.3, 0) cv2.imwrite(fvis_{i}.jpg, vis)这段代码使用固定颜色给整张 mask 叠加染色实际绘图时可以生成彩色掩码再叠加。检查的重点是车道线是否压在影像中的车道位置检测框是否落在可行驶区域内部三者有没有错位。如果检测框在可行驶区域外说明两个头空间没有对齐多半是预测时输入图像尺寸和 mask 上采样尺寸不一致需要回去检查 letterbox 参数。6.2 用可行驶区域掩码过滤检测框去掉路外误检一个常见误检是检测模型把路边的停车、树木错当成目标车辆。这类误检通常中心点落在可行驶区域外。既然多任务模型同时给了掩码就可以直接用掩码值修正检测置信度不需要另跑一个规则模型。def filter_by_drive(det_boxes, det_confs, classes, drive_mask): filtered [] for box, conf, cls in zip(det_boxes, det_confs, classes): x1, y1, x2, y2 box cx (x1 x2) / 2 cy (y1 y2) / 2 h, w drive_mask.shape[-2:] cx_pixel int(cx * w) cy_pixel int(cy * h) if drive_mask[0, cy_pixel, cx_pixel] 0.5: filtered.append((box, conf, cls)) return filtered这里假设drive_mask是模型输出经过 sigmoid 后的概率图尺寸已经还原到原图大小。对每个检测框只看框中心点是否落在可行驶区域内不在就滤掉。注意这个技巧要慎用行人可能站在路肩外车辆正处在可行驶区域边缘中心点落入 mask 外不代表这个目标完全不可用。更稳妥的方式是把中心点附近小邻域的概率均值作为置信度参考而不是单个像素。6.3 半精度推理与 TensorRT 导出的注意点验证阶段如果觉得双精度推理太慢可以先把模型转成半精度。半精度在 Ampere 架构 GPU 上有明显加速在 CPU 上没有任何收益反而可能因为不支持 FP16 报错。代码只需一行model.half() with torch.no_grad(): det_out, drive_out, lane_out model(images.half().to(device))导出 TensorRT 或 RKNN 时三个输出的排列和名称也要提前固定。很多部署工具链只能识别固定数量输出你需要把三个头的结果拼接成一个张量或分别命名。我通常会把分割头输出插值到原图尺寸之后再导出减少部署端预处理负担。这个方案在建图、避障、自动驾驶相关场景里都很常用关键是先把多任务模型本身的输出一致性验证做好再谈部署加速。我自己在 RK3588 上部署时因为输出顺序没固定整整排查了两天最后靠打印输出 shape 才发现问题。这个教训让我养成了「先可视化、后加速」的习惯。希望帮到你。本文还有配套的精品资源点击获取
返回列表