双层感知机解决非线性可分(异或)问题
BP算法原理推导+数据演示
原理推导
第一层网络的参数
因为该网络要解决的是一个二分类问题,所以输出层的激活函数可以使用一个Sigmoid型函数
反向传播的计算
很好的推导例子: https://blog.csdn.net/weixin_39441762/article/details/80446692.
很好的推导例子: https://zhuanlan.zhihu.com/p/38006693.
我们已经了解了数据沿着神经网络前向传播的过程,这一节我们来介绍更重要的反向传播的计算过程。假设我们使用随机梯度下降的方式来学习神经网络的参数,损失函数定义为,其中是该样本的真实类标。使用梯度下降进行参数的学习,我们必须计算出损失函数关于神经网络中各层参数(权重w和偏置b)的偏导数。
步骤
1.初始化网络中的权值和偏置项,w,b
2.激活前向传播,得到各层输出和损失函数的期望值,E=1/n(y-yi)的平方
3.据损失函数,计算输出单元的误差项和隐藏单元的误差项
4.更新神经网路中的权值和偏置项
5.重复步骤2-4,直到损失函数小于事先给定的阈值或迭代次数用完为止,输出此时的参数即为目前最佳参数。
1.激活函数的种类
2.前向传播
3.反向求梯度
数据演示
1.求出每层的值
2.求出每层的误差
3.更新参数
BP算法缺陷与改进
BP算法缺陷:
1.局部极小值
对于多层网络,误差曲面可能含有多个不同的局部极小值,梯度下降可能导致陷入局部极小值。
2.权值过多
当隐藏节点过多,层数越多时,权值成倍增长。权值的增长意味着对应的空间维数的增加,过高的维数易导致训练后期的过拟合。
3.容易过拟合
训练的次数过多、空间维数过高均容易过拟合。
BP算法改进:
1.利用动量法改进BP算法
动量法权值调整算法的具体做法是:将上一次权值调整量的一部分迭加到按本次误差计算所得的权值调整量上,作为本次的实际权值调整量,即:
其中,表示动量系数,表示学习率。
2)自适应调整学习率
调整的基本指导思想是:在学习收敛的情况下,增大以缩短学习时间;当偏大致使不能收敛时,要及时减小它的值,知道收敛为止。此方法适用于设置阈值的情况下。
3)动量-自适应学习速率调整算法
采用动量法,BP算法可以找到更优的解;采用自适应学习速率法时,BP算法可以缩短训练时间。将以上两种方法结合起来,就得到动量-自适应学习率调整算法。
上述2)和3)都适应于设置阈值来停止程序的方法。
代码
from __future__ import division
import math
import random
import pandas as pd
flowerLables = {0: 'Iris-setosa',
1: 'Iris-versicolor',
2: 'Iris-virginica'}
random.seed(0)
# 生成区间[a, b)内的随机数
def rand(a, b):
return (b - a) * random.random() + a
# 生成大小 I*J 的矩阵,默认零矩阵
def makeMatrix(I, J, fill=0.0):
m = []
for i in range(I):
m.append([fill] * J)
return m
# 函数 sigmoid
def sigmoid(x):
return 1.0 / (1.0 + math.exp(-x))
# 函数 sigmoid 的导数
def dsigmoid(x):
return x * (1 - x)
class NN:
""" 三层反向传播神经网络 """
def __init__(self, ni, nh, no):
# 输入层、隐藏层、输出层的节点(数)
self.ni = ni + 1 # 增加一个偏差节点
self.nh = nh + 1
self.no = no
# 激活神经网络的所有节点(向量)
self.ai = [1.0] * self.ni
self.ah = [1.0] * self.nh
self.ao = [1.0] * self.no
# 建立权重(矩阵)
self.wi = makeMatrix(self.ni, self.nh)
self.wo = makeMatrix(self.nh, self.no)
# 设为随机值
for i in range(self.ni):
for j in range(self.nh):
self.wi[i][j] = rand(-0.2, 0.2)
for j in range(self.nh):
for k in range(self.no):
self.wo[j][k] = rand(-2, 2)
def update(self, inputs):
if len(inputs) != self.ni - 1:
raise ValueError('与输入层节点数不符!')
# 激活输入层
for i in range(self.ni - 1):
self.ai[i] = inputs[i]
# 激活隐藏层
for j in range(self.nh):
sum = 0.0
for i in range(self.ni):
sum = sum + self.ai[i] * self.wi[i][j]
self.ah[j] = sigmoid(sum)
# 激活输出层
for k in range(self.no):
sum = 0.0
for j in range(self.nh):
sum = sum + self.ah[j] * self.wo[j][k]
self.ao[k] = sigmoid(sum)
return self.ao[:]
def backPropagate(self, targets, lr):
""" 反向传播 """
# 计算输出层的误差
output_deltas = [0.0] * self.no
for k in range(self.no):
error = targets[k] - self.ao[k]
output_deltas[k] = dsigmoid(self.ao[k]) * error
# 计算隐藏层的误差
hidden_deltas = [0.0] * self.nh
for j in range(self.nh):
error = 0.0
for k in range(self.no):
error = error + output_deltas[k] * self.wo[j][k]
hidden_deltas[j] = dsigmoid(self.ah[j]) * error
# 更新输出层权重
for j in range(self.nh):
for k in range(self.no):
change = output_deltas[k] * self.ah[j]
self.wo[j][k] = self.wo[j][k] + lr * change
# 更新输入层权重
for i in range(self.ni):
for j in range(self.nh):
change = hidden_deltas[j] * self.ai[i]
self.wi[i][j] = self.wi[i][j] + lr * change
# 计算误差
error = 0.0
error += 0.5 * (targets[k] - self.ao[k]) ** 2
return error
def test(self, patterns):
count = 0
for p in patterns:
target = flowerLables[(p[1].index(1))]
result = self.update(p[0])
index = result.index(max(result))
print(p[0], ':', target, '->', flowerLables[index])
count += (target == flowerLables[index])
accuracy = float(count / len(patterns))
print('accuracy: %-.9f' % accuracy)
def weights(self):
print('输入层权重:')
for i in range(self.ni):
print(self.wi[i])
print()
print('输出层权重:')
for j in range(self.nh):
print(self.wo[j])
def train(self, patterns, iterations=1000, lr=0.1):
# lr: 学习速率(learning rate)
for i in range(iterations):
error = 0.0
for p in patterns:
inputs = p[0]
targets = p[1]
self.update(inputs)
error = error + self.backPropagate(targets, lr)
if i % 100 == 0:
print('error: %-.9f' % error)
def iris():
data = []
# 读取数据
raw = pd.read_csv('iris.csv')
raw_data = raw.values
raw_feature = raw_data[0:, 0:4]
for i in range(len(raw_feature)):
ele = []
ele.append(list(raw_feature[i]))
if raw_data[i][4] == 'Iris-setosa':
ele.append([1, 0, 0])
elif raw_data[i][4] == 'Iris-versicolor':
ele.append([0, 1, 0])
else:
ele.append([0, 0, 1])
data.append(ele)
# 随机排列数据
random.shuffle(data)
training = data[0:100]
test = data[101:]
nn = NN(4, 7, 3)
nn.train(training, iterations=10000)
nn.test(test)
if __name__ == '__main__':
iris()
链接: https://cloud.tencent.com/developer/article/1460637.
数据示例: https://blog.csdn.net/hcxddd/article/details/119137414.
两个输出的推导: https://zhuanlan.zhihu.com/p/38006693.
推导: https://blog.csdn.net/weixin_44378835/article/details/111323770.