python
# 分解版
class LeakyRelu:
# 初始化
def __init__(self, slope=0.1):
# α是一个在训练时从一个均匀分布中随机选择的参数,用于控制负数区域的斜率
self.slope = slope
self.mask = None
def forward(self, x):
self.mask = (x <= 0)
y = x.copy() # 复制输入数据,避免修改原数据
y[self.mask] = self.slope * x[self.mask]
return y
def backward(self, dy):
dx = dy.copy()
# 将x<=0的值都赋为slope * x[self.mask]
dx[self.mask] = self.slope * dx[self.mask]
return dx
# np版
class LeakyRelu:
# 初始化
def __init__(self, slope=0.1):
# α是一个在训练时从一个均匀分布中随机选择的参数,用于控制负数区域的斜率
self.slope = slope
self.mask = None
def forward(self, x):
self.mask = (x <= 0)
# 使用np.where更高效,避免复制整个数组后再修改
return np.where(self.mask, self.slope * x, x)
def backward(self, dy):
# 使用np.where更高效,LeakyReLU的导数是:正数区域为1,负数区域为slope
return np.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.4 -
LeakyReLU 的局部梯度(导数):
∂y/∂x = slope = 0.1 (因为 x <= 0) -
最终得到:
∂Loss/∂x = (-2.4) × 0.1 = -0.24
代码对应关系
python
def backward(self, dy): # dy 就是上游传来的 ∂Loss/∂y = -2.4
dx = dy.copy() # 先复制上游梯度
dx[self.mask] = self.slope * dx[self.mask] # 乘以局部梯度
return dx # 返回 ∂Loss/∂x
所以 :dx(返回值)才是真正的"导数"(∂Loss/∂x),dy(输入)只是上游传过来的梯度,两者不是同一个东西。