接下来看看另一个常用的层,激活层。它们属于点式(pointwise)非线性函数。点式函数的 Jacobian矩阵是对角矩阵, 这意味着当乘以梯度时, 它是逐点相乘的。
- class ReLu(Layer):
- def forward(self, X):
- mask = X > 0
- return X * mask, lambda D: D * mask
计算Sigmoid函数的梯度略微有一点难度,而它也是逐点计算的:
- class Sigmoid(Layer):
- def forward(self, X):
- S = 1 / (1 + np.exp(-X))
- def backward(D):
- return D * S * (1 - S)
- return S, backward
当我们按序构建很多层后,可以遍历它们并先后得到每一层的输出,我们可以把backward函数存在一个列表内,并在计算反向传播时使用,这样就可以直接得到相对于输入层的损失梯度。就是这么神奇:
- class Sequential(Layer):
- def __init__(self, *layers):
- super().__init__()
- self.layers = layers
- for layer in layers:
- self.parameters.extend(layer.parameters)
-
- def forward(self, X):
- backprops = []
- Y = X
- for layer in self.layers:
- Y, backprop = layer.forward(Y)
- backprops.append(backprop)
- def backward(D):
- for backprop in reversed(backprops):
- D = backprop(D)
- return D
- return Y, backward
正如我们前面提到的,我们将需要定义批样本的损失函数和梯度。一个典型的例子是MSE,它被常用在回归问题里,我们可以这样实现它:
- def mse_loss(Yp, Yt):
- diff = Yp - Yt
- return np.square(diff).mean(), 2 * diff / len(diff)
就差一点了!现在,我们定义了两种层,以及合并它们的方法,下面如何训练呢?我们可以使用类似于scikit-learn或者Keras中的API。
- class Learner():
- def __init__(self, model, loss, optimizer):
- self.model = model
- self.loss = loss
- self.optimizer = optimizer
-
- def fit_batch(self, X, Y):
- Y_, backward = self.model.forward(X)
- L, D = self.loss(Y_, Y)
- backward(D)
- self.model.update(self.optimizer)
- return L
-
- def fit(self, X, Y, epochs, bs):
- losses = []
- for epoch in range(epochs):
- p = np.random.permutation(len(X))
- X, Y = X[p], Y[p]
- loss = 0.0
- for i in range(0, len(X), bs):
- loss += self.fit_batch(X[i:i + bs], Y[i:i + bs])
- losses.append(loss)
- return losses
这就行了!如果你跟随着我的思路,你可能就会发现其实有几行代码是可以被省掉的。
这代码能用不? (编辑:西安站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|