自己实现一个神经网络算法库

本文介绍了一个轻型神经网络算法库的实现细节,涵盖了从神经元到神经网络的构建,包括反向传播算法的训练过程。该库支持多种激活函数,并提供了文件读写功能,便于模型的保存与加载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
	NeuralNet.cpp
	轻型神经网络算法库,实现监督学习算法之一——反向传播算法 
*/
#include <vector>
#include <map>
#include <cmath>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <exception>
using namespace std;
#ifndef NeuralNet_CPP
#define NeuralNet_CPP
namespace NeuralNetLibrary
{
	const static int LINEAR = 0x000000;
	const static int SIGMOID = 0x000001;
	const static int HYPERTAN = 0x000002;
	class NeuralException:virtual exception
	{
		private:
			string msg;
		public:
			NeuralException(string msg) throw()
			{
				this->msg = msg;
			}
			~NeuralException() throw(){}
			virtual const char* what() const throw()
			{
				return msg.c_str();
			}
	};
	class RandomDouble
	{
		public:
			void init()
			{
				srand(time(NULL));
			}
			double drand()
			{
				//返回一个介于 0*(10^-6) 和 1 之间的实数 
				return (rand()%(1000000-1)+1)/1000000.0;
			}
	};
	class DataSet
	{
		private:
			map<int,double> datas;
		public:
			double &operator[](int cur)
			{
				return datas[cur];
			}
	};
	class Neuron
	{
		private:
			vector<double> weights;
			int type;
			DataSet inputs;
			double sum;
			double out;
			double derivative;
			double sensibility;
		public:
			Neuron(int type,int numofweights,RandomDouble randomer)
			{
				this->type = type;
				for(int i = 0;i < numofweights;i++)
					weights.push_back(randomer.drand());
			}
			vector<double> &getWeights()
			{
				return weights;
			}
			double getSensibility()
			{
				return sensibility;
			}
			void setSensibility(double sensibility)
			{
				this->sensibility = sensibility;
			}
			double getSum()
			{
				return sum;
			}
			double getOut()
			{
				return out;
			}
			DataSet getInputs()
			{
				return inputs;
			}
			double getDerivative()
			{
				return derivative;
			}
			double calc(DataSet datas)
			{
				inputs = datas;
				sum = 0.0;
				for(int i = 0;i < weights.size();i++)
					sum += weights[i]*datas[i];
				switch(type)
				{
					case LINEAR:
						out = sum;
						derivative = 1;
						break;
					case SIGMOID:
						out = 1/(1+exp(sum));
						derivative = out*(1-out);
						break;
					case HYPERTAN:
						out = (1-exp(sum))/(1+exp(sum));
						derivative = 1-out*out;
						break;
				}
				return out;
			}
	};
	class Layer
	{
		private:
			vector<Neuron> neurons;
			int type;
		public:
			Layer(int type,int numofweights,RandomDouble randomer,int numofneurons)
			{
				this->type = type;
				for(int i = 0;i < numofweights;i++)
				{
					Neuron neuron(type,numofweights,randomer);
					neurons.push_back(neuron);
				}
			}
			int getType()
			{
				return type;
			}
			DataSet calc(DataSet datas)
			{
				DataSet ret;
				for(int i = 0;i < neurons.size();i++)
					ret[i] = neurons[i].calc(datas);
				return ret;
			}
			vector<Neuron> &getNeurons()
			{
				return neurons;
			}
	};
	class NeuralNet
	{
		private:
			vector<Layer> layers;
			bool inited;
			int numofinputs;
			RandomDouble randomer;
		public:
			NeuralNet(int numofinputs)
			{
				this->numofinputs = numofinputs;
				inited = false;
				randomer.init();
			}
			int getNumOfInputs()
			{
				return numofinputs;
			}
			void addLayer(int type,int numofneurons)
			{
				if(inited) throw NeuralException("NeuralNet:Failed to add a new layer:The neuralnet has been initialized");
				else
				{
					int numofweights;
					if(layers.empty()) numofweights = numofinputs;
					else numofweights = layers.back().getNeurons().size();
					Layer newlayer(type,numofweights,randomer,numofneurons);
					layers.push_back(newlayer);
				}
			}
			vector<Layer> &getLayers()
			{
				return layers;
			}
			void init()
			{
				if(layers.empty()) throw NeuralException("NeuralNet:Failed to initialize the neuralnet:No layers");
				else inited = true;
			}
			bool isinited()
			{
				return inited;
			}
			DataSet calc(DataSet datas)
			{
				if(!inited) throw NeuralException("NeuralNet:Failed to calculate:The neuralnet has not been initialized");
				else
				{
					for(int i = 0;i < layers.size();i++)
						datas = layers[i].calc(datas);
					return datas;
				}
			}
	};
	class TrainSet
	{
		private:
			DataSet inputs;
			DataSet outputs;
		public:
			void setIODataSet(DataSet inputs,DataSet outputs)
			{
				this->inputs = inputs;
				this->outputs = outputs;
			}
			DataSet &getInputDataSet()
			{
				return inputs;
			}
			DataSet &getOutputDataSet()
			{
				return outputs;
			}
	};
	class Trainer
	{
		private:
			vector<TrainSet> trainsets;
		public:
			virtual void train(NeuralNet &net) = 0;
			void addTrainSet(TrainSet trainset)
			{
				trainsets.push_back(trainset);
			}
			vector<TrainSet> &getTrainSet()
			{
				return trainsets;
			}
	};
	class BackPropagation:public virtual Trainer
	{
		private:
			double learningrate;
			double goalmse;
			int maxtimes;
		public:
			BackPropagation(double learningrate,double goalmse,int maxtimes)
			{
				this->learningrate = learningrate;
				this->goalmse = goalmse;
				this->maxtimes = maxtimes;
			}
			virtual void train(NeuralNet &net)
			{
				for(int times = 1;times <= maxtimes;times++){
					bool acess = true;
					for(int cur = 0;cur < getTrainSet().size();cur++)
					{
						DataSet nowinput = getTrainSet()[cur].getInputDataSet();
						DataSet nowoutput = getTrainSet()[cur].getOutputDataSet();
						//获取网络输出
						DataSet netout = net.calc(nowinput);
						double mse = 0.0;
						for(int i = 0;i < net.getLayers().back().getNeurons().size();i++)
							mse += sqrt(netout[i]-nowoutput[i]);
						mse /= net.getLayers().back().getNeurons().size();
						//检查MSE是否满足目标
						if(mse <= goalmse) continue;
						else acess = false;
						//计算灵敏度 
						for(int i = net.getLayers().size()-1;i >= 0;i--)
						{
							Layer &nowlayer = net.getLayers()[i];
							for(int j = 0;j < nowlayer.getNeurons().size();j++)
							{
								if(i == net.getLayers().size()-1)
									nowlayer.getNeurons()[j].setSensibility((nowoutput[j]-netout[j])*nowlayer.getNeurons()[j].getDerivative());
								else
								{
									double sensibility = 0.0;
									Layer nextlayer = net.getLayers()[i+1];
									for(int k = 0;k < nextlayer.getNeurons().size();k++)
									{
										Neuron nextneuron = nextlayer.getNeurons()[k];
										sensibility += nextneuron.getSensibility()*nextneuron.getWeights()[j];
									}
									sensibility *= nowlayer.getNeurons()[j].getDerivative();
									nowlayer.getNeurons()[j].setSensibility(sensibility);
								}
							}
						}
						//更新权值 
						for(int i = net.getLayers().size()-1;i >= 0;i--)
						{
							Layer &nowlayer = net.getLayers()[i];
							for(int j = 0;j < nowlayer.getNeurons().size();j++)
							{
								Neuron &nowneuron = nowlayer.getNeurons()[j];
								for(int k = 0; k <nowneuron.getWeights().size();k++)
									nowneuron.getWeights()[k] += learningrate*nowneuron.getSensibility()*nowneuron.getInputs()[k];
							}
						}
					}
				}
			}
	};
	typedef struct
	{
		int numofinputs;
		int numoflayers;
		int layeroff;
	}NEURALNET_HEAD;
	typedef struct
	{
		int type;
		int numofneurons;
		int neuronoff;
	}LAYER_HEAD;
	typedef struct
	{
		int numofweights;
		int weightoff;
	}NEURON_HEAD;
	class NeuralNetFile
	{
		private:
			string file;
		public:
			NeuralNetFile(string file)
			{
				this->file = file;
			}
			NeuralNet read()
			{
				FILE* f = fopen(file.c_str(),"r");
				fseek(f,0,SEEK_SET);
				NEURALNET_HEAD nnhdr;
				fread(&nnhdr,sizeof(NEURALNET_HEAD),1,f);
				NeuralNet net(nnhdr.numofinputs);
				for(int i = 0;i < nnhdr.numoflayers;i++)
				{
					fseek(f,nnhdr.layeroff+i*sizeof(LAYER_HEAD),SEEK_SET);
					LAYER_HEAD lhdr;
					fread(&lhdr,sizeof(LAYER_HEAD),1,f);
					net.addLayer(lhdr.type,lhdr.numofneurons);
					Layer &nowlayer = net.getLayers()[i];
					for(int j = 0;j < lhdr.numofneurons;j++)
					{
						fseek(f,lhdr.neuronoff+j*sizeof(NEURON_HEAD),SEEK_SET);
						NEURON_HEAD nhdr;
						fread(&nhdr,sizeof(NEURON_HEAD),1,f);
						Neuron &nowneuron = nowlayer.getNeurons()[j];
						for(int k = 0;k < nhdr.numofweights;k++)
						{
							fseek(f,nhdr.weightoff+k*sizeof(double),SEEK_SET);
							double weight;
							fread(&weight,sizeof(weight),1,f);
							nowneuron.getWeights()[k] = weight;
						}
					}
				}
				net.init();
				fclose(f);
				return net;
			}
			void write(NeuralNet net)
			{
				if(!net.isinited())
				{
					throw NeuralException("NeuralNet:Failed to calculate:The neuralnet has not been initialized");
					return;
				}
				FILE* f = fopen(file.c_str(),"w");
				int off = 0;
				NEURALNET_HEAD nnhdr;
				nnhdr.numofinputs = net.getNumOfInputs();
				nnhdr.numoflayers = net.getLayers().size();
				off += sizeof(NEURALNET_HEAD);
				nnhdr.layeroff = off;
				fwrite(&nnhdr,sizeof(NEURALNET_HEAD),1,f);
				off += sizeof(LAYER_HEAD)*nnhdr.numoflayers;
				for(int i = 0;i < nnhdr.numoflayers;i++)
				{
					LAYER_HEAD lhdr;
					lhdr.type = net.getLayers()[i].getType();
					lhdr.numofneurons = net.getLayers()[i].getNeurons().size();
					lhdr.neuronoff = off;
					fwrite(&lhdr,sizeof(LAYER_HEAD),1,f);
					off += sizeof(NEURON_HEAD)*lhdr.numofneurons;
				}
				for(int i = 0;i < nnhdr.numoflayers;i++)
				{
					Layer nowlayer = net.getLayers()[i];
					int numofneurons = nowlayer.getNeurons().size();
					for(int j = 0;j < numofneurons;j++)
					{
						Neuron nowneuron = nowlayer.getNeurons()[j];
						NEURON_HEAD nhdr;
						nhdr.numofweights = nowneuron.getWeights().size();
						nhdr.weightoff = off;
						fwrite(&nhdr,sizeof(NEURON_HEAD),1,f);
						off += sizeof(double)*nhdr.numofweights;
					}
				}
				for(int i = 0;i < nnhdr.numoflayers;i++)
				{
					Layer nowlayer = net.getLayers()[i];
					int numofneurons = nowlayer.getNeurons().size();
					for(int j = 0;j < numofneurons;j++)
					{
						Neuron nowneuron = nowlayer.getNeurons()[j];
						int numofweights = nowneuron.getWeights().size();
						for(int k = 0;k < numofweights;k++)
							fwrite(&nowneuron.getWeights()[k],sizeof(double),1,f);
					}
				}
				fclose(f);
			}
	};
}
#endif
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值