解决方案:
原代码如下:
with torch.no_grad():
w1 = w1 - learning_rate * w1.grad
w2 = w2 - learning_rate * w2.grad
# 反向传播后手动将梯度设置为零
w1.grad.zero_()
w2.grad.zero_()
使用pytorch构建神经网络,在参数更新后,将梯度清0时报错:
使用"-="运算符,将代码修改为:
with torch.no_grad():
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
# 反向传播后手动将梯度设置为零
w1.grad.zero_()
w2.grad.zero_()
错误分析:
每个人的编程习惯可能不同,有的人可能在参数更新时使用的运算符是“-=”:
# 第一种方式更新参数
w1 = w1 - learning_rate * w1.grad
w2 = w2 - learning_rate * w2.grad
# 第二种方式更新参数
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
我们知道pytorch重载了运算符,因此可以直接使用基本运算符合来操作两个tensor变量。但是用第一种方式进行参数更新后(我最开始也使用的是第一种方法),接下来执行梯度清0语句时会报错:
debug的时候发现,对w1进行更新后,它的一些属性也发生了变化:
w1的grad属性变成了None,而zero_()方法是针对tensor类型的变量。除此之外,requires_grad的值也变为False。
至于为什么会发现这样的变化,我在查阅相关资料后发现,pytorch对"-“进行了重载,没有对”="进行重载。
a = 1
b = 2
print(id(a))
a = a + b
print(id(a))
运行结果如下:
1546236096
1546236160
从变量的id号可以看出,在进行赋值操作时,python其实是将计算结果保存到一个新建的内存空间,从根本上来说,就是新建了一个变量a。
print(id(w1))
w1 = w1 - learning_rate * w1.grad
w2 = w2 - learning_rate * w2.grad
print(id(w1))
输出结果为:
2181909743080
2182128952008
因此在进行参数更新时,python新建了一个tensor类型的变量,并将计算结果保存到新建的w1中,至于为什么grad的值为None,我个人猜测是因为默认的tensor变量的requires_grad的值默认为False,不对梯度进行跟踪(希望由大佬能解释一下)。
当我们用第二种方式对参数进行更新时:
print(id(w1))
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
print(id(w1))
输出结果为:
1636333876872
1636333876872
可以看出在使用"-="操作符时,pytorch会将计算结果继续保存到原来的w1空间中,所以grad的值并不为None,置零操作时也不会报错。