【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱: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 = 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

'''
about Sigmoid
'''

class Sigmoid:
    def __init__(self):
        self.out = None

    def forward(self, x):
        out = 1 / (1 + np.exp(-x))
        self.out = out
        return self.out

    def backward(self, dout):
        dx = dout * (1-self.out) * self.out
        return dx

2、全连接函数的forward化、backward化

        对于全连接函数,forward是比较容易理解的。但是backward是稍微复杂一点的,如果有兴趣,可以去找一点资料,看下全连接函数backward的证明过程。暂时,我们可以先使用,先把神经网络用起来。

'''
about Affine
'''

class Affine:
    def __init__(self, W, b):
        self.W = W
        self.b = b
        self.x = None
        self.dW = None
        self.db = None

    def forward(self, x):
        self.x = x
        out = np.dot(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)

        return dx

3、输出函数和残差函数forward化、backward化

        就推理来说,接下来就是输出函数和残差函数的forward、backward处理。输出函数一般就是softmax,而残差方程则是cross_entropy_error。现在要做的,就是把这两个函数放在一起做推导,至于训练,暂时只记住结论就可以了。

'''
about SoftmaxWithLoss
'''

class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None
        self.y = None
        self.t = None

    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)
        return self.loss

    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        dx = (self.y - self.t) / batch_size
        return dx

4、推理方法的改进

        既然所有的函数都转成class的形式,那么至此整个推导就比较容易了,

    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)
        return x

5、网络的创建

        和之前创建网络不同,现在我们创建网络,理论上可以创建无限层次了,只要训练得过来即可。

        # construct layer here
        self.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):
        #forward
        self.loss(x,t)

        #backward
        dout = 1
        dout = self.lastLayer.backward(dout)

        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # about grads
        grads = {}
        grads['W1'] = self.layers['Affine1'].dW
        grads['b1'] = self.layers['Affine1'].db
        grads['W2'] = self.layers['Affine2'].dW
        grads['b2'] = self.layers['Affine2'].db

        return grads

7、更新网络层参数

        训练得到的梯度,那么下面就可以开始更新网络参数了。因为更新的方法很多,所以通常我们也是写成class的形式来处理,这样可以灵活切换优化方法,比如像这样,

class SGD:
    def __init__(self,lr=0.01):
        self.lr = lr

    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.lr * grads[key]

        有了SGD这个class之后,更新参数就比较容易了,首先肯定是创建SGD,

optimizer=SGD()

        后期,每次更新的时候,直接输入params和grads就好了,

    params = network.params
    optimizer.update(params, grads) 

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐