本文主要详解BP神经网络编程实现,旨在一步一步解析BP神经网络细节,希望能形象明了的阐述BP神经网络,实现原理源自于斯坦福UFLDL教程,原理公式推导不再赘述,但会有些说明,本文程序由C++11实现,矩阵计算基于Eigen3(不熟悉的可以去网上搜索Eigen的使用方法,本文不做叙述),那么我们开始吧!
为了给算法列一个提纲,首先截一个UFLDL教程上关于BP算法的步骤,做个引导:
下面是神经网络一次批量迭代的过程:
为了形象的表明这里的参数含义,请看下图列举了一个[4, 4, 1]的神经网络结构(字和图很矬,请见谅,另外图中只绘制了单个样本下的情况,届时代码里会体现向量化的方式,神经网络的输入就是很多样本列向量组成的矩阵,而不是这里的单列向量了):
那么为了实现这个[4, 4, 1]的神经网络,我们需要在代码里定义来描述这个结构,下面是神经网络类的定义:
class NeuralNetwork
{
public:
NeuralNetwork(std::vector<int> _architecture
, const Eigen::MatrixXd _train_dataX
, const Eigen::MatrixXd _train_LabelY
, double _learning_rate = 0.5
, int _mini_batch_size = 30
, int _iteration_size = 1000
, double _lambda = 0.0);
// 执行训练操作
void train();
// 求解单个样本输出
Eigen::MatrixXd predict(const Eigen::MatrixXd &_input);
// 评估模型, 默认label为one_hot编码
double evaluate(const Eigen::MatrixXd &_test_dataX, const Eigen::MatrixXd &_test_dataY, bool one_hot = true);
private:
// _z为上层输出线性组合值:[z1, z2, z3, ...], 例如z1为一个样本的计算值
Eigen::MatrixXd sigmoid(const Eigen::MatrixXd &_z);
// 激励函数梯度计算,_a为激励函数输出值
Eigen::MatrixXd sigmoid_grad(const Eigen::MatrixXd &_a);
// 损失函数实现
double loss(const Eigen::MatrixXd &pre_y, const Eigen::MatrixXd &ori_y, int m);
// 前向传播, _x为样本矩阵[x1, x2, x3,...], 例如x1为一个样本
Eigen::MatrixXd feedforword(const Eigen::MatrixXd &_x);
// 反向传播, _x为训练样本,_y为样本与输出
void backforward(const Eigen::MatrixXd &_x, const Eigen::MatrixXd &_y);
// 得到列向量的最大值行号
int argmax(const Eigen::MatrixXd &_y);
// 返回将列向量_bias复制_m列的矩阵
Eigen::MatrixXd replicate(const Eigen::MatrixXd &_bias, int _m);
private:
std::vector<int> architecture; // 神经网络的结构(4, 4, 1) 表示有一个input layer(4个神经元, 和输入数据的维度一致),
//一个hidden layer(4个神经元), 一个output layer(1个神经元)
const Eigen::MatrixXd train_dataX; // 训练数据(n, m) 表示有m个训练样本, 每个样本是n维向量
const Eigen::MatrixXd train_dataY; // 训练数据label
std::vector<Eigen::MatrixXd> train_weights; // 训练权重
std::vector<Eigen::MatrixXd> train_weights_grad;// 权重梯度
std::vector<Eigen::MatrixXd> train_bias; // 训练偏置
std::vector<Eigen::MatrixXd> train_bias_grad; // 偏置梯度
std::vector<Eigen::MatrixXd> feedforword_a; // 前向传播得到的激活值的中间输出
std::vector<Eigen::MatrixXd> error_term; // 残差
std::vector<Eigen::MatrixXd> predict_a; // 预测中间值, 用于单个样本的预测输出
double learning_rate; // 反向传播学习率
double lambda;