本文实现只有一层隐藏层。贴上221网络解决两元素异或以及331网络解决三元素异或问题。
原理不懂可以参考以下博客,建议手推一遍bp怎样链式反向求导。
- 多层感知机(MultiLayer Perceptron)以及反向传播算法(Backpropagation)
- 机器学习——神经网络(四):BP神经网络
- 如果还不清楚这里有视频:PyTorch深度学习实践
BP算法的核心就是从后向前更新权重。网络上大部分代码实现两元素异或都是2-4-1的网络,因为没有加偏置项所以隐含层不得不设置4个节点才能分离。但实验要求是用221网络分离两元素异或,以及331网络分离三元素异或,所以我们再需要求出loss(损失)对bias(偏置项)的导数。
不细细推了,先看隐藏层到输出层权值更新
实际就是本应该输出的标签减去得到的结果。
就是输出对函数的导数。
就是未经过函数的输出对的导数。
如果是隐藏层到输出层的偏置的导数,那么很简单,不要 这一项就行,因为的系数就是1。
所以隐藏层到输出层的偏置应该这么更新
输入到隐藏层公式就不细讲了,主要latax敲公式不太熟。但偏置同理导数为1。
网上的代码都是隐藏层4个函数节点来分,而不是像下图一样加上偏置只需要2个节点。
但是上述图片的隐层函数是阶跃函数。而我的代码隐层非线性函数为。所以最终的权值会不一样。
对2个元素异或的bp源代码:
import numpy as np
import matplotlib.pyplot as plt
rate=0.1 #学习率
sample_num=4 #样本数据量
class my_mlp:
def __init__(self, input_size, hidden_size, output_size):
self.w1 = np.random.normal(size=(hidden_size, input_size))#输入层到隐藏层
self.w2 = np.random.normal(size=(hidden_size,output_size))#隐藏层到输出层
self.b1 = np.random.normal(size=(hidden_size))
self.b2 = np.random.normal(size=(output_size))
self.h_out = np.zeros(1)
self.out = np.zeros(1)
@staticmethod
def sigmoid(x):
'''sigmoid函数作为激活函数'''
return 1 / (1 + np.exp(-x))
@staticmethod
def d_sigmoid(x):
'''相对误差对输出和隐含层求导'''
return x * (1 - x)
def forward(self,input):
self.h_out = my_mlp.sigmoid(np.dot(input, self.w1)+self.b1)
self.out = my_mlp.sigmoid(np.dot(self.h_out, self.w2)+self.b2)
def backpropagation(self,input,output,lr=rate):
self.forward(input)
L2_delta=(output-self.out) * my_mlp.d_sigmoid(self.out)
L1_delta = L2_delta.dot(self.w2.T) * my_mlp.d_sigmoid(self.h_out)
d_w2 = rate * self.h_out.T.dot(L2_delta)
d_w1 = rate * input.T.dot(L1_delta)
self.w2 += d_w2
self.w1 += d_w1
d_b2 = np.ones((1,sample_num)).dot(L2_delta)
d_b1 = np.ones((1,sample_num)).dot(L1_delta)
self.b2 += rate*d_b2.reshape(d_b2.shape[0]*d_b2.shape[1],)
self.b1 += rate*d_b1.reshape(d_b1.shape[0]*d_b1.shape[1],)
if __name__ == '__main__':
mlp=my_mlp(2,2,1)
# x_data x1,x2
x_data = np.array([[0, 0],
[0, 1],
[1, 0],
[1, 1]])
# y_data label
y_data = np.array([[0],
[1],
[1],
[0]])
for i in range(15000):
mlp.backpropagation(x_data,y_data)
out=mlp.out # 更新权值
if i % 500 == 0:
plt.scatter(i, np.mean(np.abs(y_data - out)))
#print('当前误差:',np.mean(np.abs(y_data - out)))
plt.title('Error Curve')
plt.xlabel('iteration')
plt.ylabel('Error')
plt.show()
print('输入层到隐含层权值:\n',mlp.w1)
print('输入层到隐含层偏置:\n',mlp.b1)
print('隐含层到输出层权值:\n',mlp.w2)
print('隐含层到输出层偏置:\n',mlp.b2)
print('输出结果:\n',out)
print('忽略误差近似输出:')
for i in out:
print(0 if i<=0.5 else 1)
结果:
此外:为了实现可扩展性专门设立一个类定义输入节点数,隐藏节点数和输出节点数,这样只要一层隐含层能实现的功能上述都可以实现。比如我们想实现三元素异或
0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 1 |
0 | 1 | 1 | 0 |
1 | 0 | 0 | 1 |
1 | 0 | 1 | 0 |
1 | 1 | 0 | 0 |
1 | 1 | 1 | 1 |
只需要调整一些参数即可:
import numpy as np
import matplotlib.pyplot as plt
rate=0.1 #学习率
sample_num=8 #样本数据量
class my_mlp:
def __init__(self, input_size, hidden_size, output_size):
self.w1 = np.random.normal(size=(hidden_size, input_size))#输入层到隐藏层
self.w2 = np.random.normal(size=(hidden_size,output_size))#隐藏层到输出层
self.b1 = np.random.normal(size=(hidden_size))
self.b2 = np.random.normal(size=(output_size))
self.h_out = np.zeros(1)
self.out = np.zeros(1)
@staticmethod
def sigmoid(x):
'''sigmoid函数作为激活函数'''
return 1 / (1 + np.exp(-x))
@staticmethod
def d_sigmoid(x):
'''相对误差对输出和隐含层求导'''
return x * (1 - x)
def forward(self,input):
self.h_out = my_mlp.sigmoid(np.dot(input, self.w1)+self.b1)
self.out = my_mlp.sigmoid(np.dot(self.h_out, self.w2)+self.b2)
def backpropagation(self,input,output,lr=rate):
self.forward(input)
L2_delta=(output-self.out) * my_mlp.d_sigmoid(self.out)
L1_delta = L2_delta.dot(self.w2.T) * my_mlp.d_sigmoid(self.h_out)
d_w2 = rate * self.h_out.T.dot(L2_delta)
d_w1 = rate * input.T.dot(L1_delta)
self.w2 += d_w2
self.w1 += d_w1
d_b2 = np.ones((1,sample_num)).dot(L2_delta)
d_b1 = np.ones((1,sample_num)).dot(L1_delta)
self.b2 += rate*d_b2.reshape(d_b2.shape[0]*d_b2.shape[1],)
self.b1 += rate*d_b1.reshape(d_b1.shape[0]*d_b1.shape[1],)
if __name__ == '__main__':
mlp=my_mlp(3,3,1)
# x_data x1,x2
x_data = np.array([[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[0, 1, 1],
[1, 0, 0],
[1, 0, 1],
[1, 1, 0],
[1, 1, 1]])
# y_data label
y_data = np.array([[0],[1],[1],[0],[1],[0],[0],[1]])
for i in range(15000):
mlp.backpropagation(x_data,y_data)
out=mlp.out # 更新权值
if i % 500 == 0:
plt.scatter(i, np.mean(np.abs(y_data - out)))
#print('当前误差:',np.mean(np.abs(y_data - out)))
plt.title('Error Curve')
plt.xlabel('iteration')
plt.ylabel('Error')
plt.show()
print('输入层到隐含层权值:\n',mlp.w1)
print('输入层到隐含层偏置:\n',mlp.b1)
print('隐含层到输出层权值:\n',mlp.w2)
print('隐含层到输出层偏置:\n',mlp.b2)
print('输出结果:\n',out)
print('忽略误差近似输出:')
for i in out:
print(0 if i<=0.5 else 1)