1. 项目概述:当机器学习遇上嵌入式运动传感
在智能照明、安防监控这些我们日常接触的领域,运动传感器是背后的“隐形守护者”。你可能没注意过它,但它时刻在判断:走廊里是有人走过,还是仅仅是一阵风?办公室里是员工在伏案工作,还是空调出风口在晃动?这个看似简单的“是”或“否”的判断,在工程实现上却充满了挑战。尤其是在追求极致能效和可靠性的嵌入式设备上,传统的信号处理算法往往捉襟见肘,而引入机器学习(ML)又带来了新的难题——数据不平衡。
想象一下,你要训练一个AI来识别“人走动的信号”和“环境噪声信号”。为了确保可靠性,你需要收集海量的噪声数据,可能长达数万小时,只为捕捉那些偶尔出现的、容易误判的突发干扰(比如无线电干扰或设备开关的瞬态脉冲)。而真正有价值的“人走动”事件数据,可能只有几千个,总时长加起来还不到一小时。这就是典型的数据不平衡:两类样本的数量可能相差上万倍。直接用这些数据去训练一个标准的分类模型,比如一个神经网络,结果往往是模型会“偷懒”——它发现只要把所有输入都预测为“噪声”,就能获得极高的准确率(比如99.99%),但这对于检测“运动”这个核心任务来说,是完全失败的。
更棘手的是,应用需求本身也是“不平衡”的。在照明控制场景中,对“开灯”和“关灯”的判断要求截然不同。当灯处于关闭状态时,传感器需要检测到有人接近并立即开灯,这要求极低的误报率(比如每1000小时最多误触发1次)和极快的响应速度(如0.6秒)。因为一次误报可能导致深夜里空无一人的办公楼突然灯火通明。而当灯已经开启,传感器需要持续检测微小的活动(比如翻书、打字)以保持灯光不灭,这时允许一定的漏检(比如检测率高于25%即可),误报率要求也可以放宽(如每20小时少于1次),因为误报顶多是让灯多亮一会儿。
面对这种“数据不平衡”叠加“需求不平衡”的双重挑战,常规的“训练一个模型,然后调整输出概率阈值”的方法效率低下且难以达到最优。本文要分享的,正是我们团队在应对这一挑战时,摸索出的一套从理论到实践的完整方案:通过设计定制化的损失函数和分阶段的概率模型乘积架构,实现面向嵌入式运动传感的高效、自动化机器学习优化。这套方法不仅大幅提升了训练效率,降低了模型复杂度,更重要的是,它让模型训练直接对准最终的应用性能指标,省去了繁琐且不稳定的后期手动调参。
2. 核心挑战与设计思路拆解
2.1 深入理解数据与需求的不平衡本质
在动手设计解决方案之前,我们必须先透彻理解问题本身。运动传感的数据不平衡并非简单的数量差异,其背后有深刻的物理和工程原因。
数据层面的不平衡:噪声数据(负样本)海量但“信息密度”极低。绝大部分噪声时段,传感器信号幅值很小,近乎于基线噪声,对模型训练几乎没有贡献。我们收集数万小时噪声数据,核心目标是为了覆盖那占比可能不到0.01%的“棘手的突发噪声”(Spurious Noise),例如由远处开关电源、对讲机信号或特定电磁干扰产生的、形态上与真实运动信号有几分相似的脉冲。如图1所示,左边是典型的运动信号,中间是常见的环境噪声,而右边就是这种棘手的突发噪声。如果我们将连续数据流切割成固定长度(如3.2秒)的片段,那么“运动片段”和“噪声片段”的数量比将达到惊人的1:10000甚至更高。
需求层面的不平衡:这直接源于产品功能定义。对于“开灯检测”(灯初始为关闭状态),我们追求的是“宁可错过,不可错杀”。因为误开灯(False Positive)带来的用户体验损害和能源浪费是显性的。因此,我们需要极低的误报率(FPR,如 < 0.0001%)和较高的查全率(Recall,即True Positive Rate, TPR)。而对于“保持检测”(灯已开启状态),逻辑变为“宁可错留,不可错关”。因为漏检一次微小的活动(False Negative)可能导致灯光意外熄灭,影响用户体验,而误报(将噪声判为活动)仅仅导致计时器重置,灯光多亮一会儿,代价相对较小。因此,我们可以接受相对较低的查全率和较高的误报率。
传统方法的瓶颈:面对这种双重不平衡,标准做法是:
- 类别加权:在训练损失函数(如二元交叉熵)中,给少数类(运动)样本赋予更高的权重。如图3所示,调整权重可以改变模型在验证集上的性能曲线(ROC曲线),但找到恰好满足两个不对称需求的权重点,需要大量的试错训练,过程低效。
- 阈值调优:先训练一个模型,再在其输出概率上寻找一个阈值,使得在该阈值下的TPR和FPR满足要求。但问题在于,对于一个在高度不平衡数据上训练出的模型,其输出概率的校准性可能很差,概率值集中在0或1附近,导致可调节的阈值区间非常窄,甚至不存在一个阈值能同时满足两个严苛且不对称的指标。
我们的核心思路是绕开这些间接的、试错式的优化路径,让模型的训练目标直接就是最终的应用性能指标。换句话说,我们设计的损失函数,其最小值点就应该对应着一个同时满足“TPR > 某值”且“FPR < 某值”的模型。这听起来很理想,但实现起来需要解决几个关键问题:性能指标(TPR, FPR)是基于离散计数的,不可微,无法直接用于梯度下降;以及,如何高效处理海量的、大部分是“简单样本”的噪声数据。
2.2 方案架构:分而治之的概率模型乘积
我们的整体方案架构灵感来源于一个朴素的想法:既然大部分噪声样本都很“简单”(易于区分),为什么不让一个简单的模型先把它们过滤掉,从而让后面更复杂的模型专注于处理那些“困难的”样本(包括复杂噪声和运动)呢?
我们构建一个总的分类模型p(x, w),但它不是单一的网络,而是两个子模型的乘积:
p(x, w) = S(w_{2a} * L(p1(x, w1)) + w_{2b}) * p2(x, w_{2r})
这里:
p1(x, w1):第一级过滤器。它是一个极其简单的模型(例如,就是一个基于信号能量的线性分类器)。它的任务不是直接区分运动和噪声,而是区分“简单噪声”和“复杂噪声/运动”。它的设计目标是高召回率,即尽可能放过所有可能是运动或复杂噪声的样本。S(w_{2a} * L(p1(...)) + w_{2b}):一个可学习的门控函数。其中S是Sigmoid函数,L是Logit函数(即Sigmoid的反函数)。这个部分的作用是动态调整第一级过滤器p1的“严格程度”。w_{2a}和w_{2b在训练中学习,相当于自动为p1的输出寻找一个最优的过滤阈值。p2(x, w_{2r}):第二级精细分类器。这是一个相对复杂的模型(例如一个小型卷积神经网络),它只接收通过第一级过滤的样本。它的任务是最终区分“复杂噪声”和“真实运动”。
这种架构的巧妙之处在于:
- 训练效率:在训练主模型
p(x, w)时,对于任何一个噪声样本,如果S(...)部分的值很低(意味着被p1判定为“简单噪声”),那么无论p2的输出是什么,整个p(x, w)的输出都会趋近于0。在计算损失函数的梯度时,这些样本的贡献为零。这意味着在每一次训练迭代中,我们实际上只用了那些“困难”的样本(被p1放过或p2需要仔细判断的样本)来计算梯度,从而实现了对海量数据的高效训练,训练速度可提升数十甚至上百倍。 - 自动化:门控函数的参数
w_{2a}, w_{2b是端到端训练得到的,与p2一同优化。这避免了手动设置p1阈值的过程,让整个系统能自动学习如何分配两级模型的工作。 - 模型简化:由于
p1和p2各自只需要完成相对简单的子任务(p1抓大放小,p2精雕细琢),它们各自都可以设计得比一个试图解决所有问题的单一模型更简单、参数更少。这对于嵌入式部署至关重要。
3. 定制化损失函数的设计与实现
要让模型训练直接优化最终指标,核心在于设计一个可微的、能够编码具体TPR和FPR要求的损失函数。我们的方法是将离散的“计数”转化为连续的“分数”,从而使其可导。
3.1 从离散计数到连续分数
对于一个样本i,模型会输出一个预测概率y_pred_i(介于0和1之间)。在最终决策时,我们设定一个阈值(通常是0.5),若y_pred_i > 0.5则判为正类(运动)。
传统的TPR和FPR计算是硬性的:TPR = (TP计数) / (总正样本数),FPR = (FP计数) / (总负样本数)。计数是不可微的。
我们的技巧是引入一个“软化”的计数。定义一个“分数正例”函数fp_i,对于单个样本:fp_i = ∫_{1/2}^{∞} s(y, y_pred_i, λ) dy
其中,s(y, y_pred_i, λ)是一个以y_pred_i为中心、宽度为2λ的矩形窗函数(当λ→0时,它趋近于一个冲激函数)。如图4所示,这个函数在[y_pred_i - λ, y_pred_i + λ]区间内值为1/(2λ),否则为0。
这样设计的意义:
- 当
λ→0时,fp_i就退化成了硬判决:如果y_pred_i > 0.5,则fp_i = 1;否则为0。 - 当
λ > 0时,fp_i是一个关于y_pred_i的连续、可微的函数。特别是,当y_pred_i < 0.5 - λ时,fp_i恒为0。这意味着,对于那些被模型非常确信为负例(噪声)的样本,它们对损失函数的梯度贡献为0。这正是我们实现“聚焦困难样本”的数学基础。
基于此,我们定义整个数据集上的分数假正例(FFP)和分数真正例(FTP):
FFP(w) = Σ_{i ∈ 负样本} fp_i(近似于FP计数)FTP(w) = Σ_{i ∈ 正样本} fp_i(近似于TP计数)
3.2 构建直接面向需求的损失函数
有了连续的FFP和FTP,我们就可以构建一个直接衡量模型表现与目标差距的损失函数。假设我们的需求是:FTP >= TPreq(真正例分数要求)且FFP <= FPreq(假正例分数要求)。
我们定义两个误差项:
E_FP(w) = [ (FFP(w) / FPreq) - 1 ] * θ( FFP(w) - FPreq )E_TP(w) = [ (TPreq / FTP(w)) - 1 ] * θ( TPreq - FTP(w) )
这里的θ是阶跃函数(Heaviside step function)。这个设计非常精妙:
E_FP:只有当实际的FFP超过了允许的FPreq时,该项才不为零。超过得越多,惩罚越大。如果FFP满足要求(小于等于FPreq),该项为零,模型不会在降低FFP上浪费优化能力。E_TP:只有当实际的FTP低于要求的TPreq时,该项才不为零。低得越多,惩罚越大。如果FTP满足要求,该项为零。
最终的损失函数是两者的欧几里得范数:E(w) = sqrt( E_FP(w)^2 + E_TP(w)^2 )
这个损失函数的优化目标非常明确:驱动模型参数w,使得FFP和FTP同时满足要求。它是一个“可行域搜索”过程,而不是传统的“最小化所有错误”。
3.3 参数λ的作用与训练策略
宽度参数λ在这里扮演着“平滑度控制”和“训练引导”的双重角色。
- 控制平滑度:较大的
λ使得fp_i函数更平滑,损失函数的景观(Landscape)也更平滑,有助于在训练初期避免陷入糟糕的局部极小值。 - 控制样本选择:如前所述,
λ定义了“梯度消失区域”。对于负样本,如果y_pred_i < 0.5 - λ,则其梯度为零。通过调整λ,我们可以控制有多少“简单负样本”被排除在梯度计算之外。
因此,我们采用了一种退火式训练策略:
- 阶段一:使用较大的
λ值开始训练。此时损失函数平滑,模型可以自由地探索参数空间,初步学习区分模式。 - 阶段二:随着训练进行,逐步减小
λ(例如每次减半)。随着λ变小,损失函数越来越接近真实的、离散的指标,同时梯度计算也越来越聚焦于那些预测概率在决策边界(0.5)附近的“困难样本”。 - 阶段三:在训练
p(x,w)时,我们固定第一级模型p1的参数w1,只训练第二级门控和分类器的参数w2a, w2b, w_{2r}。每次迭代,我们只选取那些对当前损失函数有贡献(即fp_i > 0)的样本参与计算,这构成了一个动态的、自适应的困难样本挖掘机制,极大提升了训练效率。
4. 实战:从数据到嵌入式友好型模型
4.1 数据准备与特征工程
我们使用的数据集来自实际的微波运动传感器:
- 运动数据:869段时长3.2秒的时间序列(采样率240Hz),总计不到1小时的数据。
- 噪声数据:超过840万段同样3.2秒的时间序列,总计约7600小时。
数据按2:1划分为训练集和验证集。这种极端不平衡(约1:10000)是现实场景的真实写照。
特征提取流程(针对每3.2秒的768个原始采样点):
- 去均值:减去该片段的均值,消除直流偏移。
- 加窗FFT:使用一个长度为128点的汉宁窗,以32点为步长滑动,对768点数据计算出21个重叠的窗口。
- 计算幅度谱:对每个窗口做FFT,取前64个正频率点的幅度值,得到21x64的幅度谱矩阵。第0个频率点(直流分量)被丢弃。
- 归一化:使用一个随机数据子集计算平均功率谱,然后将所有特征除以这个平均功率谱的均值进行归一化。这一步有助于稳定训练。
注意:这里只使用了幅度谱,丢失了相位信息。对于检测信号中的不连续性(如运动的起始点),相位信息可能很有用。在实际项目中,可以根据需要引入复数频谱或其他的时频特征。
4.2 模型具体实现
第一级模型 p1(x, w1):简单能量过滤器这个模型的目标是快速过滤掉“安静”的噪声。我们计算每个3.2秒片段内,所有21个窗口、64个频率点的平均能量<E>。p1(x, w1) = S(w11 * <E> + w12)这本质上是一个逻辑回归模型,w11和w12是两个可训练参数。它非常简单,计算量极小,非常适合作为第一道粗筛。
第二级模型 p2(x, w2):轻量级卷积神经网络这个模型负责精细分类。我们借鉴了用于手写数字识别(如MNIST)的经典轻量CNN架构,并针对嵌入式场景进行了约束优化,其结构如图5所示:
- 卷积层:使用5个3x3的卷积核。为了控制模型大小和提升部署稳定性,我们对卷积核施加了单位范数约束(即每个卷积核的权重向量L2范数为1),对偏置施加了
[0,1]区间的约束。激活函数使用ReLU。 - 池化层:3x3的最大池化层,用于降维和引入平移不变性。
- 展平层:将多维特征拉平为一维向量。
- 全连接层1:10个神经元,权重和偏置同样约束在
[0,1]区间,激活函数为ReLU。 - 全连接层2(输出层):1个神经元,使用Sigmoid激活函数输出最终概率,权重和偏置约束在
[0,1]区间。
实操心得:对权重和偏置施加
[0,1]的约束是一个关键技巧。这并非为了提升精度,而是为了部署友好性。在嵌入式设备(如ARM Cortex-M系列MCU)上运行定点数或低精度浮点数计算时,有界的参数值能极大减少量化误差和溢出风险,提高模型在边缘设备上的推理稳定性。这是工业级嵌入式AI与纯学术研究的一个显著区别。
4.3 训练过程与结果分析
我们使用Keras和TensorFlow后端进行训练。作为对比,首先尝试了两种常规方法:
- 不平衡训练:直接使用原始数据,批量大小32768。由于每个批次中运动样本占比极低(约0.5%),模型无法有效学习,训练失败。
- 类别平衡训练:在每个批次中平衡正负样本数量。模型可以训练,但其性能(如表1所示)远未达到目标需求(TPR > 50%, FPR < 0.0044%)。无论怎么调整输出阈值,都无法同时满足两个指标。
随后,我们应用了本文提出的分阶段训练与定制损失函数方法,关键步骤如下:
第一阶段:训练p1从训练集的噪声数据中随机抽取一部分,与全部运动数据组成一个平衡的数据集(共65536个样本)。使用类别平衡的二元交叉熵损失函数训练p1。如表2第1行所示,p1能有效区分大部分简单噪声,将约98.4%的噪声滤除,同时保留了60%的运动信号(这正是我们想要的:高召回率)。
第二阶段:端到端训练p(x,w)冻结p1的参数w1。使用完整的训练集(约56万个噪声样本+运动样本),以及我们设计的定制损失函数,来训练p(x,w)中的参数w2(即门控参数w2a, w2b和CNN参数w_{2r})。
- 我们采用退火策略,初始设置较大的
λ值(如0.49)。 - 在每次训练迭代中,自动选择那些
S(...)值较高的样本(即未被p1完全过滤的样本)参与梯度计算。这使每次迭代的有效批量大小远小于总数据量,训练非常高效。 - 训练收敛或达到最大迭代次数后,将
λ值减半,继续训练,如此重复。
最终结果:如表2第4行所示,经过多轮λ退火训练后,模型在测试集上的性能达到了TPR 49%(接近50%的目标)和FPR 0.0011%(优于0.0044%的目标),成功满足了“保持检测”场景的严苛要求。整个训练过程仅使用了最多65536个样本/迭代,相比使用全部56万样本的传统方法,训练时间减少了约100倍。
5. 避坑指南与扩展思考
5.1 常见问题与排查技巧
在实际复现和应用该方法时,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
| 损失函数不下降,或震荡剧烈。 | 初始λ值设置过小。 | λ是平滑因子。初始值应设得较大(如0.3-0.5),以确保损失函数在初期足够平滑,便于优化器找到下降方向。采用退火策略,逐步减小λ。 |
| 模型性能始终无法同时满足TPR和FPR要求。 | 1. 模型容量不足。 2. 特征工程不到位,信号与噪声难以区分。 3. 需求指标过于严苛,超出数据可提供的信息限度。 | 1. 适当增加p2模型的复杂度(如增加卷积核数量、全连接层神经元数)。2. 重新审视特征:尝试引入时域特征(过零率、峰值)、相位谱、或更复杂的时频分析(小波变换)。 3. 与产品部门确认需求是否合理,或收集更多、更具代表性的“困难样本”(复杂噪声)。 |
训练后期,λ很小时,梯度爆炸或消失。 | λ过小导致fp_i函数过于尖锐,梯度不稳定。 | 1. 设置λ的下限(如不小于0.01)。2. 使用梯度裁剪(Gradient Clipping)技术。 3. 尝试更稳定的优化器,如AdamW,并调小学习率。 |
| 在嵌入式设备上部署后,性能下降明显。 | 1. 训练与部署时的数据分布不一致(协变量偏移)。 2. 定点量化或低精度计算引入误差。 | 1. 确保部署环境采集的校准数据参与最终模型的微调(Fine-tuning)。 2. 在训练后引入量化感知训练(QAT),或使用 [0,1]约束的模型本身就更利于量化。测试时在目标硬件上验证精度。 |
p1过滤器过于激进,过滤掉了太多运动样本。 | p1模型太复杂或训练数据中的“简单噪声”定义不清。 | p1的目的应是高召回率。确保用于训练p1的“负样本”确实是典型的、简单的噪声。可以手动检查被p1过滤掉的运动样本,看是否是信号极弱的边缘案例。 |
5.2 方案优势与适用场景总结
回顾整个方案,其核心价值在于提供了一条通往“需求驱动、嵌入式友好”机器学习模型的清晰路径:
- 效率革命:通过“困难样本挖掘”机制,训练过程自动忽略海量的简单样本,将计算资源集中在关键的数据上,训练速度提升百倍,使得在有限算力下迭代优化模型成为可能。
- 自动化与最优化:定制损失函数将业务需求(TPR, FPR)直接转化为优化目标,省去了繁琐且不精确的阈值调优步骤,得到的模型在指定指标上就是Pareto最优的。
- 嵌入式友好:分阶段的模型设计(简单过滤器+复杂分类器)和参数约束(
[0,1]区间),使得最终模型更轻量、更稳定,易于在资源受限的微控制器(MCU)上部署和运行。
这套方法不仅适用于微波/红外运动传感,其思想可以推广到任何面临极端数据不平衡和明确、不对称性能指标的嵌入式分类场景。例如:
- 工业异常检测:正常生产数据海量,故障样本极少,且对误报和漏报的成本要求不同。
- 关键词唤醒:日常环境音频数据极多,特定唤醒词片段很少,需要极低的误唤醒率。
- 物联网设备的状态识别:设备大部分时间处于空闲状态(简单样本),需要识别少数几种特定工作模式。
5.3 个人实操体会
在实际项目中落地这套方法,有几个点让我感触颇深。首先,特征工程依然是基石。无论损失函数设计得多巧妙,如果原始特征不能有效表征信号的本质差异,模型的天花板会很低。在运动传感中,我们花了很多时间对比不同时频变换、不同窗函数的效果。其次,λ退火策略的节奏需要耐心调试。初期λ大,学习率可以稍大;后期λ小,模型在做精细调整,学习率必须相应减小,否则容易在最优解附近震荡。最后,与硬件团队的早期沟通至关重要。我们之所以对p2网络施加[0,1]约束,就是因为硬件工程师明确告知了他们定点数DSP的位宽和动态范围限制。让算法从一开始就拥抱部署约束,能避免后期大量的移植和优化工作。
这套框架的价值在于它提供了一套系统化的“解题思路”,而不仅仅是几个技巧。当你面对一个不平衡的、有明确性能红线的嵌入式分类问题时,你可以沿着“分析需求 -> 设计目标函数 -> 构建分层模型 -> 实施聚焦训练”这条路径,一步步将模糊的业务问题,转化为可执行、可优化的机器学习任务。