哈喽,大家好~
今儿咱们来聊聊Transformer 和 LSTM 的对比,序列建模方式、训练速度、任务适配性等等方面的比较~
首先,序列建模的核心问题,给定一个序列:
我们的目标是建模其条件分布:
即如何表示历史信息,并捕捉长程依赖。
LSTM:递归式记忆建模
LSTM 是 RNN 的改进型,通过引入门控机制,解决梯度消失/爆炸问题。
设输入为 ,隐藏状态为 ,细胞状态为 :
-
输入门
-
遗忘门
-
输出门
-
候选记忆
-
状态更新
-
隐藏状态
LSTM 具有序列依赖,必须按时间步展开,训练不可并行。
记忆长度有限:虽然能比普通 RNN 更长,但仍难以捕捉远距离依赖。
适配性:适合中短期依赖任务(如语音、时间序列预测)。
Transformer:注意力驱动的全局建模
Transformer 直接通过 自注意力机制 (Self-Attention) 来建模全局依赖,不依赖递归。
自注意力机制,输入序列嵌入表示为 。
映射到 Query, Key, Value:
其中 。
注意力权重:
多头注意力:
其中
Transformer,并行训练,所有位置并行计算注意力,大幅提升训练速度。全局依赖方面,理论上一次即可捕获任意远距离的关系。
任务适配性上,在 NLP、CV、时间序列预测、强化学习等多模态任务上都表现优越。
训练速度与计算复杂度对比:
|
|
|
|
|
|
|
|---|---|---|---|---|---|
|
|
|
|
|
||
|
|
|
|
|
-
LSTM:复杂度随序列长度线性,但因无法并行,训练瓶颈严重。 -
Transformer:复杂度是二次方 ,但 GPU 并行使其更快,尤其在长序列上更高效。
完整案例
我们生成一段由正弦波 + 随机噪声构成的序列,让模型预测下一个时间步的值。
LSTM:经典循环神经网络,适合顺序依赖明确的序列。
Transformer:通过自注意力机制捕获全局依赖,训练速度快,适合长序列。
这里,比较四方面的数据:
-
训练速度(每个 epoch 时间) -
预测精度(MSE) -
模型收敛情况(loss 曲线) -
实际预测效果可视化(预测序列 vs 原始序列)
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader, Subset
import numpy as np
import matplotlib.pyplot as plt
import time
# 1. 数据集
class SineDataset(Dataset):
def __init__(self, seq_len=20, size=1000):
x = np.linspace(0, 50, size)
y = np.sin(x) + 0.1*np.random.randn(size)
self.seq_len = seq_len
self.data = []
for i in range(len(y)-seq_len):
seq = y[i:i+seq_len].astype(np.float32)
target = y[i+seq_len].astype(np.float32)
self.data.append((seq, target)) # 确保是 tuple
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
seq, target = self.data[idx]
return torch.tensor(seq), torch.tensor(target)
seq_len = 20
dataset = SineDataset(seq_len)
# 切分训练集和测试集
train_dataset = Subset(dataset, range(800))
test_dataset = Subset(dataset, range(800, len(dataset)))
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=32)
# 2. 模型
class LSTMModel(nn.Module):
def __init__(self, input_size=1, hidden_size=64, num_layers=2):
super().__init__()
self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
self.fc = nn.Linear(hidden_size, 1)
def forward(self, x):
x = x.unsqueeze(-1) # [B, seq_len, 1]
out, _ = self.lstm(x)
out = self.fc(out[:, -1, :])
return out.squeeze()
class TransformerModel(nn.Module):
def __init__(self, seq_len=20, d_model=64, nhead=4, num_layers=2):
super().__init__()
self.pos_emb = nn.Parameter(torch.randn(1, seq_len, d_model))
self.input_fc = nn.Linear(1, d_model)
encoder_layer = nn.TransformerEncoderLayer(d_model=d_model, nhead=nhead)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)
self.fc_out = nn.Linear(d_model, 1)
def forward(self, x):
x = x.unsqueeze(-1) # [B, seq_len, 1]
x = self.input_fc(x) + self.pos_emb
x = self.transformer(x)
x = self.fc_out(x[:, -1, :])
return x.squeeze()
# 3. 训练函数
def train_model(model, train_loader, test_loader, epochs=20, lr=0.001):
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
criterion = nn.MSELoss()
train_loss = []
start_time = time.time()
for epoch in range(epochs):
model.train()
epoch_loss = 0
for seq, target in train_loader:
optimizer.zero_grad()
pred = model(seq)
loss = criterion(pred, target)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
train_loss.append(epoch_loss / len(train_loader))
train_time = time.time() - start_time
# 测试预测
model.eval()
preds, targets = [], []
with torch.no_grad():
for seq, target in test_loader:
pred = model(seq)
preds.append(pred)
targets.append(target)
preds = torch.cat(preds).numpy()
targets = torch.cat(targets).numpy()
mse = np.mean((preds - targets)**2)
return train_loss, train_time, preds, targets, mse
# 4. 实验
lstm_model = LSTMModel()
trans_model = TransformerModel(seq_len=seq_len)
lstm_loss, lstm_time, lstm_pred, lstm_true, lstm_mse = train_model(lstm_model, train_loader, test_loader)
trans_loss, trans_time, trans_pred, trans_true, trans_mse = train_model(trans_model, train_loader, test_loader)
print(f"LSTM MSE: {lstm_mse:.4f}, Time: {lstm_time:.2f}s")
print(f"Transformer MSE: {trans_mse:.4f}, Time: {trans_time:.2f}s")
# 5. 可视化
fig, axs = plt.subplots(2,2, figsize=(14,10))
# 1) 训练损失对比
axs[0,0].plot(lstm_loss, label='LSTM Loss', color='orange', linewidth=2)
axs[0,0].plot(trans_loss, label='Transformer Loss', color='deepskyblue', linewidth=2)
axs[0,0].set_title("训练损失对比", fontsize=14)
axs[0,0].set_xlabel("Epoch")
axs[0,0].set_ylabel("MSE Loss")
axs[0,0].legend()
# 2) 训练速度对比
axs[0,1].bar(['LSTM','Transformer'], [lstm_time, trans_time], color=['orange','deepskyblue'])
axs[0,1].set_title("训练时间对比", fontsize=14)
axs[0,1].set_ylabel("Seconds per 20 Epochs")
# 3) 测试集预测效果
axs[1,0].plot(lstm_true, label='True', color='black', linewidth=2)
axs[1,0].plot(lstm_pred, label='LSTM Pred', color='orange', linestyle='--')
axs[1,0].plot(trans_pred, label='Transformer Pred', color='deepskyblue', linestyle=':')
axs[1,0].set_title("测试集预测对比", fontsize=14)
axs[1,0].legend()
# 4) 误差随时间变化
axs[1,1].plot(lstm_pred - lstm_true, label='LSTM Error', color='orange')
axs[1,1].plot(trans_pred - trans_true, label='Transformer Error', color='deepskyblue')
axs[1,1].set_title("模型误差随时间变化", fontsize=14)
axs[1,1].legend()
plt.tight_layout()
plt.show()
-1.png)
-
训练损失对比:展示两种模型的收敛速度和最终损失值。可看出 LSTM 收敛更快或达到更低 loss(取决于数据)。 -
训练时间对比:对比训练速度,通常 LSTM 在长序列上更快(可通过 GPU 并行优化)。 -
测试集预测效果:实际预测曲线对比真实值,直观感受预测精度。 -
误差随时间变化:展示误差波动情况,观察哪个模型对序列波动捕捉更稳定。
通过这个可视化的结果,能够清晰说明 Transformer vs LSTM 在时序建模上的差异,包括收敛速度、预测精度和误差波动。
大家也可以基于此,继续通过调整数据量或者参数进行实验~
最后

