# 分解版classLeakyRelu:# 初始化def__init__(self,slope=0.1):# α是一个在训练时从一个均匀分布中随机选择的参数,用于控制负数区域的斜率self.slope=slope self.mask=Nonedefforward(self,x):self.mask=(x<=0)y=x.copy()# 复制输入数据,避免修改原数据y[self.mask]=self.slope*x[self.mask]returnydefbackward(self,dy):dx=dy.copy()# 将x<=0的值都赋为slope * x[self.mask]dx[self.mask]=self.slope*dx[self.mask]returndx# np版classLeakyRelu:# 初始化def__init__(self,slope=0.1):# α是一个在训练时从一个均匀分布中随机选择的参数,用于控制负数区域的斜率self.slope=slope self.mask=Nonedefforward(self,x):self.mask=(x<=0)# 使用np.where更高效,避免复制整个数组后再修改returnnp.where(self.mask,self.slope*x,x)defbackward(self,dy):# 使用np.where更高效,LeakyReLU的导数是:正数区域为1,负数区域为slopereturnnp.where(self.mask,self.slope*dy,dy)链式法则
假设网络结构是:x → LeakyReLU → y → Loss
前向传播:
x = -2 y = LeakyReLU(-2) = slope * (-2) = -0.2 Loss = (y - 1)² = 1.44反向传播要求什么?
我们要计算∂Loss/∂x,也就是"当 x 变化一点点时,Loss 会变化多少"。
链式法则的应用
∂Loss/∂x = ∂Loss/∂y × ∂y/∂x ↑ ↑ 上游梯度 当前层的局部梯度具体计算:
上游传来的梯度(从 Loss 传来):
∂Loss/∂y = 2(y - 1) = 2(-0.2 - 1) = -2.4LeakyReLU 的局部梯度(导数):
∂y/∂x = slope = 0.1 (因为 x <= 0)最终得到:
∂Loss/∂x = (-2.4) × 0.1 = -0.24
代码对应关系
defbackward(self,dy):# dy 就是上游传来的 ∂Loss/∂y = -2.4dx=dy.copy()# 先复制上游梯度dx[self.mask]=self.slope*dx[self.mask]# 乘以局部梯度returndx# 返回 ∂Loss/∂x所以:dx(返回值)才是真正的"导数"(∂Loss/∂x),dy(输入)只是上游传过来的梯度,两者不是同一个东西。