我想创建一个递归模型来解决我所知道的最简单的序列,即算术级数。以
a
作为基础,
d
作为步长,序列如下:
a, a+d, a+2d, a+3d, a+4d, ...
为了解决这个问题,将隐藏状态表示为
h
,模型必须学习一个简单的 2*2 矩阵。这其实就是设置
h1 = t0
.
换句话来说,你也可以这样看:
所以这个2*2全连接层的模型应该能够学习到这个矩阵:|| |但令我惊讶的是并没有收敛!我的设置应该有问题。如果你帮我找到它,我将不胜感激。我怀疑问题应该出在我的训练循环中。
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.fc1 = nn.Linear(2, 2, bias=False)
def forward(self, x):
x = self.fc1(x)
return x
P.S.我现在特意将批量大小设置为 1。我想稍后填充输入数据。无论如何,模型应该无需批量学习。
我的训练循环:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
import numpy as np
class CustomDataset(Dataset):
def __init__(self, size):
self.size = size
def __len__(self):
return self.size
def __getitem__(self, index):
a0 = (np.random.rand() - 0.5) * 200
d = (np.random.rand() - 0.5) * 40
length = np.random.randint(2, MAX_Length_sequence + 1)
sequence = np.arange(length) * d + a0
next_number = sequence[-1] + d
return length, torch.tensor(sequence, dtype=torch.float32), torch.tensor(next_number, dtype=torch.float32)
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.fc1 = nn.Linear(2, 2, bias=False)
def forward(self, x):
x = self.fc1(x)
return x
# Hyperparameters
EPOCHS = 10
BATCH_SIZE = 1
LEARNING_RATE = 0.001
DATASET_SIZE = 10000
criterion = nn.MSELoss()
# Model
model = Model()
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
My traning loop:
for epoch in range(EPOCHS):
dataset = CustomDataset(DATASET_SIZE)
dataloader = DataLoader(dataset, batch_size=BATCH_SIZE)
model.train()
total_loss = 0
for length, sequence, next_number in dataloader:
optimizer.zero_grad()
loss = 0
h = torch.zeros(BATCH_SIZE)
for i in range(length):
x = torch.cat([h, sequence[0, i].unsqueeze(0)])
y = sequence[0, i + 1] if i != length - 1 else next_number[0]
output = model(x)
h, y_hat = output[0].unsqueeze(0), output[1]
loss += criterion(y_hat, y)
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f'Epoch {epoch+1}, Loss: {total_loss/len(dataloader)}')
你的RNN模型设置和训练循环中有一些问题导致它无法收敛:
-
缺少非线性激活函数:
RNN 依靠非线性激活函数(如 ReLU 或 Tanh)来学习复杂模式。你的模型中缺少此函数。在
forward
方法中,将self.fc1(x)
的输出传递给一个非线性激活函数。 -
不正确的隐藏状态更新:
你直接将模型的输出用作下一个时间步长的隐藏状态,而没有将其与前一个隐藏状态结合起来。修改模型的
forward
方法以正确地更新隐藏状态。 - 输入表示: 虽然你的推理是正确的,但将时间步长作为网络输入的一部分传递可能会使它难以学习模式。尝试创建一个明确地将时间步长信息编码为网络输入的嵌入。
以下是如何解决这些问题的改进代码:
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
import numpy as np
class CustomDataset(Dataset):
def __init__(self, size, max_length):
self.size = size
self.max_length = max_length
def __len__(self):
return self.size
def __getitem__(self, index):
a0 = (np.random.rand() - 0.5) * 200
d = (np.random.rand() - 0.5) * 40
length = np.random.randint(2, self.max_length + 1)
sequence = np.arange(length) * d + a0
next_number = sequence[-1] + d
return torch.tensor([a0, d], dtype=torch.float32), torch.tensor(sequence, dtype=torch.float32), torch.tensor(next_number, dtype=torch.float32)
class Model(nn.Module):
def __init__(self, hidden_size=2):
super(Model, self).__init__()
self.hidden_size = hidden_size
self.fc1 = nn.Linear(hidden_size + 1, hidden_size) # +1 用于时间步长输入
self.fc2 = nn.Linear(hidden_size, 1)
self.tanh = nn.Tanh() # 非线性激活函数
def forward(self, x, hidden):
# 将时间步长信息连接到输入
combined = torch.cat((x, hidden), dim=1)
hidden = self.tanh(self.fc1(combined)) # 更新隐藏状态
output = self.fc2(hidden)
return output, hidden
# 超参数
EPOCHS = 100
BATCH_SIZE = 16
LEARNING_RATE = 0.001
DATASET_SIZE = 10000
MAX_LENGTH_SEQUENCE = 10
criterion = nn.MSELoss()
# 模型
model = Model()
optimizer = optim.Adam(model.parameters(), lr=LEARNING_RATE)
# 训练循环
for epoch in range(EPOCHS):
dataset = CustomDataset(DATASET_SIZE, MAX_LENGTH_SEQUENCE)
dataloader = DataLoader(dataset, batch_size=BATCH_SIZE)
model.train()
total_loss = 0
for a0_d, sequence, next_number in dataloader:
optimizer.zero_grad()
loss = 0
hidden = torch.zeros(BATCH_SIZE, model.hidden_size)
for i in range(sequence.size(1) - 1):
input_data = a0_d # 在每个时间步长使用 a0 和 d
time_step = torch.full((BATCH_SIZE, 1), i + 1, dtype=torch.float32) # 创建时间步长输入
input_data = torch.cat((input_data, time_step), dim=1)
output, hidden = model(input_data, hidden)
loss += criterion(output.squeeze(), sequence[:, i + 1])
loss.backward()
optimizer.step()
total_loss += loss.item()
print(f'Epoch {epoch+1}, Loss: {total_loss/len(dataloader)}')
这些更改包括:
-
向模型添加
Tanh
激活函数。 -
通过引入第二个线性层
self.fc2
来重新组织模型,用于输出预测。 - 修改隐藏状态更新以结合前一个隐藏状态和当前输入。
- 向模型输入添加时间步长信息。
通过这些调整,你的 RNN 模型应该能够学习算术级数模式并成功收敛。
标签:python,machine-learning,deep-learning,pytorch,recurrent-neural-network From: 78784723