机器学习BP神经网络,任意拓扑结构 (C++)

这次的版本更优秀了的样子!

按照老板说的,每个节点是单独的导出节点(会导致sigmod训练变慢,原因sigmod层数多了,梯度会下降很快导致爆炸。)


换个激活函数就行了。

net10表示网咯有10个节点

然后input.txt里的文件格式大概是

n

0 1

1 2

3 4

....

n表示有n行,每行2个数字,表示2个点有边。(0下标开始,不超过net初始化的节点数量)。任意拓扑结构都可以运行。


#include bits/stdc++.h
#include "recordlog.h"
#include memory

using std::cin;
using std::endl;
using std::cout;
#define pr(x)	cout#x" = "x" "
#define prln(x)	cout#x" = "xendl


#define NODE		(neurons[node])
#define NODE_GAIN	(NODE.energy)
#define NODE_THETA	(NODE.loss_energy)			//node节点的theta,也就是阈值
#define NODE_VALUE	(NODE_GAIN + NODE_THETA)	//node节点的实际能量(获得能量+theta)
#define NODE_OUTPUT 	(NODE.output)				//node节点的实际输出
#define NODE_PE		(NODE.partial_derivative)		//node节点output,对最终答案的导数
#define D_NODE		(derivative(NODE_VALUE))		//node节点获得的所有能量(加过theta的),对于node节点output的导数



#define NEXT_NODE		(neurons[nextnode.first])
#define NEXT_NODE_OUTPUT 	(NEXT_NODE.output)				//node节点的实际输出
#define NEXT_NODE_GAIN		(NEXT_NODE.energy)
#define NEXT_NODE_PE		(NEXT_NODE.partial_derivative)			//node节点output,对最终答案的导数
#define NEXT_NODE_THETA		(NEXT_NODE.loss_energy)				//node节点的theta,也就是阈值
#define NEXT_NODE_VALUE		(NEXT_NODE_GAIN + NEXT_NODE_THETA)	//node节点的实际能量(获得能量+theta)
#define D_NEXT_NODE		(derivative(NEXT_NODE_VALUE))			//node节点获得的所有能量(加过theta的),对于node节点output的导数
#define NODE_TO_NEXTNODE_WEIGHT (nextnode.second)

class neuron_t;
typedef std::pairint, double 		PID;
typedef std::vectorPID 		neuron_array_t;
typedef std::vectorint	  	vector_map_t;
typedef std::unique_ptrneuron_t	neuron_ptr_t;

class neuron_t
{
	public:
		double 			energy;
		double			output;
		int 			number;
		double 			loss_energy;
		neuron_array_t 		neuron_array;
		double			partial_derivative;
		bool			is_input;
		bool			is_output;
};

templateint neuron_size
class net_t
{
	public:
		neuron_t		neurons[neuron_size];
		vector_map_t		vector_map[neuron_size];
		std::string		activation_way;			//激活函数的选择,默认ReLU

		std::vectordouble	input_weight;

		std::vectorint 	output_number;
		std::vectorint 	input_number;
		int 			tmp[neuron_size];		//临时数组,生成过n的全排列,和拓扑排序中记录入度。
		int 			height[neuron_size];		//辅助构图的高度数组
		int			topology[neuron_size];		//拓扑序
		double			rate;				//学习率
		int			userful_neuron_size;

		static double sigmoid(double x)
		{
			return 1.0/(1.0 + exp(-x));
		}

		static double line(double x)
		{
			return x;
		}

		static double ReLU(double x)
		{
			if (x=0)	return 0;
			return x;
		}

		double derivative(double x)
		{
			if (activation_way == "sigmoid"){
				return sigmoid(x) * (1 - sigmoid(x));
			}
			if (activation_way == "ReLU"){
				if (x0)	return 0;
				return 1;
			}
			if (activation_way == "line"){
				return 1;
			}
			cout"no activationFunction!"endl;
			return 0;
		
		}

		double activationFunction(double sum, double theta)
		{
			if (activation_way == "sigmoid"){
				return sigmoid(sum + theta);
			}
			if (activation_way == "ReLU"){
				return ReLU(sum + theta);
			}
			if (activation_way == "line"){	
				return line(sum + theta);
			}
			cout"no activationWay !"  endl;
			return 0;
		}

		static double randomDouble(double l, double r)
		{
			return randomInt(l*10000, r * 10000)/10000.0;
		}

		static long long randomInt(long long L, long long R)
		{
			long long tmp = (unsigned long long)rand()
				*(unsigned long long)rand()
				*(unsigned  long long)rand()
				*(unsigned long long)rand() % (R - L + 1);
			return L + tmp;
		}

		~net_t()
		{
		}


		net_t (std::string file_name)
		{
			//初始化新的网络
			FILE *file = fopen(file_name.c_str(), "r");
			printf("[%s]\n", file_name.c_str());
			int n;
			fscanf(file, "%d", n);
			this - activation_way = "sigmoid";
			this - rate = 0.1;	//xuexilv
			for (int i = 0; i  neuron_size; ++ i){
				vector_map[i].clear();
				tmp[i] = i;
				neurons[i].number = i;
				neurons[i].is_input = false;
				neurons[i].is_output = false;
			}
			this - output_number.clear();
			this - input_number.clear();
			prln(neuron_size);
			while (n--){
				int s, t;
				fscanf(file, "%d%d", s, t);
				couts" "tendl;
				vector_map[s].push_back(t);
				vector_map[t].push_back(s);
			}
			fclose(file);
		}

		net_t()
		{
			*this = net_t("input.txt");
		}

		void initInputNeuron(std::vectorint input_num){
			int sz = input_num.size();
			input_weight.resize(sz);
			for (int i = 0; i  sz; ++ i){
				input_weight[i] = randomDouble(-1, 1);
				neurons[input_num[i]].is_input = true;
			}
		}

		void setIO(std::vectordouble input, std::vectordouble output, std::vectorint *input_num = NULL, std::vectorint *output_num = NULL){
			if (input.size() == 0){
				//throws something TODO
				return;
			}
			if (output.size() == 0){
				//throws something TODO
				return;
			}

			if (input_num  output_num)
			{
				output_number = *output_num;
				input_number = *input_num;
			}
			else
			{
				std::random_shuffle(tmp, tmp + neuron_size);
				printf("output nodes are: ");
				for (int i = 0; i  output.size(); ++ i){
					output_number.push_back(tmp[i]);
					printf("%d ",tmp[i]);
				}
				printf("\n");
				printf("input nodes are:");
				for (int i = output.size(); i  input.size() + output.size(); ++ i){
					input_number.push_back(tmp[i]);
					printf("%d ",tmp[i]);
				}
				printf("\n");
			}
			initInputNeuron(*input_num);
			for (int i = 0; i  output.size(); ++ i){
				neurons[output_number[i]].is_output = true;
				//pr(i),prln(output_number[i]);
			}

			std::queueintq[output.size() + input.size()];
			memset(height, -1, sizeof(height));
			int painted = output.size();
			int cnt=0;
			for (auto curnode : output_number){
				q[cnt++].push(curnode);
				height[curnode] = 0;
			}
			for (auto curnode : input_number){
				q[cnt++].push(curnode);
				height[curnode] = neuron_size;
			}
			bool flag = true;
			while (flag){
				int cnt = 0;
				flag = false;
				for (auto curnode : output_number){
					flag |= bfs(q[cnt++], 1);
				}
				for (auto curnode : input_number){
					flag |= bfs(q[cnt++], -1);
				}
			}
			auto build_map = [=](int from, int to){
				neurons[from].neuron_array.push_back(std::make_pair(to, randomDouble(-1,1)));
			};
			for (int i = 0; i  neuron_size; ++ i){
				for (auto curnode : vector_map[i]){
					if (height[i]  height[curnode]){
						build_map(i, curnode);
					}
				}
			}
			for (int i = 0; i  neuron_size; ++ i){
				neurons[i].loss_energy = randomDouble(-1, 1);
			}
			getTopology();
			//至此构造完网络的拓扑结构
		}

		void getTopology()
		{
			memset(tmp, 0, sizeof(tmp));
			for (int i = 0; i  neuron_size; ++ i){
				for (auto nextnode : neurons[i].neuron_array){
					++ tmp[nextnode.first];
				}
			}
			std::queueintq;
			for (auto curnode : input_number){
				q.push(curnode);
			}
			int pos = 0;
			while (!q.empty())
			{
				int curnode = q.front();
				q.pop();
				topology[pos++] = curnode;
				for (auto nextnode : neurons[curnode].neuron_array){
					if(-- tmp[nextnode.first] == 0){
						q.push(nextnode.first);
					}
				}
			}
			userful_neuron_size = pos;
			//DEBUG
			//for (int i = 0; i  neuron_size; ++ i)
			//	pr(i),prln(topology[i]);

		}

		bool bfs(std::queueint q, int delta){
			if (q.empty()){
				return false;
			}
			int h = height[q.front()];
			while (!q.empty()  height[q.front()] == h){
				int curnode = q.front();
				q.pop();
				for (auto nextnode : vector_map[curnode]){
					if (height[nextnode] != -1){
						continue;
					}
					height[nextnode] = h + delta;
					q.push(nextnode);
				}
			}
			return true;
		}

		void cal_propagate(int node){
			NODE_OUTPUT = activationFunction(NODE_GAIN, NODE_THETA);
			for (auto nextnode : NODE.neuron_array){
				NEXT_NODE_GAIN += NODE_OUTPUT * NODE_TO_NEXTNODE_WEIGHT;
			}
		}

		void propagate(std::vectordouble input){
			//TODO
			
			
			/*
			for (int i = 0; i  input_number.size(); ++i)
			{
				input_weight[i] = 1;
			}
			*/
			

			for (int i = 0; i  neuron_size; ++ i){
				neurons[i].energy = 0;
				neurons[i].output = 0;
			}

			for (int i = 0; i != input.size(); ++ i){
				int node = input_number[i];
				NODE_GAIN += input_weight[i] * input[i];
			}

			for (int i = 0; i  userful_neuron_size; ++ i){
				int node = topology[i];
				cal_propagate(node);
			}
		}

		void cal_back(int node){
			for (auto nextnode : NODE.neuron_array){
				NODE_PE += NEXT_NODE_PE * NODE_TO_NEXTNODE_WEIGHT * D_NEXT_NODE;
			}

			for (auto nextnode : NODE.neuron_array){
				NODE_TO_NEXTNODE_WEIGHT -= NODE_OUTPUT * D_NEXT_NODE * NEXT_NODE_PE * rate;
			}
			NODE_THETA -= NODE_PE * D_NODE * rate;
		}

		void back(std::vectordouble input, std::vectordouble output){
			for (int i = 0; i  neuron_size; ++ i){
				neurons[i].partial_derivative = 0;
			}

			for (int i = 0; i != output.size(); ++ i)
			{
				int node = output_number[i];
				NODE_PE = NODE_OUTPUT - output[i];
				NODE_THETA -= NODE_PE * D_NODE * rate;
			}

			for (int i = userful_neuron_size - 1; i = 0; -- i){
				int node = topology[i];
				if (NODE.is_output){
					continue;
				}
				else{
					cal_back(node);
				}
			}

			for (int i = 0; i  input.size(); ++ i){
				int node = input_number[i];
				double tmp = input[i] * NODE_PE * D_NODE;
				//prln(tmp);
				//prln(input_weight[i]);
				input_weight[i] -= tmp * rate;
				//prln(input_weight[i]);
			}
		}

		double train(std::vectordouble input, std::vectordouble output){
			propagate(input);
			//	outputNetwork();
			back(input, output);
			double error=0;
			for (int i = 0; i  output.size(); ++ i){
				error += 0.5*pow((neurons[output_number[i]].output - output[i]), 2);
			}
			return error;
		}

		void outputNetwork(){
			printf("---------------input nodes------------:\n");
			for (int i = 0; i  input_number.size(); ++ i)
			{
				printf("[%d] weight:(%.7lf) \n", input_number[i], input_weight[i]);
			}
			printf("---------------other nodes------------\n");
			printf("other nodes\n");
			for (int i = 0; i  neuron_size; ++ i){
				pr(topology[i]),prln(i);
				int node = topology[i];
				printf("[%d] gain(%.7lf) theta(%.7lf) par_derivative(%.7lf) output(%.7lf) d(%.7lf)\n", 
						node,
						NODE_GAIN,
						NODE_THETA, 
						NODE_PE, 
						NODE_OUTPUT, 
						D_NODE);
				for (auto nextnode : NODE.neuron_array){
					printf("  - %d (%.7lf)\n", nextnode.first, nextnode.second);
				}
			}
			printf("=============End====================\n");
		}

		void testOutput(std::vectordouble input)
		{
			propagate(input);
			cout"output: ";
			for (auto curnode : output_number)
			{
				printf("%.7lf ", neurons[curnode].output);
			}
			coutendl;
		}

		std::vectordouble getTest(std::vectordouble input)
		{
			std::vectordouble q;
			for (auto curnode : output_number)
			{
				q.push_back(neurons[curnode].energy);
			}
			return move(q);
		}
};

void doit251()
{
	std::vectorintin({0,1});
	std::vectorintout({7});
	net_t8 net;//("input.txt");
	srand(0);
	std::vectordoubleinput1({0,0});
	std::vectordoubleinput2({0,1});
	std::vectordoubleinput3({1,0});
	std::vectordoubleinput4({1,1});
	std::vectordoubleoutput1({0});
	std::vectordoubleoutput2({1});
	std::vectordoubleoutput3({1});
	std::vectordoubleoutput4({0});

	net.setIO(input1, output1, in, out);
	net.activation_way = "sigmoid";
	net.rate = 20;



	/*
	net.propagate(input3);
	net.outputNetwork();
	net.back(input3, output3);
	net.outputNetwork();
	return;
	*/




	double error=0;
	for (int i = 1;i=20000;++i){
		error = 0;
		error += net.train(input1, output1);
		error += net.train(input2, output2);
		error += net.train(input3, output3);
		error += net.train(input4, output4);
		error/=4;
		couterror"\r";
	}
	prln(error);
	//prln(net.userful_neuron_size);
	net.testOutput(input1);
	net.testOutput(input2);
	net.testOutput(input3);
	net.testOutput(input4);
}


int main()
{
	doit251();
	return 0;
}




最新回复(0)
/jishuQf4p6BFTdfMCPTsIS1D9zRKmpE7cOGaPE3TFjbtBzD0_3D4858424
8 简首页