自己动手写深度学习框架(优化深度学习框架)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
用过tensorflow、pytorch的同学都知道,这些框架是可以任意选择激励函数、任意选择优化方法的。要做到这一点,其实并不复杂,只需要把我们之前写的代码流程更新一下。也就是说基本上所有的函数,都要写成class的形式,在这个函数里面再添加forward函数和backward函数。这样推导的时候,就可以使用forward函数,如果是训练的时候,就使用backward函数。

1、激励函数的forward化、backward化
目前遇到的激励函数主要是relu和sigmoid,可以先把这两个函数转换成class,
'''
about Relu
'''class Relu:def __init__(self):self.mask = Nonedef forward(self, x):self.mask = (x <= 0)out = x.copy()out[self.mask] = 0return outdef backward(self, dout):dout[self.mask] = 0dx = doutreturn dx'''
about Sigmoid
'''class Sigmoid:def __init__(self):self.out = Nonedef forward(self, x):out = 1 / (1 + np.exp(-x))self.out = outreturn self.outdef backward(self, dout):dx = dout * (1-self.out) * self.outreturn dx2、全连接函数的forward化、backward化
对于全连接函数,forward是比较容易理解的。但是backward是稍微复杂一点的,如果有兴趣,可以去找一点资料,看下全连接函数backward的证明过程。暂时,我们可以先使用,先把神经网络用起来。
'''
about Affine
'''class Affine:def __init__(self, W, b):self.W = Wself.b = bself.x = Noneself.dW = Noneself.db = Nonedef forward(self, x):self.x = xout = np.dot(x, self.W) + self.breturn outdef 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)return dx3、输出函数和残差函数forward化、backward化
就推理来说,接下来就是输出函数和残差函数的forward、backward处理。输出函数一般就是softmax,而残差方程则是cross_entropy_error。现在要做的,就是把这两个函数放在一起做推导,至于训练,暂时只记住结论就可以了。
'''
about SoftmaxWithLoss
'''class SoftmaxWithLoss:def __init__(self):self.loss = Noneself.y = Noneself.t = Nonedef forward(self, x, t):self.t = tself.y = softmax(x)self.loss = cross_entropy_error(self.y, self.t)return self.lossdef backward(self, dout=1):batch_size = self.t.shape[0]dx = (self.y - self.t) / batch_sizereturn dx
4、推理方法的改进
既然所有的函数都转成class的形式,那么至此整个推导就比较容易了,
def predict(self, x):for layer in self.layers.values():x = layer.forward(x)return x
5、网络的创建
和之前创建网络不同,现在我们创建网络,理论上可以创建无限层次了,只要训练得过来即可。
# construct layer hereself.layers = OrderedDict()self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])self.layers['Relu1'] = Relu()self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])self.lastLayer = SoftmaxWithLoss()6、网络的训练
解决了推导的问题,下面就是网络的训练问题。如果推导是forward,那么训练就是backward的过程。这个时候,backward走一遍,那么不同网络层的梯度就会计算一遍了。
def gradient(self, x, t):#forwardself.loss(x,t)#backwarddout = 1dout = self.lastLayer.backward(dout)layers = list(self.layers.values())layers.reverse()for layer in layers:dout = layer.backward(dout)# about gradsgrads = {}grads['W1'] = self.layers['Affine1'].dWgrads['b1'] = self.layers['Affine1'].dbgrads['W2'] = self.layers['Affine2'].dWgrads['b2'] = self.layers['Affine2'].dbreturn grads7、更新网络层参数
训练得到的梯度,那么下面就可以开始更新网络参数了。因为更新的方法很多,所以通常我们也是写成class的形式来处理,这样可以灵活切换优化方法,比如像这样,
class SGD:def __init__(self,lr=0.01):self.lr = lrdef update(self, params, grads):for key in params.keys():params[key] -= self.lr * grads[key]有了SGD这个class之后,更新参数就比较容易了,首先肯定是创建SGD,
optimizer=SGD()后期,每次更新的时候,直接输入params和grads就好了,
params = network.paramsoptimizer.update(params, grads) 