一、全局方法:
random.seed(0)
def rand(a, b):
return (b-a) * random.random() + a
def generate_w(m, n):
w = [0.0] *m
for i in range(m):
w[i] = [0.0]*n
for j in range(n):
w[i][j] =rand(-0.69,1)#rand(-1,1) #
return w
def generate_b(m):
b = [0.0]*m
for i in range(m):
b[i] =rand(-2.409,0.02)# rand(-1,1)#
return b
def fit_function(x,deriv=False):
if deriv==True:
#return x*(1-x)
return 1-np.tanh(x) * np.tanh(x) #tanh鍑芥暟鐨勫鏁?
return np.tanh(x)
#return 1/(1+np.exp(-x))
二、 定义了BPNetwork类,其内部函数方法说明如下:
⑴ self.input_n = 0:表示输入层的神经元个数,拟合sinx需要self.input_n = 2,一个是输入的数据,另一个用来调节bias
⑵ self.input_cells = []:表示输入层的神经元输入的数据,有两个元素,一个是输入的数据;另一个用来调节bias,默认为1
⑶ self.output_n = 0:输出层的神经元个数
⑷ self.output_cells = []:输出层的神经元的输出值
⑸ self.input_w = []:输入层到第一层隐藏层的weight
⑹ self.output_w = []:最后一层隐藏层到输出层的weight
⑺ self.hidden_ns = []:隐藏层的设置,它的长度是指隐藏层个数,它的每个元素指该层隐藏层的神经元个数
⑻ self.hidden_ws = []:隐藏层的weights,其中的每个元素为上一层隐藏层到下一层隐藏层的weight,一共有n-1个weight
⑼ self.hidden_bs = []:其中的每个元素self.hidden_bs[i]都是第i层隐藏层的bias设置,其长度为隐藏层的个数
⑽ self.output_b = []:输出层的bias
⑾ self.hidden_results = []:其中的每个元素是每一层隐藏层的输出值
⑿ self.output_deltas = []:指输出层的∂Error/∂(output_w)
⒀ self.hidden_deltases = []:其中的每个元素self.hidden_deltases[i] 指从第i层出发的∂Error/∂(weight),该weight指从第i层到第i+1层的weight
2. setup(self, input_n, output_n, hidden_set): 初始化类中所需参数,输入的参数中,
input_n是定义输入的参数个数,但并不等于输入层神经元个数,self.input_n等于输入的参数input_n+1,原因是需要新增一个神经元用来调节bias,且这个神经元的输入值记为1,这样就可以通过用output_deltas来调节input_b了
output_n是输出层个数
hidden_set是一个list,里面的每个元素是隐藏层的神经元个数,如[10,10]表示有两层隐藏层,两层隐藏层都有11个神经元,+1的原因同input_cells中增加一个神经元的作用是一样的,是为了调节该隐藏层的bias
通过这些信息就可以初始化类中所需要的数据了。即在__init__方法中的那些数据.
需要注意的是在初始化weight的时候使用generate_w方法形成一个由随机数的矩阵,初始化bias的时候使用generate_b形成一个由随机数组成的一维数组
def __init__(self):
self.input_n = 0
self.input_cells = []
self.output_n = 0
self.output_cells = []
self.input_w = []
self.output_w = []
self.hidden_ns = []
self.hidden_ws = []
self.hidden_bs = []
self.output_b = []
self.hidden_results = []
self.output_deltas = []
self.hidden_deltases = []
def setup(self, input_n, output_n, hidden_set):
#初始化各种参数
self.input_n = input_n +1
self.output_n = output_n
self.hidden_ns = [0.0]*len(hidden_set)
for i in range(len(hidden_set)):
self.hidden_ns[i] = hidden_set[i]+1
self.input_cells = [1.0]*self.input_n
self.output_cells = [1.0]*self.output_n
#初始化weights和bias
self.input_w = generate_w(self.input_n, self.hidden_ns[0])
self.hidden_ws = [0.0]*(len(self.hidden_ns)-1)
for i in range(len(self.hidden_ns)-1):
self.hidden_ws[i] = generate_w(self.hidden_ns[i],self.hidden_ns[i+1])
self.output_w = generate_w(self.hidden_ns[len(self.hidden_ns)-1], self.output_n)
self.output_b = generate_b(self.output_n)
self.hidden_bs = [0.0]*len(self.hidden_ns)
for i in range(len(self.hidden_ns)):
self.hidden_bs[i] = generate_b(self.hidden_ns[i])
self.hidden_results = [0.0]*(len(self.hidden_ns))
3. forward_propagate(self, input):向前传播。
⑴ 将输入的input作为前n-1个输入层神经元的输入值,第n个输入层神经元设为1,然后进行向前传播
⑵ 传播分为三个部分,输入层到隐藏层第一层,在隐藏层之中,然后从最后一层隐藏层到输出层。 传播公式为:
Output[h]=fit_function((Σi=0输入层神经元个数weight[i][h]*input[i])+bias[h])
传播的思路就是:前一层的输入的值乘以前一层到后一层的weight,然后得到积的加和,再用这个加和加上下一层的对应的bias,得到后一层的输出。
def forward_propagate(self, input):
for i in range(len(input)):
self.input_cells[i] = input[i]
#输入层
self.hidden_results[0] = [0.0]* self.hidden_ns[0]
for h in range(self.hidden_ns[0]):
total = 0.0
for i in range(self.input_n):
total += self.input_w[i][h] * self.input_cells[i]
self.hidden_results[0][h] = fit_function(total+self.hidden_bs[0][h])
#隐藏层
for k in range (len(self.hidden_ns)-1):
self.hidden_results[k+1] = [0.0]*self.hidden_ns[k+1]
for h in range(self.hidden_ns[k+1]):
total = 0.0
for i in range(self.hidden_ns[k]):
total += self.hidden_ws[k][i][h] * self.hidden_results[k][i]
self.hidden_results[k+1][h] = fit_function(total + self.hidden_bs[k+1][h])
#输出层
for h in range(self.output_n):
total = 0.0
for i in range(self.hidden_ns[len(self.hidden_ns)-1]):
total += self.output_w[i][h] * self.hidden_results[len(self.hidden_ns)-1][i]
self.output_cells[h] = fit_function(total + self.output_b[h])
return self.output_cells[:]
4. get_deltas(self, label):该函数是得到self.output_deltas和self.hidden_deltases的函数。
self.output_deltas = ∂Error/∂(output_w):指输出层的deltas公式为
output_deltas[o]=fit_function(output_cell[o])*(label[o]-output_cells[o])
self.hidden_deltases = ∂Error/∂(weight) weight是指hidden_ws中的元素。
hidden_deltases[k][o]=fit_function_deriv(hidden_result[k][o])*(deltas[i]*weight[o][i]
这里的deltas是指的上一层神经元的deltas
def get_deltas(self, label):
self.output_deltas = [0.0] * self.output_n
#输出层 deltas
for o in range(self.output_n):
error = label[o] - self.output_cells[o]
self.output_deltas[o] = fit_function(self.output_cells[o],True) * error
#隐藏层deltas
tmp_deltas = self.output_deltas
tmp_w = self.output_w
self.hidden_deltases = [0.0]*(len(self.hidden_ns))
k = len(self.hidden_ns) - 1
while k >= 0:
self.hidden_deltases[k] = [0.0] * (self.hidden_ns[k])
for o in range(self.hidden_ns[k]):
error = 0.0
for i in range(len(tmp_deltas)):
error += tmp_deltas[i] * tmp_w[o][i]
self.hidden_deltases[k][o] = fit_function(self.hidden_results[k][o],True) * error
k = k - 1
if k>=0:
tmp_w = self.hidden_ws[k]
tmp_deltas = self.hidden_deltases[k+1]
else:
break
5. renew_w(self,learn): 更新weight的函数,仍然分成三个部分,先更新最后一层隐藏层到输出层的权重,再是隐藏层之间的权重,然后再更新第一层隐藏层到输入层的权重。更新weight的公式为:
Weight[i][o] += deltas[o]*input[i]*learn,其中的o是输出层神经元的index,i是输入层的神经元的index,input是指输入层的输入,learn是学习率。
def renew_w(self, learn):
#更新隐藏层→输出层的权重
k = len(self.hidden_ns)-1
for i in range(self.hidden_ns[k]):
for o in range(self.output_n):
change = self.output_deltas[o] * self.hidden_results[k][i]
self.output_w[i][o] += change * learn
#更新隐藏层的权重
while k > 0 :
for i in range(self.hidden_ns[k-1]):
for o in range(self.hidden_ns[k]):
change = self.hidden_deltases[k][o] * self.hidden_results[k-1][i]
self.hidden_ws[k-1][i][o] += change * learn
k = k - 1
#更新输入层→隐藏层权重
for i in range(self.input_n):
for o in range(self.hidden_ns[0]):
change = self.hidden_deltases[0][o] * self.input_cells[i]
self.input_w[i][o] += change * learn
6. renew_b(self,learn): 更新bias的函数,分成两个部分,第一部分是隐藏层的bias,第二部分是输出层的bias。更新bias的公式为:
bias[i]+= deltas[i]*learn
def renew_b(self,learn):
#更新隐藏层bias
k = len(self.hidden_bs)-1
while k>=0:
for i in range(self.hidden_ns[k]):
self.hidden_bs[k][i] = self.hidden_bs[k][i] + learn * self.hidden_deltases[k][i]
k = k - 1
#更新输出层bias
for o in range(self.output_n):
self.output_b[o] += self.output_deltas[o] * learn
7. back_propagate(self, input, label, learn):先进行前向传播,再进行反向传播,然后获取deltas,更新weight和bias,之后再计算此次正向传播得到的损失值,并返回。这个函数的每一行都在调用其他的函数,是一次完整的反向传播。
def back_propagate(self, input, label, learn):
self.forward_propagate(input)
self.get_deltas(label)
self.renew_w(learn)
self.renew_b(learn)
return self.get_loss(label,self.output_cells)
8. get_loss(self,label,output_cell):用损失函数获取损失值,公式为
Σo输出值的个数=0((label[o]-output_cell[o])**2) *0.5
#计算损失值
def get_loss(self,label,output_cell):
error = 0.0
for o in range (len(output_cell)):
error += 0.5 * (label[o] - output_cell[o]) ** 2
return error
9. test(self):设置bp网的结构,然后得到20组数据进行训练,然后再用200组数据进行测试。直接调用test()方法就可以测试。
#测试
def test(self):
input_datas,labels = self.get_train()
self.setup(1,1,[10,10])
self.train(input_datas,labels,0.05,3000)
test,test_label = self.get_test()
error = self.get_average_loss(test,test_label)
print(error)
'''ylabels = []
error = 0
for i in range(len(test)):
ylabels.append(self.forward_propagate(test[i]))
error += self.get_loss (test_label[i], self.output_cells)
print("---测试误差---")
error = error/200
print(error)