认真学习,佛系更博。
上一章介绍了全连接层的实现和原理,本章将介绍优化器和激活函数的实现;
首先,我们来实现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,不断更新中;