赞
踩
学习资料来源:零基础入门深度学习(3) - 神经网络和反向传播算法
目录
神经元和感知器本质上是一样的,只是神经元的激活函数是sigmoid函数tanh函数,如下图所示:

对于sigmoid函数,其定义如下:

sigmoid函数是一个非线性函数,值域是(0,1)。函数图像如下图所示:

同时,sigmoid函数的导数是:

可见,sigmoid函数的导数是用sigmoid函数自身来表示。一旦计算出sigmoid函数的值,计算它的导数的值就非常方便。
假设神经元的输入是向量x,权重向量是w,激活函数是sigmoid函数,则其输出y:

神经网络:按照一定规则连接起来的多个神经元。
全连接(full connected,FC)神经网络如下图所示:

规则如下:
神经网络就是一个输入向量x到输出向量y的函数,即:

根据输入计算神经网络的输出,需要首先将输入向量x的每个元素xi的值赋给神经网络的输入层的对应神经元,然后根据y=sigmoid(wx+b)依次计算每一层的每个神经元的值,直到最后一层输出层的所有神经元的值计算完毕。最后,将输出层每个神经元的值串在一起就得到了输出向量y。
神经网络是一个模型,权值(模型要学习的东西)是模型的参数。然而,一个神经网络的连接方式、网络的层数、每层的节点数这些参数(不是学习出来的)是人为事先设置的。对于这些人为设置的参数,我们称之为超参数(Hyper-Parameters)。
神经网络的训练算法:反向传播算法(Back Propagation)
假设每个训练样本为(x,t),其中向量x是训练样本的特征,而t是样本的目标值。

首先,根据神经网络的输出计算方法,用样本的特征x,计算出神经网络中每个隐藏层节点的输出ai,以及输出层每个节点的输出yi。
然后,按照下面的方法计算出每个节点的误差项δi,即

其中,δi是节点i的误差项,yi是节点i的输出值,ti是样本对应于节点i的目标值。

其中,ai是节点i的输出值,wki是节点i到它的下一层节点k的连接的权重,δk是节点i的下一层节点k的误差项。
最后,更新每个连接上的权值:

其中wji是节点i到节点j的权重,η是学习速率,δj是节点j的误差项,xji是节点i传递给节点j的输入。
可见,计算每一个节点的误差项,需要先计算每个节点与其相连的下一层节点的误差项。这就要求误差项的计算顺序必须是从输出层开始,然后反向依次计算每个隐藏层的误差项,直到与输入层相连的那个隐藏层。这就是反向传播算法的名字的含义。当所有节点的误差项计算完毕后,我们就可以来更新所有的权重。
因此,推导出的训练规则是根据激活函数是sigmoid函数、目标函数是平方和误差、神经网络是全连接网络、优化算法是随机梯度下降优化算法。如果激活函数、误差计算方式、网络连接结构、优化算法不同,则具体的训练规则也会不一样。
首先,做一个基本的模型,可以分解出5个领域对象来实现神经网络:

- # Node实现
- # 节点类,负责记录和维护节点自身信息以及与这个节点相关的上下游连接,实现输出值和误差项的计算。
- class Node(object):
- def __init__(self, layer_index, node_index):
- '''
- 构造节点对象。
- layer_index: 节点所属的层的编号
- node_index: 节点的编号
- '''
- self.layer_index = layer_index
- self.node_index = node_index
- self.downstream = []
- self.upstream = []
- self.output = 0
- self.delta = 0
- def set_output(self, output):
- '''
- 设置节点的输出值。如果节点属于输入层会用到这个函数。
- '''
- self.output = output
- def append_downstream_connection(self, conn):
- '''
- 添加一个到下游节点的连接
- '''
- self.downstream.append(conn)
- def append_upstream_connection(self, conn):
- '''
- 添加一个到上游节点的连接
- '''
- self.upstream.append(conn)
- def calc_output(self):
- '''
- 根据式1计算节点的输出
- '''
- output = reduce(lambda ret, conn: ret + conn.upstream_node.output * conn.weight, self.upstream, 0)
- self.output = sigmoid(output)
- def calc_hidden_layer_delta(self):
- '''
- 节点属于隐藏层时,根据式4计算delta
- '''
- downstream_delta = reduce(
- lambda ret, conn: ret + conn.downstream_node.delta * conn.weight,
- self.downstream, 0.0)
- self.delta = self.output * (1 - self.output) * downstream_delta
- def calc_output_layer_delta(self, label):
- '''
- 节点属于输出层时,根据式3计算delta
- '''
- self.delta = self.output * (1 - self.output) * (label - self.output)
- def __str__(self):
- '''
- 打印节点的信息
- '''
- node_str = '%u-%u: output: %f delta: %f' % (self.layer_index, self.node_index, self.output, self.delta)
- downstream_str = reduce(lambda ret, conn: ret + '\n\t' + str(conn), self.downstream, '')
- upstream_str = reduce(lambda ret, conn: ret + '\n\t' + str(conn), self.upstream, '')
- return node_str + '\n\tdownstream:' + downstream_str + '\n\tupstream:' + upstream_str
-
-
- # ConstNode对象
- class ConstNode(object):
- def __init__(self, layer_index, node_index):
- '''
- 构造节点对象。
- layer_index: 节点所属的层的编号
- node_index: 节点的编号
- '''
- self.layer_index = layer_index
- self.node_index = node_index
- self.downstream = []
- self.output = 1
- def append_downstream_connection(self, conn):
- '''
- 添加一个到下游节点的连接
- '''
- self.downstream.append(conn)
- def calc_hidden_layer_delta(self):
- '''
- 节点属于隐藏层时,根据式4计算delta
- '''
- downstream_delta = reduce(
- lambda ret, conn: ret + conn.downstream_node.delta * conn.weight,
- self.downstream, 0.0)
- self.delta = self.output * (1 - self.output) * downstream_delta
- def __str__(self):
- '''
- 打印节点的信息
- '''
- node_str = '%u-%u: output: 1' % (self.layer_index, self.node_index)
- downstream_str = reduce(lambda ret, conn: ret + '\n\t' + str(conn), self.downstream, '')
- return node_str + '\n\tdownstream:' + downstream_str
-
-
- # Layer对象
- class Layer(object):
- def __init__(self, layer_index, node_count):
- '''
- 初始化一层
- layer_index: 层编号
- node_count: 层所包含的节点个数
- '''
- self.layer_index = layer_index
- self.nodes = []
- for i in range(node_count):
- self.nodes.append(Node(layer_index, i))
- self.nodes.append(ConstNode(layer_index, node_count))
- def set_output(self, data):
- '''
- 设置层的输出。当层是输入层时会用到。
- '''
- for i in range(len(data)):
- self.nodes[i].set_output(data[i])
- def calc_output(self):
- '''
- 计算层的输出向量
- '''
- for node in self.nodes[:-1]:
- node.calc_output()
- def dump(self):
- '''
- 打印层的信息
- '''
- for node in self.nodes:
- print node
-
-
- # Connection对象
- class Connection(object):
- def __init__(self, upstream_node, downstream_node):
- '''
- 初始化连接,权重初始化为是一个很小的随机数
- upstream_node: 连接的上游节点
- downstream_node: 连接的下游节点
- '''
- self.upstream_node = upstream_node
- self.downstream_node = downstream_node
- self.weight = random.uniform(-0.1, 0.1)
- self.gradient = 0.0
- def calc_gradient(self):
- '''
- 计算梯度
- '''
- self.gradient = self.downstream_node.delta * self.upstream_node.output
- def get_gradient(self):
- '''
- 获取当前的梯度
- '''
- return self.gradient
- def update_weight(self, rate):
- '''
- 根据梯度下降算法更新权重
- '''
- self.calc_gradient()
- self.weight += rate * self.gradient
- def __str__(self):
- '''
- 打印连接信息
- '''
- return '(%u-%u) -> (%u-%u) = %f' % (
- self.upstream_node.layer_index,
- self.upstream_node.node_index,
- self.downstream_node.layer_index,
- self.downstream_node.node_index,
- self.weight)
-
-
- # Connections对象
- class Connections(object):
- def __init__(self):
- self.connections = []
- def add_connection(self, connection):
- self.connections.append(connection)
- def dump(self):
- for conn in self.connections:
- print conn
-
-
- #Network对象
- class Network(object):
- def __init__(self, layers):
- '''
- 初始化一个全连接神经网络
- layers: 二维数组,描述神经网络每层节点数
- '''
- self.connections = Connections()
- self.layers = []
- layer_count = len(layers)
- node_count = 0;
- for i in range(layer_count):
- self.layers.append(Layer(i, layers[i]))
- for layer in range(layer_count - 1):
- connections = [Connection(upstream_node, downstream_node)
- for upstream_node in self.layers[layer].nodes
- for downstream_node in self.layers[layer + 1].nodes[:-1]]
- for conn in connections:
- self.connections.add_connection(conn)
- conn.downstream_node.append_upstream_connection(conn)
- conn.upstream_node.append_downstream_connection(conn)
- def train(self, labels, data_set, rate, iteration):
- '''
- 训练神经网络
- labels: 数组,训练样本标签。每个元素是一个样本的标签。
- data_set: 二维数组,训练样本特征。每个元素是一个样本的特征。
- '''
- for i in range(iteration):
- for d in range(len(data_set)):
- self.train_one_sample(labels[d], data_set[d], rate)
- def train_one_sample(self, label, sample, rate):
- '''
- 内部函数,用一个样本训练网络
- '''
- self.predict(sample)
- self.calc_delta(label)
- self.update_weight(rate)
- def calc_delta(self, label):
- '''
- 内部函数,计算每个节点的delta
- '''
- output_nodes = self.layers[-1].nodes
- for i in range(len(label)):
- output_nodes[i].calc_output_layer_delta(label[i])
- for layer in self.layers[-2::-1]:
- for node in layer.nodes:
- node.calc_hidden_layer_delta()
- def update_weight(self, rate):
- '''
- 内部函数,更新每个连接权重
- '''
- for layer in self.layers[:-1]:
- for node in layer.nodes:
- for conn in node.downstream:
- conn.update_weight(rate)
- def calc_gradient(self):
- '''
- 内部函数,计算每个连接的梯度
- '''
- for layer in self.layers[:-1]:
- for node in layer.nodes:
- for conn in node.downstream:
- conn.calc_gradient()
- def get_gradient(self, label, sample):
- '''
- 获得网络在一个样本下,每个连接上的梯度
- label: 样本标签
- sample: 样本输入
- '''
- self.predict(sample)
- self.calc_delta(label)
- self.calc_gradient()
- def predict(self, sample):
- '''
- 根据输入的样本预测输出值
- sample: 数组,样本的特征,也就是网络的输入向量
- '''
- self.layers[0].set_output(sample)
- for i in range(1, len(self.layers)):
- self.layers[i].calc_output()
- return map(lambda node: node.output, self.layers[-1].nodes[:-1])
- def dump(self):
- '''
- 打印网络信息
- '''
- for layer in self.layers:
- layer.dump()

向量化编程(底层算法库会针对向量运算做优化,甚至有专门的硬件比如GPU,程序效率会提升很多)
- # 全连接层实现类
- class FullConnectedLayer(object):
- def __init__(self, input_size, output_size,
- activator):
- '''
- 构造函数
- input_size: 本层输入向量的维度
- output_size: 本层输出向量的维度
- activator: 激活函数
- '''
- self.input_size = input_size
- self.output_size = output_size
- self.activator = activator
- # 权重数组W
- self.W = np.random.uniform(-0.1, 0.1,
- (output_size, input_size))
- # 偏置项b
- self.b = np.zeros((output_size, 1))
- # 输出向量
- self.output = np.zeros((output_size, 1))
- def forward(self, input_array):
- '''
- 前向计算
- input_array: 输入向量,维度必须等于input_size
- '''
- # 式2
- self.input = input_array
- self.output = self.activator.forward(
- np.dot(self.W, input_array) + self.b)
- def backward(self, delta_array):
- '''
- 反向计算W和b的梯度
- delta_array: 从上一层传递过来的误差项
- '''
- # 式8
- self.delta = self.activator.backward(self.input) * np.dot(
- self.W.T, delta_array)
- self.W_grad = np.dot(delta_array, self.input.T)
- self.b_grad = delta_array
- def update(self, learning_rate):
- '''
- 使用梯度下降算法更新权重
- '''
- self.W += learning_rate * self.W_grad
- self.b += learning_rate * self.b_grad
-
-
- # 修改netowork
- # Sigmoid激活函数类
- class SigmoidActivator(object):
- def forward(self, weighted_input):
- return 1.0 / (1.0 + np.exp(-weighted_input))
- def backward(self, output):
- return output * (1 - output)
- # 神经网络类
- class Network(object):
- def __init__(self, layers):
- '''
- 构造函数
- '''
- self.layers = []
- for i in range(len(layers) - 1):
- self.layers.append(
- FullConnectedLayer(
- layers[i], layers[i+1],
- SigmoidActivator()
- )
- )
- def predict(self, sample):
- '''
- 使用神经网络实现预测
- sample: 输入样本
- '''
- output = sample
- for layer in self.layers:
- layer.forward(output)
- output = layer.output
- return output
- def train(self, labels, data_set, rate, epoch):
- '''
- 训练函数
- labels: 样本标签
- data_set: 输入样本
- rate: 学习速率
- epoch: 训练轮数
- '''
- for i in range(epoch):
- for d in range(len(data_set)):
- self.train_one_sample(labels[d],
- data_set[d], rate)
- def train_one_sample(self, label, sample, rate):
- self.predict(sample)
- self.calc_gradient(label)
- self.update_weight(rate)
- def calc_gradient(self, label):
- delta = self.layers[-1].activator.backward(
- self.layers[-1].output
- ) * (label - self.layers[-1].output)
- for layer in self.layers[::-1]:
- layer.backward(delta)
- delta = layer.delta
- return delta
- def update_weight(self, rate):
- for layer in self.layers:
- layer.update(rate)

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。