首页 > 编程问答 >为什么我的 RNN 无法收敛到简单任务?

为什么我的 RNN 无法收敛到简单任务?

时间:2024-07-24 04:56:01浏览次数:13  
标签:python machine-learning deep-learning pytorch recurrent-neural-network

我想创建一个递归模型来解决我所知道的最简单的序列,即算术级数。以 a 作为基础, d 作为步长,序列如下:

a, a+d, a+2d, a+3d, a+4d, ...

为了解决这个问题,将隐藏状态表示为 h ,模型必须学习一个简单的 2*2 矩阵。这其实就是设置 h1 = t0 .

enter image description here

换句话来说,你也可以这样看:

enter image description here

所以这个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模型设置和训练循环中有一些问题导致它无法收敛:

  1. 缺少非线性激活函数: RNN 依靠非线性激活函数(如 ReLU 或 Tanh)来学习复杂模式。你的模型中缺少此函数。在 forward 方法中,将 self.fc1(x) 的输出传递给一个非线性激活函数。
  2. 不正确的隐藏状态更新: 你直接将模型的输出用作下一个时间步长的隐藏状态,而没有将其与前一个隐藏状态结合起来。修改模型的 forward 方法以正确地更新隐藏状态。
  3. 输入表示: 虽然你的推理是正确的,但将时间步长作为网络输入的一部分传递可能会使它难以学习模式。尝试创建一个明确地将时间步长信息编码为网络输入的嵌入。

以下是如何解决这些问题的改进代码:

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

相关文章

  • 如何使用 Python 和 Numpy 重现 Matlab 文件读取以解码 .dat 文件?
    我有一个Matlab脚本,可以读取编码的.dat文件,对其进行解码并保存。我试图使用numpy将其转换为Python。我发现对于同一个文件,我得到不同的输出结果(python数字没有意义)。该代码最初作为从串行端口读取的脚本的一部分运行,因此是数据的结构。我首先认为位移是问题所在,因为......
  • 在Python中调整pdf页面大小
    我正在使用python裁剪pdf页面。一切正常,但如何更改页面大小(宽度)?这是我的裁剪代码:input=PdfFileReader(file('my.pdf','rb'))p=input.getPage(1)(w,h)=p.mediaBox.upperRightp.mediaBox.upperRight=(w/4,h)output.addPage(p)当我裁剪页面时,我也需要......
  • 如何使用 python 更改资源管理器窗口中的路径?
    没有人知道如何在不使用python打开新实例的情况下更改资源管理器窗口中的当前路径吗?例如,如果用户使用C:\Users\User打开资源管理器窗口。然后我必须将该路径更改为C:\Windows\System32例如。提前致谢。很遗憾,无法直接使用Python更改现有文件资源管理器窗口的......
  • python 以及将数组传递给函数的问题
    我需要求解一些常微分方程$\frac{dy}{dx}=f(x)=x^2ln(x)$并继续在限制0之间创建数组xpt。<=xpt<=2因为我必须小心xpt=0,所以我将函数定义如下deff(x):ifx<=1.e-6:return0.else:returnnp.square(x)*np.log(x)我的调用程序读取Np......
  • 如果 Python 脚本正在使用文件夹,如何在文件资源管理器中进行更改时防止 Windows 的“
    我有一个简单的脚本,显示在QTreeView中的QListView中选择的目录的内容,我想添加打开文件资源管理器的功能,以让用户编辑目录内的内容。但是,添加新的文件夹和文件可以,但删除或移动文件夹或文件会提示“文件夹正在使用”错误:此操作无法完成,因为该文件已在另一个程......
  • 如何使用 Python API 获取每个模型的活跃用户列表、最后登录信息
    我想通过PythonAPI获取我的dbt项目的所有模型中的活动或非活动用户列表。这可能吗?我尝试列出模型,但无法获取用户信息,如用户名、项目、以及上次活动或上次登录。不幸的是,dbt本身并不跟踪你所寻找的用户活动数据(最后登录、活跃用户等)。dbt的主要功能是转换数据,而不......
  • Python tkinter 窗口不断关闭,我不知道为什么
    我正在尝试制作一个有趣的小程序,其中每小时左右就会有一只毛茸茸的动物走过屏幕。我有一个主窗口,它启动一个循环,每小时左右播放一次动画/声音,但是在口袋妖怪第一次完成行走后,整个程序就会结束。我认为这可能与我设置tkinter窗口的方式有关,但我无法弄清楚。我认为在这里包含......
  • 用于自动访问 MongoDB Atlas CLI 的 Python 脚本
    我想编写一个Python脚本,以便普通用户可以访问他的数据库并从他的终端执行CRUD操作。我正在查看官方文档,但我有点迷失。有人可以给我指点基本教程来开始吗?当然,以下是如何构建Python脚本来访问MongoDBAtlasCLI的基本教程:先决条件:MongoDBAtlas......
  • Python实现简单学生登陆系统
     代码:importhashlibclassStudent:def__init__(self,username,password):#初始化学生对象,存储用户名和加密后的密码self.username=usernameself.password=hashlib.sha256(password.encode()).hexdigest()defcheck_passwo......
  • python猜数字游戏
    好的,下面是一个简单的小游戏代码示例,使用Python编写。这个游戏是一个猜数字游戏,玩家需要猜一个1到100之间的随机数。importrandomdefguess_number_game():print("欢迎来到猜数字游戏!")print("我已经想好了一个1到100之间的数字,请你猜一猜。")secret_num......