PyTorch梯度下降参数更新两种赋值写法的差异问题
问题本质原因
两种写法的核心差异是对PyTorch张量的操作模式完全不同,直接导致变量指向的对象发生了变化:
w = w - learning_rate * w.grad属于out-of-place(异地)操作
普通的减法运算会返回一个全新开辟内存的张量,和你最开始初始化的、设置了requires_grad=True的原w张量没有绑定关系。加上这段代码运行在torch.no_grad()上下文内,所有运算不会构建计算图,新生成的张量默认requires_grad=False,本身不会自动存储梯度属性。
当你把新张量赋值给变量w时,w就不再指向最初那个带梯度记录的叶子参数张量了,此时访问w.grad得到的就是None,调用zero_()方法自然会抛出属性不存在的错误。而原来的参数张量因为失去了变量引用,会被Python自动回收。w -= learning_rate * w.grad属于in-place(原地)操作
自减写法不会创建新张量,而是直接在原w张量的内存地址上修改存储的数值,变量w始终指向最开始初始化的、绑定了梯度配置的参数对象。在l.backward()执行完成后,原张量的.grad属性已经存储了反向传播计算得到的梯度值,后续调用w.grad.zero_()是对原有张量的梯度做清零操作,完全合法不会报错。
你可以自行验证:在参数更新前后分别打印
id(w),使用第一种写法时更新前后的内存地址会发生明显变化,使用-=写法时内存地址始终保持一致。
额外注意:不管是否在torch.no_grad()上下文内,手动更新参数时都不要使用w = w - lr * w.grad这类替换变量指向的写法,这会把原本的叶子参数节点替换成计算图中的中间节点,后续梯度计算、清零逻辑都会失效。除了原地运算外,你也可以通过w.data = w.data - learning_rate * w.grad的方式修改参数数值,或者直接调用PyTorch内置的优化器(比如SGD、Adam)自动完成参数更新,避免这类低级错误。
内容的提问来源于stack exchange,提问作者高造擎
相关产品推荐
相关产品推荐

