Python实现三层神经网络

神经网络的实现看似复杂,但只要理清各层关系,实现也不难。这里以CNN网络为例,用python实现一个简单的神经网络。

主要思路

神经网络运算,主要有:

  • Forward-propagate
  • Backward-propagate
  • Parameters update

数据集

使用数据集](http://archive.ics.uci.edu/ml/datasets/seeds

实现

构建网络

CNN网络由输入层、隐藏层、输出层构成。隐藏层和输出层的每个神经元都有自己的weight和bias,在运算过程中,还要有输出output和导数derivative。构建网络时,神经元的output和derivative可以没有初始值。

Python实现时,网络由List构成,每一层也是一个List。层中包含神经元,每一个神经网也是List,神经元包含weight、bias、output、derivative,这些可以有Dict的key-value结构表示,value是数值或List。

CNN网络由$k$层,每层神经元个数为$ni$,则$net= List[n_0, n1, \dotc, n{k-1}]$,构建网络时,第$ni$层每个神经元的权重个数为第$n{i-1}$层神经元个数,第一层为输入层,不需权重。

def InitializeNet(net):

#0 层为输入层,初始化 [1 len(net) -1]层
cnn_net = list()
for layer_index in range(1, len(net)):
    layer = [{'weight':[random() for num in range(net[layer_index - 1])], 
             'bias': random(),
             'derivative':0,
             'output':0} 
             for num in range(net[layer_index])]
    cnn_net.append(layer)
return cnn_net

前向传播

激活函数

用sigmod作为激活函数
def Activation(value):
return 1.0 / (1 + exp(-value) )

前向传播

前向传播是从第0层到n-1层,具体计算是计算每层每个神经元的输出。

先定义一个神经元的前向传播:

1
2
3
4
5
def ForwardNeuron(inputs, weight, bias):
value = bias
for i in range(len(inputs)):
value += inputs[i] * weight[i]
return Activation(value)

前向传播是按层传播,计算每层输出时,即为计算每层中每个神经元的输出,因此可以定义出前向传播函数:

1
2
3
4
5
6
7
8
9
def Forward(cnn_net, inputs):

for layer in cnn_net:
next_layer_inputs = list()
for neruon in layer:
next_layer_inputs.append(ForwardNeuron(inputs, neruon['weight'], neruon['bias']))
inputs = next_layer_inputs

return inputs

反向传播

反向传播时,误差由输出层,依次向前传播,传播过程中,计算weight和bias的权重。

输出层误差计算,为了简便,采用均方误差。
$$
error = \frac{1}{2}(true_label - output)^2
$$

激活函数为sigmod,它的导数可以求得,为

1
2
def Derivative(value):
return value * (1.0 - value)

那么输出层误差为:
$$
error = \frac{1}{2}(true_label - output)^2 * Derivate(output)
$$

隐藏层误差的计算,与它连接下一层的权重以及下一层每个神经元的误差相关。下一层第k个神经元到当前层某一个神经元的误差:
$$
error = (weight_k error_k) Derivate(output)
$$
其中$weight_k$为第当前神经元到下一层第k个神经元的权重,$error_k$是下一层第k个神经元的误差,output是当前神经元的输出。

可以实现反向传播算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def BackForward(cnn_net, true_label):

for layer_index in reversed(range(len(cnn_net))):
layer = cnn_net[layer_index]
error_list = list()

#最后一层为输出层
if layer_index == len(cnn_net) - 1:
for neuron_index in range(len(layer)):
neruon = layer[neuron_index]
error_list.append(1.0 / 2 * (true_label[neuron_index] - neruon['output'])**2 )
#中间层为隐藏层
else:
for neuron_index in range(len(layer)):
error = 0.0
for neuron in cnn_net[layer_index + 1]:#下一层每个神经元到当前神经元的error
error += (neuron['weight'][neuron_index] * neuron['derivative'])
error_list.append(error)

#计算好误差后,计算导数
for neuron_index in range(len(layer)):
neuron = layer[neuron_index]
neuron['derivative'] = error_list[neuron_index] * Derivative(neuron['output'])

梯度更新

上面的导数部分,已经对激活函数求导,更新梯度,只需对对应梯度求导。
$$
e=\sum_i x_i w_i
$$
$$
output= activation(e)
$$
可以看出,对梯度$w_i$求导,即为乘以输入$x_i$。
因此梯度更新公式为:
weight += lr neuron[‘derivative’] input

1
2
3
4
5
6
7
8
def UpdateWeight(cnn_net, inputs, lr):
for layer_index in range(len(cnn_net)):
if 0 != layer_index:
inputs = [neuron['output'] for neuron in cnn_net[layer_index - 1]]
for neuron in cnn_net[layer_index]:
for weight_index in range(inputs):
neuron['weight'][weight_index] += lr * neuron['derivative'] * inputs[weight_index]
neuron['bias'] += lr * neuron['derivative']

训练

训练神经网络

文章目录
  1. 1. 主要思路
  2. 2. 数据集
  3. 3. 实现
    1. 3.1. 构建网络
    2. 3.2. 前向传播
      1. 3.2.1. 激活函数
      2. 3.2.2. 前向传播
      3. 3.2.3. 反向传播
      4. 3.2.4. 梯度更新
      5. 3.2.5. 训练
,
#add by kangyabing