时间序列转图像全攻略:GAF、MTF、递归图与STFT原理及工程实践 做故障诊断和时序分类这行绕不开一个尴尬情况手里全是传感器采出来的波形一维序列一条接一条但深度学习最成熟的工具预训练模型、数据增强、注意力结构全都长在二维图像上。ResNet、EfficientNet、Vision Transformer这些名字一说出口默认输入就是224×224的图。你总不能放着现成的预训练权重不用非得从零训一个一维网络。所以“把一维序列变成二维图像”这件事不是花活是工程刚需。我这两年陆续试过不少转换方法踩坑也踩了不少最后沉淀下来最常用、最该掌握的四个GAF格拉姆角场、MTF马尔可夫转移场、Recurrence Plot递归图、STFT短时傅里叶变换。这篇就把它们一次性讲透原理是什么、代码怎么写、参数怎么调、什么地方容易翻车。想直接抄作业的看完照着敲就能用。1. 为什么非要把一维时间序列变成二维图像1.1 图像模型的“预训练红利”别浪费做时间序列分类最朴素的办法是上LSTM、TCN或者一维卷积。问题是这些模型的训练数据量要求不小而工程现场的实际标注样本往往只有几千条甚至几百条。图像领域不一样ImageNet上训好的权重一抓一大把哪怕你的样本风格和自然图像差得远用预训练模型做特征提取器或者微调通常都比从零训练一个一维小网络稳定得多。把序列转成图像本质上是在“蹭”图像领域的成熟基建。ResNet的残差结构、BatchNorm的分布优化、RandomResizedCrop这类增强策略全都直接可用。还有一个隐形好处图像分类的推理部署链路非常成熟TensorRT、ONNX、各种边缘盒子对图像模型支持最到位转成图像等于降低了上线成本。用生活里的逻辑来类比你手里是一堆不规则零件但标准图纸全是按方砖画的。要么自己设计一套非标的组装流程要么想办法把零件规整成方砖。转图像就是后者——改造数据复用基建。1.2 先看一眼四种方法的全貌在进入细节之前先给个总览表后面每一章再展开。四种方法没有绝对的优劣适用的信号特征和任务场景都不一样。方法全称本质编码图像特征最适合场景主要短板GAFGramian Angular Field极坐标角度加格拉姆矩阵带有对角线规律的彩色/灰度热图趋势变化、周期形态差异明显的信号图像尺寸随序列长度平方增长MTFMarkov Transition Field马尔可夫状态转移概率网格状纹理局部块状结构状态切换、模式突变、噪声较大的信号分箱数选择影响较大Recurrence Plot递归图相空间距离阈值二值化黑白点阵纹理对角线结构非线性、混沌系统、系统的确定性检测阈值与嵌入参数敏感STFT短时傅里叶变换时间-频率-幅值三维映射时频谱图横轴时间竖轴频率周期性、调频调幅信号特征频率演化时频分辨率受海森堡不确定原理约束实际使用时我通常不是只选一个而是把两三种方法组合成多通道图像充分利用各自的信息侧重。这部分放到后面工程落地章节细说。2. GAF从极坐标里长出来的“角场”2.1 GAF核心思路归一化、极坐标、格拉姆矩阵很多人第一次看到GAF的公式会有点晕其实拆开就三步。第一步把时间序列归一化到[-1, 1]区间这一步可以用min-max缩放也可以按全局边界缩放后面我会专门说这个细节。第二步把归一化后的每一个数值转换成极坐标下的角度取反余弦φ arccos(x)。因为x的取值范围是[-1, 1]arccos的结果落在[0, π]上数值越大角度越小是一一对应的单调映射。第三步把这些角度两两组合计算格拉姆矩阵就得到图像。这里最关键的设计是半径 r i / Ni 是当前点的时间序号N 是序列长度。也就是说每个采样点的角度编码的是“数值大小”半径编码的是“时间位置”这样极坐标图里每一个点都同时携带了数值和时间双重信息。格拉姆矩阵再把任意两个时间点的角度做运算得到一个N×N的矩阵矩阵的每个位置都属于一个时间点对天然保留了时间顺序又不完全依赖平移。我自己第一次实现时有个疑问既然arccos能映射角度为什么不直接用角度差或者角度和关键在于arccos的单调性保证了原始数值的相对大小关系不丢失而且对角线的位置恰好能还原出原始序列的幅度变化。这是GAF特别好用的一个性质后面会展开。2.2 GASF和GADF到底差在哪格拉姆角场下面还分两派写法上有人写GASF有人写GADF还有写GAFS的。先纠正一下命名严格来说GAF是总称GASFGramian Angular Summation Field用角度之和的余弦GADFGramian Angular Difference Field用角度之差的正弦。公式上就一行区别GASF[i][j] cos(φi φj) GADF[i][j] sin(φi - φj)很多文章把GAFS当复数或者笔误其实指的就是这一类方法。用哪个好我的经验是如果信号的主要差异表现在幅度大小的相对变化GASF更敏感如果差异更多体现在波形的斜率、方向变化上GADF表现更好。实际做分类时没必要二选一把两种编码当成两个通道一起用信息互补效果一般会更好。代码实现也不复杂用pyts库一行就能生成。这里给一个手动实现的版本方便理解内在逻辑import numpy as np def gasf_gadf(x, image_sizeNone): # 1. 归一化到[-1, 1] x (x - x.min()) / (x.max() - x.min()) * 2 - 1 # 2. 极坐标角度 phi np.arccos(x) # 3. 构建格拉姆矩阵 GASF np.cos(phi[:, None] phi[None, :]) GADF np.sin(phi[:, None] - phi[None, :]) return GASF, GADF如果序列太长生成的矩阵会爆炸可以用pyts里的image_size参数控制输出尺寸它内部会做聚合降采样from pyts.image import GramianAngularField gasf GramianAngularField(methodsummation, image_size128) gp gasf.fit_transform(seq.reshape(1, -1))[0] # seq是一维numpy数组2.3 GAF实操心得图像尺寸与对角线信息用GAF踩过的第一个坑就是序列长度。N个点的序列生成N×N图像N256时是256×256还好N到了2048一张图就是2048×2048显存直接报警。pyts的image_size参数可以强制缩放到128或者224但代价是高频信息会被平均掉属于一种隐式的低通滤波。所以我的习惯是序列分段或降采样先控制输入长度在128到512之间再决定要不要缩放。还有个容易被忽视的细节GAF图像的对角线。因为cos(φi φi) cos(2φi)对角线上其实保存了每个时刻原始数值的双倍角映射沿着对角线读一遍就能看到原始波形的“压缩版”。这意味着GAF编码并没有把原始信号完全打散而是把信息重新排列在二维空间里。做可解释性分析的时候可以借这个性质反查图像里的热点区域对应原始序列的哪个时间段这对故障定位很有用。3. MTF用状态转移概率画纹理3.1 先回答“mtf是什么”搜索框一输入MTF出来一半是镜头评测里的调制传递函数另一半才是这里要说的马尔可夫转移场Markov Transition Field。两个东西同名但完全不同镜头MTF衡量的是成像系统的分辨率对比度时序MTF编码的是状态转移概率。以后看到MTF先看语境别混。MTF的核心思路和前面GAF完全不一样。GAF关注的是数值本身的大小关系MTF关注的是数值从一个状态迁移到另一个状态的概率。具体来说先把序列的值域分成Q个分箱例如把[0,1]区间均匀分成8个箱子每个采样点落到一个箱子里。然后统计相邻两个采样点的状态转移次数归一化得到Q×Q的一阶马尔可夫转移矩阵WW[i][j]就是从分箱i到分箱j的转移概率。但转移矩阵有个致命问题它只统计了全局的转移频次丢掉了一维序列里“谁挨着谁”的时间位置信息。马尔可夫转移场做的就是把概率填回对应的时间坐标上M[i][j] W[分箱(序列[i])][分箱(序列[j])]。也就是说图像上第i行第j列的像素值代表原始序列第i个点所在分箱转移到第j个点所在分箱的概率。3.2 分箱数与转移矩阵的实现分箱数Q是MTF最重要的超参数。Q太小比如2到4状态太粗糙图像纹理都快糊成一团Q太大比如32以上很多分箱之间根本没有转移样本矩阵稀疏计算量也明显上升。我一般从Q8开始试复杂信号用Q16超过16后收益就明显递减了。这个和分箱方法的原理有关它本来就是对噪声和细节的一种降维平滑没必要让状态空间过度精细。pyts里实现很简单from pyts.image import MarkovTransitionField mtf MarkovTransitionField(n_bins8, image_size128) mtf_img mtf.fit_transform(seq.reshape(1, -1))[0]生成出来的图像有一个特点呈现明显的网格纹理因为状态转移概率天然带有离散化痕迹。不同类别信号如果状态切换模式不同图像上的纹理排列差异会非常大这也是MTF在小样本分类上表现不错的原因——它把“状态怎么变”这个高层次特征直接可视化出来了。3.3 MTF实操心得噪声鲁棒性与样本量试过几种方法后我对MTF的评价是最“耐造”的编码。原因在于分箱和概率统计本身都有平滑效应个别采样点的尖峰噪声会被分摊到转移频次里不像GAF那样每个点的数值都直接映射成像素。所以信噪比不太好的传感器数据MTF往往比GAF更稳。但MTF也有个隐蔽的坑它对序列的起始和结尾不敏感。全局转移矩阵的统计量基本不随序列起点偏移而剧烈变化这在步态识别、动作识别这类任务里可能是优点但在需要精确时间对齐的任务里会让你损失定位能力。如果既想要状态模式信息又想要时间结构建议把MTF和GAF其中一个通道堆在一起用互补性很好。4. 递归图Recurrence Plot把相空间轨迹的“回头路”画出来4.1 从相空间重构到递归判定递归图最早来自非线性动力学不是专门为深度学习设计的但图像化之后意外地适合当分类特征。先解释它到底在画什么。一维时间序列其实只是系统状态在高维空间中运动时的一个投影。按Takens嵌入定理可以用延迟坐标把一维序列重构成高维相空间中的轨迹对每个时刻i构造向量 [x(i), x(iτ), x(i2τ), ..., x(i(m-1)τ)]m是嵌入维度τ是延迟步长。这就好比一个人走在迷宫里你只记录他走了多少步看不到方向但把最近几步串起来就能大概还原出方向信息。重构出相空间轨迹后递归图做的就是一件很简单的事比较任意两个时刻的状态向量是否“足够接近”。如果向量之间的距离小于某个阈值ε就在第i行第j列上画一个点表示系统在此时刻回到了之前某个状态的邻域。上图里那些对角线和方块本质上就是系统周期行为、间歇行为的视觉证据。4.2 阈值选择与参数经验递归图的生成涉及三个关键参数嵌入维度m、延迟时间τ、距离阈值ε。m和τ的选择在非线性时间序列分析里有各种自适应算法比如互信息法选τ、假近邻法选m但对工程分类任务来说我建议别过度纠结直接给固定值m3或4τ1或2大多数传感器信号都能得到可用的纹理。阈值ε才是真正需要调的重点。阈值太小递归点稀疏图像接近全黑阈值太大所有点都算递归图像接近全白失去区分度。pyts的RecurrencePlot支持两种方式一种是固定threshold一种是按距离分布百分比设定threshold例如percentage10表示取所有距离的10%分位数作为阈值。我的经验是percentage在5到15之间效果比较稳定先试10。from pyts.image import RecurrencePlot rp RecurrencePlot(thresholddistance, percentage10) rp_img rp.fit_transform(seq.reshape(1, -1))[0]递归图颜色通常是黑白的但可以是二值图像。做深度学习输入时单通道黑白图需要复制成三通道才能送进预训练网络也可以和其他三种方法堆叠成多通道图像。4.3 递归图适合什么信号递归图最擅长捕捉两类信号特征一是周期性周期信号会在图上形成规律的平行对角线纹理二是确定性混沌信号产生的递归图往往有复杂的不规则图案。机械故障诊断里轴承磨损信号和非磨损信号的递归纹理差异很大肉眼都能分辨。心电图分析里不同心律失常的递归图也有典型结构。不过递归图不是万能的。它对平稳性比较敏感信号含有明显趋势项时轨迹会漂移递归点急剧减少。所以用之前最好先做趋势消除或差分处理。这个预处理步骤经常被忽略但没有它递归图的纹理往往会被趋势淹没分类效果大打折扣。5. STFT时频图老牌方法依然最能打5.1 为什么直接做傅里叶变换不够短时傅里叶变换可能是四种方法里历史最悠久、工业软件支持最完善的一种。标准傅里叶变换能把信号从时间域变到频率域得到每个频率的幅值但代价是彻底丢掉时间定位。拿一段语音做FFT你只能知道有哪些频率成分不知道这些频率是什么时候出现的。对轴承故障、电机异响这类非平稳信号来说频率随时间变化恰恰是最关键的诊断信息。STFT的解决思路朴素又有效把信号切成很多小段对每一段分别做FFT把结果按时间顺序拼起来。形象地说就像给连续信号拍一组X光片每张X光片只覆盖一小段时间的频率结构连在一起就是一张时频图。图像的横轴是时间纵轴是频率像素颜色是幅值强度。5.2 窗长、重叠、频率分辨率的博弈STFT的核心参数有三组窗函数类型、窗长nperseg、重叠率noverlap。这三组参数直接决定了时频图的质量。窗函数我用得最多的是汉宁窗hann旁瓣抑制效果好频谱泄漏小。矩形窗虽然主瓣窄但旁瓣高容易产生虚假频率成分。窗长nperseg决定频率分辨率窗长越大频率分辨率越高但时间分辨率越低这是一个此消彼长的关系根源是海森堡不确定原理。fs是采样率时nperseg128频率分辨率大约是fs/128nperseg256时分辨率提升一倍但时间上每帧覆盖的时长也翻倍。重叠率noverlap影响时间轴的连续性。相邻窗完全没有重叠时时间分辨率等于窗长比较粗糙设置75%重叠比如nperseg128、noverlap96时间分辨率明显提升图像过渡也更平滑计算量只增加一点。我的经验是至少50%重叠追求图像质量就直接上75%。from scipy.signal import stft import numpy as np fs 1000 f, t, Zxx stft(seq, fsfs, windowhann, nperseg128, noverlap96) # Zxx是复数矩阵取幅值作为图像像素 spec np.abs(Zxx) # shape: (频率点数, 时间帧数)把spec标准化成0到255之间的灰度图或者用matplotlib的imshow配合jet、viridis等色图保存成彩色图就能作为深度学习的输入。5.3 STFT实操心得频带裁剪与颜色映射STFT生成的原始图像尺寸受两个维度影响频率点数约等于nperseeg/21时间帧数约等于样本点数除以滑动步长。直接把整张图送进网络通常不是最优解。以采样率1000Hz的振动信号为例有效分析频带往往集中在0到500Hz但实际有用的故障特征可能只在0到200Hz其他频段全是噪声。我的做法是先画出来看一遍确定目标频带把非目标频带裁掉再保存这样既减小图像尺寸又提升信噪比。颜色映射也要留意。深度神经网络学的是像素值分布不是人眼看到的颜色。同一张谱图用jet色图和gray色图保存像素数值完全不同训练出来的模型行为也不一样。我的规矩是统一用灰度图或统一的colormap保存别今天用jet明天用viridis否则模型学到的其实是颜色风格的差异不是信号特征的差异。6. 四种方法横向对比与工程落地建议6.1 信息侧重与可解释性对比四种方法编码的是信号的不同侧面放到一起对比就更清楚了对比维度GAFMTFRecurrence PlotSTFT核心信息幅值相对关系与时间位置状态转移概率相空间轨迹递归性频率随时间变化图像纹理特点对角线渐变、连续平滑网格块状、离散纹理点阵纹理、对角线结构连续时频谱带对噪声鲁棒性中高中低中高计算复杂度O(N^2)O(N×QQ^2)O(N^2)O(N log N)可解释路径对角线可还原波形分箱状态可追溯递归率可对应动力学特征频率成分直观可读6.2 工程落地三通道堆叠与预训练模型工程上我推荐的做法选三种互补方法分别生成单通道图再堆叠成RGB三通道图像。一个经过验证的经典组合是GADFMTFSTFTGADF捕捉幅值形态MTF捕捉状态转移STFT捕捉时频演化三个维度互不冲突图像纹理特征丰富预训练网络能提取出更稳定的高阶特征。具体操作上要注意图像尺寸一致性。GAF和MTF直接生成的图像是N×NSTFT是频率点×时间帧尺寸大概率不一致。处理办法是统一resize到224×224或者在生成时就通过pyts的image_size、STFT的窗长把尺寸控制到接近。我个人的习惯是先控制序列长度为224个点左右GAF和MTF生成224×224STFT用nperseg64、noverlap48频率维大概是33点时间维约109点再用双线性插值resize到224×224效果比较平衡。6.3 选型决策先看清你的信号长什么样这里给一个简洁选型思路信号有明显周期成分、要看频率演化优先STFT信号是开关过程、状态切换明显优先MTF信号来自非线性系统、怀疑有混沌特性优先递归图信号趋势形态复杂、类别之间差异体现在波形曲率上优先GAF/GADF。如果不知道选什么就做一个小实验把四种方法的图都生成出来肉眼扫一遍看哪类的类间差异最明显。特征在图像上肉眼看不出差异的网络大概率也学不到。7. 常见问题与避坑技巧实录7.1 训练集和测试集用了不同的归一化边界这是新手最容易踩的坑。GAF要求序列归一化到[-1,1]很多人对每条样本单独做min-max归一化训练集和测试集的数值尺度完全不一致模型学习到的是“相对形状”而非真实幅值。正确做法是在训练集上统计出全局最小值和最大值用同一组边界对训练集、验证集、测试集做归一化。STFT的幅值标准化同样要用训练集的统计量。7.2 图像尺寸过大导致显存溢出GAF和递归图都是N×N的矩阵序列长度2048时一张图就是2048×20484MB一个像素值批量16就是128MB看起来还能接受但经过CNN中间层特征图放大后GPU显存压力会快速增长。处理办法有两个一是控制序列长度在512以内二是用pyts的image_size参数提前缩小。提前缩小比生成大图再resize更省内存因为中间不会出现巨大的临时张量。7.3 染色风格不统一导致模型“学颜色不学特征”把单通道图复制成三通道或者用不同colormap染色后送进网络只要训练集和测试集染色一致模型其实也能学但可解释性和稳定性都不好。我遇到过因为换了一个colormap导致测试准确率掉了近10个点的情况原因就是模型过拟合到了颜色分布的细微差异上。建议所有实验统一灰度图存储需要彩色图时固定同一个colormap并把它当成超参数记录下来。7.4 增广策略用错方向图像领域常用的水平翻转、垂直翻转在时间序列转图像的任务上不能乱用。GAF的时间轴是沿对角线排列的水平翻转等价于把时间顺序反过来对某些周期性信号可能无害但对有方向性的趋势信号就是致命的。随机旋转90度更是直接破坏编码结构。安全增广包括轻微缩放、少量平移、颜色抖动、Cutout。我通常只用随机平移和Cutout效果稳定且不破环编码语义。7.5 别忘了原始序列的“底噪”转换方法是特征工程不是降噪手段。如果原始信号里有强烈的工频干扰或直流偏置任何转换方法都会把噪声编码进图像。我开始做的时候忽略了这个STFT图像里50Hz倍频的竖条纹成为了最显著特征模型学得很好换一台设备数据就废了。建议转换前先做高通滤波或去趋势处理保证进入编码器的是真正关心的信号成分。以上是我在实践中的核心经验和教训。有一点最后想再强调这四种编码方法之间不排斥甚至刻意组合才是更优工程做法。我第一次做故障诊断项目时只用GAF单通道准确率在88%左右后来改成GADFMTFSTFT三通道相同网络结构直接跳到94%。图像尺寸、分箱数、窗长这些参数多数时候没有全局最优解换数据集就要重新调。建议从这个组合起步再做采样率降采样、序列分段长度的对比实验你会发现图像编码这个方向能折腾的细节远比想象中多。