ARTICLE DETAIL

资讯详情

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

时间序列平均新思路:从DTW到DBA质心平均算法解析

时间序列平均新思路:从DTW到DBA质心平均算法解析 做时间序列分析的人迟早会遇到一个问题想把一批形状相似但节奏不同的序列“揉”成一条代表性的平均曲线。最初都会以为这事很简单把每个时间点取个均值不就行了真这么干了才发现波形对齐不上均值被拉出一堆根本没有意义的毛刺原本该是两段式的趋势被抹成一段斜坡。这个问题在语音识别、动作识别、工业传感器信号处理里太常见了我在处理人体姿态数据和设备振动信号时都踩过同样的坑。后来换用DTW Barycenter AveragingDBA这套基于动态时间规整的质心平均方法才真正得到一条既有代表性、形状又保持原貌的平均序列。这篇文章就围绕DBA展开从为什么逐点平均会失效到算法的迭代机制、实现细节、调参陷阱再到适用场景完整拆一遍。不管你是刚接触时间序列的初学者还是已经在用DTW做项目的老手应该都能从中拿走一些能直接落地的东西。1. 为什么简单的“平均”在时间序列上会失效1.1 时间序列的相位偏移问题先看一个最直观的例子。假设你在采集一批跑步姿态数据传感器是同一套设备同一个动作重复做十次。十次序列的整体形态几乎一样但每次动作的快慢并不完全相同峰值出现的时间点会有前后偏移。少数情况下偏移可能达到3到5个采样点。这时候如果按照时间索引直接逐点平均每一次的峰值在不同索引位置出现算下来平均序列的峰值会被明显压低同时峰值附近的波形会变得又宽又平。更有意思的是如果两个序列的波峰错开了几个点逐点平均会在中间产生一个“小小的高地”看起来像是有两个靠得很近的峰实际原始数据里根本没有这个结构。这背后的原因说白了很清楚常规平均隐含了一个假设就是两条曲线的横坐标是一一对应的同一时间索引代表同一语义状态。但对带相位偏移的时间序列来说这个假设不成立。第1个序列的第10个点可能是“摆臂到最高点”第2条序列的第10个点可能只是“摆臂到中间”。把它们当成同一个时刻来平均等于在拿苹果和香蕉相加。1.2 从欧氏距离到DTW距离要解决相位偏移问题第一步是把“两个点之间的对应关系”从固定索引改成可变对齐。这就是动态时间规整DTW做的事。DTW不再要求第i个点必须和第i个点比较而是允许序列A的第i个点匹配序列B的第j个点只要这种匹配满足时间顺序和连续性约束。最终它会在一个叫累积代价矩阵的格子里找一条路径让两条序列的整体对齐代价最小。把DTW从“距离度量”升级成“平均方案”就是DBA要做的事。DBA的完整名称是DTW Barycenter Averaging直译就是“在DTW距离意义下的质心平均”。它不再是把原始序列按时间轴做均值而是在DTW对齐关系的基础上把每一轮对齐时匹配到同一个位置的点归为一组对这个组求平均。也就是说每次迭代得到的平均序列都是在当前对齐关系下对全部原始序列的“最优妥协”。这套思路的转变非常关键。逐点平均是在固定时间网格上做均值DBA则是在一个不断迭代调整的“对齐网格”上做均值。每一次迭代对齐关系都会随着新平均序列的变化而更新经过若干轮之后平均序列就会稳定到一条在DTW意义下到所有原始序列距离之和最小的序列。这个结果才是真正符合“平均”直觉的序列。2. DBA算法的核心思路一次“带权重的对齐平均”2.1 DBA的迭代流程DBA本质上是一个迭代重估的过程整体可以拆成两个阶段。第一阶段是“对齐”计算当前平均序列与每一条原始序列之间的DTW对齐关系第二阶段是“更新”根据对齐关系重新计算平均序列的每个点。两个阶段交替进行直到收敛。具体展开就是假设现在有一个初始平均序列C长度可以自己设定。对每一条原始序列S用DTW找到C和S之间的最佳路径。DTW路径记录了C的哪些点和S的哪些点配对。做完所有序列的对齐后把C的第一个点所有可能匹配到的S上的点收集起来取平均值得到C的第一个点的新值。对C的第二个点、第三个点做同样的操作一轮结束后就得到了一条全新的C。再用新的C去做下一轮DTW对齐反复迭代。这里有个很重要的细节C上的一个点可能同时匹配到S上的多个连续点这在DTW的路径里很常见。所以C的第k个点在第t轮迭代里收集到的不是某一条序列上的单个点而是一个“配对集合”这个集合的规模在不同序列之间经常不一样。这就是为什么DBA不是简单的“对齐后逐点平均”而是带权重的平均。原始序列的局部节奏越不稳定同一个平均点分担的原始点就越多最终呈现出来的效果就是这些原始点对这个平均点的影响被均匀分摊掉了。2.2 为什么不是直接求平均而是反复“对齐再平均”刚接触DBA的人都会有这个疑问既然DTW能算出两两之间的对齐路径那把所有序列都和某一条参考序列对齐然后直接平均不就行了为什么要反复迭代这个问题我一开始也没想透直到自己动手实现才发现直接对齐再平均的致命缺陷对齐结果是严重依赖参考序列的。你选第一条原始序列做参考得到的平均序列偏向第一条你选中间某条做参考平均结果又会被那条的噪声带偏。更麻烦的是DTW对齐本身是对称的但对齐后的“平均”并不保证在几何上有任何对称性。一次对齐得到的平均序列很可能比真正想要的质心偏差更大。DBA的迭代让这个偏差逐步缩小。迭代的过程有点像聚类算法里的K-Means先给一个中心点的猜测然后根据当前中心重新计算每个样本的归属再根据归属更新中心。DBA也是类似先给一条初始平均序列然后用DTW把每条原始序列“归位”到这条平均序列附近再重新计算中心。如此循环中心会逐步往数据集的“中间地带”移动。经过足够多轮后结果就和初始猜测关系不大了更多地由全部原始序列的几何结构决定。2.3 复杂度与收敛行为DBA的实际代价主要来自反复计算DTW。每轮迭代都要对N条原始序列分别计算一次与平均序列的DTW每条序列长度越长DTW的计算量越大。经典DTW的复杂度是O(L1 * L2)如果平均序列长度是L_c原始序列长度是L_s单轮复杂度就是O(N * L_c * L_s)。如果迭代了T轮总复杂度就是O(T * N * L_c * L_s)。这个复杂度在序列长度动辄几千上万时会相当可观。收敛行为方面DBA并不能保证收敛到全局最优只能保证目标函数单调不增。也就是说每一轮迭代后平均序列到所有原始序列的DTW距离之和不会比上一轮更大。这个性质已经足够支撑实际使用了因为实际场景中我们并不需要绝对最优只需要得到一个稳定、可复现、有代表性的结果。经验上大部分数据在10到20轮迭代后就会进入一个平稳区域后续轮次的变化非常小。如果你的数据量很大或者序列很长完全可以用早停策略当相邻两轮平均序列的变化低于某个阈值时就提前结束迭代。3. DBA的实操要点与细节处理3.1 初始化方式的选择DBA的第一步是确定初始平均序列。很多开源实现默认直接用第一条原始序列作为初始平均序列这种做法省事但有一个显而易见的问题如果第一条序列正好是离群点或者噪声特别大迭代收敛速度会变慢最终结果也可能滞留在一个不理想的地方。虽然理论上迭代次数足够多时可以消解掉这部分偏差但实际项目里我们往往没有那么多迭代预算。我个人的经验是如果数据量不大可以先随机选几个不同的初始序列各跑一轮DBA取其中目标函数最低的结果。这样能有效避开初始化导致的次优解。更工程化的做法是先用逐点平均的结果作为初始序列。逐点平均虽然形状会被相位偏移搞坏但它的整体位置通常已经接近真正的质心用它做初始序列可以让DBA进入收敛状态更快。在某些时候逐点平均会被严重拉宽这时可以先把所有原始序列用DTW对齐到某一条参考序列上再做一次普通平均拿这个结果初始化效果也不错。3.2 窗口约束与距离度量实际使用DTW时没人敢直接在全矩阵上跑因为长序列的计算量太吓人。最常见的优化是加窗口约束比如Sakoe-Chiba窗口或Itakura平行四边形。窗口约束限制了路径允许偏离对角线的范围本质上是限制了时间轴上的最大扭曲量。窗口越小计算越快但能容忍的相位偏移也越小。DBA同样可以加窗口约束。因为平均序列的长度和被平均的原始序列长度通常不完全一致窗口大小一般用百分比来定义。比如窗口比例为0.2意思是路径只能在距对角线约20%长度的范围内波动。对不同场景需要调这个比例动作速度变化很大的数据集窗口比例要适当放大如果序列节奏稳定窗口比例设小一点能有效避免过度扭曲。窗口约束还有一个额外的作用防止DTW把个别离群点强行匹配到不该匹配的位置。在DBA迭代中如果路径过于自由一条异常的高噪声片段可能会被过度拉伸从而拽动平均序列产生失真。约束窗口等于给对齐加了先验规则让平均结果更符合物理世界的“正常扭曲”。3.3 多元时间序列的处理如果你处理的是传感器数据大概率会碰到多元时间序列比如IMU的加速度计三轴数据、姿态四元数或者设备振动监测的多个通道。直接把多通道拼接成一维向量丢给DBA是不合适的因为不同通道的尺度差很大计算DTW时大尺度通道会主导整个对齐路径其他通道的对齐质量就没法保证。正确做法通常有几种。第一种是对每个通道分别做DBA最后把每个通道的平均结果拼成多元平均序列。这个方案最简单缺点是各通道分别迭代通道之间可能出现对齐不一致也就是通道A的峰值位置和通道B的峰值位置在不同迭代阶段没有保持同步。第二种做法是先用某种方式确定一条统一的对齐路径再用这条路径把多通道数据一起平均。比如先对某个最能代表整体状态变化的通道比如加速度幅值做DBA得到平均序列后用其他通道在这个平均序列的对齐关系下进行重组。第三种做法是距离度量升级用多变量DTW对齐时把同一时刻的多个通道差别的加权和作为局部代价路径计算完后再对所有通道共同更新。实际使用中如果各通道尺度差得不多而且变化趋势有共同的时间结构第二种方法效果最好。如果通道之间本来就是相对独立的物理量各做各的DBA反而更干净。3.4 DBA的小技巧归一化、长度控制、外点影响DBA在迭代时对序列的绝对尺度很敏感。不同序列如果在幅值上相差较大幅值大的序列会主导DTW路径平均序列会偏向它们。展开工作前先做归一化是一个低成本高收益的操作。用z-score归一化处理每个序列或者至少按全局幅值缩放可以让DBA平均的结果少受外点影响。长度控制也很关键。DBA本身可以输出一条任意长度的平均序列但实际场景里平均序列长度应该和原始序列长度保持接近否则后续特征提取会很难受。我在做动作识别时通常让平均序列长度等于所有原始序列长度的中位数。太长的平均序列会让每个点对应的原始点数量变少平均结果容易保留过多噪声太短则会把多条原始序列强行压在一起细节被抹掉。外点影响方面如果有某条序列明显和其他序列差异巨大不管是形状异常还是幅值异常它都会在DTW迭代中不断拉偏平均序列。常规统计里我们会说“均值受外点影响”DBA也逃不过这个问题。工程上的处理方式是把每条序列先两两计算DTW距离形成距离矩阵然后用多维标度法或简单的聚合排序识别出离群序列先剔除再跑DBA。这个方法虽然糙一点但很实用。4. 常见问题与排查手册4.1 结果出现锯齿/异常波动DBA输出的平均序列如果出现高频锯齿先别急着加平滑滤波因为滤波会把真实结构也抹掉。锯齿的常见原因是对齐路径过于局部化平均序列的某些点匹配到的原始点集合里混入了不属于同一语义相位的内容。尤其当窗口约束过小时平均序列可能出现“抖一抖才能同时讨好两边序列”的怪异形状。排查步骤一般是先检查窗口比例是否过小适当放宽10%到20%再跑一轮观察锯齿是否消失。如果锯齿依然存在再看是否属于原始序列数量太少的情况。只有两三条序列做DBA迭代很容易陷入局部震荡此时考虑给每次迭代的更新结果加一个轻度的移动平均或平滑目标项能有效抑制锯齿。要提醒的是加平滑目标项会导致最终平均序列和原始序列的DTW距离抬升所以不要下手太重。4.2 平均结果偏向某一条原始序列如果你发现最终平均序列几乎就是某条原始序列的翻版大概率是初始化出了问题或者迭代还没收敛就提前停了。初始序列如果特别接近某一条原始序列而且该序列又位于数据集边缘DBA确实可能被“粘”住不往前走。对策可以分开两条线。一条是增加迭代轮数把迭代预算放到50轮以上并打开早停机制确保收敛另一条是多起点初始化跑3到5个不同的初始序列选目标函数最低的结果。这种方法牺牲一点计算时间但能换来更稳定的平均结果在样本量小的时候尤其值得。4.3 收敛太慢或迭代次数过多如果你的数据集有几千条序列每条几千个点纯Python实现跑DBA会慢到让你怀疑人生。收敛慢大多是三个原因叠加在一起序列太长、窗口约束没加、DTW实现在用两重循环跑。优化手段从低到高排列先把DTW和DBA用numpy向量化把累积矩阵的计算变成逐行并行然后加窗口约束计算量直接砍到全矩阵的一个百分比再进一步可以做金字塔或下采样粗对齐先用粗粒度序列快速跑DBA再把结果映射回原始尺度做精修。最后一招是使用基于C/C后端的现成库很多库提供了针对DTW矩阵的快速计算能力。收敛慢的问题不能只盯着迭代轮数要从单轮效率和总轮数两头一起压。4.4 DBA产生不合理的“平均形状”有些时候DBA会生成一种在原始序列里完全不存在的形状比如本该是尖峰的波形被平均出两个小波峰或者本该平滑上升的趋势变成了阶梯状。这种情况通常是DTW路径过度扭曲造成的。DTW倾向于用变形来减少距离如果距离度量里没有对变形幅度做惩罚路径就会任意拉长某些局部区域。平均序列为了兼顾这些不合理的拉伸就会出现奇怪的形状。处理思路有两个方向一是收紧窗口约束从物理上限制最大扭曲比例二是改用带惩罚项的DTW变体比如在局部代价中加入时间差惩罚让远距离匹配的成本变高。另一个容易被忽视的点是数据预处理时如果去噪去得不干净DBA会把噪声点当作真实结构去对齐这时先做一次带截止频率的滤波反而能让平均序列更合理。下表汇总了几类高频问题与推荐排查方式方便实际项目里对照使用现象最可能的原因推荐处理方式平均序列有锯齿窗口过小、原始序列数过少放宽窗口10%-20%或加轻度平滑目标项结果偏向某条序列初始化偏差、迭代未收敛多起点初始化增加迭代轮数到50轮以上收敛过慢序列长、无窗口、实现低效向量化、设置Sakoe-Chiba窗口、粗粒度预对齐平均形状怪异DTW路径过度扭曲收紧窗口、使用带时间惩罚的DTW变体受离群序列影响大外点拉偏对齐先两两DTW计算距离矩阵剔除离群序列5. 应用场景与选型建议5.1 典型应用场景DBA最常见的应用是生成类模板。比如在基于加速度计的人体动作识别里一个动作通常会采集很多次每次的长度和节奏都不一样。直接用原始序列做分类模板会受相位干扰用DBA生成一条代表性的平均序列后新样本只要和这条模板算DTW距离就能得到一个稳定的相似度。这个方法在工业设备故障诊断里也一样把同类故障的振动信号做DBA可以得到一条该故障类型的“标准波形”后续实时采集的信号直接和标准波形做匹配。DBA在语音处理里也有位置。语音信号里同一句话用不同语速说出来时间轴差异很大用DBA对多个发音实例做平均可以提取出更稳定的语音特征模板。心电图等生理信号里不同心率下同一个心动周期的长度不同DBA能在保留P波、QRS波群、T波相对结构的前提下生成一条典型心跳波形。除此之外DBA也常用于时间序列聚类算法中作为K-Means在DTW距离下的“均值中心计算器”也就是K-Shape等算法的替代方案之一。做聚类的时候有个细节值得注意DBA作为聚类中心时计算量会直接影响每一轮聚类的更新耗时。如果数据集大可以不用每一轮都迭代到收敛而是只跑3到5轮DBA聚类整体迭代多次后效果依然不错整体耗时却大大降低。5.2 DBA vs 其他平均方法现在能求时间序列平均的方法不少容易搞混。简单列一下我的选型经验。逐点平均点对点均值最快但只适合序列天然等长且时间对齐良好的情况适合信号已经做过时间归一化。基于特征对齐的平均比如先提取峰值位置按峰值对齐再平均速度快但需要人工定义特征点对没有明显标志点的数据不适用。DBA不需要特征工程能处理复杂的不规则相位偏移代价就是计算量大。还有一种叫Soft-DTW的变体它在DBA基础上把对齐路径做了软化处理让路径选择可微分梯度能回传。Soft-DTW在深度学习里很香因为可以嵌入到神经网络中作为损失函数或池化操作。如果你在做深度学习相关的时间序列任务优先考虑Soft-DTW如果是在传统机器学习流程或者纯分析任务里经典DBA更简单稳定可解释性也更强。DBA内部也有变体可以选择。部分改进版本允许对原始序列赋权重这在处理不平衡样本时很关键。比如某一类动作重复了100次另一类只重复了5次做交叉类别的平均模板时如果不加权数量多的类别完全主导模板。这时候用加权DBA让每个类别的总权重一致会更合理。6. 实操经验如何把DBA真正用到自己的项目里分享几个我自己用DBA的真实经验也许能帮你少走一些弯路。第一不管最初跑出来效果多差先别急着否定这个算法。DBA对预处理极其敏感数据归一化、去噪、截断方式稍有不同结果都会差很远。宁可多花时间调前处理管线也别一上来就玩命调DBA内部参数。第二DBA输出平均序列后一定要做质量评估不要只看肉眼的“像不像”。我常用的指标是把平均序列和每一条原始序列的DTW距离算出来平均之后看这个值再和逐点平均那组的平均DTW距离做对比。DBA一般应该显著更小如果没有就说明对齐环节出了问题。第三有一个非常隐蔽的坑如果原始序列之间存在长度差异有些实现会把长度归一化到某个固定值再算DTW这其实改变了原始数据的语义。DBA本身不要求序列等长但要求你在做长度统一时想清楚到底是重采样还是截断。重采样会平滑掉快变部分截断又会丢掉信息两种方式都会改变DTW的对齐结构建议在实际项目里先做个小实验看看哪种处理方式让下游任务的精度更高。第四关于内存占用。长序列跑DTW时累积距离矩阵会占不少内存但DBA并不需要存储完整的路径二维矩阵只要在对齐时逐行维护当前行和上一行就能算出代价然后通过回溯记录对齐关系。如果用的是现成实现确认它是不是这样实现的。有些库为了通用性会保留整个矩阵长序列场景下内存会爆掉。最后再说一个小技巧。做DBA前可以先对原始序列做降采样用粗数据跑一遍DBA确定大致的平均形态再用原始尺度在这条粗平均序列的引导下精修。这个策略看起来是“做两遍”实际总耗时反而比直接全精度跑一遍要低得多而且结果更稳定相当于给优化问题提供了一个强先验。各个项目的数据特性不一样这个trick不一定每次都灵但在处理上万点长序列时值得优先尝试。
返回列表