Just show the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

"""
神经网络模型简介:
单层神经元(感知机):
1. 输入向量Wi: 多个同维向量,其中包括多个输入节点和标注为+1的偏置节点
2. 输入权值j1: 每个输入向量(除去偏置节点)在输入神经元时都需要乘上一个相应的权值
3. 输入值 ΣWiji + b: 为每个输入节点乘上相应的权值然后求和再加上偏置节点
4. 激活函数: 通常使用sigmoid函数,作为激活函数,有单极性和双极性之分
5. 输出值: 激活函数的返回值,为神经元网络的输出或者为下一个神经元的输入

多层神经元模型:
为单层神经元的级联,每一层的结构包括多个神经元,接受上一层神经元的输入
产生下一神经元的输出,不同层的神经元之间有相应的权值。
* 层次结构:输入层+隐藏层+输出层
1. 输入层:包含多个输入节点和一个标注为+1的偏置节点,偏置节点不接受输入。
2. 隐藏层:为多个神经元级联而成,每一层数目与输入层数目一致
3. 输出层:只有一个神经元结构,最后一层的隐藏层的作为输出层的输入,输出整个神经网络的结果

前向传播与后向(反向)传播
--- 前向传播对应预测(分类),后向传播对应训练
* 前向传播:就是给定模型参数即输入层的输入和偏置节点,逐一计算各层的输出值,直到最后输出神经网络的结果
* 后向传播:
"""



import math

# 单极性激活函数,优点连续可导
def sigmoid1(x):
"""
:sigmoid单极性函数 1/(1+e^-x)
:param: x, 函数自变量
:return: sigmoid函数值
"""
return 1.0/(1.0 + math.exp(-x))

# 单极性激活函数的导数
def dsigmoid1(y):
"""
:sigmoid 单极性函数的导数
:param y 函数自变量:
:return 函数 运算结果
"""
return y*(1-y)

#双极性激活函数
def sigmoid2(x):
"""
:sigmoid双极性函数 tanh (z)
: param x,函数自变量
: return 函数因变量
"""
return math.tanh(x)

#双性极性激活函数的导数
def dsigmoid2(y):
"""
: sigmoid双极性函数的导数
:param x,函数自变量
:return 函数因变量
"""
return 1.0 - y ** 2

# 神经网络前向传播实现方法
"""
前向传播算法,神经网络的输出值即预测值可作为后向传播误差的计算
"""
def runNN(self, inputs):
"""
:前向传播进行分类
:param: inputs-输入参数
:return: 所属类别
"""
# 输入的数目必须为每一层规定节点数-1,除去偏置节点,不接受输入
if len(inputs) != self.ni - 1:
print ("incorrect number of inputs")

# 将输入向量映射到神经元的输入节点值
# ai - 输入层
for i in range(self.ni - 1):
self.ai[i] = inputs[i]

#输入层到隐藏层,隐藏层的运算
# ah - 隐藏层的输出值
for j in range(self.nh):
sum = 0.0
for i in range(self.ni):
sum += (self.ai[i] * self.wi[i][j]) # wi为输入层到隐藏层的权值 权值求和
self.ah[j] = sigmoid(sum) #输入激活函数,产生下一神经元的输入

#隐藏层到输出层,输出层运算
# ao - 最终输出结果
for k in range(self.no):
sum = 0.0
for j in range(self.nh):
sum += (self.ah[j] * self.wo[j][k]) # wo为隐藏层到输出层的权值
self.ao[k] = sigmoid(sum)

return self.ao

"""
后向传播
指的是在训练的时候,根据最终输出的误差(预测值-目标值的平方和/2)
来调整倒数第二层、倒数第三层……第一层的参数的过程。

主要有三种调整
1. 输出层权值的调整
2. 隐藏层权值的调整
3. 偏置节点的调整

算法步骤
1. 随机初始化参数(指权值和偏置节点),对输入利用前向传播计算输出
2. 对输出和隐藏节点进行调整,计算delta。公式比较难写。。
3. 计算梯度可定义学习率影响训练速度,并更新权值参数偏置参数。
"""

def backPropagate(self, targets, N, M):
"""
后向传播算法
:param targets: 实例的类别
:param N: 本次学习率
:param M: 上次学习率
:return: 最终的误差平方和的一半
"""
# http://www.youtube.com/watch?v=aVId8KMsdUU&feature=BFa&list=LLldMCkmXl4j9_v0HeKdNcRA

# 计算输出层 deltas
# dE/dw[j][k] = (t[k] - ao[k]) * s'( SUM( w[j][k]*ah[j] ) ) * ah[j]
output_deltas = [0.0] * self.no
for k in range(self.no):
error = targets[k] - self.ao[k]
output_deltas[k] = error * dsigmoid(self.ao[k])

# 更新输出层权值
for j in range(self.nh):
for k in range(self.no):
# output_deltas[k] * self.ah[j] 才是 dError/dweight[j][k]
change = output_deltas[k] * self.ah[j]
self.wo[j][k] += N * change + M * self.co[j][k]
self.co[j][k] = change

# 计算隐藏层 deltas
hidden_deltas = [0.0] * self.nh
for j in range(self.nh):
error = 0.0
for k in range(self.no):
error += output_deltas[k] * self.wo[j][k]
hidden_deltas[j] = error * dsigmoid(self.ah[j])

# 更新输入层权值
for i in range(self.ni):
for j in range(self.nh):
change = hidden_deltas[j] * self.ai[i]
# print 'activation',self.ai[i],'synapse',i,j,'change',change
self.wi[i][j] += N * change + M * self.ci[i][j]
self.ci[i][j] = change

# 计算误差平方和
# 1/2 是为了好看,**2 是平方
error = 0.0
for k in range(len(targets)):
error = 0.5 * (targets[k] - self.ao[k]) ** 2
return error

来源来自大牛 http://www.hankcs.com/ml/back-propagation-neural-network.html thx!