Python lambda表达式未使用参数w的作用及梯度计算疑问
疑问
请帮忙理解如下lambda定义:其参数w未出现在表达式部分,对应写法如下:
loss_w = lambda w: loss(x, t) # 注释:参数w未在表达式中使用 gradient(loss_w, W)
loss_w会以f(arg)的形式仅传入一个参数被调用,但loss函数本身需要两个入参,对应gradient函数的定义片段如下:
def gradient(f, arg): ... fh2: float = f(arg) # 疑问:为什么loss可以像单参数函数一样被f(W)调用?
按照对lambda的常规认知,传入的参数应当被用于表达式计算,例如lambda w: w**2的写法,参考Python官方文档6.14节对Lambdas的定义:
- Lambda表达式的语法规则:
lambda_expr ::= "lambda" [parameter_list] ":" expression lambda_expr_nocond ::= "lambda" [parameter_list] ":" expression_nocond Lambda表达式(有时称为lambda形式)用于创建匿名函数。表达式`lambda parameters: expression`会生成一个函数对象,其行为和定义如下普通函数完全一致: def <lambda>(parameters): return expression
相关代码
该代码用于计算损失函数loss的数值导数,参考cs231n课程的梯度检查教程实现逻辑:
import numpy as np W = 0.01 * np.random.randn(2, 3) def relu(x): return np.maximum(0, x) def loss(x, t): # loss函数定义 global W a = relu(np.matmul(X, W.T)) a:float = a - np.max(a, axis=-1, keepdims=True) p:float = np.exp(a) / np.sum(np.exp(a, axis=-1, keepdims=True)) batch_size = p.shape[0] return -np.sum(np.log(p[np.arange(batch_size), t] + 1e-7)) / batch_size def gradient(f, arg): # 梯度计算函数,接收单参数的f作为输入 h:float = 1e-4 # 步长 grad = np.zeros_like(arg, dtype=float) it = np.nditer(arg, flags=['multi_index'], op_flags=['readwrite']) while not it.finished: idx = it.multi_index tmp_val = arg[idx] # 计算x+h处的函数值 arg[idx] = tmp_val + h fh1: float = f(arg) # 疑问:为什么loss可以只传一个参数就被调用? # 计算x-h处的函数值 arg[idx] = tmp_val - h fh2: float = f(arg) grad[idx] = (fh1 - fh2) / (2*h) arg[idx] = tmp_val it.iternext() return grad
def numerical_gradient(x, t): t = t.reshape(1, t.size) if t.ndim == 1 else t x = x.reshape(1, y.size) if x.ndim == 1 else t loss_w = lambda w: loss(x, t) # 疑问:这里定义的w到底有什么用? global W return gradient(loss_w, W)
原始实现参考《深度学习入门》配套源码中的两层神经网络实现,对应cs231的梯度检查逻辑。
解答
语法层面的合法性
Python的lambda语法完全允许定义形式参数但不在函数体中使用,不会触发语法报错。类似lambda w: "固定返回值"的写法是合法的:调用时必须按位置传入一个参数,但函数内部会忽略这个入参,永远返回固定结果。这也是这段代码能正常运行不报错的原因。
这是明确的代码笔误
这段写法完全破坏了数值梯度计算的核心逻辑:
- 数值梯度的计算原理是对每个待求梯度的元素,分别给参数加上/减去小步长
h,重新计算对应变化后的损失值,再通过有限差分公式推导梯度。这就要求:损失函数必须接收当前被修改过的参数值作为输入,基于这个传入的参数值计算损失,而不是读取全局变量的固定值。 - 当前的错误lambda写法
lambda w: loss(x, t),不管调用f(arg)时传入的w是被修改加了h、还是减了h的权重值,lambda内部都不会把这个w传给loss函数,永远返回基于全局固定W计算的损失值。这会导致每一步计算的fh1和fh2值完全相等,最终算出来的梯度全为0,完全失去梯度检查的作用。 - 按照原设计意图,正确的lambda写法应该是将传入的w作为权重参数传给loss函数:
对应的loss函数实现也需要调整签名,把权重W作为入参接收,而不是读取全局变量:loss_w = lambda w: loss(w, x, t)def loss(W, x, t): a = relu(np.matmul(x, W.T)) # 后续计算逻辑保持不变
代码中存在的其他笔误
numerical_gradient函数中的reshape逻辑完全写反了:判断x的维度后,else分支错误返回了t,还引用了未定义的变量y,正常逻辑应该是对x做对应维度的reshape。loss函数中矩阵乘法使用了大写的X,而非入参的小写x,运行时会触发未定义变量报错。
内容的提问来源于stack exchange,提问作者mon
相关产品推荐
相关产品推荐

