为了做到老妪能训练,老妪能编程……
使用现代C++构建的前向、反向传播神经网络的例子。构建一个具有任意层数和每层任意结点数的全连接神经网络(backpropagation Neural Network)。这个例子也包括了基本的梯度下降优化,要求实现异或xor的神经网络,要求输入数据有四组{1,0}、{0,1}、{1,1}、{0,0},训练目标数据target也有四个{1}、{1}、{0}、{0}……要求每次train训练结束以后将weights权重矩阵保存到文本文件,以供下次添加其它训练数据后,使用上次训练的权重矩阵接着训练!
// modern异或c++ArbitraryLayerBackpropagationNN调整权重矩阵230830a.cpp :
//使用现代C++构建的前向、反向传播神经网络的例子。构建一个具有任意层数和每层任意结点数的全连接神经网络(backpropagation Neural Network)。这个例子也包括了基本的梯度下降优化,要求实现异或xor的神经网络,要求输入数据有四组{1,0}、{0,1}、{1,1}、{0,0},训练目标数据target也有四个{1}、{1}、{0}、{0}……要求每次train训练结束以后将weights权重矩阵保存到文本文件,以供下次添加其它训练数据后,使用上次训练的权重矩阵接着训练!要求增加1个菜单,输入4则初始化权重矩阵! modern异或c++ArbitraryLayerBackpropagationNN调整权重矩阵230830a.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include
#include
//#include
#include
#include
#include //std::to_string
#pragma warning(disable : 4996)
using namespace std;
class NeuralNetwork {
//private:
public:
vector>> weights;
vector> layers;
vector> deltas;
public:
NeuralNetwork(vector topology) {
random_device rd;
mt19937 gen(rd());
normal_distribution<> d001(0.0, 1.0);
// Initialize weights
for (int i = 1; i < topology.size(); ++i) {
vector> layer_weights(topology[i], vector(topology[i - 1]));
for (auto& neuron_weights : layer_weights) {
for (auto& weight : neuron_weights) {
weight = d001(gen);
}
}
weights.push_back(layer_weights);
}
// Initialize layers and deltas
for (int neurons : topology) {
layers.push_back(vector(neurons, 0.0));
deltas.push_back(vector(neurons, 0.0));
}
}
//-----------------------------------------------------------------
// 初始化网络权重和偏置
std::vector>> initialize_weights(const std::vector& layers) {
std::vector>> weights;
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(0.0, 1.0);
for (size_t i = 1; i < layers.size(); ++i) {
std::vector> layer_weights;
for (int j = 0; j < layers[i]; ++j) {
std::vector neuron_weights;
for (int k = 0; k < layers[i - 1]; ++k) {
neuron_weights.push_back(dis(gen));
}
layer_weights.push_back(neuron_weights);
}
weights.push_back(layer_weights);
}
return weights;
}
//===========================================================
void feedForward(vector input) {
layers[0] = input;
for (int i = 1; i < layers.size(); ++i) {
for (int j = 0; j < layers[i].size(); ++j) {
layers[i][j] = 0;
for (int k = 0; k < layers[i - 1].size(); ++k) {
layers[i][j] += layers[i - 1][k] * weights[i - 1][j][k];
}
layers[i][j] = 1 / (1 + exp(-layers[i][j]));
}
}
}//void feedForward(
void feedForwarOut(vector input) {
layers[0] = input;
for (int i = 1; i < layers.size(); ++i) {
for (int j = 0; j < layers[i].size(); ++j) {
layers[i][j] = 0;
for (int k = 0; k < layers[i - 1].size(); ++k) {
layers[i][j] += layers[i - 1][k] * weights[i - 1][j][k];
}
layers[i][j] = 1 / (1 + exp(-layers[i][j]));
cout << layers[i][j];
cout << "]," << endl;
}
cout << "}; " << endl;
cout << endl;
}
}//void feedForwarOut(
//----------------------------------------------------------------------
void backPropagate(vector target) {
//计算损失值?
for (int i = 0; i < layers.back().size(); ++i) {
double output = layers.back()[i];
deltas.back()[i] = output * (1 - output) * (target[i] - output);
}
//反向传播
for (int i = layers.size() - 2; i > 0; --i) {
for (int j = 0; j < layers[i].size(); ++j) {
double sum = 0;
for (int k = 0; k < layers[i + 1].size(); ++k) {
sum += deltas[i + 1][k] * weights[i][k][j];
}
deltas[i][j] = layers[i][j] * (1 - layers[i][j]) * sum; //激活函数导函数==简称 导激活函数
}
}
for (int i = 0; i < weights.size(); ++i) {
for (int j = 0; j < weights[i].size(); ++j) {
for (int k = 0; k < weights[i][j].size(); ++k) {
//weights[i][j][k] += 0.5 * deltas[i + 1][j] * layers[i][k];
weights[i][j][k] += 0.05 * deltas[i + 1][j] * layers[i][k]; //学习率 LearningRate
}
}
}
}
void train(vector> inputs, vector> targets, int epochs) {
for (int i = 0; i < epochs; ++i) {
for (int j = 0; j < inputs.size(); ++j) {
feedForward(inputs[j]);
backPropagate(targets[j]);
}
}
}
void saveWeights(const string& filename) {
ofstream file(filename);
if (file.is_open()) {
for (const auto& layer : weights) {
for (const auto& neuron : layer) {
for (const auto& weight : neuron) {
file << weight << " ";
}
file << endl;
}
}
file.close();
}
}
void loadWeights(const string& filename) {
ifstream file(filename);
if (file.is_open()) {
for (auto& layer : weights) {
for (auto& neuron : layer) {
for (auto& weight : neuron) {
file >> weight;
}
}
}
file.close();
}
}
};
std::string getCurrentTimeStamp() {
std::time_t t = std::time(nullptr); // 获取当前时间
std::tm tm = *std::localtime(&t); // 将 time_t 转换为 tm 结构体
char buffer[20];
// 格式化时间字符串为 "yyMMddHHmmss" 格式
std::strftime(buffer, sizeof(buffer), "%y%m%d%H%M%S", &tm);
return std::string(buffer);
}
int main() {
vector> inputs = { {1, 1}, {0, 0}, {1, 0}, {0, 1} };
vector> targets = { {0}, {0}, {1}, {1} };
vector>inputs22= { {1, 1}, {0, 1}, {1, 0}, {0, 0} };
NeuralNetwork nn({ 2, 6,6,4, 1 }); //网络结构
string weightFile = "\/weights220101.txt";
int choice;
for (int ii = 0; true; ++ii) {//for110ii
cout << "Menu: \n";
cout << "1: (调用上次weights)并继续训练,并保存weights!Load and continue training, then save weights\n";
cout << "2:(调用上次weights)继续训练,且不保存weights!\n";
cout << "3:不训练,只保存weights!\n";
cout << "4: 初始化weights矩阵!Initialize new weights and train, then save weights\n";
cout << "5:保存weights到 特定文件(带时间戳的)!\n";
cout << "Enter your choice: ";
cin >> choice;
if (choice == 1) {
nn.loadWeights(weightFile);
}
cout << "Training..." << endl;
nn.train(inputs, targets, 5000);// 10000);
if (5==choice||choice == 1 || choice == 4) {
nn.saveWeights(weightFile);
cout << "Weights saved to " << weightFile << endl;
}
if (4 == choice) {
nn.weights = nn.initialize_weights({ 2, 6,6,4, 1 }); 网络结构
}//
//-----------------------------------------------------
inputs22[0] = { 1,1 };
cout << "[1,1:_ ";
nn.feedForwarOut(inputs22[0]);
inputs22[1] = { 0,0 };
cout << "[0,0:_ ";
nn.feedForwarOut(inputs22[1]);
inputs22[2] = { 1,0 };
cout << "[1,0:_ ";
nn.feedForwarOut(inputs22[2]);
// inputs22[3] = { 0,1 };
cout << "[1,1:_ ";
nn.feedForwarOut(inputs22[3]);
cout << endl;
if (5 == choice) {
time_t now = time(0);
tm* ltm = localtime(&now);
std::string filename02 = "\/weights_" + getCurrentTimeStamp() + std::to_string(ltm->tm_mday) + ".txt";
nn.saveWeights( filename02);
}//if(5==choice
//======================================================
}//for110i
return 0;
}//main(