【BP】c++实现
BP网络c++实现
单隐藏层流程
下面实现一个单隐藏层的简单BP算法,其中输入层、隐藏层、输出层节点个数可以自定义,而前向传播和后向传播是主要过程,下面是按上图进行的公式推导
1. 前向传播
1.1 输入层向隐藏层传播
其中为第个隐藏层节点的值,为第个输入层节点的值,为第个输入层节点到第个隐藏层节点的权重,为第个隐藏层节点偏置值,为Sigmoid
激活函数,其公式如下
代码
1 | //计算h_j(q) |
1.2 隐藏层向输出层传播
其中为第个输出层节点的值(预测值),为第个隐藏层节点的值,为第个隐藏层节点到第个输出层节点的权重,为第个输出层节点的偏置值,为激活函数。
代码
1 | //计算\hat{y},默认OUTNODE为1,不使用循环也可以 |
2. 计算损失函数
损失函数定义如下:
其中为第个输出层节点的目标值(真实值),为第个输出层节点的值(预测值)。
代码
1 | // 计算误差 |
3. 反向传播
利用梯度下降法进行优化,激活函数使用
Sigmoid
,公式
3.1 计算(输出层节点偏置值的修正值)
其计算公式如下:
其中为学习率,默认。
代码
1 | for (size_t i = 0; i < OUTNODE; i++) { |
3.2 计算(隐藏层节点到输出层节点权重的修正值)
其计算公式如下:
其中为第个隐藏层节点的值。
代码
1 | for (size_t i = 0; i < HIDENODE; i++) { |
3.3 计算(隐藏层节点偏置值的修正值)
其计算公式如下:
其中为第个隐藏层节点到第个输出层节点的权重。
代码
1 | for (size_t i = 0; i < HIDENODE; i++) { |
3.4 计算(输入层节点到隐藏层节点权重的修正值)
其计算公式如下:
其中为第个输入层节点的值。
代码
1 | for (size_t i = 0; i < INNODE; i++) { |
4. 训练部分
4.1 初始化函数
- 初始化三层神经网络:输入层(
INNODE
个神经元),隐藏层(HIDENODE
个神经元),输出层(OUTNODE
个神经元)。 - 每个神经元的权重和偏置(
bias
)被初始化为随机值
4.2 读取训练数据
- 使用
getData
函数从文件中读取训练数据(包含输入和输出)。
4.3 训练过程
- 对于训练数据的每一个样本:
- 正向传播 (
forwardPropagation
):输入数据被传递到输入层,然后经过隐藏层,并最终到达输出层。每一层的节点值根据权重和偏置计算,并通过激活函数处理。 - 计算误差:使用均方误差,计算输出层的值与训练数据中的实际输出之间的差异。
- 反向传播 (
backwardPropagation
):通过反向传播算法调整网络中每个神经元的权重和偏置的梯度(即delta
)。 - 更新权重和偏置 (
revise
):根据计算得到的梯度调整每个神经元的权重和偏置。
- 正向传播 (
4.4 停止条件
- 如果达到最大训练次数(
mosttimes
)或误差小于设定的阈值(threshold
),则停止训练。
代码
1 | // 训练 |
5. 预测部分
5.1 读取测试数据
- 使用
getData
函数从文件中读取测试数据(仅输入,无输出)。
5.2 预测过程
- 对于测试数据的每一个样本:
- 通过与训练类似的正向传播过程计算输出层的值。
- 输出每个样本的输入和对应的预测输出。
代码
1 | void predict(vector<Sample> test_data){ |
多隐藏层流程
每个隐藏层的输出都依赖于前一层的输出,误差梯度需要逐层反向传播。
1. 前向传播
前向传播是指从输入层到输出层的计算流程,具体过程如下:
- 输入层到第一个隐藏层: 输入层的节点直接与第一个隐藏层的节点相连。对于每个隐藏层节点,计算所有输入层节点的加权和,减去偏置,并通过激活函数(使用sigmoid函数)。
- 隐藏层之间的传播: 每个隐藏层的输出成为下一个隐藏层的输入。对于每个隐藏层节点,计算前一隐藏层所有节点的加权和,减去偏置,并通过激活函数。
- 最后一个隐藏层到输出层: 最后一个隐藏层的输出经过加权和和偏置调整后,通过激活函数形成输出层的值。
代码
1 | void forwardPropagation(const Sample& sample) { |
2. 反向传播
反向传播用于计算误差并更新网络中的权重和偏置,具体过程如下
- 输出层误差和梯度计算: 首先计算输出层节点的误差(实际输出与期望输出之差)。然后,计算这些误差关于激活函数的导数。
- 隐藏层误差和梯度传播: 误差从输出层反向传播到每个隐藏层。对于每个隐藏层节点,计算其对应的误差,这通常是由后一层的误差和权重导出的。然后,计算误差关于激活函数的导数。
- 权重和偏置更新: 以学习率为标准,利用计算出的梯度更新每层的权重和偏置。
代码
1 | void backwardPropagation(const Sample& sample) { |
数字距离测试
实现功能:输入两个值,若两个值差的绝对值小于0.05,则输出1,否则输出0
初始化:输入层节点为2,隐藏层节点为4,输出层节点为1,学习率0.8,误差阈值
train数据集样例,完整用例见traindata.txt
1 | 0 0 0 |
test数据集样例,完整用例见testdata.txt
1 | 0.111 0.112 |
实际输出
根据输出结果能看出,正确率为100%(前两个数字差的绝对值小于0.05,输出应该为0)在此简单分类的测试时,BP网络发挥很好
1 | 0.111 0.112 0.00887687 |
西瓜数据集测试
从网上取得小量西瓜数据集,来源https://gist.github.com/zhenghaoz/abb86b21797b6274b0fcb1dfc96274b2,根据西瓜的各项指标来判断是否是好瓜,好瓜输出1,否则输出0
需要将输入层节点修改为8,隐藏层节点修改为10
1 | 编号,色泽,根蒂,敲声,纹理,脐部,触感,密度,含糖率,好瓜 |
将上述数据简单转换,得到面的训练数据(traindata_g.txt)
1 | 1 1 1 1 1 1 0.697 0.460 1 |
下面是测试数据(testdata_g.txt)
1 | 3 1 2 2 2 1 0.227 0.160 |
实际输出
最后一项是输出,根据结果看出,输出正确率较高,接近100%
1 | Success with 122614 times training. |
完整代码
单隐藏层(main.cpp)
1 |
|
多隐藏层(multi_hidden_layers_version.cpp)
1 |
|