使用torch.nn.utils.clip_grad_norm_()遇RuntimeError:stack期望非空TensorList
问题分析与解决方案
这个问题我之前调试模型时也碰到过,核心是你的代码执行顺序完全搞反了,导致梯度还没生成就调用了梯度裁剪函数!
报错的直接原因
看你的代码流程:
for epoch in progress_bar(range(num_epochs)): lstm.train() outputs = lstm(trainX.to(device)) optimizer.zero_grad() torch.nn.utils.clip_grad_norm_(lstm.parameters(), 1)
这里存在两个关键缺失:
- 没有计算损失(Loss):你只执行了前向传播得到输出,但没有和训练标签计算损失值;
- 没有执行反向传播:损失值是触发梯度计算的核心,只有调用
loss.backward()后,模型参数的grad属性才会被赋值。
在这种情况下,所有参数的grad都是None,当clip_grad_norm_尝试把这些空的梯度张量堆叠起来计算范数时,就会抛出RuntimeError: stack expects a non-empty TensorList——因为根本没有有效的梯度张量可以处理。
关于Parameter和Tensor的疑问
你提到lstm.parameters()返回的是Parameter列表而非Tensor列表,其实这完全没问题:torch.nn.Parameter是torch.Tensor的子类,它本质上就是带requires_grad=True属性的特殊Tensor,完全符合clip_grad_norm_对输入参数的要求,这不是报错的原因。
正确的代码流程
调整顺序,确保梯度生成后再执行裁剪,完整流程如下:
for epoch in progress_bar(range(num_epochs)): lstm.train() optimizer.zero_grad() # 先清空上一轮残留的梯度 outputs = lstm(trainX.to(device)) # 前向传播得到输出 # 关键步骤1:计算损失(需要替换成你实际使用的损失函数) loss = torch.nn.functional.mse_loss(outputs, trainY.to(device)) # 关键步骤2:反向传播,生成参数梯度 loss.backward() # 现在梯度已存在,可以执行裁剪 torch.nn.utils.clip_grad_norm_(lstm.parameters(), 1) # 最后更新模型参数 optimizer.step()
额外提示
如果你的模型有部分参数被冻结(设置了requires_grad=False),这些参数的grad会是None,但clip_grad_norm_会自动忽略它们,只要至少有一个参数有有效梯度,就不会触发这个报错。
内容的提问来源于stack exchange,提问作者Brian Feeny
相关产品推荐
相关产品推荐

