ARTICLE DETAIL

资讯详情

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

HAR深度学习模型全链路:从时间序列分类到落地部署

HAR深度学习模型全链路:从时间序列分类到落地部署 做时间序列分类这个方向绕不开人类活动识别Human Activity RecognitionHAR这个经典场景。我最早接触它是在一个可穿戴设备的小项目里当时手里只有三轴加速度计的原始波形要判断佩戴者到底是在走路、跑步、上楼还是坐着。那会儿第一反应是提特征喂给随机森林结果准确率卡在七十几上不去后来换了深度学习模型同样的数据直接拉到九十以上那种换了个脑子的感觉让我彻底入了这个坑。这篇就围绕人类活动识别深度学习模型这条主线把时间序列分类里从数据到模型、从训练到落地的完整链条捋一遍既讲清楚为什么这么选也把踩过的坑摊开说。无论你是刚接手传感器项目的新手还是想把HAR模型调到能上线的老手下面这些内容应该都能直接用得上。1. 项目背景与问题定义1.1 从传感器信号到活动标签HAR问题的本质人类活动识别说到底是一个从连续信号里切出语义的任务。传感器加速度计、陀螺仪、磁力计有时还有心率、气压计以固定频率采样比如手机常见的50Hz专业设备能做到100Hz甚至200Hz。这些数字本身没有意义只有当我们按时间把一段段信号切片再给每片贴上一个活动标签才构成监督学习可用的样本。所以HAR的第一性问题不是选哪个模型而是怎么切片、切多长、标什么。从形态上看这就是典型的多变量时间序列分类Multivariate Time Series Classification。输入是一个形状为(时间步长, 通道数)的矩阵输出是走路/跑步/静坐/站立等离散类别。它和普通图像分类最大的区别在于时间维度上的局部模式和长程依赖都很关键一秒钟的下蹲动作节奏和几分钟的步态周期需要的感受野完全不同。这就决定了单一尺度模型往往不够后面讲架构时你会看到大量多尺度设计的影子。我习惯把HAR拆成几个子问题来看传感器配准与对齐、窗口切分、特征表征、分类决策、后处理平滑。很多团队一上来就堆模型结果发现收益都被窗口和后处理吃掉了。先把问题定义清楚比急着写网络结构重要得多。1.2 为什么传统方法在复杂场景里会掉链子传统机器学习做HAR的流程非常标准化加窗然后在每个窗口上手工计算时域特征均值、方差、峰峰值、过零率、相关系数和频域特征FFT能量、谱熵、小波系数再交给SVM、随机森林或XGBoost。这套方法在数据干净、活动种类少的时候表现相当能打我见过不少工业项目至今还在用它因为推理快、可解释、部署成本低。但它的天花板也很明显。第一手工特征依赖领域知识换一个传感器位置手腕换到腰部特征分布就变了需要重新调。第二复杂活动比如做饭这种由多个子动作拼接的活动的手工特征很难刻画其时间结构。第三特征工程和分类器是割裂的两步无法联合优化。深度学习恰恰在这三点上占优端到端学习表征、自动捕捉时序模式、可以在同一网络里融合多传感器信息。不过我得泼盆冷水深度学习不是银弹。如果你的活动类别只有三四种、数据量只有几百个窗口、还要求跑在极低功耗的MCU上那老老实实用手工特征加树模型可能才是正解。选型的核心永远是场景约束而不是模型的新旧。1.3 数据来源与主流公开数据集盘点自己从零采数据成本极高所以入门阶段建议先用公开数据集把流程跑通。下面这张表是我实际用过、觉得值得推荐的几个标注了采样率、传感器类型和典型难点方便你按需选择。数据集采样率传感器活动类别主要难点UCI HAR50Hz手机加速度陀螺仪6类数据干净适合入门WISDM20Hz手机加速度6类采样率低类别不平衡PAMAP2100Hz3个IMU心率18类多传感器配准复杂Opportunity30Hz全身多IMU多类手势标注细粒度难度高SHL100Hz手机多传感器8类真实户外场景噪声大HAPT50Hz手机传感器12类含姿态转换类边界模糊选数据集有个我反复验证的经验先拿UCI HAR验证你的预处理和基线模型再换到PAMAP2或SHL测试泛化能力。如果一套模型在UCI HAR上95%、换到SHL掉到60%那大概率是过拟合了数据集的采集习惯而不是模型真的学懂了活动这件事。2. 数据预处理与窗口切分2.1 滑动窗口参数不是拍脑袋定的滑动窗口是HAR里最关键的预处理步骤两个核心参数是窗口长度和重叠率。窗口太短一个完整动作周期都装不下模型看到的是碎片窗口太长一次窗口里混进多种活动标签就变得含糊。我的做法是从活动的最短周期出发推算走路步态周期约1秒按50Hz采样就是50个点那么窗口至少取这个的2到3倍也就是128到256点对应2.56到5.12秒。重叠率一般取50%它本质是在做数据增强让相邻窗口共享一半信息既能增加样本量又能缓解边界处标签跳变的问题。但重叠率不是越高越好90%重叠会让训练集里充满近似重复样本验证集如果也这样切指标会虚高模型上线后性能打脸。import numpy as np def sliding_window(data, labels, window_size128, overlap0.5): step int(window_size * (1 - overlap)) windows, window_labels [], [] for i in range(0, len(data) - window_size 1, step): windows.append(data[i:i window_size]) # 用窗口内出现最多的标签作为窗口标签 seg labels[i:i window_size] window_labels.append(np.bincount(seg).argmax()) return np.array(windows), np.array(window_labels)注意标签对齐必须按窗口内多数投票或窗口末端对齐处理直接取窗口起点标签是最常见的低级错误会让边界样本训出噪声。2.2 归一化与滤波别小看这两步传感器数据里不同通道的量纲差异很大加速度单位是g或m/s²陀螺仪是deg/s直接喂网络会让梯度被大量纲通道主导。标准做法是对每个通道单独做标准化减均值除标准差而且统计量只能从训练集算然后应用到验证和测试集否则就是数据泄漏。滤波方面人体活动的有效频段通常在0.3到20Hz之间高频多是传感器噪声和抖动。我一般上一个巴特沃斯低通滤波器截止频率设在20Hz左右。这里有个坑滤波要用零相位滤波filtfilt普通lfilter会引入相位延迟让信号在时间上整体偏移直接影响动作边界的判断。from scipy.signal import butter, filtfilt def lowpass(data, fs50, cutoff20, order4): nyq 0.5 * fs b, a butter(order, cutoff / nyq, btypelow) return filtfilt(b, a, data, axis0)重力分量也值得单独说一句。加速度计原始信号里混着重力很多人直接把它当作特征的一部分其实更好的做法是把重力分离出来用低通滤波提取低频分量即为重力剩下的线性加速度才是纯运动信息。是否分离取决于任务但在区分站立和行走这类场景时重力方向信息往往很有用别一刀切丢掉。2.3 数据增强让模型见更多变体HAR数据标注成本高样本量往往有限数据增强就成了提点的关键手段。时序数据不像图像可以随便翻转旋转但有几招特别好用一是加性高斯噪声模拟传感器噪声二是时间扭曲time warping在时间轴上做非线性拉伸模拟动作快慢差异三是幅度缩放模拟不同人体型的信号强度差异四是通道置换或丢弃模拟部分传感器失效提升鲁棒性。def jitter(x, sigma0.05): return x np.random.normal(0, sigma, x.shape) def scaling(x, sigma0.1): factor np.random.normal(1.0, sigma, (1, x.shape[1])) return x * factor def time_warp(x, sigma0.2, knot4): from scipy.interpolate import CubicSpline orig np.arange(x.shape[0]) warp np.random.normal(locorig, scalesigma * x.shape[0] / knot, size(knot 2,)) warp np.sort(np.clip(warp, 0, x.shape[0] - 1)) spline CubicSpline(np.linspace(0, x.shape[0] - 1, knot 2), warp) new_time spline(orig).astype(int) new_time np.clip(new_time, 0, x.shape[0] - 1) return x[new_time]我实测下来jitter加scaling的组合性价比最高几乎不增加训练成本就能涨1到2个点。time warp效果也好但实现要小心扭曲过猛会制造出不存在于真实世界的动作模式。3. 主流深度学习模型架构拆解3.1 CNN系列把时序当作一维图像一维卷积1D-CNN是HAR里最常用的骨干网络没有之一。它的直觉很清楚卷积核在时间轴上滑动学到的就是局部时序模式浅层学边缘和抖动深层学动作基元。相比RNNCNN训练快、并行度高、参数量可控非常适合嵌入式部署。经典的DeepConvLSTM里先堆几层Conv1D每层后面接BatchNorm和ReLU再用池化降采样。我常推荐的基线配置是4层卷积通道数32/64/128/128卷积核5或3池化窗口2最后接全局平均池化再分类。这套结构在UCI HAR上裸跑就能到92%以上。import torch.nn as nn class CNN1D(nn.Module): def __init__(self, in_ch9, n_classes6): super().__init__() self.net nn.Sequential( nn.Conv1d(in_ch, 32, 5, padding2), nn.BatchNorm1d(32), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(32, 64, 5, padding2), nn.BatchNorm1d(64), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(64, 128, 3, padding1), nn.BatchNorm1d(128), nn.ReLU(), nn.AdaptiveAvgPool1d(1) ) self.fc nn.Linear(128, n_classes) def forward(self, x): # x: (B, C, T) return self.fc(self.net(x).squeeze(-1))提示输入务必转成(batch, channels, time)的格式把通道放中间维度卷积沿时间滑动。新手常见的bug就是通道和时间轴搞反模型照样能训但性能会莫名很差。3.2 RNN/LSTM/GRU捕捉长程依赖的经典选择循环网络的强项在于对时间顺序的建模尤其适合动作的当前状态依赖于前一段历史的场景比如从坐下到站起的过渡过程。LSTM通过门控机制缓解了梯度消失GRU用更少的参数达到接近的效果。在HAR里双向LSTMBiLSTM也常用因为它能同时看前后文。但RNN的短板也突出训练慢、难以并行、序列一长就很难训。我的经验是纯RNN做HAR现在已经不流行了更多是作为混合模型的一部分。如果你非要用窗口别超过256点层数控制在1到2层隐藏维度64到128再配dropout基本就够。3.3 CNN-LSTM混合各取所长的黄金组合这是HAR领域过去几年最经典的架构范式CNN负责从原始信号里提局部特征LSTM负责对这些特征做时序建模。前面提到的DeepConvLSTM就是这个思路的代表。它的逻辑很顺——卷积把每个时间点附近的信息压缩成语义向量RNN再把这些向量串起来理解动作演进。我调这类模型有几点心得。一是CNN部分不要堆太深3到4层足矣再深容易过拟合而且收益递减。二是CNN和LSTM之间最好加一层dropout或空间dropout抑制过拟合。三是LSTM只取最后时间步的输出还是取全序列的平均要实验决定我做下来平均池化通常更稳。四是序列长度在CNN池化后会缩短注意LSTM的输入维度要对上。对比一下这几类骨干在UCI HAR上的典型表现方便你有个数量级的概念架构参数量UCI HAR准确率训练速度适合场景1D-CNN约10万92-94%快嵌入式、实时LSTM约20万90-93%慢强时序依赖CNN-LSTM约30万94-96%中通用首选Transformer约50万94-97%慢数据量大时3.4 注意力与Transformer数据够多才玩得转注意力机制让模型能动态关注窗口里最相关的片段而不是平等对待每个时间步。在HAR里很多窗口大部分时间是冗余的真正区分上楼和下楼的可能就是某几个关键相位注意力恰好能放大这些局部。自注意力Self-Attention进一步去掉了循环结构实现全并行Transformer因此在大规模HAR数据上表现亮眼。不过我要提醒Transformer对数据量非常敏感。UCI HAR这种几千样本的规模硬上Transformer往往不如CNN-LSTM因为注意力参数多、缺少归纳偏置。我一般建议样本窗口数超过五万再考虑纯Transformer否则用轻量注意力比如在CNN特征上挂一个注意力池化性价比更高。把注意力当特征聚合器是我觉得最实用的用法CNN提特征然后让模型自己学每个时间步的权重最后加权求和送入分类头。代码就一小段效果却经常比直接平均池化好一两个点。class AttentionPool(nn.Module): def __init__(self, dim): super().__init__() self.score nn.Linear(dim, 1) def forward(self, x): # x: (B, C, T) x x.transpose(1, 2) # (B, T, C) w torch.softmax(self.score(x), dim1) # (B, T, 1) return (x * w).sum(1) # (B, C)3.5 多传感器融合策略早融合还是晚融合HAR常常用多个传感器手腕、腰部、大腿各一个IMU怎么把它们合起来是个绕不开的决策。早融合是把多传感器通道直接拼在一起当输入实现简单但要求各传感器严格时间对齐且无法处理某个传感器缺失的情况。晚融合是每个传感器单独过一个分支最后把各自的特征或预测拼接融合鲁棒性强但要设计融合层。还有混合融合在中间层用注意力交叉融合。我的建议是传感器位置固定且数据干净就用早融合传感器可能缺失或需要处理异构信号就用晚融合别为了炫技上复杂的交叉注意力除非确实能涨点。4. 训练策略与调参实操4.1 损失函数与类别不平衡处理HAR数据集几乎都是类别不平衡的静坐、站立这类静态活动样本远多于跑步、上楼这类动态活动。直接用交叉熵会让模型偏向多数类少数类召回惨淡。处理方式有几种给类别加权权重反比于频次、用Focal Loss压易分样本、过采样少数类或欠采样多数类。我通常先跑一遍看混淆矩阵定位到底哪几类混淆再决定是加权重还是做重采样。# 按类别频次的倒数计算权重 weights 1.0 / torch.bincount(train_labels).float() weights weights / weights.sum() * len(weights) criterion nn.CrossEntropyLoss(weightweights)注意验证集千万不要做重采样保持真实分布才能反映实际性能。训练集随便折腾验证测试集必须干净。优化器方面AdamW配上余弦退火学习率调度是我最常用的组合学习率从1e-3开始配合权重衰减1e-2。如果模型跑到后面loss震荡把batch size调大一点往往比调学习率更管用。4.2 正则化与防过拟合组合拳HAR模型的过拟合来得特别快因为样本量小、模型又喜欢堆参数。我通常同时上这几招dropout0.3到0.5、权重衰减、早停、以及前面说的数据增强。空间dropout在CNN上尤其有效它随机丢弃整个特征通道逼模型不依赖单一通道。还有个小技巧是标签平滑Label Smoothing把硬标签从1和0变成0.9和0.1左右能缓解模型过度自信对提升泛化有稳定帮助。代价是训练指标的绝对值会略微下降别被这个数字吓到看验证集和测试集才有意义。4.3 超参数搜索的务实做法全量网格搜索在HAR上非常低效动辄几十上百次训练。我推荐分层搜索先用经验值定死窗口、骨干、优化器这些大件只调学习率、dropout、batch size这三个影响最大的找到较优组合后再微调卷积核大小、通道数这些细粒度参数。用Optuna之类的贝叶斯搜索效率会更高但别设太多搜索维度每一维都要有明确的先验范围。另外要固定随机种子做对比实验HAR训练的方差有时能到1到2个点不固定种子的话你根本分不清是模型改了有效还是随机波动。想更严谨就用多次运行取均值和标准差虽然费时间但结论才靠得住。4.4 训练过程监控与关键指标只看准确率是不够的尤其类别不平衡时。我盯的是这几个验证集macro-F1对少数类敏感、各类召回率、以及混淆矩阵。如果某两类持续混淆比如上楼和下楼那说明特征层面信息不足可能要从传感器选型或窗口设计上找原因光调模型没用。学习率曲线也值得看。如果训练loss下降但验证loss早早抬头是过拟合如果两条都居高不下是欠拟合或学习率不对如果loss剧烈震荡多半是batch size太小或学习率太大。这些判断没有巧劲就是把曲线画出来看。5. 常见问题与排查技巧实录5.1 各类翻车现场的排查表跑HAR项目这些年遇到的坑大同小异我把高频问题和对应排查方向整理成表出事时照着查能省不少时间。现象可能原因排查与解决训练集很高、验证集很低过拟合、数据泄漏检查是否在划分前做了全局归一化加正则和数据增强所有样本预测为同一类类别极不平衡、学习率过大加类别权重检查标签映射是否错位准确率久久卡在随机水平输入格式错误、标签未对齐打印输入shape和标签分布可视化几个样本某两类反复混淆特征区分度不足、传感器不合适看混淆矩阵尝试多尺度或加频域特征验证指标虚高但上线很差窗口重叠导致训练验证泄漏按受试者或时间段划分而非随机划分推理延迟过高模型过深、序列过长剪枝/量化缩短窗口用轻量骨干5.2 数据泄漏HAR里最隐蔽的坑随机划分训练测试集是新手最容易踩的雷。因为滑动窗口有重叠相邻窗口几乎一样随机划分会让同一个动作的近似副本同时出现在训练和测试集里准确率虚高到离谱我见过报99%的。正确做法是按受试者划分leave-one-subject-out或按采集时间段划分确保测试集和训练集来自不同人、不同时间。这样做出来的指标通常下降好几个点但那才是真实水平上线就不会打脸。5.3 落地部署时的取舍经验模型做得再好跑不起来也是白搭。嵌入式部署时我会优先考虑这几个方向一是减少通道数很多传感器通道其实是冗余的做通道重要性分析可以砍掉一部分二是用深度可分离卷积替代标准卷积参数量和计算量大幅下降三是定点量化把FP32转成INT8推理速度经常能翻倍精度损失通常在1个点以内四是缩短窗口加多帧投票用小窗口堆叠推理再平滑。后处理平滑也特别关键。原始逐窗预测会有大量跳变明明在走路却偶尔蹦出个坐姿用中值滤波或多数投票在时间维上平滑能明显改善用户体验。这套东西不复杂但很多论文里的模型直接上线就翻车缺的往往就是这层看不到的后处理。提示部署前务必在真机采集的数据上验证而不是只在公开数据集上测。不同设备、不同佩戴方式的分布差异往往比模型结构差异带来更大的性能波动。6. 模型选型决策与工程化建议6.1 按场景挑模型的决策逻辑到底选哪个模型我的决策树大概是这样先看算力约束——如果目标是MCU或低功耗设备1D-CNN加深度可分离卷积基本是唯一解如果跑在手机或边缘GPU上CNN-LSTM是稳妥选择如果数据量上万且算力充裕可以上Transformer搏一搏上限。再看数据量——小数据几千窗口走CNN或CNN-LSTM别碰Transformer中等数据可以试轻量注意力大数据再放开用重型架构。还有一个常被忽略的因素是标注质量。如果标签本身就是粗粒度、边界模糊的比如做家务这种复合活动再复杂的模型也提不动这时候花精力在标注规范和窗口对齐上回报远高于换模型。6.2 从论文到落地的差距在哪论文里动辄95%以上的准确率落到真实产品里能保住80%就不错了。差距主要来自三块一是数据分布差异公开数据集是受控采集的真实场景里传感器位置漂移、设备型号混杂、用户行为千奇百怪二是标签定义差异论文的活动划分是预设的真实需求往往更细碎三是算力与延迟约束论文不关心推理速度产品却卡得死死的。我的建议是别把公开数据集指标当成毕业标准尽早搭一个小的真机测试集哪怕只有几百个窗口用它来指导模型迭代方向才对。我在一个手腕设备项目上就是这么干的一开始全盯着UCI HAR调涨到94%沾沾自喜结果真机一测只有68%回头看全是数据集偏置惹的祸。后来用真机数据反复微调指标虽然只有85%但那是能用的85%心理踏实。6.3 值得持续跟进的方向这个领域还有不少值得投入的方向。自监督预训练正在兴起用大量无标签传感器数据先学表征再小样本微调能显著降低标注依赖这对HAR这种标注贵的任务特别对症。多模态融合也在往前走把惯性信号和图像、音频结合起来能覆盖更丰富的场景。边缘侧的模型压缩和硬件协同设计是把HAR真正铺开的最后一公里。我个人现在最看好的是自监督加轻量骨干这条路线。原因很实在标注成本是HAR项目里最大的一块支出谁能把这块降下来谁就能把模型铺得更广。至于具体用哪种对比学习或掩码重建的预训练目标那都是细节先把数据管线和评估体系搭扎实再往上叠方法节奏才不会乱。模型会过时数据和评估的信度不会这是我折腾了这么多项目之后最实在的一条经验。
返回列表