m0_46315864 2024-06-17 20:12 采纳率: 100%
浏览 8
已结题

为什么GRU神经网络model.eval()时结果都是一样的

写了一个GRU代码,用来估计上证指数的收盘价,模型训练都没有什么问题,一旦变为model.eval(),所有的输出值都变成一样的了,请大家帮忙看一看究竟是怎么一回事

import pandas as pd
import numpy as np
import torch
from torch import nn, optim
from torch.utils.data import TensorDataset, DataLoader
import matplotlib.pyplot as plt
data_all = pd.read_csv('shangzheng_close.CSV',parse_dates=['date'])
data_all.set_index('date', inplace=True)
data = data_all['close']

# 定义create_sequences函数来创建序列
def create_sequences(data, seq_length):
    xs, ys = [], []
    for i in range(len(data)-seq_length-1):
        x = data[i:(i+seq_length)]
        y = data[i+seq_length]
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# 设置序列长度为10
seq_length = 10
# 调用create_sequences函数创建序列
X, Y = create_sequences(data, seq_length)

# 转换为PyTorch张量
x_tensor = torch.tensor(X, dtype=torch.float32).unsqueeze(-1)# 添加一个维度以匹配输出形状
y_tensor = torch.tensor(Y, dtype=torch.float32).unsqueeze(-1)


# 划分训练集和测试集
train_size = int(len(x_tensor) * 0.8)
test_size = len(x_tensor) - train_size
train_x, test_x = x_tensor[0:train_size, :], x_tensor[train_size:len(x_tensor), :]
train_y, test_y = y_tensor[0:train_size], y_tensor[train_size:len(y_tensor)]

# 创建数据加载器
batch_size = 1
train_loader = DataLoader(TensorDataset(train_x, train_y), batch_size=batch_size, shuffle=True)
test_loader = DataLoader(TensorDataset(test_x, test_y), batch_size=batch_size, shuffle=False)


class GRUModel(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, output_size):
        super(GRUModel, self).__init__()
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.gru = nn.GRU(input_size, hidden_size, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
        out, _ = self.gru(x, h0)
        out = self.fc(out[:,-1,:])
        return out

input_size = 1  # 一个特征(收盘价)
hidden_size = 150  # 隐藏层大小
num_layers = 1  # GRU层数
output_size = 1  # 输出大小(预测下一个收盘价)
model = GRUModel(input_size, hidden_size, num_layers, output_size)

criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.1)

num_epochs = 10
train_losses = []
test_losses = []

for epoch in range(num_epochs):
    model.train()
    running_loss = 0.0
    outputs = []
    for i, (inputs, labels) in enumerate(train_loader):
        optimizer.zero_grad()
        outputs = model(inputs)
        #print(f'60 time-step estimate [{outputs}], real: {labels}')
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()
    # 计算每个epoch的平均训练损失
    train_loss = running_loss / len(train_loader)
    train_losses.append(train_loss)
    # 在每个epoch结束后评估模型
    model.eval()
    with torch.no_grad():
        total_test_loss = 0.0
        for inputs, labels in test_loader:
            outputs = model(inputs)
            print(f'60 time-step estimate [{outputs}], real: {inputs}')
            loss = criterion(outputs, labels)
            total_test_loss += loss.item()
        avg_test_loss = total_test_loss / len(test_loader)
        test_losses.append(avg_test_loss)
    if (epoch + 1) % 5 == 0:
        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {train_loss}')


  • 写回答

3条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2024-06-17 20:12
    关注

    引自免费微信小程序:皆我百晓生

    在你提供的代码中,观察到model.eval()模式下的输出值相同的问题,这通常与模型在推理阶段如何处理内部状态有关。在PyTorch中,model.eval()将模型设置为评估模式,其中一些层(如Dropout和BatchNorm)会改变行为以适应评估过程。然而,在GRU或LSTM等循环神经网络中,更关键的因素是隐藏状态的初始化和管理。

    在你当前的代码中,模型在评估模式下每次调用forward方法时,都是从零开始初始化隐藏状态(通过h0 = torch.zeros(...))。这意味着无论输入是什么,只要模型参数固定,相同的初始隐藏状态将导致相同的输出,特别是在短序列的情况下,模型可能没有足够的时间来发展不同的状态响应。

    解决这个问题的一个策略是在评估模式下正确地管理和传递隐藏状态。具体来说,你应该在每个批次之间保存并重用隐藏状态,而不是每次都重新初始化它。这样,即使在评估模式下,模型也能根据序列的不同表现出差异化的输出。

    为了实现这一点,可以修改GRUModel类中的forward方法,使其接受并返回隐藏状态,如下所示:

    class GRUModel(nn.Module):
        def __init__(self, input_size, hidden_size, num_layers, output_size):
            super(GRUModel, self).__init__()
            self.hidden_size = hidden_size
            self.num_layers = num_layers
            self.gru = nn.GRU(input_size, hidden_size, num_layers, batch_first=True)
            self.fc = nn.Linear(hidden_size, output_size)
    
        def forward(self, x, h0=None):
            if h0 is None:
                h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
            out, h_n = self.gru(x, h0)
            out = self.fc(out[:, -1, :])
            return out, h_n
    

    然后,在评估模式下,你需要像在训练模式中那样正确地管理隐藏状态:

    # 在每个epoch结束后评估模型
    model.eval()
    with torch.no_grad():
        hidden_state = None
        for inputs, labels in test_loader:
            outputs, hidden_state = model(inputs, hidden_state)
            loss = criterion(outputs, labels)
            total_test_loss += loss.item()
    

    请注意,上述代码中的hidden_state需要在每个epoch开始时重新初始化,以防止跨不同序列的依赖。此外,对于连续的预测任务,你可能还需要考虑如何在序列之间正确地重置隐藏状态,以避免长期依赖的影响。

    通过这些修改,你应该能看到模型在评估模式下产生不同的输出,因为它们将反映输入序列的实际变化。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录
查看更多回答(2条)

报告相同问题?

问题事件

  • 系统已结题 6月25日
  • 已采纳回答 6月17日
  • 创建了问题 6月17日

悬赏问题

  • ¥15 Windows Script Host 无法找到脚本文件"C:\ProgramData\Player800\Cotrl.vbs”
  • ¥15 matlab自定义损失函数
  • ¥15 35114 SVAC视频验签的问题
  • ¥15 impedancepy
  • ¥15 求往届大挑得奖作品(ppt…)
  • ¥15 如何在vue.config.js中读取到public文件夹下window.APP_CONFIG.API_BASE_URL的值
  • ¥50 浦育平台scratch图形化编程
  • ¥20 求这个的原理图 只要原理图
  • ¥15 vue2项目中,如何配置环境,可以在打完包之后修改请求的服务器地址
  • ¥20 微信的店铺小程序如何修改背景图