反向传播上一章内容可见http://t.csdn.cn/SHW3m
1 激活函数的反向传播
ReLU
ReLU函数在x > 0的时候返回x,在X <= 0的时候返回0。对该函数求导可得∂y/∂x 在x > 0时为1,在x <= 0时为0
class Relu:
def __init__(self):
self.mask = None
def forward(self, x):
self.mask = (x <= 0)
out = x.copy()
out[self.mask] = 0
return out
def backward(self, dout):
dout[self.mask] = 0
dx = dout
return dx
在该程序中,mask为一个boolean数组。self.mask = (x <= 0)将x中所有小于0的元素位置对应为True,将x大于0的元素位置对应False
Sigmoid
sigmoid函数求导结果为(1 + exp(-x)) ^ -2 * exp(-x),这一结果可以进一步简化为y(1 - y)
class Sigmoid:
def __init__(self):
self.out = None
def forward(self, x):
out = sigmoid(x)
self.out = out
return out
def backward(self, dout):
dx = dout * (1.0 - self.out) * self.out
return dx
Affine
Affine层即为矩阵乘法。作用是将输入值乘以权重再加上偏置
反向传播输出矩阵形状要和输入矩阵形状一致,如X形状为(N,2),∂L/∂X 的形状也要为(N,2)。通过这点我们可以推出图中的反向传播计算公式。如要计算∂L/∂X,我们要将反向传播的输入(N,3)乘以W的同时确保输出形状为(N,2)。要实现这一点,我们可以将(N,3)* (3, 2)得到(N,2),对应过来即为∂L/∂Y x WT
WT为转置矩阵,其作用是将矩阵横纵颠倒
对于偏置量,由于在正向传播中将X * W结果的每一维都加上了偏置。如对[[1, 1, 1], [2, 2, 2], [3, 3, 3]]加上偏置[1, 2, 3]的结果为[[2, 3, 4], [3, 4, 5], [4, 5, 6]]。在加法反向传播中,导数结果∂L/∂Y,这里需要将∂L/∂Y关于第0维加和结果作为∂L/∂B。在例子中即为[6, 6, 6]
class Affine:
def __init__(self, W, b):
self.W =W
self.b = b
self.x = None
self.original_x_shape = None
# 权重和偏置参数的导数
self.dW = None
self.db = None
def forward(self, x):
# 对应张量
self.original_x_shape = x.shape
x = x.reshape(x.shape[0], -1)
self.x = x
out = np.dot(self.x, self.W) + self.b
return out
def backward(self, dout):
dx = np.dot(dout, self.W.T)
self.dW = np.dot(self.x.T, dout)
self.db = np.sum(dout, axis=0)
dx = dx.reshape(*self.original_x_shape) # 还原输入数据的形状(对应张量)
return dx
softmax-with-loss
在分类问题的输出层,我们一般使用softmax函数。这里我们将softmax和cross-entropy层相连直接得到损失函数,因此被称为softmax-with-loss层。该层计算图如下:
这里由计算图一步步推得导数结果过程过于复杂。这里我们直接给出反向传播结果为y - t,即为预测值和标签值的差值。
class SoftmaxWithLoss:
def __init__(self):
self.loss = None
self.y = None # softmax的输出
self