从零开始开发自己的类keras深度学习框架3:实现优化器和激活函数

认真学习,佛系更博。

上一章介绍了全连接层的实现和原理,本章将介绍优化器和激活函数的实现;

首先,我们来实现SGD优化器,sgd是最简单的优化器,每次批量计算的梯度直接运用在神经网络参数上即可,在enet下新建一个模块optimizer,并新建文件sgd.py;

import numpy as np


class SGD(object):
    """
    momentum优化器
    """

    def __init__(self, **k_args):

        # params主要用于有分枝的网络连接,比如有两个分支,则需要先汇总梯度,然后再作为总体计算;
        self.params = dict()

    def grand(self, **k_args):
        """
        记录当前的梯度信息
        :param k_args: 梯度参数
        :return:
        """
        for key, array in k_args.items():
            if key not in self.params:
                self.params[key] = np.zeros_like(array)

            self.params[key] += array

    def get_delta_and_reset(self, lr, *args):
        """
        获取obj对象下的梯度
        :param lr: 学习率
        :param args: 参数列表
        :return:
        """
        result_list = []
        for key in args:
            result_list.append(- lr * self.params[key])

            # 更新完后需要将params的梯度置为0
            self.params[key] *= 0

        return result_list

上一章已经说了,我们将为每个网络层添加一个独立的优化器,因此这里的优化器只负责一个神经网络层的优化。grand用于保存当前梯度,get_delta_and_reset用于返回保存的梯度并将梯度清空,避免累计到下一次的参数更新。这里说明一下,使用grand和get_delta_and_reset机制只要是为了以后的功能拓展,比如RNN的实现中,可能需要多次计算同一个网络层的梯度,使用累加机制可以很方便地控制;

我们再来列举一个momentum的实现:

import numpy as np


class Momentum(object):
    """
    momentum优化器
    """

    def __init__(self, momentum=0.9, **k_args):

        self.momentum = momentum

        # 相比于一般的实现,我们设立了两个参数,params主要用于累计梯度;
        self.v = dict()
        self.params = dict()

    def grand(self, **k_args):
        """
        记录当前的梯度信息
        :param k_args: 梯度参数
        :return:
        """
        for key, array in k_args.items():
            if key not in self.params:
                self.params[key] = np.zeros_like(array)
                self.v[key] = np.zeros_like(array)

            self.params[key] += array

    def get_delta_and_reset(self, lr, *args):
        """
        获取obj对象下的梯度
        :param lr: 学习率
        :param args: 需要取出的梯度
        :return:
        """
        result_list = []
        for key in args:
            self.v[key] = self.momentum * self.v[key] - lr * self.params[key]
            result_list.append(self.v[key])

            # 更新完后需要将params的梯度置为0
            self.params[key] *= 0

        return result_list

比sgd稍微复杂一点,需要额外保存一个冲量值,关于优化器的原理可以自己查阅资料,这里不做过多叙述,其他优化器的实现见github代码, 里面有关于rmsprop、adagrad、adam的实现;

接下来我们来实现激活函数层, 是的,激活函数也是层,我们在上一章定义dense的时候,只用了一个变量保存activation的值,在内部实现时没有添加任何激活特性,原因是因为我们在实现sequential的时候,将在compile中检测activation的值,并添加适当的激活函数层,详情请见下一章;

这里,我们先实现sigmoid层,在layers下新建sigmoid.py文件:

from enet.layers.base_layer import Layer

import numpy as np


class Sigmoid(Layer):
    """
    sigmoid层
    """

    def __init__(self):
        super(Sigmoid, self).__init__(layer_type="sigmoid")

    def build(self, input_shape):
        """
        构建sigmoid层,此层无参数
        :param input_shape: 输入数据
        :return: 无
        """
        self.input_shape = input_shape
        self.output_shape = input_shape

    def forward(self, input_signal, *args, **k_args):
        """
        前向运算
        :param input_signal: 输入数据
        :return: 输出数据
        """
        self.cache = 1. / (1 + np.exp(-input_signal))

        return self.cache

    def backward(self, delta):
        """
        反向传播
        :param delta: 梯度
        :return: 继续向前传播梯度
        """
        return delta * self.cache * (1 - self.cache)

原理非常简单,推导也很容易,这里不做过多叙述,

relu层如下所示:

from enet.layers.base_layer import Layer

import numpy as np


class Relu(Layer):
    """
    relu
    """

    def __init__(self):
        super(Relu, self).__init__(layer_type="relu")

    def build(self, input_shape):
        """
        构建relu层,此层无参数
        :param input_shape: 输入数据
        :return: 无
        """
        self.input_shape = input_shape
        self.output_shape = input_shape

    def forward(self, input_signal, *args, **k_args):
        """
        前向运算
        :param input_signal: 输入数据
        :return: 输出数据
        """
        self.cache = input_signal

        return np.maximum(0., input_signal)

    def backward(self, delta):
        """
        反向传播
        :param delta: 梯度
        :return: 继续向前传播梯度
        """
        return delta * (self.cache > 0).astype(int)

好了,本篇文章先写到这里,下一篇将介绍本框架的重中之重:sequential的实现;

整个代码的github网址为:https://github.com/darkwhale/neural_network,不断更新中;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值