如何从AI神经网络层(输入、隐藏、输出)理论到C++代码实现
实现C++神经网络层的核心思路与代码示例
神经网络的层核心要完成**前向传播(输入x转输出y)和反向传播(更新参数)**两大功能,通常先定义抽象基类统一接口,再针对不同层类型实现具体子类。
核心函数与结构
所有层必备的核心要素:
forward(const Eigen::VectorXd& x):输入张量转输出的核心函数,是你需要的x到y的计算逻辑载体。backward(const Eigen::VectorXd& dy):接收上层传来的梯度,计算当前层参数梯度和输入梯度,用于反向更新参数。- 成员变量:存储可训练参数(如权重、偏置)和前向传播的中间输入(用于反向计算)。
基础Layer抽象类实现
#include <Eigen/Dense> class Layer { public: // 纯虚前向传播函数,子类必须实现 virtual Eigen::VectorXd forward(const Eigen::VectorXd& x) = 0; // 纯虚反向传播函数 virtual Eigen::VectorXd backward(const Eigen::VectorXd& dy) = 0; // 虚析构函数,确保子类正确析构 virtual ~Layer() = default; };
具体层示例:全连接层(Dense Layer)
全连接层的前向公式为 y = W*x + b,其中W是权重矩阵,b是偏置向量。
class DenseLayer : public Layer { private: Eigen::MatrixXd weights; // 权重矩阵,形状[输出维度, 输入维度] Eigen::VectorXd bias; // 偏置向量,形状[输出维度] Eigen::VectorXd input; // 存储前向输入,用于反向计算 public: // 构造函数:初始化维度并随机初始化参数 DenseLayer(int input_dim, int output_dim) { // He初始化权重,缓解梯度消失 weights = Eigen::MatrixXd::Random(output_dim, input_dim) * sqrt(2.0 / input_dim); bias = Eigen::VectorXd::Zero(output_dim); } Eigen::VectorXd forward(const Eigen::VectorXd& x) override { input = x; return weights * x + bias; } Eigen::VectorXd backward(const Eigen::VectorXd& dy) override { // 计算权重和偏置的梯度 Eigen::MatrixXd d_weights = dy * input.transpose(); Eigen::VectorXd d_bias = dy; // 计算输入梯度,传给上一层(反向传播的上游) Eigen::VectorXd dx = weights.transpose() * dy; // 简单SGD更新参数,学习率0.01 weights -= 0.01 * d_weights; bias -= 0.01 * d_bias; return dx; } };
具体层示例:ReLU激活层
激活层无训练参数,前向逻辑为y = max(0, x),反向逻辑为dy * (x > 0 ? 1 : 0)。
class ReLULayer : public Layer { private: Eigen::VectorXd input; // 保存输入用于反向计算 public: Eigen::VectorXd forward(const Eigen::VectorXd& x) override { input = x; return x.unaryExpr([](double val) { return std::max(0.0, val); }); } Eigen::VectorXd backward(const Eigen::VectorXd& dy) override { Eigen::VectorXd dx = dy; for (int i = 0; i < input.size(); ++i) { if (input(i) <= 0) { dx(i) = 0; } } return dx; } };
使用示例
#include <iostream> int main() { // 创建输入维度2、输出维度3的全连接层 DenseLayer dense(2, 3); // 创建ReLU激活层 ReLULayer relu; Eigen::VectorXd input(2); input << 1.0, 2.0; // 前向传播 Eigen::VectorXd dense_output = dense.forward(input); Eigen::VectorXd relu_output = relu.forward(dense_output); std::cout << "输入: " << input.transpose() << std::endl; std::cout << "全连接层输出: " << dense_output.transpose() << std::endl; std::cout << "ReLU输出: " << relu_output.transpose() << std::endl; // 模拟反向传播(假设上层传来的梯度为全1) Eigen::VectorXd dy(3); dy << 1.0, 1.0, 1.0; Eigen::VectorXd relu_dx = relu.backward(dy); Eigen::VectorXd dense_dx = dense.backward(relu_dx); std::cout << "输入的梯度: " << dense_dx.transpose() << std::endl; return 0; }
关键说明
- 用Eigen库简化矩阵向量运算,避免手动实现复杂线性代数逻辑。
- 抽象基类保证所有层可统一调用,新增层只需继承基类并实现
forward和backward。 - 若仅做推理(不训练),可只保留
forward函数。
更新:感谢各位的指导,我已记录并回复了几乎所有建议,今晚晚些时候会展示相关进展。
内容的提问来源于stack exchange,提问作者Wasthisit
相关产品推荐
相关产品推荐

