在过去聊了不少关于LSTM的时间序列预测的内容。
传统的长短期记忆网络(LSTM)主要通过门控机制捕捉时间序列中的长短期依赖,但在面对具有空间结构(如视频、气象数据等)的数据时,将输入展平为一维向量会丢失局部空间信息。
事实上,为了解决这一问题,**卷积LSTM(ConvLSTM)**将矩阵乘法替换为卷积操作,不仅捕捉时间依赖,还能保留空间局部性。
另一方面,实际问题中,数据往往具有多尺度特性:
- 空间上:局部细节与全局信息共存;
- 时间上:既有短期波动也有长期趋势。
因此,在ConvLSTM的基础上引入多尺度建模,可以通过不同感受野的卷积核或金字塔结构提取不同尺度的特征,进而提高预测和建模精度。
卷积LSTM是什么
ConvLSTM保留了标准LSTM的门控机制,但将输入、隐藏状态与细胞状态之间的线性变换由全连接层改为卷积操作。
设在时刻 的输入为 (保持空间维度),上一时刻的隐藏状态为 ,细胞状态为 ,则该单元的计算过程为:
- 输入门
- 遗忘门
- 细胞状态更新
- 输出门
- 隐藏状态
其中:
- “”表示卷积操作;
- “”表示逐元素(Hadamard)乘法;
- 为sigmoid激活函数, 为双曲正切函数;
- 为不同门控的卷积核权重, 为偏置项。
多尺度建模策略
在基于ConvLSTM的框架上,引入多尺度建模主要有两大方向:空间多尺度和时间多尺度。
空间多尺度特征提取
多尺度卷积核分支
利用不同尺寸的卷积核可以捕捉局部细节和全局结构。设有 个尺度,每个尺度使用不同尺寸的卷积核 (例如 3×3、5×5、7×7),则对输入 计算:
随后将各尺度得到的特征通过融合函数整合:
其中融合方法可以为拼接(Concatenation)或加权求和,例如:
其中 为可学习的权重。
金字塔结构
另一种方法是构建金字塔,将输入数据在空间上进行多级下采样,形成不同分辨率的表示:
其中 表示第 层的下采样操作。每个尺度的 分别输入到对应的ConvLSTM单元,得到尺度下的隐藏状态 ;最后通过上采样(如反卷积或插值)恢复后进行融合:
这里 表示上采样操作。
时间多尺度建模
在时间维度上,不同的时间窗口能够捕捉短期变化与长期趋势。
可以采用如下策略:
- 多时间窗口输入:构造多个时间窗口,每个窗口长度为 (例如短期窗口 与长期窗口 ),对每个窗口使用独立的ConvLSTM:
- 融合时序输出:将不同时间尺度得到的预测结果融合:同样,融合可以是拼接或加权求和,甚至可以引入注意力机制对各尺度赋予不同权重。
融合策略
无论是空间还是时间多尺度,如何将多尺度特征有效融合是关键。
常见策略包括:
- 拼接(Concatenation):直接将各尺度特征在通道维度上拼接,之后使用卷积层进行特征整合;
- 加权求和:各尺度特征乘以自适应权重后求和;
- 注意力机制:引入注意力模块自动学习各尺度特征的重要性权重;
- 残差连接:在融合过程中利用残差连接,避免信息在深层网络中丢失。
详细推理
以多尺度卷积核分支为例,详细推导:
-
多尺度卷积特征提取
对于每个尺度 ,先对输入 进行卷积:得到不同尺度的初步特征表示。
-
卷积LSTM单元处理
将各尺度特征 分别输入对应的ConvLSTM单元。以尺度 的输入为例,其输入门计算为:遗忘门、细胞状态更新和输出门的计算同理,得到尺度 下的隐藏状态 。
-
跨尺度特征融合
融合各尺度隐藏状态,常见方式之一为加权求和:或者在通道维度上拼接后再通过卷积层整合:
其中 为后续卷积或全连接映射。
-
最终预测
融合后的特征 可输入到后续的预测模块(例如全连接层)得到最终输出:其中 表示映射到预测空间的函数,如回归或分类映射。
基于卷积LSTM的时间序列多尺度建模方法融合了以下两个优势:
- 时空信息捕捉:通过将LSTM的全连接运算替换为卷积操作,ConvLSTM能够同时建模数据的时间依赖和空间局部性。
- 多尺度特征提取:利用多尺度卷积核、金字塔结构以及不同时间窗口,模型能够捕捉数据在不同空间和时间尺度下的变化规律,通过多尺度特征融合提高预测性能。
完整案例
这里我们构造一个虚拟数据集。假设每个样本是一个长度为T(例如10)的时间序列,每个时间步的数据为1通道的16×16二维网格。数据生成思路如下:
- 利用二维网格的坐标构造低频全局趋势,使用缓慢变化的正弦函数作为全局背景。
- 同时加入局部高频扰动(利用高频正弦函数和随机噪声)以模拟局部细节。
- 数据标准化后作为输入,目标为预测下一个时间步的状态。
数据集将划分为训练集和测试集,并利用PyTorch的Dataset与DataLoader进行封装。
整个案例分为以下几个部分:
-
数据集构造与预处理
-
多尺度ConvLSTM模型设计
-
- 多尺度空间特征提取分支(不同卷积核尺寸、金字塔结构)
- ConvLSTM单元及其公式推导
- 融合模块(拼接或加权求和)
-
模型训练与预测过程
-
可视化分析:在一张图中绘制4幅图形,分别展示训练损失曲线、预测曲线与真实曲线、某一尺度特征的热力图、预测误差分布直方图;每幅图都采用鲜艳配色,图像复杂且吸引人
-
算法优化与调参思路,包括参数初始化、学习率调整、正则化策略、网络结构的改进等
模型设计
基于多尺度ConvLSTM的架构,模型主要分为两个部分:
- 多尺度特征提取分支
利用不同卷积核(例如3×3、5×5、7×7)的并行分支,对输入数据进行卷积提取。每个分支提取不同尺度的空间信息。公式如下:融合方式可以采用加权求和或通道拼接。 - ConvLSTM单元时序建模
对每个尺度提取出的特征进行时序建模,计算过程如前文所述: - 融合模块与输出映射
将各尺度的隐藏状态进行融合(例如:或者简单拼接后接卷积映射),再通过全连接层或卷积层映射到预测输出空间。
import random
import matplotlib.gridspec as gridspec
import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
# 1 数据集构造:虚拟时间序列数据
class SyntheticTimeSeriesDataset(Dataset):
def __init__(self, num_samples=500, seq_len=10, height=16, width=16):
self.num_samples = num_samples
self.seq_len = seq_len
self.height = height
self.width = width
self.data, self.targets = self.generate_data()
def generate_data(self):
data = []
targets = []
# 创建二维坐标网格
x = np.linspace(0, 2 * np.pi, self.width)
y = np.linspace(0, 2 * np.pi, self.height)
xv, yv = np.meshgrid(x, y)
# 随机初始化不同的相位和频率参数
for i in range(self.num_samples):
seq = []
phase_shift = random.uniform(0, 2 * np.pi)
freq_global = random.uniform(0.05, 0.15) # 低频趋势
freq_local = random.uniform(0.5, 1.0) # 高频扰动
for t in range(self.seq_len + 1): # 多生成1个时间步作为目标
# 全局趋势:低频正弦波
global_trend = np.sin(freq_global * t + phase_shift)
# 局部细节:二维正弦波加上高频扰动
local_pattern = np.sin(xv * freq_local + t / 2.0) * np.cos(yv * freq_local + t / 3.0)
noise = np.random.normal(0, 0.1, (self.height, self.width))
frame = global_trend + local_pattern + noise
seq.append(frame.astype(np.float32))
seq = np.array(seq) # shape: (seq_len+1, height, width)
# 将前seq_len帧作为输入,最后1帧作为目标
data.append(seq[:-1])
targets.append(seq[-1])
data = np.expand_dims(np.array(data), axis=2) # shape: (num_samples, seq_len, 1, height, width)
targets = np.expand_dims(np.array(targets), axis=1) # shape: (num_samples, 1, height, width)
return data, targets
def __len__(self):
return self.num_samples
def __getitem__(self, idx):
return self.data[idx], self.targets[idx]
# 2 定义ConvLSTM单元(单尺度实现,后续会用于各尺度分支)
class ConvLSTMCell(nn.Module):
def __init__(self, input_channels, hidden_channels, kernel_size, bias=True):
super(ConvLSTMCell, self).__init__()
padding = kernel_size // 2 # 保持尺寸
self.input_channels = input_channels
self.hidden_channels = hidden_channels
self.kernel_size = kernel_size
self.conv = nn.Conv2d(in_channels=input_channels + hidden_channels,
out_channels=4 * hidden_channels,
kernel_size=kernel_size,
padding=padding,
bias=bias)
def forward(self, x, h, c):
# x: (batch, input_channels, H, W)
# h, c: (batch, hidden_channels, H, W)
combined = torch.cat([x, h], dim=1) # (batch, input+hidden, H, W)
conv_output = self.conv(combined) # (batch, 4*hidden, H, W)
(cc_i, cc_f, cc_o, cc_g) = torch.split(conv_output, self.hidden_channels, dim=1)
i = torch.sigmoid(cc_i)
f = torch.sigmoid(cc_f)
o = torch.sigmoid(cc_o)
g = torch.tanh(cc_g)
c_next = f * c + i * g
h_next = o * torch.tanh(c_next)
return h_next, c_next
# 3 定义多尺度ConvLSTM模块
class MultiScaleConvLSTM(nn.Module):
def __init__(self, input_channels, hidden_channels, kernel_sizes=[3, 5, 7]):
"""
input_channels: 输入通道数
hidden_channels: 每个尺度ConvLSTM的隐藏状态通道数
kernel_sizes: 每个分支使用的卷积核尺寸列表
"""
super(MultiScaleConvLSTM, self).__init__()
self.scales = len(kernel_sizes)
self.kernel_sizes = kernel_sizes
# 每个尺度先做卷积特征提取(多尺度卷积分支)
self.conv_branches = nn.ModuleList()
for ks in kernel_sizes:
padding = ks // 2
self.conv_branches.append(nn.Conv2d(in_channels=input_channels,
out_channels=input_channels, # 保持通道数
kernel_size=ks,
padding=padding))
# 对每个尺度使用ConvLSTMCell进行时序建模
self.convlstm_cells = nn.ModuleList()
for _ in range(self.scales):
self.convlstm_cells.append(ConvLSTMCell(input_channels=input_channels,
hidden_channels=hidden_channels,
kernel_size=3)) # ConvLSTM内部卷积核设为3×3
# 融合层,将各尺度输出拼接后映射到目标通道数(本例输出1通道预测图)
self.fusion_conv = nn.Sequential(
nn.Conv2d(in_channels=hidden_channels * self.scales,
out_channels=hidden_channels,
kernel_size=3,
padding=1),
nn.ReLU(),
nn.Conv2d(in_channels=hidden_channels,
out_channels=1,
kernel_size=1)
)
def forward(self, x):
"""
x: 输入序列,形状 (batch, seq_len, channels, H, W)
输出:预测图像,形状 (batch, 1, H, W)
"""
batch_size, seq_len, channels, H, W = x.size()
# 初始化各尺度的隐状态与细胞状态
h_states = []
c_states = []
for _ in range(self.scales):
h_states.append(torch.zeros(batch_size, self.convlstm_cells[0].hidden_channels, H, W, device=x.device))
c_states.append(torch.zeros(batch_size, self.convlstm_cells[0].hidden_channels, H, W, device=x.device))
# 遍历时序,对每个时间步同时进行多尺度处理
for t in range(seq_len):
x_t = x[:, t] # (batch, channels, H, W)
branch_outputs = []
for i in range(self.scales):
# 多尺度卷积特征提取
feat = torch.relu(self.conv_branches[i](x_t))
# 输入到对应的ConvLSTM单元
h, c = self.convlstm_cells[i](feat, h_states[i], c_states[i])
h_states[i] = h
c_states[i] = c
branch_outputs.append(h)
# 融合各尺度的最后时刻隐藏状态(可以使用加权求和或拼接,本例采用拼接后卷积融合)
fused = torch.cat(branch_outputs, dim=1) # (batch, hidden_channels*scales, H, W)
out = self.fusion_conv(fused)
return out, branch_outputs # 同时返回各尺度特征用于后续可视化
# 4 定义完整的预测模型与训练过程
class MultiScalePredictor(nn.Module):
def __init__(self, input_channels=1, hidden_channels=16, kernel_sizes=[3, 5, 7]):
super(MultiScalePredictor, self).__init__()
self.ms_convlstm = MultiScaleConvLSTM(input_channels=input_channels,
hidden_channels=hidden_channels,
kernel_sizes=kernel_sizes)
def forward(self, x):
out, multi_features = self.ms_convlstm(x)
return out, multi_features
def train_model(model, train_loader, criterion, optimizer, num_epochs=50, device='cpu'):
model.train()
train_losses = []
for epoch in range(num_epochs):
epoch_loss = 0.0
for inputs, targets in train_loader:
inputs = inputs.to(device) # (batch, seq_len, 1, H, W)
targets = targets.to(device) # (batch, 1, H, W)
optimizer.zero_grad()
outputs, _ = model(inputs)
loss = criterion(outputs, targets)
loss.backward()
optimizer.step()
epoch_loss += loss.item() * inputs.size(0)
epoch_loss /= len(train_loader.dataset)
train_losses.append(epoch_loss)
print(f"Epoch {epoch + 1}/{num_epochs}, Loss: {epoch_loss:.4f}")
return train_losses
def evaluate_model(model, test_loader, criterion, device='cpu'):
model.eval()
test_loss = 0.0
preds = []
gts = []
with torch.no_grad():
for inputs, targets in test_loader:
inputs = inputs.to(device)
targets = targets.to(device)
outputs, _ = model(inputs)
loss = criterion(outputs, targets)
test_loss += loss.item() * inputs.size(0)
preds.append(outputs.cpu().numpy())
gts.append(targets.cpu().numpy())
test_loss /= len(test_loader.dataset)
preds = np.concatenate(preds, axis=0)
gts = np.concatenate(gts, axis=0)
print(f"Test Loss: {test_loss:.4f}")
return test_loss, preds, gts
# 5 训练及测试主流程
def main():
# 设置随机种子,确保结果可复现
torch.manual_seed(42)
np.random.seed(42)
random.seed(42)
# 参数设置
num_samples = 500
seq_len = 10
height, width = 16, 16
batch_size = 16
num_epochs = 50
learning_rate = 0.001
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 构造数据集
dataset = SyntheticTimeSeriesDataset(num_samples=num_samples, seq_len=seq_len, height=height, width=width)
# 划分训练集与测试集(8:2比例)
train_size = int(0.8 * len(dataset))
test_size = len(dataset) - train_size
train_dataset, test_dataset = torch.utils.data.random_split(dataset, [train_size, test_size])
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
# 模型、损失函数与优化器
model = MultiScalePredictor(input_channels=1, hidden_channels=16, kernel_sizes=[3, 5, 7]).to(device)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)
print("开始训练模型...")
train_losses = train_model(model, train_loader, criterion, optimizer, num_epochs=num_epochs, device=device)
print("评估模型...")
test_loss, preds, gts = evaluate_model(model, test_loader, criterion, device=device)
# 6 可视化:生成一张包含4个子图的图像
# 选取第一个测试样本进行预测结果对比与特征可视化
sample_input, sample_target = test_dataset[0]
sample_input_tensor = torch.unsqueeze(torch.tensor(sample_input), dim=0).to(device) # (1, seq_len, 1, H, W)
model.eval()
with torch.no_grad():
sample_pred, multi_features = model(sample_input_tensor)
sample_pred = sample_pred.squeeze().cpu().numpy()
sample_target = sample_target.squeeze()
# 计算预测误差(残差)
residual = sample_pred - sample_target
# 绘图:使用matplotlib绘制4个子图
fig = plt.figure(constrained_layout=True, figsize=(14, 10))
gs = gridspec.GridSpec(2, 2, figure=fig)
# 图1:训练损失曲线
ax1 = fig.add_subplot(gs[0, 0])
ax1.plot(range(1, num_epochs + 1), train_losses, color='tab:blue', marker='o')
ax1.set_title("Training Loss Curve", fontsize=14)
ax1.set_xlabel("Epochs")
ax1.set_ylabel("MSE Loss")
ax1.grid(True, linestyle='--', alpha=0.7)
# 图2:预测结果与真实值对比(以某个局部区域为例,这里取中心区域的均值)
# 为直观展示,我们取中心区域(例如8×8)的均值曲线,横轴为时间步(假设预测目标也可视为时序演化的趋势)
# 由于我们的预测仅输出一个时刻结果,此处模拟取测试集中连续几个样本(本例简化模拟趋势变化)
pred_series = []
gt_series = []
# 这里取连续10个样本的均值作为模拟趋势(实际中应有连续时间步的预测)
for i in range(10):
sample_i, target_i = test_dataset[i]
sample_i = torch.unsqueeze(torch.tensor(sample_i), dim=0).to(device)
with torch.no_grad():
out_i, _ = model(sample_i)
pred_series.append(np.mean(out_i.squeeze().cpu().numpy()))
gt_series.append(np.mean(target_i))
ax2 = fig.add_subplot(gs[0, 1])
ax2.plot(range(1, 11), gt_series, color='tab:green', marker='s', label='Ground Truth Mean')
ax2.plot(range(1, 11), pred_series, color='tab:red', marker='^', label='Predicted Mean')
ax2.set_title("Comparison of Predicted and Ground Truth Mean Values", fontsize=14)
ax2.set_xlabel("Sample Index")
ax2.set_ylabel("Central Region Mean")
ax2.legend()
ax2.grid(True, linestyle='--', alpha=0.7)
# 图3:多尺度特征热力图(取第一个尺度的最后时刻特征)
feature_map = multi_features[0].squeeze().cpu().numpy() # shape: (hidden_channels, H, W)
# 选取其中一个通道(例如第0个通道)进行展示
heatmap = feature_map[0]
ax3 = fig.add_subplot(gs[1, 0])
im = ax3.imshow(heatmap, cmap='hot', interpolation='nearest')
ax3.set_title("ConvLSTM Feature Heatmap (Scale 1)", fontsize=14)
plt.colorbar(im, ax=ax3, fraction=0.046, pad=0.04)
# 图4:预测残差直方图
ax4 = fig.add_subplot(gs[1, 1])
ax4.hist(residual.flatten(), bins=20, color='purple', alpha=0.8)
ax4.set_title("Histogram of Prediction Residuals", fontsize=14)
ax4.set_xlabel("Residual Values")
ax4.set_ylabel("Frequency")
ax4.grid(True, linestyle='--', alpha=0.7)
plt.suptitle("Time Series Forecasting Analysis Based on Multi-Scale ConvLSTM", fontsize=16)
plt.show()
if __name__ == "__main__":
main()
- 训练损失曲线:横轴为训练轮数,纵轴为损失值。通过曲线变化可以观察模型的收敛情况。
- 预测结果与真实值对比曲线:选择一个测试样本,将真实数据与模型预测结果在时间轴上对比,展示预测的准确性。
- 多尺度特征热力图:提取某一尺度的ConvLSTM中间特征,并以热力图形式展示,直观展示该尺度所捕捉的空间信息。
- 预测误差分布直方图:绘制预测残差(预测值与真实值之差)的分布情况,以便了解误差的集中趋势与分布形态。
我们将多尺度卷积核与ConvLSTM相结合,实现了同时捕捉空间局部细节与全局趋势的能力。
针对该模型,可从以下几个方面进行优化:
- 多尺度分支设计:分支数量与卷积核尺寸:不同的任务可能需要调整不同尺度分支的数量和各分支卷积核的尺寸。融合方式:目前我们采用拼接后卷积融合,可尝试采用注意力机制,对各尺度特征赋予不同权重,从而实现更优的自适应融合。
- ConvLSTM单元改进:可在ConvLSTM单元中引入门控残差连接或批归一化(Batch Normalization),以缓解梯度消失和加速收敛。在卷积操作中,可引入可分离卷积等轻量化操作,降低参数量。
- 数据增强与正则化:对于虚拟数据集,可通过不同噪声水平、平移、旋转等方式扩充数据,提高模型鲁棒性。加入Dropout层或L2正则化,避免模型过拟合。
最后
基于卷积LSTM的时间序列多尺度建模方法,通过充分利用多尺度信息与时序依赖性,展现了在复杂数据预测中的巨大潜力。在以后的可以进一步探索注意力机制、多任务学习、模型压缩等方向,以适应更大规模、更高分辨率的数据应用场景。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。