游乐游手机版
首页/AI热点日报/热点详情

感知检测小模型训练效率提升150%的优化方法

类型:热点整理2026-07-19
针对智能驾驶感知检测小模型训练,从调度层、应用层和挂载区三方面优化:创建进程池与共享内存加速CPU处理,启用pin_memory、增加num_workers和使用torch compile提升计算效率,选用支持高速eRDMA网络的CPFS存储并选择合适可用区,显著提升训练效率。

近年来,智能驾驶技术的演进速度明显加快,车辆对周围环境的实时感知与决策能力,已成为决定整个系统性能上限的关键因素。无论是目标检测、语义分割,还是多传感器融合,这些核心感知模块的背后,都离不开大规模深度学习模型的训练与部署。随着自动驾驶等级从L2向L3乃至L4迈进,模型复杂度与数据量呈现指数级增长,这对底层计算平台——尤其是算力、内存带宽、并行处理能力和能效比——提出了前所未有的高要求。

目前行业内主流的高性能计算平台大多基于高速GPU集群,这类方案在内存容量和带宽上具有显著优势,能够支撑大批量数据的分布式训练,也为更复杂的模型架构创造了条件。在此背景下,在典型的智能驾驶场景中——例如高精度目标检测、点云感知以及多模态融合感知任务——对不同算力卡进行全面的性能对比测试,成为一项极具实际价值的工作。它能为客户在选择算力资源时,提供真实可信的数据支撑。

先来看本次测试的基础配置:环境方面采用mmdet3d、mmcv、flash-attn、nuscenes-devkit,以及torchrun分布式训练框架;算力部分涉及两种不同的算力卡,分别定义为机器一和机器二;模型方面选择了maptr、sparsedrive、qcnet和Gaussianformer四个有代表性的小模型;数据集则使用了nuScenes和Agoverse。

技术架构与完整流程

整个对比测试基于PAI DSW实例完成。在DSW机器上进行训练实验,整体的训练步骤大致如下(以maptr模型为例):

第一步,选择合适的DSW镜像,核心原则是确保Python和CUDA版本与模型的要求相匹配。这里我们直接选了autodrive镜像,里面已经预装了必要的mmcv等依赖,可以减少不少环境配置的麻烦。

接下来,根据模型在GitHub上的官方文档安装剩余依赖。这一步往往是整个过程中最磨人的——版本冲突、缺少某个库、编译失败……碰到报错就换一个版本重新试,反复几次才算把环境清理干净。

创建DSW实例时,需要指定一个CPFS挂载点。模型文件和数据集就放在这个挂载的CPFS里,实现持久化存储,避免每次训练都重新下载。

最后,执行训练命令,通过日志记录下训练时间和吞吐量。

下面这张表列出了本次测试涉及的四个模型,它们都属于目标检测和感知领域的小模型,很适合智能驾驶的应用场景:

类型模型主要依赖官方地址
感知-地图构造maptr v1mmdet3d 0.17.2 / mmcv 1.4.0https://github.com/hustvl/MapTR
感知-端到端sparsedrivemmcv 1.7.1 / flash-attn 2.3.2https://github.com/swc-17/SparseDrive
感知-预测QCNetmmdet3d 0.17.2https://github.com/ZikangZhou/QCNet
感知-目标检测GaussianFormermmcv 2.0.1 / mmdet3d 1.1.1https://github.com/huang-yh/GaussianFormer

实际遇到的坑与解法

环境依赖冲突:一个常见但棘手的问题

在实际测试中,环境依赖冲突几乎是绕不过去的坎。以maptr模型为例,安装mmcv=1.4.0时,build wheels直接报错了。

根据mmcv官方文档仔细排查后发现,1.4.0这个版本的mmcv属于较老的依赖,它要求torch版本必须在1.9.1到1.10.0之间。问题在于,DSW官方的镜像中已经很难找到满足这个要求的低版本torch了。如果强行卸载torch安装低版本,又会引发大量镜像自带库的兼容错误。而mmcv 1.4.0又是maptr的硬性依赖。

最终针对maptr的解决方案是:选择带py38+cu111版本的Ubuntu裸镜像,重新安装合适的torch版本,再装mmcv。虽然折腾,但确实有效。

另一个典型案例是在sparsedrive模型测试中碰到的flash-attn安装问题。安装flash-attn=2.6.1时,setup阶段再次失败,错误信息看起来像是CUDA版本不对。但查了flash-attn的官方文档,又显示它和torch 1.2.3是兼容的。深入排查后发现,根因出在Python 3.8的torch/_utils_internal.py文件里——它调用了transformers库的一个函数,而这个函数在高版本的transformers中已经被废弃了。只有该模型指定的transformers 4.30.1还在使用这个函数。缺少这个函数,就会导致"undefined symbol"的错误。

解决方法是降级transformers库:直接执行pip install transformers==4.30.1,问题即可解决。

源代码适配:不同硬件带来的差异

不同算力卡之间的底层编译环境差异,也会给模型测试带来困扰。处理这种问题,通常需要对源代码做一些适配。

local_rank处理就是一个典型的例子。local_rank是用来指定GPU序号的参数变量,在多卡并行训练时必须传入默认值。目前大部分模型——包括本次测试的四个模型——都默认以Nvidia算力作为环境,因此会在启动脚本中硬编码传入参数--local_rank。但不同的算力卡调度逻辑不一样,可能使用不同的GPU序号变量。

比如sparsedrive在机器一上执行启动命令时,就会出现变量错误。解决方式是在训练脚本中修改代码:parser.add_argument('--local-rank', type=int, default=0)

再来看NCCL P2P的问题。DDP多卡并行训练时,默认会使用多卡之间的点对点通信,多张卡之间直接共享模型权重和数据集。点对点通信会利用卡间的内部通信网络,不经过主机内存中转。

在实际测试中,偶尔会出现rank0和rank1参数不一致的错误:rank1没有读取到rank0的状态,导致多卡通信失败。这时候需要禁用点对点通信来解决。具体做法是在训练脚本中加入EXPORT NCCL_P2P_DISABLE=1这个参数。

需要注意的是,虽然P2P被禁用后多卡状态共享不再通过高速网络,训练效率会略有下降,但至少能让训练跑起来。

性能优化:让算力卡真正释放潜能

在对比测试中,我们可以采用一些优化手段,让算力卡充分发挥算力优势。下面从CPU处理、应用层优化和外部挂载三个方面来聊。

CPU处理加速

在maptr模型测试中,我们发现了一个很有意思的现象:算力卡的显存使用率和显存占用量都比较低。监控数据显示,训练进程大量时间在CPU核心上执行,GPU上的运算很快就结束了,然后一直在等CPU处理完才能继续。

原因在于,maptr属于小模型,本身的模型计算节点数远少于大模型。根据源码分析,GPU主要负责模型的梯度计算和参数更新,而CPU要负责读取图片数据做预处理,生成tensor。监控结果清晰地表明,CPU的处理时间成了瓶颈,GPU只能干等着。这种情况下,算力卡自然无法发挥全力。

针对这个痛点,我们引入了两种优化手段:

进程池是一个很实用的工具。它通过预先创建一组工作进程,把多个任务分配给这些进程并发执行,从而充分利用多核CPU资源。用进程池的好处在于,可以避免频繁创建和销毁进程带来的性能开销,因为池里的进程是复用的。对于CPU计算要求高的小模型,进程池的加速效果非常明显。进程数一般设置为CPU核心数:

from multiprocessing import Pool

def image_process(image):
    ...
    return tensor

if __name__ == '__main__':
    with Pool(processes=n) as pool:
        results = pool.map(image_process, image_list)
        print(results)

共享内存是另一种高效的进程间通信机制。它允许多个进程访问同一块内存区域,数据交换时不需要在内核与用户空间之间频繁复制,通信开销极低。多进程配合共享内存使用,可以显著加快CPU处理速度。共享内存的大小一般设置成实例的分配内存即可;但如果数据占用量很大,需要酌情增加。

Torch应用加速

PyTorch自身也提供了一些内置的加速方法,能够在数据加载和模型计算层面提升效率。

pin_memory这个参数的作用是将数据加载到page-locked内存中。这种内存不会被操作系统交换到磁盘,因此可以更高效地通过PCIe总线传输到GPU,让GPU直接异步访问。这样一来,从CPU到GPU的数据拷贝速度会明显加快。使用方法很简单:

dataloader = DataLoader(
    dataset,
    batch_size=32,
    shuffle=True,
    num_workers=4,
    pin_memory=True)

num_workers则是指定用于数据预处理和加载的子进程数量。每个子进程负责从磁盘读取数据、做预处理,然后把处理好的batch数据传给主进程。多个workers并行工作,能够实现“GPU计算+数据加载”同时进行,训练效率自然就上去了。原理和上面的进程池类似,只是这个是torch自带的专用加速方法。num_workers的值一般设置为CPU核心数。

最后是torch.compile,这个功能值得多说两句。它使用torchdynamo对模型进行追踪,把模型的计算路径记录下来,忽略掉非必要的控制流,构建出一个可优化的计算图。然后对这张计算图进行算子融合、常量折叠、内存布局优化等一系列操作,最后把优化后的图转换成目标平台的高效代码。PyTorch默认使用Inductor编译器来执行,在后续的前向/反向传播中直接调用,完全绕过Python解释器,实现高速执行。

不过有一点需要警惕:compile无法编译非PyTorch的指令,如果你用了自定义的C/CUDA扩展,可能会遇到兼容性问题。

model = torch.compile(
    model,
    backend='inductor',
    dynamic=False,
    fullgraph=False,
)

CPFS优化加速

在maptr模型测试中,我们还发现了一个很奇怪的性能差异:模型在写入checkpoint文件到CPFS时,耗时的差别非常大。在同一地域乌兰察布的CPFS A区,机器一写入时延高达2分钟左右,而机器二秒级就能写完。理论上同一套CPFS不应该有这么大的读写性能差异。后台监控显示,两台机器上的CPFS时延差了整整3倍。

后来分析了CPFS的实例情况,发现挂载的CPFS都位于乌兰A区,但根据智算CPFS官方文档,CPFS使用高速eRDMA网络通信,目前只有乌兰C区支持。初步判断就是可用区的问题,导致机器一无法使用高速网络读写,效率才这么低。

重新新建一个C区的CPFS后重新训练,写入延迟大幅降低,问题迎刃而解。

这里可以总结一下:要启用CPFS的高速eRDMA网络读写能力,CPFS的可用区必须选择乌兰C区。

最终测试结果

从loss曲线来看,机器一在100,000 step时基本收敛,机器二在180,000 step时基本收敛,收敛趋势保持一致,基本符合预期的加速比。

总结

如果在实际业务场景中需要训练感知检测类的小模型,可以从调度层、应用层、挂载区三个维度来优化性能:

模型训练过程中的tensor计算,会先进入底层CPU做数据预处理,然后处理完的tensor交由Cuda GPU算力做梯度计算,训练过程中产生的checkpoint会写入到挂载区CPFS。针对这三个阶段,分别对应三方面的优化:

  1. 调度层:创建进程池,提高CPU并行处理速度;增加共享内存,加快CPU读取内存的速度。
  2. 应用层:在torch DataLoader中启用pin_memory,加快数据传输速度;增加num_workers,提高并行计算速度;用compile加速模型计算。
  3. 挂载区:使用有高速读写网络的CPFS存储,并选择合适的可用区。
来源:https://www.53ai.com/news/finetuning/2025072471386.html

相关热点

继续查看同栏目近期热点。

延伸阅读

补充最近整理过的热点入口。