150 lines
4.0 KiB
C++
150 lines
4.0 KiB
C++
#pragma once
|
|
#include <random>
|
|
#include "nn/Matrix.hh"
|
|
#include "nn/ILayerFullyConnectted.hpp"
|
|
|
|
namespace qing {
|
|
|
|
/* 人工神经网络 - 全连接层 */
|
|
class NeuralNetwork: public ILayerFullyConnectted {
|
|
public:
|
|
|
|
/* 构造函数 使用基本参数构造
|
|
* FIXME: 只能输入已经存在的向量 */
|
|
NeuralNetwork(
|
|
const long inputno, const long outputno,
|
|
const std::vector<float> &weights,
|
|
const std::vector<float> &bias,
|
|
const float learning_rate,
|
|
const ActivationFunc func_type
|
|
);
|
|
|
|
|
|
/* 获取激活函数枚举类型 */
|
|
const ActivationFunc& get_f() const {
|
|
return f_type;
|
|
}
|
|
|
|
/* 获取学习率 */
|
|
const float& get_lr () const {
|
|
return learning_rate;
|
|
}
|
|
|
|
/*
|
|
* 获取输入层大小
|
|
*
|
|
* FIXME: 优化掉这两个访问器,通过直接返回矩阵,获取层的形状
|
|
*/
|
|
long get_inputno() const;
|
|
|
|
/* 获取输出层大小 */
|
|
long get_outputno() const;
|
|
|
|
|
|
/* 获取权重矩阵 */
|
|
Matrx<float>& get_weights();
|
|
|
|
/* 获取偏置矩阵 */
|
|
Matrx<float>& get_bias();
|
|
|
|
|
|
/* 正态分布Xavier初始化 */
|
|
static void xavierNormalInit(std::vector<float> &weights, long fan_in, long fan_out);
|
|
|
|
/* 均匀分布Xavier初始化函数 */
|
|
static void xavierUniformInit(std::vector<float> &weights, int fan_in, int fan_out);
|
|
|
|
/* 工厂模式 通过形状构建全连接层 */
|
|
static NeuralNetwork Create_in_Factory(const long inputno, const long outputno, float learning_rate, ActivationFunc func_type);
|
|
|
|
/* 工厂模式 通过输入流读形状构建 */
|
|
static NeuralNetwork Load_in_Factory(std::istream& in) {
|
|
std::string f_name;
|
|
if (!(in >> f_name))
|
|
throw std::runtime_error("read out of lines");
|
|
float learning_rate;
|
|
in >> learning_rate;
|
|
/*------------------*/
|
|
size_t row, col;
|
|
in >> row; /* FIXME: 如果是负数会怎样 */
|
|
in >> col;
|
|
auto weis = load_vec(in, row * col);
|
|
size_t rowb, colb;
|
|
in >> rowb >> colb; /* FIXME: 如果这里读错了,也要抛出异常 */
|
|
auto bias = load_vec(in, col);
|
|
auto layer = NeuralNetwork(
|
|
row, col, weis, bias, learning_rate, ILayer::parse_ff_type(f_name));
|
|
/*------------------*/
|
|
return layer;
|
|
}
|
|
|
|
static std::vector<float> load_vec(std::istream& in, size_t length) {
|
|
std::vector<float> data;
|
|
for (size_t i = 0; i < length; ++i) {
|
|
float item;
|
|
in >> item;
|
|
data.push_back(item);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
|
|
/* 前向反馈 */
|
|
std::vector<float> forward(const std::vector<float>& x) override;
|
|
|
|
/* 反向传播 */
|
|
std::vector<float> backward(const std::vector<float>& errors) override;
|
|
|
|
/*
|
|
* 更新权重
|
|
*
|
|
* 可以输入一个折扣系数
|
|
*/
|
|
void update(float discount = 1.0);
|
|
|
|
/*
|
|
* 神经网络层长大
|
|
*
|
|
* 重新初始化权重矩阵,使用均匀分布初始化
|
|
*
|
|
* num: 增加的神经元数量
|
|
* flag: true为增加输入层,false为增加输出层
|
|
*/
|
|
//void grow(int num, bool flag) {
|
|
|
|
/* 输出神经网络到输出流 */
|
|
void save(std::ostream& out) override {
|
|
out << get_f_type(f_type) << " ";
|
|
out << learning_rate << " ";
|
|
weights.save(out); out << " ";
|
|
bias.save(out); out << "\n";
|
|
}
|
|
|
|
/* 从输入流加载模型
|
|
* FIXME: 只能从工厂模式中构造 */
|
|
//void load(std::istream& in) override
|
|
|
|
|
|
private:
|
|
|
|
/* 神经网络层规格 */
|
|
long inputno, outputno;
|
|
|
|
/* 权重与偏置矩阵 */
|
|
Matrx<float> weights; /* (outputs, inputs) */
|
|
Matrx<float> bias; /* (1, outputs) */
|
|
|
|
/* 学习率 */
|
|
float learning_rate;
|
|
|
|
/* 激活函数 */
|
|
ActivationFunc f_type;
|
|
|
|
/* 存储在类内部的上一次的输入,输出,和激活前输出 */
|
|
Matrx<float> prev_inputs, prev_raw_inputs, prev_outputs;
|
|
/* 存储本次学习的权重梯度以及偏置梯度 */
|
|
Matrx<float> weights_grad, bias_grad;
|
|
|
|
};
|
|
}
|