我正在 CIFAR-10 上训练 ResNet34。当我尝试为 param.grad
操作 model.parameters()
时,我有一个非常奇怪的行为。
以下函数是所有混乱发生的地方。由于试图了解会发生什么,它目前没有做任何有用的事情。
def add_error(error):
params = (param for param in model.parameters() if param.requires_grad)
# [param.grad + err for param, err in zip(params, error)] # Line 2
# new_error = [param.grad + err for param, err in zip(params, error)] # Line 3
for param in params:
param.grad.zero_()
# new_error = [torch.zeros(param.grad.shape, device=device) for param in params] # Line 6
return new_error
用于梯度下降步骤:
def step(model, optimizer, batch, labels, error):
optimizer.zero_grad()
loss = compute_loss(model, batch, labels)
loss.backward()
new_error = add_error(error=error) <- add_error is called here
optimizer.step()
return new_error
其中优化器是 optim.SGD(model.parameters(), lr=0.1)
和 compute_loss
本质上在 nn.CrossEntropyLoss()
和 model(batch)
上调用 labels
。
我的期望:由于我将梯度设置为 0,所以无论我做什么,都不会发生任何变化:损失应该一直在原始值(2.4)附近
实际发生的事情:
2.4
。add_error
一样。 IE。损失以与通常 SGD 相同的速度下降:2.4 -> 1.7 -> 1.3 -> ...
(每个时期)。换句话说,以某种方式传播了梯度。4.3
,然后缓慢减少4.3 -> 4.2 -> 4.14 -> 4.1 -> ...
(我怀疑这种减少是批量标准化的结果)。请注意,在这两种情况下,我实际上都使用 error
,实际上我从未使用 error
来更新渐变。
此外,添加更多行(如第 2 行)不会影响结果。
问题:发生了什么?
如果有帮助,我可能会尝试制作 MCVE 并将其发布在 pastebin 上(这将是一堵代码墙,太大而无法放在这里)。
答案 0 :(得分:0)
这个问题非常愚蠢:params
是一个生成器,在第一次迭代后就耗尽了。创建一个列表而不是一个生成器可以解决这个问题。