PyTorch中如何将前馈神经网络输出作为新输入训练并解决梯度报错
问题原因
- 原地修改训练张量触发梯度计算错误
PyTorch的自动求导机制需要保留前向传播过程中所有张量的原始状态才能正确计算梯度。你代码里的x_train[i+1, :-1] = prediction[:-2]是原地操作,直接修改了作为计算图输入的x_train张量的数值,导致反向传播时找不到前向传播时的原始张量版本,就触发了版本不匹配的报错。 - 梯度更新逻辑错误
你把optimizer.zero_grad()放在了epoch的开头而不是每个step的开头,会导致梯度累计错误,同时每个时间步都调用optimizer.step()直接更新模型权重,会导致前面时间步的梯度被后面的权重更新覆盖,不符合多步自回归训练的逻辑。
解决方案
你的自回归预测思路本身是符合需求的,只需要调整实现逻辑即可:
- 不要修改原始的
x_train张量,单独维护当前步的输入张量,每一步用前一步的预测结果生成新的输入,不触碰原始训练数据 - 优化器梯度清零和更新的位置要和训练step对应
- 生成下一时间步输入时用
detach()切断不必要的梯度关联,避免计算图无限累积占用内存
修改后的核心代码示例:
def optimize_model(model: pt.nn.Module, x_train: pt.Tensor, y_train: pt.Tensor, x_val: pt.Tensor, y_val: pt.Tensor, epochs: int=1000, lr: float=0.001, save_best: str="") ->Tuple[List[float], List[float]]: criterion = pt.nn.MSELoss() optimizer = pt.optim.Adam(params=model.parameters(), lr=lr) best_val_loss, best_train_loss = 1.0e5, 1.0e5 train_loss, val_loss = [], [] for e in range(1, epochs+1): epoch_train_loss = 0.0 # 每个epoch从初始输入重新开始,克隆避免修改原始张量 current_input = x_train[0].clone() for i in range(len(x_train)-1): optimizer.zero_grad() prediction = model(current_input).squeeze() loss = criterion(prediction, y_train[i, :]) loss.backward() optimizer.step() epoch_train_loss += loss.item() # 生成下一步输入,切断梯度关联避免计算图累积 next_input = x_train[i+1].clone() next_input[:-1] = prediction[:-2].detach() current_input = next_input # 验证逻辑保持不变 with pt.no_grad(): val_pred = model(x_val[i, :]).squeeze() val_loss_i = criterion(val_pred, y_val[i, :]) val_loss.append(val_loss_i.item()) train_loss.append(epoch_train_loss / (len(x_train)-1)) # 可自行补充最佳模型保存逻辑 if save_best and train_loss[-1] < best_train_loss: best_train_loss = train_loss[-1] pt.save(model.state_dict(), save_best) return train_loss , val_loss
补充说明
如果需要让模型更好地适应长序列预测的误差累积,可以选择多步预测联合损失模式:跑完所有时间步的预测后,把所有步的损失加总再统一调用一次backward()和step(),不需要每个时间步单独更新权重,可以根据你的实际需求选择训练模式。
内容的提问来源于stack exchange,提问作者maintown
相关产品推荐
相关产品推荐

