咱们今天来聊一个话题:基于CNN的时间序列分类方法。
其中给大家展示了其中详细的原理,以及完整的一个案例,完整的代码~
假设我们有一组一维时间序列数据,每个样本的输入为长度为
的信号
,目标是将其分为
个类别中的一个。为此,我们构建一个基于 CNN 的分类器,利用局部特征提取和层级特征融合实现分类。
数据预处理
通常,输入数据需要经过归一化或标准化处理,例如将每个时间序列归一化到均值为0、方差为1:
其中
和
分别为该序列的均值和标准差。
CNN 模型架构
对于一维时间序列,常采用 1D 卷积操作。典型的 CNN 模型结构可以包含以下几个模块:
激活层(Activation Layer,如 ReLU)
全连接层(Fully Connected Layer)
下面咱们就来详细介绍各部分的计算过程~
卷积层
卷积操作:
给定输入信号
和一个长度为
的卷积核(滤波器)
,以及偏置
,卷积操作在位置
上的输出
定义为:
其中
。
对于多通道(例如前几层特征图)的情况,假设输入为
通道,每个通道的信号为
,则卷积核也扩展为
,卷积结果为:
步幅与填充:
若使用填充,输出长度
为:
激活函数
常用的激活函数为 ReLU,其公式为:
即,对卷积层输出的每个元素
应用:
池化层
池化层用于降维和提取最显著的局部特征。以 1D 最大池化(Max Pooling)为例,假设池化窗口大小为
,步幅为
,在窗口
内,输出为:
这样可以减少特征图的长度,同时增强特征的不变性。
多层 CNN 结构
一个典型的 CNN 模型可以堆叠多个卷积层和池化层。
例如,假设模型包含两层卷积层,每层后接 ReLU 激活和池化层:
经过以上层级提取后,将得到形状为
的特征图,再将其展平为一个向量,输入到全连接层。
全连接层与 Softmax 分类器
设展平后的特征向量为
,其中
。全连接层对
的线性变换为:
其中
,
。
接下来通过 Softmax 层计算各类别的预测概率:
损失函数与模型训练
通常采用交叉熵损失函数(Cross-Entropy Loss)来衡量模型输出与真实标签之间的差距。对于单个样本,若真实标签的 one-hot 编码为
(其中只有正确类别为1,其余为0),则交叉熵损失定义为:
其中
为真实类别。
在训练过程中,模型参数(卷积核权重、全连接层权重等)通过反向传播算法(Backpropagation)和梯度下降(如 Adam、SGD 等优化器)进行更新。
模型训练流程概述
对输入时间序列
依次经过各个卷积、激活、池化层提取局部特征。
将多层提取的特征展平后输入全连接层,得到类别得分
。
完整案例
传统方法通常依赖特征工程和经典机器学习算法,而深度学习中的卷积神经网络(CNN)在自动提取局部特征、捕捉时序数据局部模式等方面具有明显优势。
这里,主要想给大家展现的是:利用一维卷积网络对时间序列数据进行特征提取,并结合全连接层进行分类。相较于传统的基于 RNN 或 LSTM 的模型,CNN具有并行计算和局部感受野的优势,能够更快收敛且具有较好的鲁棒性。
数据集
为了说明问题,我们构造一个虚拟时间序列数据集。数据集包括三类信号,分别代表不同的模式(例如:正弦波、方波、锯齿波),并加入噪声模拟真实数据中的随机扰动。数据集共包含3000个样本,每个样本的时间步长为128,标签取值为0、1、2。这样可以模拟多类别分类场景。
数据可视化说明
在数据构造阶段,我们会绘制以下图形:
样本波形图
:原始时间序列数据的波形形态,便于观察各类别信号的不同模式。
训练过程损失曲线
:模型在训练集和验证集上的损失变化。
分类准确率曲线
:每个训练周期(epoch)在训练集和验证集上的分类准确率。
预测曲线(混淆矩阵、ROC 曲线等)
:模型在测试集上的预测结果,如混淆矩阵可以直观反映各类别的识别效果;另外我们也可以绘制各类别的 ROC 曲线。
import numpy as npimport matplotlib.pyplot as pltimport seaborn as snsimport torchimport torch.nn as nnimport torch.optim as optimfrom torch.utils.data import Dataset, DataLoader, random_splitfrom sklearn.metrics import confusion_matrix, roc_curve, aucimport itertoolsimport random# 固定随机种子,保证结果可重复 np.random.seed(42 ) torch.manual_seed(42 ) random.seed(42 )# 虚拟数据集生成 def generate_time_series (n_samples=3000 , seq_length=128 ) : """ 生成包含三类的虚拟时间序列数据:正弦波、方波、锯齿波,并加入高斯噪声。 参数: n_samples: 总样本数 seq_length: 每个样本的时间步数 返回: X: 数据矩阵,形状 (n_samples, seq_length) y: 标签向量,取值 0,1,2 """ X = [] y = [] t = np.linspace(0 , 2 *np.pi, seq_length) for i in range(n_samples): label = np.random.choice([0 ,1 ,2 ]) if label == 0 : # 正弦波 signal = np.sin(t) + np.random.normal(0 , 0.1 , seq_length) elif label == 1 : # 方波:利用正弦波阈值化 signal = np.where(np.sin(t) > 0 , 1.0 , -1.0 ) + np.random.normal(0 , 0.1 , seq_length) else : # 锯齿波:使用线性函数再取周期性 signal = ((t / np.pi) - 1 ) + np.random.normal(0 , 0.1 , seq_length) X.append(signal) y.append(label) X = np.array(X) y = np.array(y) return X, y# 生成数据 X, y = generate_time_series()# 数据可视化:样本波形图 def plot_sample_waveforms (X, y, n_samples=3 ) : """ 随机选择n_samples个样本,并绘制波形图,每种类别选择一个样本。 """ plt.figure(figsize=(12 , 6 )) colors = ['dodgerblue' , 'crimson' , 'limegreen' ] # 鲜艳的蓝色、红色、绿色 labels = ['Sine Wave' , 'Square Wave' , 'Sawtooth Wave' ] for class_label in range(3 ): idx = np.where(y==class_label)[0 ] sample_idx = np.random.choice(idx, 1 )[0 ] plt.plot(X[sample_idx], color=colors[class_label], linewidth=2 , label=labels[class_label]) plt.title("Sample Waveforms of Time Series" , fontsize=16 ) plt.xlabel("Time Step" , fontsize=14 ) plt.ylabel("Signal Value" , fontsize=14 ) plt.legend(fontsize=12 ) plt.grid(alpha=0.3 ) plt.tight_layout() plt.show() plot_sample_waveforms(X, y)# 数据可视化:数据分布直方图 def plot_data_distribution (y) : """ 绘制不同类别的样本数直方图 """ plt.figure(figsize=(8 ,6 )) colors = ['darkorange' , 'mediumorchid' , 'teal' ] # 橙色、紫色、青色 sns.countplot(x=y, palette=colors, edgecolor='black' ) plt.title("Class Distribution" , fontsize=16 ) plt.xlabel("Class Label" , fontsize=14 ) plt.ylabel("Count" , fontsize=14 ) plt.tight_layout() plt.show() plot_data_distribution(y)
样本波形图
:展示了三个典型时间序列样本(正弦波、方波、锯齿波),便于观察各信号形态及噪声干扰情况。