§1.题目
§2.分析
§单隐层BP网络
§BP算法伪代码
Algorithms 1. 标准BP算法
----
输入: 训练集 D,学习率 η.
过程:
1. 随即初始化连接权与阈值 (ω,θ).
2. Repeat:
3. for x_k,y_k in D:
4. 根据当前参数计算出样本误差 Ek.
5. 根据公式计算出输出层神经元的随机梯度项 gj.
6. 根据公式计算出隐层神经元的梯度项 eh.
7. 根据公式更新 (ω,θ)(ν,γ).
8. end for
9. until 达到停止条件
输出:(ω,θ) (ν,γ) - 即相应的多层前馈神经网络.
----
Algorithms 2. 累积BP算法
----
输入: 训练集 D,学习率 η,迭代次数 n.
过程:
1. 随即初始化连接权与阈值 (ω,θ).
2. Repeat:
3. 根据当前参数计算出累积误差 E.
4. 根据公式计算出标准梯度项 g.
5. 根据公式计算出隐层神经元的梯度项 e.
6. 根据公式更新 (ω,θ)(ν,γ).
7. n = n-1
8. until n=0 or 达到停止条件
输出:(ω,θ) (ν,γ) - 即相应的多层前馈神经网络.
----
§基本实现步骤
-
1.处理数据集
- 离散数据归一化(可用独热编码技术改进)
-
2.定义初始化各参数
-
定义初始化输入神经元的个数d(n个属性描述)
d = n
-
定义初始化输出神经元的个数l(输出l维实值向量)
l = 1
-
定义初始化隐层神经元的个数q
q = d + 1
-
定义初始化输出层神经元的阈值矩阵θ(1行l列)
theta = [random.random() for i in range(l)]
-
定义初始化隐层神经元的阈值矩阵γ(1行q列)
gamma = [random.random() for i in range(q)]
-
定义初始化输入层和隐层神经元之间的连接权重v矩阵(d行q列 输入层->输出层)
v = [[random.random() for i in range(q)] for j in range(d)]
-
定义初始化隐层和输出层神经元之间的连接权重w矩阵(q行l列 隐层->输入层)
w = [[random.random() for i in range(l)] for j in range(q)]
-
定义初始化学习(速)率(固定值)eta
eta = 0.49 # the training speed
-
定义初始化最大允许误差error
error = 0.0001
-
定义初始化训练次数及上限trainingTimes,maxTrainingTimes
trainingTimes = maxTrainingTimes = 10000
-
-
3.构造sigmoid()函数作为激活函数
- 按照待处理的矩阵维(层)数划分情况
-
4.标准BP算法实现
-
输入: 训练集 D,学习率 η. 过程: 1. 随即初始化连接权与阈值 (ω,θ). 2. Repeat: 3. for x_k,y_k in D: 4. 根据当前参数计算出样本误差 Ek. 5. 根据公式计算出输出层神经元的随机梯度项 gj. 6. 根据公式计算出隐层神经元的梯度项 eh. 7. 根据公式更新 (ω,θ)(ν,γ). 8. end for 9. until 达到停止条件 输出:(ω,θ) (ν,γ) - 即相应的多层前馈神经网络.
-
-
5.累计BP算法实现
-
输入: 训练集 D,学习率 η,迭代次数 n. 过程: 1. 随即初始化连接权与阈值 (ω,θ). 2. Repeat: 3. 根据当前参数计算出累积误差 E. 4. 根据公式计算出标准梯度项 g. 5. 根据公式计算出隐层神经元的梯度项 e. 6. 根据公式更新 (ω,θ)(ν,γ). 7. n = n-1 8. until n=0 or 达到停止条件 输出:(ω,θ) (ν,γ) - 即相应的多层前馈神经网络.
-
-
6.构造预测函数
- 输入测试集
- 使用训练好的网络模型数据(权值和阈值)
- 输出预测结果
-
7.算法训练结果可视化表示
- 在限定学习率和误差的情况下对比训练次数和收敛情况等
§3.代码实现
§数据集(watermelonDataset_3.0)
编号,色泽,根蒂,敲声,纹理,脐部,触感,密度,含糖率,好瓜
1,青绿,蜷缩,浊响,清晰,凹陷,硬滑,0.697,0.46,是
2,乌黑,蜷缩,沉闷,清晰,凹陷,硬滑,0.774,0.376,是
3,乌黑,蜷缩,浊响,清晰,凹陷,硬滑,0.634,0.264,是
4,青绿,蜷缩,沉闷,清晰,凹陷,硬滑,0.608,0.318,是
5,浅白,蜷缩,浊响,清晰,凹陷,硬滑,0.556,0.215,是
6,青绿,稍蜷,浊响,清晰,稍凹,软粘,0.403,0.237,是
7,乌黑,稍蜷,浊响,稍糊,稍凹,软粘,0.481,0.149,是
8,乌黑,稍蜷,浊响,清晰,稍凹,硬滑,0.437,0.211,是
9,乌黑,稍蜷,沉闷,稍糊,稍凹,硬滑,0.666,0.091,否
10,青绿,硬挺,清脆,清晰,平坦,软粘,0.243,0.267,否
11,浅白,硬挺,清脆,模糊,平坦,硬滑,0.245,0.057,否
12,浅白,蜷缩,浊响,模糊,平坦,软粘,0.343,0.099,否
13,青绿,稍蜷,浊响,稍糊,凹陷,硬滑,0.639,0.161,否
14,浅白,稍蜷,沉闷,稍糊,凹陷,硬滑,0.657,0.198,否
15,乌黑,稍蜷,浊响,清晰,稍凹,软粘,0.36,0.37,否
16,浅白,蜷缩,浊响,模糊,平坦,硬滑,0.593,0.042,否
17,青绿,蜷缩,沉闷,稍糊,稍凹,硬滑,0.719,0.103,否
§参数
§公式
§输出层和隐层的输入
§预测输出
§
§均方误差
§输出层神经元的梯度项
§隐层神经元的梯度项
§更新公式:
- 更新隐层和输出层神经元之间的连接权重
- 更新输出层神经元的阈值
- 更新输入层和隐层神经元之间的连接权重
- 更新隐层神经元之间的阈值
§公式5.16
§3.1标准BP算法
§核心代码:
# 4 标准BP算法实现
# do the repeat
while (trainingTimes > 0):
# 训练次(轮)0数
trainingTimes -= 1
# 定义初始化累计误差
sumE = 0
for i in range(m):
# 向前传播
# 矩阵积 获得隐层各神经元的输入矩阵(shape=(1*d)*(d*q)=(1*q))
alpha = np.dot(X[i], v)
# 激活函数处理 获得隐层各神经元的实际值 b=f(alpha-gamma), shape=1*q
b = sigmoid(alpha - gamma, 1)
# 矩阵积 获得输出层各神经元的输入矩阵(shape=(1*q)*(q*l)=(1*l))
beta = np.dot(b, w)
# 公式5.3 激活函数处理 获得(预测值)样本输出矩阵(shape=1*l)
predictY = sigmoid(beta - theta, 1)
# 公式5.4 获得均方误差(1层)
Ek = sum((predictY - trueY[i]) * (predictY - trueY[i])) / 2
# 公式5.16 获得累计误差
sumE += Ek
# 公式5.10 获得输出层神经元的梯度项矩阵(shape=1*l)
g = predictY * (1 - predictY) * (trueY[i] - predictY)
# 公式5.15 获得隐层神经元的梯度项矩阵(shape=1*q)
e = b * (1 - b) * ((np.dot(w, g.T)).T)
# 向后更新
# 公式5.11 更新隐层和输出层神经元之间的连接权重矩阵(shape=q*l)
w += eta * np.dot(b.reshape((q, 1)), g.reshape((1, l)))
# 公式5.12 更新输出层神经元的阈值矩阵(shape=1*l)
theta -= eta * g
# 公式5.13 更新输入层和隐层神经元之间的连接权重矩阵(shape=d*q)
v += eta * np.dot(X[i].reshape((d, 1)), e.reshape((1, q)))
# 公式5.14 更新隐层神经元之间的阈值矩阵(shape=1*q)
gamma -= eta * e
# print(sumE)
print("累计误差:",sumE, "\t\t训练次数:", maxTrainingTimes - trainingTimes+1)
if(sumE <= error):
break
§3.2累计BP算法
§核心代码:
# 5 累计BP算法实现
trueY=trueY.reshape((m,l))
while(trainingTimes>0):
# 向前传播
# 训练次(轮)0数
trainingTimes-=1
# 定义初始化累计误差
sumE=0
# 矩阵积 获得隐层各神经元的输入矩阵(shape=(m*d)*(d*q)=(m*q))
alpha = np.dot(X, v)
# 激活函数处理 获得隐层各神经元的实际值 b=f(alpha-gamma), shape=m*q
b = sigmoid(alpha - gamma,2)
# 公式5.3 激活函数处理 获得(预测值)样本输出矩阵(shape=m*l=(m*q)*(q*l))
beta = np.dot(b, w)
# 公式5.3 激活函数处理 获得(预测值)样本输出矩阵(shape=m*l)
predictY = sigmoid(beta - theta,2)
# 公式5.4 获得均方误差(2层)
E = sum(sum((predictY - trueY) * (predictY - trueY))) / 2
# print(round(E,5))
# 公式5.10 获得输出层神经元的梯度项矩阵 (shape=m*l)
g = predictY * (1 - predictY) * (trueY - predictY)
# 公式5.15 获得隐层神经元的梯度项矩阵(shape=m*q)
e = b * (1 - b) * ((np.dot(w, g.T)).T)
# 向后更新
# 公式5.11 更新隐层和输出层神经元之间的连接权重矩阵(shape=q*l=(q*m)*(m*l))
w += eta * np.dot(b.T, g)
# 公式5.12 更新输出层神经元的阈值矩阵(shape=1*l)
theta -= eta * g # 5.12
# 公式5.13 更新输入层和隐层神经元之间的连接权重矩阵(shape=d*q=(d,m)*(m,q))
v += eta * np.dot(X.T, e)
# 公式5.14 更新隐层神经元之间的阈值矩阵(shape=1*q)
gamma -= eta * e
print("累计误差:",E, "\t\t训练次数:", maxTrainingTimes - trainingTimes+1)
if(E <= error):
break
# # print(E if(trainingTimes %500 == 0) )
§4.比较总结
§标准BP算法达到限定累计误差(0.001)所需训练次数(学习率:0.48)
§累计BP算法达到限定累计误差(0.001)所需训练次数(学习率:0.48)
§综合情况
§分析:
在相同学习率(0.48)的条件下,为达到0.001左右的误差,标准BP算法需要训练6765轮左右,而累积BP算法大概需要1722轮。
同时也注意到随着训练次数的增加,累计BP算法中累计误差的下降开始变得较标准BP算法更为缓慢。
§结论:
标准BP算法每次更新只针对单个样例,参数更新的非常频繁,而且对不同样例进行更新的效果可能出现“抵消”现象。因此为了达到同样的累积误差极小点,标准BP算法往往需要进行更多次的迭代。
累积BP算法直接针对累积误差最小化,它读取整个训练集D一遍后才对参数进行更新,其参数更新的频率低得多。但是在很多任务中,累积误差下降到一定程度后,进一步下降会非常缓慢,这时标准BP算法往往会更快获得较好的解,尤其是在训练集D非常大时更明显。
用累积bp算法,更新神经网络的次数会少,所以学的快一些,而且能避免个别异常变量带来的误差导致过拟合。但是需要的样本量比较大,忽略个体而重视整体,就模型泛化能力上来讲,用累积bp算法训练出的模型对于边界条件的把握不一定强。