02-自动求导搞不定nn_Module和autograd原理一次讲透

02-自动求导搞不定nn_Module和autograd原理一次讲透
自动求导搞不定?nn.Module和autograd原理一次讲透我之前写训练循环的时候,遇到一个诡异的问题:loss在下降,但模型参数完全没更新。debug了半天,发现是requires_grad=False搞的鬼。更气人的是,这种bug不报错,你还得自己发现模型没学到东西。这篇把PyTorch自动求导和nn.Module的核心原理讲透。不是那种"autograd是自动微分引擎"的废话,而是真正搞清楚:梯度怎么算的、计算图怎么建的、nn.Module帮你管了什么。autograd到底在干什么一句话:autograd在前向传播时记录操作,构建计算图;反向传播时沿计算图自动求导。但"计算图"长什么样?我用一个具体例子拆给你看:importtorch x=torch.tensor(2.0,requires_grad=True)y=torch.tensor(3.0,requires_grad=True)z=x*y+x**2# z = xy + x²z.backward()print(x.grad)# dz/dx = y + 2x = 3 + 4 = 7print(y.grad)# dz/dy = x = 2前向传播时,PyTorch做的事:x * y→ 创建Mul节点,记录"z₁ = x * y"x ** 2→ 创建Pow节点,记录"z₂ = x²"z₁ + z₂→ 创建Add节点,记录"z = z₁ + z₂"反向传播时,从z开始,沿着Add→Mul/Pow→x/y的路径,用链式法则逐节点计算梯度。计算图的两种模式:模式原理PyTorch用的特点前向模式沿前向方向同时算值和导数否适合输入维度小于输出维度反向模式先算值,再反向算导数是适合输出维度小于输入维度(训练就是这种)深度学习几乎都是"很多参数→一个loss",反向模式效率更高。PyTorch选对了。requires_grad的门道requires_grad是控制梯度计算的总开关,但它的规则比你想的复杂。规则1:运算结果的requires_grad由输入决定a=torch.tensor([1.0,2.0],requires_grad=True)b=torch.tensor([3.0,4.0],requires_grad=False)c=a+b# c.requires_grad = True,因为有a参与d=b*2# d.requires_grad = False,全是不求梯度的输入只要有一个输入要梯度,输出就要梯度。这是合理的设计——否则梯度链断了,a的梯度算不出来。规则2:叶子张量和非叶子张量这是很多人搞混的概念:x=torch.tensor([1.0,2.0],requires_grad=True)# 叶子张量y=x*2# 非叶子张量z=y.sum()z.backward()print(x.grad)# tensor([2., 2.]) ← 叶子张量保留梯度print(y.grad)# None ← 非叶子张量的梯度被释放了叶子张量:直接创建的张量(不是通过运算得到的)。它们的.grad会被保留。非叶子张量:运算产生的中间结果。默认反向传播后梯度就释放了,省内存。如果你需要非叶子张量的梯度,用retain_grad():y=x*2y.retain_grad()# 告诉PyTorch:这个中间结果的梯度我也想看z=y.sum()z.backward()print(y.grad)# tensor([2., 2.]) ← 现在有了规则3:no_grad和inference_mode推理时不需要梯度,用这两种方式关闭:# 方式1:no_gradwithtorch.no_grad():output=model(input)# 不构建计算图,省内存# 方式2:inference_mode(更快,更严格)