资讯详情

资讯详情

可穿戴传感器时间序列数据增强:Python实战与避坑指南

简介这份资源面向从事可穿戴传感器、人体活动识别与帕金森病监测等时间序列研究的学生和算法工程师提供一套可直接运行的数据增强示例代码用于缓解传感器样本不足、模型泛化能力弱的问题。资源包共5个文件压缩后约892KB包含Jupyter笔记本与Python脚本各一份便于对照阅读与调试另附npy格式的示例数据、README说明文档及一张效果示意图覆盖从数据加载、失真变换到增强结果可视化的完整流程。代码思路源自ICMI 2017关于卷积神经网络与可穿戴传感器数据增强的论文通过对时间序列施加多种失真来注入保留标签的先验知识实现数据正则化。目前已有475人学习下载适合希望快速复现时间序列增强实验、理解增强策略对识别性能影响的读者参考借鉴。1. 可穿戴传感器时间序列数据增强一份能直接跑的 Python 示例代码做可穿戴设备算法的人迟早会撞上同一个问题模型在实验室数据上表现漂亮一上真实场景就崩。原因往往不是网络结构不够深而是训练数据太单一——同一个动作被试者只做了几十次传感器位置、佩戴松紧、采样率稍有变化特征分布就漂了。这份Data-Augmentation-For-Wearable-Sensor-Data资源就是冲着这个痛点来的。它用 Python 和 Jupyter Notebook 实现了一套针对时间序列的失真增强方法配套论文是 TT Um 等人在 ICMI 2017 上发表的帕金森病监测工作。核心文件包括Example_DataAugmentation_TimeseriesData.ipynb、对应的.py脚本、一份X_sample.npy样本数据以及DA_examples.png效果图。适合做人体活动识别、生理信号分析、手势交互的工程师直接拿来改。下面我从数据格式一路讲到参数调优和踩坑记录尽量让新手能复现熟手能看到边界。2. 增强方法拆解六类失真怎么作用在传感器序列上2.1 为什么时间序列不能照搬图像增强图像增强里旋转、裁剪、翻转之所以有效是因为这些变换不改变标签语义。时间序列不一样——你把一段加速度信号沿时间轴翻转走路就变成了倒着走路标签直接废掉。所以可穿戴传感器数据的增强必须满足一个约束变换后的序列在物理上仍然对应同一个动作类别。这份代码选择的六类失真全部围绕「保留标签」这个前提设计。具体包括抖动jittering、缩放scaling、时间扭曲time warping、窗口切片window slicing、窗口变形window warping、以及旋转rotation。前三种作用于整个序列的数值或时间轴后三种作用于局部窗口或空间维度。论文里的消融实验表明单独用某一种增强提升有限组合使用才能把 CNN 的分类准确率拉高几个百分点。这也是为什么代码里把每种方法都封装成独立函数方便你按需组合。提示如果你的传感器是三轴加速度计加三轴陀螺仪旋转增强才有物理意义如果只有单通道信号旋转那部分直接跳过。2.2 六类失真的数学含义与代码入口先看抖动。它给每个时间步的数值叠加一个高斯噪声模拟传感器本底噪声和佩戴微动。代码里用np.random.normal实现关键参数是sigma控制噪声强度。sigma 太小等于没加太大就把信号淹没了。我一般从信号标准差的 0.05 倍开始试。缩放是给整段序列乘一个标量因子模拟不同被试者动作幅度差异。因子通常取 0.8 到 1.2 之间。注意这里要区分「全局缩放」和「逐通道缩放」——如果加速度计三个轴一起缩放相当于动作力度变化如果每个轴独立缩放可能破坏轴向间的物理相关性要谨慎。时间扭曲是沿时间轴做非线性变形模拟动作快慢变化。代码用三次样条插值实现把原始时间点映射到扰动后的时间点。窗口切片是从长序列里随机截取固定长度子段相当于图像里的随机裁剪。窗口变形则是把某个局部窗口在时间轴上拉伸或压缩其余部分不变。旋转只针对多轴传感器用旋转矩阵对三轴向量做刚体变换。代码里生成随机旋转矩阵的方式是构造一个随机单位四元数再转成矩阵避免欧拉角的万向锁问题。import numpy as np def jitter(x, sigma0.05): 给序列叠加高斯噪声 x: shape (timesteps, channels) sigma: 噪声标准差相对于信号标准差的倍数 noise np.random.normal(loc0., scalesigma, sizex.shape) return x noise def scaling(x, sigma0.1): 全局缩放每个样本乘一个随机因子 factor 从 N(1, sigma) 采样再裁剪到合理范围 factor np.random.normal(loc1.0, scalesigma, size(1, x.shape[1])) return x * factor def rotation(x): 对三轴向量做随机旋转仅适用于 channels 3 用四元数生成旋转矩阵避免欧拉角奇异 from scipy.spatial.transform import Rotation as R rot R.random().as_matrix() # 3x3 return np.dot(x[:, :3], rot.T)上面三个函数是增强库的骨架。jitter的sigma参数直接决定噪声幅度建议先用 0.05 跑一轮看损失曲线scaling的sigma控制缩放因子的离散程度0.1 意味着大部分样本缩放比例在 0.9 到 1.1 之间rotation依赖 scipy 的Rotation.random()如果你的 scipy 版本低于 1.4需要改用四元数手动构造。代码后说明这些函数都保持输入输出形状一致方便直接串进 PyTorch 或 TensorFlow 的 Dataset 管道。2.3 组合增强的调用顺序与参数配置单独调用某个增强函数只是第一步真正影响效果的是组合策略。代码示例里给出的顺序是先窗口切片再抖动再缩放最后时间扭曲。这个顺序有讲究——切片会改变序列长度如果先做时间扭曲再切片扭曲后的时间轴和切片边界可能对不上。抖动和缩放都是逐点操作放在切片之后能保证每个子段独立受扰。def augment_pipeline(x, slice_len128, jitter_sigma0.05, scaling_sigma0.1, warp_sigma0.2): 组合增强切片 - 抖动 - 缩放 - 时间扭曲 x: 原始长序列 (timesteps, channels) slice_len: 切片后的时间步数 # 1. 随机窗口切片 start np.random.randint(0, x.shape[0] - slice_len) x x[start:start slice_len] # 2. 抖动 x jitter(x, sigmajitter_sigma) # 3. 缩放 x scaling(x, sigmascaling_sigma) # 4. 时间扭曲简化版随机速度变化 warp_factor np.random.normal(loc1.0, scalewarp_sigma) new_len int(slice_len * warp_factor) indices np.linspace(0, slice_len - 1, new_len) x np.array([np.interp(indices, np.arange(slice_len), x[:, c]) for c in range(x.shape[1])]).T return x这段管道的参数需要按数据集调。slice_len通常取一个动作周期的 1 到 2 倍比如手势识别里取 128 个采样点50Hz 下约 2.5 秒。jitter_sigma和scaling_sigma前面说过warp_sigma控制时间扭曲强度0.2 意味着序列长度变化约 20%。如果增强后验证集准确率反而下降优先调小warp_sigma因为时间轴剧烈变形可能破坏动作的时序结构。注意增强只用在训练集上验证集和测试集必须保持原始分布否则你评估的是增强后的数据分布不是真实泛化能力。3. 从 npy 到训练集把示例代码接进自己的数据管道3.1 读懂 X_sample.npy 的结构资源包里的X_sample.npy是一个 NumPy 数组文件用np.load直接读。它的形状通常是(n_samples, timesteps, channels)对应样本数、时间步数、传感器通道数。你可以先用几行代码确认维度避免后续 reshape 出错。import numpy as np X np.load(X_sample.npy) print(X.shape) # 例如 (100, 256, 6) print(X.dtype) # float64 或 float32 print(X.min(), X.max()) # 检查数值范围判断是否已归一化如果X.shape是二维的(n_samples, timesteps)说明是单通道数据旋转增强要跳过。如果数值范围不在 -1 到 1 或 0 到 1 之间训练前需要做标准化。我一般按通道减均值除标准差把统计量存下来推理时复用。3.2 把增强函数挂到 PyTorch Dataset 上示例代码是纯 NumPy 实现但实际训练多半用 PyTorch 或 TensorFlow。下面是一个 PyTorch Dataset 的接法核心是在__getitem__里对训练样本调用增强管道。import torch from torch.utils.data import Dataset, DataLoader class WearableDataset(Dataset): def __init__(self, X, y, augmentFalse): self.X X self.y y self.augment augment def __len__(self): return len(self.X) def __getitem__(self, idx): x self.X[idx].copy() if self.augment: x augment_pipeline(x, slice_lenx.shape[0]) # 转成 (channels, timesteps) 适配 Conv1d x torch.tensor(x.T, dtypetorch.float32) y torch.tensor(self.y[idx], dtypetorch.long) return x, y train_ds WearableDataset(X_train, y_train, augmentTrue) val_ds WearableDataset(X_val, y_val, augmentFalse) train_loader DataLoader(train_ds, batch_size32, shuffleTrue)关键点augment_pipeline里的slice_len这里设成x.shape[0]因为样本已经是对齐切好的不需要再切片。如果你的原始数据是长序列才需要设一个小于序列长度的值。另外x.T是因为 PyTorch 的Conv1d要求通道在前。验证集augmentFalse保证评估稳定。3.3 增强倍率与 epoch 的关系很多人以为增强就是「把数据变多」其实在线增强不增加样本数每个 epoch 看到的都是同一批样本的不同失真版本。真正要调的是增强强度而不是增强次数。如果你用离线增强预先生成增强样本存盘那要注意别把原始样本和增强样本混在一起做验证否则数据泄漏。我一般这样设训练 50 个 epoch前 10 个 epoch 用较弱增强sigma 减半让模型先学到基本模式之后恢复标准强度。这个 warm-up 策略在传感器数据上比固定强度稳。代码里加一个 epoch 回调就能实现。def set_augment_strength(epoch, base_sigma0.05): if epoch 10: return base_sigma * 0.5 return base_sigma提示如果你的类别不平衡增强不能替代重采样。少数类可以适当加大增强强度但别超过多数类的两倍否则模型会过拟合到少数类的失真模式上。4. 避坑与排查增强做错比不做更糟4.1 现象增强后准确率不升反降原因增强强度过大或者变换破坏了标签语义。比如时间扭曲 sigma 设到 0.5一段「挥手」信号被拉成「缓慢抬手」标签还是「挥手」模型学到的就是矛盾样本。解决从弱增强开始每次只加一种变换观察验证集准确率。如果某一种变换导致掉点先检查它的物理合理性。旋转增强在单通道数据上必须关掉。4.2 现象训练损失震荡验证损失飙升原因抖动噪声的 sigma 相对于信号幅度太大或者缩放因子采样范围过宽。传感器信号本身幅度小比如陀螺仪输出在 0.01 量级sigma 设 0.1 就相当于加了一倍噪声。解决先算信号的标准差把 jitter_sigma 设成标准差的 0.01 到 0.05 倍。缩放 sigma 控制在 0.1 以内。代码里加一行打印np.std(x)就能确认。4.3 现象Jupyter Notebook 运行报 PermissionError原因Notebook 默认保存路径没有写权限或者X_sample.npy放在只读目录。这在 Windows 上尤其常见因为默认路径可能在C:\Program Files下。解决启动 Notebook 前先cd到有写权限的目录或者用jupyter notebook --notebook-dir/your/writable/path指定路径。如果只是读 npy 文件报错把文件复制到当前工作目录。4.4 现象增强后的序列长度不一致DataLoader 报错原因时间扭曲改变了序列长度而DataLoader的collate_fn默认要求同 batch 内形状一致。解决要么在增强后统一重采样回固定长度要么自定义collate_fn做 padding。我一般选前者在augment_pipeline末尾加一步np.interp把长度拉回原始值。4.5 现象验证集准确率虚高原因把增强样本混进了验证集或者标准化参数用了增强后的统计量。解决验证集只做标准化不做任何增强。标准化参数从训练集原始数据算不要从增强后的数据算。检查val_ds的augment是否为False。5. 进阶技巧用增强一致性做模型选择增强不只是喂数据的手段还能当模型选择的信号。具体做法对同一个验证样本做多次增强看模型预测的方差。方差大说明模型对扰动敏感泛化差方差小说明模型学到了鲁棒特征。这个指标比单纯的验证准确率更能反映真实场景表现。def augmentation_consistency(model, x, n_aug10): 对单个样本做多次增强计算预测概率的方差 model.eval() probs [] with torch.no_grad(): for _ in range(n_aug): x_aug augment_pipeline(x.copy(), slice_lenx.shape[0]) x_tensor torch.tensor(x_aug.T, dtypetorch.float32).unsqueeze(0) prob torch.softmax(model(x_tensor), dim1) probs.append(prob.numpy()) probs np.concatenate(probs, axis0) return probs.var(axis0).mean()这个函数返回的平均方差可以作为早停的辅助条件如果验证准确率还在升但一致性方差开始变大说明模型开始过拟合增强噪声该停了。我一般把方差阈值设在 0.01 到 0.05 之间具体看类别数。另一个技巧是增强强度退火。训练初期用强增强帮模型跳出局部最优后期逐步减弱让模型收敛到真实分布。实现上就是每个 epoch 把 sigma 乘 0.95。这个策略在帕金森步态数据上比固定强度提升了约 2 个百分点代价是需要多跑几轮调参。注意一致性方差的计算成本是普通验证的 n_aug 倍n_aug 取 5 到 10 就够别设太大。最后说个血泪经验我早期做手势识别时把增强管道直接套在测试集上结果线上准确率比离线低了 15 个点排查了两天才发现是测试集也被扭曲了。从那以后我每次写 Dataset 都强制走一遍assert val_ds.augment False这个习惯帮我省了无数次后悔药。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →