#include "nn/NeuralNetwork.hpp" namespace qing{ /* 构造函数 使用基本参数构造 */ NeuralNetwork::NeuralNetwork( const long inputno, const long outputno, const std::vector &weights, const std::vector &bias, const float learning_rate, const ActivationFunc func_type ): inputno(inputno), outputno(outputno), weights(inputno, outputno, weights), bias( 1, outputno, bias), learning_rate(learning_rate), f_type(func_type) { ;/* 进行形状检查 */ } /* 获取输入层大小 */ long NeuralNetwork::get_inputno() const { return this->inputno; } /* 获取输出层大小 */ long NeuralNetwork::get_outputno() const { return this->outputno; } /* 获取权重 */ Matrx& NeuralNetwork::get_weights() { return this->weights; } /* 获取偏置 */ Matrx& NeuralNetwork::get_bias() { return this->bias; } /* 正态分布Xavier初始化 */ void NeuralNetwork::xavierNormalInit(std::vector &weights, long fan_in, long fan_out) { /* 高质量的随机数生成器 */ static std::mt19937 generator(std::random_device{}()); /* 计算标准差 */ float sigma = std::sqrt(2.0f / (fan_in + fan_out)); /* 创建正态分布对象,均值为0,标准差为sigma */ std::normal_distribution distribution(0.0f, sigma); /* 遍历权重矩阵,进行赋值 */ long k=0; for (long i=0; i &weights, int fan_in, int fan_out) { /* 使用高质量的随机数生成器 */ static std::mt19937 generator(std::random_device{}()); /* 计算区间范围:sqrt(2 / (fan_in + fan_out)) */ float scale = std::sqrt(2.0f / (fan_in + fan_out)); /* 创建均匀分布对象,范围为[-scale, scale] */ std::uniform_real_distribution distribution(-scale, scale); /* 遍历权重矩阵,进行赋值 */ long k=0; for (long i=0; i weights(inputno * outputno); xavierUniformInit(weights, inputno, outputno); std::vector bias(outputno); NeuralNetwork layer(inputno, outputno, weights, bias, learning_rate, func_type); return layer; /* FIXME: 使用移动语义 */ } /* 前向反馈 */ std::vector NeuralNetwork::forward(const std::vector& x) { /* * 矩阵乘法(点积) * wi => weights inputs(column) * wo => weights outputs(row) * * NOTE: 通常矩阵乘法是三层循环,这里我们用两层 */ //std::vector res(this->outputno); /* (1, outputs) */ //for (long i=0; ioutputno; ++i) { // long wo = i; // float sum=0.0; // for (long j=0, wi=wo; jinputno; ++j, wi+=this->outputno) { // sum += x[j] * this->weights[wi]; // } // res[i]= sum; //} Matrxmx{1, this->inputno, x}; /* * 为输入进行备份 * * FIXME: 使用移动语义传入参数,并且储存 */ this->prev_inputs = mx; /* (1, inputs) */ Matrxr = mx * this->weights; /* (1, outputs) */ /* 偏置操作 */ //for (long i=0; ioutputno; ++i) { // res[i]+=this->bias[i]; //} r = r + this->bias; /* * 为原始计算结果进行备份 */ this->prev_raw_inputs = r; /* * 使用ReLU激活函数 * * FIXME: 建立一个算法类 */ f_t f = getf(f_type); std::vector vec =r.get_data(); for (long i=0; ioutputno; ++i) { float& v = vec[i]; v = f(v); } r = Matrx(1, outputno, vec); /* * 为输出进行备份 */ this->prev_outputs = r; /* 返回前向反馈的结果。 (1, outputs) */ return r.get_data(); } /* 反向传播 */ std::vector NeuralNetwork::backward(const std::vector& errors) /* errors => (outputs, 1) */ { /* 匹配激活导数 */ f_t fdot = getfdot(f_type); std::vector vec = this->prev_raw_inputs.get_data(); /* 将原始输出转化为导数 */ for (long i=0; ioutputno; ++i) { float& v = vec[i]; v = errors[i] * fdot(v); } /* 误差乘以导数 */ //std::vector first(this->outputno); //for (long i=0; ioutputno; ++i) { // first[i] = errors[i] * this->prev_raw_inputs[i]; //} Matrx first{1, this->outputno, vec}; /* 更新权重梯度 => (errors * 导数) @ this->inputs => (1, outputs ) @ (inputs, 1) = (outputs, inputs) */ //this->weights_grad = std::vector(this->outputno * this->inputno); //long k = 0; //for (long i=0; iinputno; ++i) { // for (long j=0; joutputno; ++j) { // this->weights_grad[k++] = first[j] * this->prev_inputs[i]; // } //} Matrx mx {this->inputno, 1, this->prev_inputs.get_data()}; /* (inputs, 1) */ this->weights_grad = mx * first; /* (inputs, 1) @ (1, outputs) => (inputs, outputs) */ /* * 更新偏置梯度 * * NOTE: 在多批次情况下,应该沿着每一样本纵向求平均数 */ //this->bias_grad = std::vector(this->outputno); //for (long i=0; ioutputno; ++i) { // this->bias_grad[i] = first[i]; //} this->bias_grad = first; /* * 计算上一层的误差责任分担,然后返回 * * this->weights @ first => (outputs, inputs) @ (1, outputs) = (inputs, 1) */ //std::vector uplevel_errors(this->inputno); //for (long i=0; iinputno; ++i) { // long wii = i * this->outputno; /* 每一列的起始点 */ // float sum = 0.0f; // for (long j=0, wi=wii; j < this->outputno; ++j, ++wi) { // sum += this->weights[wi] * first[j]; // } // uplevel_errors[i] = sum; //} Matrx first1{this->outputno, 1, first.get_data()}; Matrx uplevel_errs = this->weights * first1; /* (inputs, outputs) @ (outputs, 1) */ return uplevel_errs.get_data(); /* 使用移动语义返回上一层误差 */ //return std::move(uplevel_errors); } void NeuralNetwork::update(float discount) { this->weights = this->weights + (this->weights_grad * this->learning_rate * discount); this->bias = this->bias + (this->bias_grad * this->learning_rate * discount); } }