本项目是对项目 GavinTechStudio/bpnn_with_cpp 的代码重构,基于C++实现基础BP神经网络,有助于深入理解BP神经网络原理。
.
├── CMakeLists.txt
├── data
│ ├── testdata.txt
│ └── traindata.txt
├── lib
│ ├── Config.h
│ ├── Net.cpp
│ ├── Net.h
│ ├── Utils.cpp
│ └── Utils.h
└── main.cpp
- Net:网络具体实现
- Config:网络参数设置
- Utils:工具类
- 数据加载
- 激活函数
- main:网络具体应用
具体公式推导请看视频讲解彻底搞懂BP神经网络 理论推导+代码实现(C++)
- 如果您使用的是Chrome、Edge、Firefox等浏览器,可以安装插件MathJax Plugin for Github(需要网络能够访问chrome web store)。
- 使用PDF的方式进行阅读。
- 使用预渲染的静态网页进行阅读(推荐)。
- 按
.
键或点击链接进入GitHub在线IDE预览README.md
文件。
其中$h_j$为第$j$个隐藏层节点的值,$x_i$为第$i$个输入层节点的值,$w_{ij}$为第$i$个输入层节点到第$j$个隐藏层节点的权重,$\beta_j$为第$j$个隐藏层节点偏置值,$\sigma(x)$为Sigmoid激活函数,后续也将继续用这个表达,其表达式如下 $$ \sigma(x) = \frac{1}{1+e^{-x}} $$ 本项目中的代码实现如下:
for (size_t j = 0; j < Config::HIDENODE; ++j) {
double sum = 0;
for (size_t i = 0; i < Config::INNODE; ++i) {
sum += inputLayer[i]->value *
inputLayer[i]->weight[j];
}
sum -= hiddenLayer[j]->bias;
hiddenLayer[j]->value = Utils::sigmoid(sum);
}
其中$\hat{y_k}$为第$k$个输出层节点的值(预测值),$h_j$为第$j$个隐藏层节点的值,$v_{jk}$为第$j$个隐藏层节点到第$k$个输出层节点的权重,$\lambda_k$为第$k$个输出层节点的偏置值,$\sigma(x)$为激活函数。
本项目中的代码实现如下:
for (size_t k = 0; k < Config::OUTNODE; ++k) {
double sum = 0;
for (size_t j = 0; j < Config::HIDENODE; ++j) {
sum += hiddenLayer[j]->value *
hiddenLayer[j]->weight[k];
}
sum -= outputLayer[k]->bias;
outputLayer[k]->value = Utils::sigmoid(sum);
}
损失函数定义如下:
其中$y_k$为第$k$个输出层节点的目标值(真实值),$\hat{y_k}$为第$k$个输出层节点的值(预测值)。
本项目中的代码实现如下:
double loss = 0.f;
for (size_t k = 0; k < Config::OUTNODE; ++k) {
double tmp = std::fabs(outputLayer[k]->value - label[k]);
los += tmp * tmp / 2;
}
利用梯度下降法进行优化。
其计算公式如下(激活函数为Sigmoid时):
其中$\eta$为学习率(其余变量上方已出现过不再进行标注)。
本项目中的代码实现如下:
for (size_t k = 0; k < Config::OUTNODE; ++k) {
double bias_delta =
-(label[k] - outputLayer[k]->value) *
outputLayer[k]->value *
(1.0 - outputLayer[k]->value);
outputLayer[k]->bias_delta += bias_delta;
}
其计算公式如下(激活函数为Sigmoid时):
其中$h_j$为第$j$个隐藏层节点的值(其余变量上方已出现过不再进行标注)。
本项目中的代码实现如下:
for (size_t j = 0; j < Config::HIDENODE; ++j) {
for (size_t k = 0; k < Config::OUTNODE; ++k) {
double weight_delta =
(label[k] - outputLayer[k]->value) *
outputLayer[k]->value *
(1.0 - outputLayer[k]->value) *
hiddenLayer[j]->value;
hiddenLayer[j]->weight_delta[k] += weight_delta;
}
}
其计算公式如下(激活函数为Sigmoid时):
其中$v_{jk}$为第$j$个隐藏层节点到第$k$个输出层节点的权重(其余变量上方已出现过不再进行标注)。
本项目中的代码实现如下:
for (size_t j = 0; j < Config::HIDENODE; ++j) {
double bias_delta = 0.f;
for (size_t k = 0; k < Config::OUTNODE; ++k) {
bias_delta +=
-(label[k] - outputLayer[k]->value) *
outputLayer[k]->value *
(1.0 - outputLayer[k]->value) *
hiddenLayer[j]->weight[k];
}
bias_delta *=
hiddenLayer[j]->value *
(1.0 - hiddenLayer[j]->value);
hiddenLayer[j]->bias_delta += bias_delta;
}
其计算公式如下(激活函数为Sigmoid时):
其中$x_i$为第$i$个输入层节点的值(其余变量上方已出现过不再进行标注)。
本项目中的代码实现如下:
for (size_t i = 0; i < Config::INNODE; ++i) {
for (size_t j = 0; j < Config::HIDENODE; ++j) {
double weight_delta = 0.f;
for (size_t k = 0; k < Config::OUTNODE; ++k) {
weight_delta +=
(label[k] - outputLayer[k]->value) *
outputLayer[k]->value *
(1.0 - outputLayer[k]->value) *
hiddenLayer[j]->weight[k];
}
weight_delta *=
hiddenLayer[j]->value *
(1.0 - hiddenLayer[j]->value) *
inputLayer[i]->value;
inputLayer[i]->weight_delta[j] += weight_delta;
}
}