一辆智能车在路上跑一天,保守估计也会产生几十GB的传感器数据:图像、点云、里程、驾驶状态。如果车队有个两三百辆,这个数据量足以把存储机房撑得满满当当。但真正让人头疼的不是"多",而是"多而重复"——同一个路口反复排队,同一条高架桥的同一个弯道,同一个晴天下午的匀速巡航,这些画面今天录一遍、明天录一遍,内容几乎一模一样。
真正稀缺的、能让模型能力上限往上抬一截的样本,可能只有总数据里的百分之几。基于这个观察,我所在的某个智驾项目里逐步尝试了一种做法:把"是否值得学习"这个判断权下放给车端,让车自己决定学习哪些数据,自动筛选高价值数据,再用这些精挑细选的数据去替代传统全量训练,最终输出一个更高效的模型。整套思路我做下来之后发现,它不是在原有训练流程上加一个过滤器,而是从根本上把数据闭环的逻辑颠倒了一遍。这篇文章就把这套方案的来龙去脉、落地细节和坑都写出来,给同样被数据量压得喘不过气的人一个参考。
1. 全量训练不是不好,是账越来越算不过来
1.1 数据量在涨,算力在涨,但"有效信息增量"没涨
很多人谈到训练数据,默认的思维是"越多越好"。这在模型早期确实成立,早期模型表达能力有限,数据多一点点,泛化能力就好一点点。可当模型规模和数据规模都上来之后,你会发现一个尴尬的事实:边际收益在快速递减。
我习惯把训练数据想象成学生刷题。一个学生做第一遍题,错误率降得很快;做到第五遍同一套题,几乎是在浪费时间。全量训练就是让模型一遍遍刷同一套卷子,刷一遍要花费大量GPU时间、大量存储带宽,最后模型真正学到的新知识却很少。
具体到智能驾驶场景,全量训练的浪费更直观。车辆每天在城市道路上跑,95%以上的时间处于正常行驶状态:直行、等红灯、跟车、变道,这些场景模型早就见过无数遍。我见过一个车载日志统计,一天下来真正包含"可学习信息"的片段,按帧数算经常不到15%。把这15%提取出来训练,效果不一定比全量训练差,但成本可以低一个数量级以上。
1.2 简单样本刷屏,让训练过程"营养失衡"
全量训练的第二个问题不只是浪费,而是它会产生一种隐性的偏差:简单样本过量,复杂样本稀缺,模型被简单样本"喂饱"了。
在一个未经筛选的数据集里,晴天直行、车道线清晰、无车干扰的样本占比极高。这样的样本在损失函数里贡献了大部分梯度,模型很容易就把它们学到接近满分。而真正影响自动驾驶安全的长尾场景——夜晚光线突变、施工路段的无序引导、前车突然切入、行人从遮挡物后探出——本来数量就少,再被海量简单样本稀释,在训练过程中几乎翻不起浪花。
你可以去看训练loss曲线,通常前几轮降得很快,后面就在一个平台期徘徊。这个平台期不是因为模型能力到顶了,而是因为数据里已经没有足够多的"新东西"可学。全量训练相当于把大量预算花在了巩固已学会的知识上。
1.3 迭代速度被拖垮,出了问题来不及修
全量训练另一个容易被低估的代价是迭代周期。一辆车发现了一个新的问题场景——比如某个地库出口的坡度识别错误——对应数据传回云端,进入训练集,重新训练一个版本。如果训练集是过去一整年的全量数据,一次训练可能要在上百张GPU上跑几天,等模型发回车上,已经过去一两周。
智能驾驶的数据问题讲究"发现即修复"。新问题发生之后,最有价值的是问题发生前后几十秒内的上下文。如果让车自己决定学习哪些数据,把那些"异常时刻"自动选出来,云端只需要把这个小批量数据接到已有模型上做增量训练或者微调,一天之内就能发布新版本。全量训练保证的是覆盖面,但跟不上变化速度。
2. 把"该学什么"变成车端能算的分数:价值评分的四个信号
让车自己决定学习哪些数据,听上去像是一种"主观意愿",落到工程上其实是一个评分问题。我们需要给每一帧或者每一个连续片段算出一个数值,这个数值代表"它值不值得进入训练集"。我在实践中发现,单纯靠某一种信号很容易出现偏科,最好组合四类信号。
2.1 不确定性:模型自己知道自己没把握
最直觉的信号是模型当前输出有多"犹豫"。如果一个检测模型对前方障碍物的置信度只有0.6,或者分割模型对路肩边界给出了一团模糊的预测,这帧数据很可能包含它没见过或没学透的模式。
车端在正常行驶时本来就在跑感知模型,顺手把每一帧输出的置信度、熵值记录下来,几乎不增加额外算力。具体可以看几个指标:检测框的类别置信度、多目标之间的混淆程度、车道线回归结果的方差。这些信号总结成一句人话就是:如果连模型自己都拿不准,这帧数据就该进训练集。
2.2 新颖性:和"已经会的东西"越不像越值得学
不确定性解决"没把握",但光看不确定性还不够。有些场景模型输出很自信,比如第一次见到一段特殊纹理的隧道内壁,模型可能粗暴地把它识别成普通墙面,同时置信度还很高。这种"自信的错误"恰恰最危险,光靠不确定性信号抓不到它。
所以还要有一个新颖性信号,衡量当前帧与已训练数据分布之间的距离。做法是把当前帧经过一个轻量embedding网络变成特征向量,和历史训练集里的特征中心算距离。距离越大,说明这种数据此前没见过,价值越高。这里需要注意,embedding网络不要太重,不然车端算力吃不消,后面会单独说。
2.3 风险暴露:把场景的危险程度也当成学习信号
并不是所有有价值的数据都体现在模型输出上。有些场景即使模型处理得很好,也应该被记录下来反复学习,因为这些场景一旦出错后果更严重。比如车辆紧贴大货车并行的画面、儿童在路边嬉戏的画面、夜间对向车开远光灯的画面。
这一块我是用规则引擎来做打分的:把危险场景的关键要素拆成规则,比如目标类别、相对距离、相对速度、道路属性、光照条件,只要命中高危组合,就给这一帧的风险项加分。这部分的实现不依赖模型,纯靠工程规则,好处是稳定、可解释,坏处是规则需要人工维护。
2.4 分布漂移:当车跑进"陌生的世界"时
第四类信号是分布漂移。智能车经常跑陌生区域,或者遇到季节更替、天气剧变,整个输入分布会整体偏移。比如一条新开通的城市快速路,交通标识牌样式和训练数据里的不一样;又比如深秋满地落叶,车道线被遮挡。
分布漂移信号的做法是:车端每隔一段时间计算当前帧特征与基准分布的距离,如果连续一段时间偏移都很大,就把这段时间的数据整体标记为高价值。这有点像人到了一个陌生城市,自然会放慢速度多看路况。分布漂移信号能把一批"看起来普通但实际是新世界"的数据抓回来。
2.5 加权合成比分与一键触发规则
这四个信号算出来后,我会做一个加权合成:
value = alpha * uncertainty + beta * novelty + gamma * risk + delta * drift权重在项目初期靠经验定,后期靠实验对比调整。但我不建议只用一个总分来决策,因为总分会把某些极端情况平均掉。更稳的做法是双轨制:一轨看总分,超过阈值就保留;另一轨看单项,如果风险项超过单独设定的阈值,即使总分不高也必须保留。
实际车端部署时,不需要每帧都跑完整评分。我的习惯是按事件组织数据:先让车端以低帧率持续评估,一旦评分超过阈值,就把触发前6秒、触发后10秒这一段完整数据打包上传。这样既避免逐帧上传的碎数据问题,也保留了事件的上下文,给标注和后续训练留出理解空间。
3. 车端挑数据、云端重训:一个最小的闭环设计
3.1 车端:滚动缓存、评分、打包上传
说完了分数,来看整个系统在车端是怎么组织的。很多人以为"让车自己决定学习哪些数据"就是简单地在车里放一个判断程序,觉得重要就传,觉得不重要就删。实际上更稳妥的设计是多加一层滚动缓存。
车端传感器数据先写进一个循环缓冲区,容量大约能覆盖几十秒到几分钟。评分器持续对缓冲区里的新数据打分,一旦触发高价值事件,就把事件前后的一段数据固化下来,而不是只存当前触发帧。为什么这样做?因为真正有用的信息经常出现在事件发生之前的几秒——比如前车司机有一个微妙的偏离意图,人的预测系统能从车头轻摆中读出来,而纯图像模型可能要到前车真正压线时才发现。如果只存触发帧,等于把最关键的因果信息丢掉了。
上传之前,车端还会做一个轻量的后处理:剔除完全静止的帧、做基本去重、统一压缩格式。能省一点带宽是一点,尤其车队规模大了之后,带宽成本不是小数目。
3.2 云端:二次编排、主动采样兜底、优先级标注
云端收到的不是一堆原始数据,而是一批带价值分数的数据包。云端数据中心要做的第一件事,不是直接丢进训练集,而是先做二次编排。
二次编排主要包括三件事。一是跨车辆、跨时间的去重,同一辆车在同一个路口反复触发的高价值事件,价值已经在递减,只保留代表性样本即可。二是标注优先级的排序,标注员人力有限,分数最高的数据包优先安排标注。三是主动采样兜底,我强烈建议无论怎么筛选,必须保留5%-10%的随机样本进训练集。这个兜底非常关键,后面讲坑的时候会展开。
云端训练这边,通常的做法是在已有模型基础上做增量训练或微调。因为筛选后的数据量远小于全量数据,一次训练的时间和资源消耗都大幅下降,团队可以用更高频率来发布模型版本,形成快节奏的学习闭环。
3.3 阈值不是定死的,要用"模型自信度"动态调整
价值评分的阈值如果定成固定值,会有一个隐患:模型在持续变强,同样一段数据,旧模型看很新奇,新模型看已经平淡。不确定性分数会随着模型迭代整体下降,如果阈值不变,筛选出来的数据会越来越少,慢慢退化回"只挑危险场景"的路线。
我比较推荐的做法是:每发布一个新模型,就在云端用一批固定的历史数据重新计算一遍价值分,找到当前分布下的某个分位数作为新阈值。比如把阈值定在"分数超过当前分布80%分位数的数据才保留"。这样阈值会跟着模型能力自动调整——模型变强了,阈值自动变严;模型某方面变弱了,相关数据的不确定性分数抬升,又会自动被筛进来。
4. 跑了小半年之后,我印象最深的五个坑
这套方案听起来逻辑通顺,但真正放到车队里跑,踩过的坑比我想象的多。我挑几个影响最大的展开。
4.1 模型越练越强,数据价值会"缩水"
第一个坑就是上面说的阈值漂移。项目初期我们设定了一个固定阈值,第一周筛出来的数据量还正常,跑了两三个模型版本之后,筛出来的数据量肉眼可见地变少。原因不是路上的场景变简单了,而是模型对新场景的适应速度变快,同一个场景第二次遇到时,不确定性分数已经降下来了。
这个坑的教训是:数据价值不是数据的绝对属性,而是数据和当前模型之间的关系。同一个路口的夕阳逆光场景,对V1模型是地狱难度,对V3模型可能已经是小菜一碟。系统设计一定要围绕"当前模型能力"来动态调整,而不是一次性写死。
4.2 筛选偏差:只学自己想学的,必然出现盲区
第二个坑比较隐蔽。如果车端完全靠评分器决定学什么,训练集就会慢慢变成"评分器认为有价值"的数据的集合,这个集合不等于"所有值得学的数据"的集合。评分器本身也有盲区,它没见过的东西自然不会给它高分。
举个真实的例子:某次我们发现模型对一种非常规施工路障的识别能力几乎为零,但回查筛选记录,这种施工路障的画面从来没被筛进来过,因为评分器里没有针对它的规则,embedding距离也没有把它判成足够新颖。这是一个鸡生蛋蛋生鸡的问题。
解决的办法是给筛选系统打一个"补丁":在保留高价值事件的同时,按固定比例混入随机采样数据。随机采样不一定都能用,但它能保证训练集不会完全被筛选器的主观偏好主导,总有一部分"意外数据"能流进来。比例我建议控制在5%-10%,太低起不到兜底作用,太高又失去了筛选的意义。
4.3 冷启动:没有历史分布,新颖性分数从哪来
第一个版本上线时,我们根本没有"历史训练集特征中心"这个东西,新颖性信号无从算起。一开始直接用预训练embedding来替代,但预训练模型的特征空间和车辆实际场景的分布差得挺远,算出来的分数参考意义有限。
后来我学乖了:冷启动阶段不做精细评分,先用规则加不确定性打底,同时保证随机采样的比例拉高到20%左右。跑了两周,积累了足够的新数据之后,再逐渐降低随机比例,把新颖性信号接进来。这个过程有点像一个新员工先跟着师傅做,熟悉业务之后才让他独立判断。不能上来就全靠评分器。
4.4 高价值数据挑出来了,但没人标注
筛选只是一个环节,选出高价值数据之后还得标注,标注才是真正的瓶颈。我记得有一次,车端筛选器一天传上来大量夜间复杂场景的数据包,云端AI平台存了几百GB,但标注团队按正常节奏要消化两周。等标注完、训练完,这些场景的时效性已经下降了。
应对思路有两条。一是给标注队列做严格排序,让分数最高的数据包优先被处理,不必按到达时间排队。二是引入半监督和自动预标注,用已有模型先跑一轮粗标签,标注员只负责修正关键目标,大幅压缩人工标注时间。对智驾来说,要接受一个现实:筛选器挑选数据的速度可以远超人类标注的速度,所以系统里必须设计好标注预算的分配策略。
4.5 触发时机没抓好,想要的数据根本不在包里
最后一个坑是关于数据包的截取时机。早期我们只在评分超过阈值后触发记录,结果发现有些高价值事件的前半段被浪费了。比如一次轻微剐蹭事故,真正有用的信息其实在事故发生前10秒——前车异常减速、路面湿滑、驾驶员开始变道。等我们的评分器通过感知结果识别到"异常"时,前几秒的传感器流已经被循环缓冲区覆盖掉了。
后来我把方案改成两层触发:第一层是低阈值预览,只要评分有上升趋势就先冻结缓冲区,给后续保留空间;第二层是高阈值确认,真正打包上传。同时把事件包的截取范围往前扩展了几秒。这样虽然会上传一些"误报"数据,但漏报的概率大幅下降。
5. 复刻一条自筛选数据管道的最短路径
如果你现在想在自己的项目里试一下这套东西,不一定要先搭车队,可以先在一个小范围里把它跑通。我给你一条我验证过的最短路径。
5.1 先只加一个信号,不要一上来就搞四信号加权
很多人听到四个信号之后的第一反应是:马上把四套算法都实现出来,再调一堆权重。我劝你打住。先只做不确定性信号,理由很实际:它实现成本最低,车端本来就在跑模型,顺手记录置信度就行。
用一段时间之后,你会自然发现哪些高价值样本被漏掉了,这时再针对性地补第二个信号。比如发现晴天高速巡航的重复数据太多,就补一个简单的新颖性信号;发现危险场景经常漏,就补规则风险信号。增量式迭代比一步到位稳得多。
5.2 一个可参考的评分器简化实现
这块我贴一个伪代码级别的参考,实际工程里需要根据你的模型接口自己做适配。
def compute_frame_value(sensor_frame, model, embedder, history_index): # 1. 不确定性:取模型关键输出的平均熵 pred = model.predict(sensor_frame) uncertainty = average_entropy(pred.key_outputs) # 2. 新颖性:当前帧与历史特征中心的最短距离 feat = embedder.encode(sensor_frame) novelty = distance_to_nearest_cluster(feat, history_index) # 3. 风险:规则引擎打分,命中危险要素直接抬分 risk = rule_risk_engine(sensor_frame) # 4. 组合 value = ( 0.4 * normalize(uncertainty) + 0.2 * normalize(novelty) + 0.3 * normalize(risk) + 0.1 * normalize(drift_score(sensor_frame, history_index)) ) return value def should_trigger(value, single_alerts): if value >= config.HIGH_VALUE_THRESHOLD: return True if any(single_alerts[k] >= config.ALERT_THRESHOLD[k] for k in single_alerts): return True return False这个简版实现有三个关键点:所有信号都要先做归一化再加权,否则数值范围大的信号会压过其他信号;触发逻辑是"总分过线"和"单项过线"的或关系;drift_score一开始可以直接设成0,后面有数据了再接。
5.3 用一场仿真实验验证整体收益
落地之前,强烈建议先在仿真环境里跑一组对照实验。我当初做的方式是:同一个场景集,一组用全量数据训练,一组用跑完评分器之后筛选出的5%-10%数据训练,对比两组在保留场景和长尾场景上的表现。
第一次做这个实验时,我的预期是"筛选组和全量组持平,但成本降低",实际结果比预期好一点:筛选组在典型场景上准确率略有波动,但在长尾困难场景上往往能超过全量组。原因也容易解释:全量训练被大量简单样本稀释,而筛选训练把数据量集中在了模型真正欠缺的地方。如果你做出来的结果不是这样,大概率是筛选器漏掉了某些重要信号,好好检查一下你的评分项。
6. 车只是第一个例子:自主选数据思路的通用配方
最后想多说一点,这套"让终端自己决定学习哪些数据"的思路,绝对不只在智能驾驶领域有用。它本质上是一个通用配方:终端设备判断自己的数据价值,只把高价值数据传回中心,中心用精挑细选的数据更新模型,再将更聪明的模型下发到终端,形成循环。
我在工业质检项目里试过类似的做法。产线上的相机如果每张照片都传回服务器,网络和存储很快会爆;改成在相机端跑一个轻量评分器,只把"疑似缺陷但模型置信度低"的照片传回去,服务器的存储压力立刻降了下来,模型对新型缺陷的发现速度反而更快。还有语音助手设备、智能安防摄像头、工业机器人,场景不同,逻辑一模一样。
我觉得这套思路最核心的认知转变是:终端设备不再是一个被动的数据采集器,而是一个有判断能力的参与者。它虽然不能像云端一样做大规模训练,但它离数据最近,最清楚当前这个时刻遇到的事情是平淡还是异常。把"是否值得学习"这个判断下放给终端,云端就能从海量低价值数据里解放出来,把算力花在真正改变模型能力的数据上。
如果要从这套方法论里提炼一句最有价值的经验,我的体会是:不要追求一个完美的评分器,而是设计一个能兜底的筛选闭环。评分器一定会犯错,但只要你保留了一部分随机采样、让你的筛选器能识别自己的盲区,系统的整体安全性就依然可控。筛选器的目标不是帮助你找到所有高价值数据,而是帮助你用最低的成本找到绝大部分高价值数据,同时不被自己的偏差困住。