前言
时序预测任务中,我们可以使用一个预训练的深度神经网络(DNN)模型的预测结果和另一个模型(例如:LSTM模型)的输出结果来计算损失函数,并进一步训练这个LSTM模型。
示例任务
假设我们有一个时序数据集,并使用预训练的MLP模型(多层感知器)获取预测结果,同时我们定义一个新的LSTM模型并使用它来进行训练。我们的损失函数包括预训练模型和LSTM模型的输出结果。
1. 导入需要的库
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import matplotlib.pyplot as plt
2. 创建数据集
使用一个简单的正弦波数据集作为示例。
# 生成示例时序数据 - 正弦波
def generate_sine_wave(seq_length, num_samples):
X = np.linspace(0, num_samples, num_samples)
y = np.sin(X)
data = []
for i in range(len(y) - seq_length):
data.append(y[i:i + seq_length])
return np.array(data[:-1]).astype(np.float32), np.array(data[1:]).astype(np.float32)
# 设置参数
seq_length = 20
num_samples = 1200
# 生成数据
X_train, y_train = generate_sine_wave(seq_length, num_samples)
# 转换为 Tensor
X_train = torch.tensor(X_train).unsqueeze(2) # 加一个维度作为特征数量
y_train = torch.tensor(y_train).unsqueeze(2)
# 创建数据加载器
batch_size = 16
train_dataset = torch.utils.data.TensorDataset(X_train, y_train)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
3. 定义并加载预训练模型
这里我们假设一个预训练的简单MLP模型。
class PretrainedMLP(nn.Module):
def __init__(self, input_size):
super(PretrainedMLP, self).__init__()
self.fc1 = nn.Linear(input_size, 64)
self.fc2 = nn.Linear(64, 32)
self.fc3 = nn.Linear(32, 1)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x
# 假设已加载预训练权重
pretrained_model = PretrainedMLP(seq_length)
pretrained_model.eval() # 设置为评估模式
4. 定义学生模型
我们定义一个LSTM模型,从头开始训练。
class LSTMModel(nn.Module):
def __init__(self, input_size, hidden_size, num_layers, output_size):
super(LSTMModel, self).__init__()
self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
self.fc = nn.Linear(hidden_size, output_size)
def forward(self, x):
h0 = torch.zeros(num_layers, x.size(0), hidden_size).to(x.device)
c0 = torch.zeros(num_layers, x.size(0), hidden_size).to(x.device)
out, _ = self.lstm(x, (h0, c0))
out = self.fc(out[:, -1, :])
return out
hidden_size = 50
num_layers = 1
output_size = 1
student_model = LSTMModel(input_size=1, hidden_size=hidden_size, num_layers=num_layers, output_size=output_size)
5. 定义损失函数和优化器
我们使用均方误差损失函数(MSE),将主要的学生模型输出与目标值的损失和预训练模型输出的损失结合。
criterion_main = nn.MSELoss()
criterion_aux = nn.MSELoss()
optimizer = optim.Adam(student_model.parameters(), lr=0.001)
6. 训练模型
在训练过程中,我们先通过预训练模型获得预测结果,然后由学生模型进行预测,并根据预训练模型和学生模型的输出计算损失。
num_epochs = 20
for epoch in range(num_epochs):
student_model.train()
for inputs, labels in train_loader:
optimizer.zero_grad() # 清零梯度
# 获取预训练模型的输出
with torch.no_grad():
pretrained_output = pretrained_model(inputs.view(inputs.size(0), -1))
# 学生模型的预测
student_output = student_model(inputs)
# 主要任务的损失(MSE)
loss_main = criterion_main(student_output, labels)
# 辅助损失(MSE)
loss_aux = criterion_aux(student_output, pretrained_output)
# 总损失
loss = loss_main + 0.5 * loss_aux # 辅助损失的权重为0.5,可调整
# 反向传播和优化
loss.backward()
optimizer.step()
print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')
7. 预测和评价模型
我们可以简单地在训练集上评估模型的性能。
student_model.eval()
with torch.no_grad():
predicted = []
for inputs, labels in train_loader:
student_output = student_model(inputs)
predicted.append(student_output)
predicted = torch.cat(predicted).cpu().numpy()
plt.plot(X_train.view(-1).numpy(), label='True Data')
plt.plot(predicted.flatten(), label='Predicted Data')
plt.legend()
plt.show()
分析
- 预训练模型的使用:通过利用预训练模型的输出,可以为学生模型提供更好的开始,从而引导学生模型在时序预测任务中的训练。
- 联合损失函数:将主要任务的损失和辅助损失结合起来,帮助学生模型不仅学会进行时序预测,还能从预训练模型中学习到有效的特征。
- 知识迁移和蒸馏:这种方法可以被视为知识蒸馏的一种形式,特别是在预训练模型包含丰富的领域知识但学生模型更为轻量的场景下,效果显著。