编程语言
首页 > 编程语言> > 轻量级C++神经网络应用库CreativeLus:3、复杂函数逼近。案例:多输入混合逼近。

轻量级C++神经网络应用库CreativeLus:3、复杂函数逼近。案例:多输入混合逼近。

作者:互联网


github资源地址:[Release-x86/x64]

上一篇:轻量级C++神经网络应用库CreativeLus:2、分类问题。案例:空间点在平面上2分类。
下一篇:轻量级C++神经网络应用库CreativeLus:4、CNN卷积神经网络。案例:(MNIST)手写数字识别。


案例3:复杂函数逼近

本章介绍以下几个主要内容,本章内容非常重要:
1、创建自定义结构的神经网络;
2、模型自调整介绍(不是调参);
3、自定义过程监控和输出。

本例问题描述

为表现神经网络的强大逼近能力,我们将【案例1:简单sin函数逼近】的问题升级,通过复杂的多输入多输出逼近问题,了解神经网络运作模式。

命题如下:
定义维度为4的输入向量
X={x1,x2,x3,x4},xi[π,π]X=\{x_1,x_2,x_3,x_4\},其中xi\in\mathbb [-\pi,\pi]X={x1​,x2​,x3​,x4​},其中xi∈[−π,π]
xixixi取定义域内的随机值,通过映射Γ(X)\Gamma(X)Γ(X),得到维度为4的输出向量YYY,即
Y=Γ(X)={y1,y2,y3,y4} Y=\Gamma(X) =\{y_1,y_2,y_3,y_4\} Y=Γ(X)={y1​,y2​,y3​,y4​}
={sin(x4),cos(x3),min(0.1ex2,1),min(1/(1+e(x1)),1)},yi[1,1] =\{sin(x_4),cos(x_3),min(0.1e^{x_2},1),min(1/(1+e^{(-x_1)}),1)\},其中yi\in\mathbb [-1,1] 。={sin(x4​),cos(x3​),min(0.1ex2​,1),min(1/(1+e(−x1​)),1)},其中yi∈[−1,1]。
[π,π][-\pi,\pi][−π,π]的随机输入到[1,1][-1,1][−1,1]范围的交叉输出,形成训练集。构造适当的神经网络,通过训练找到映射关系Γ\GammaΓ,并通过随机输入验证模型有效性

映射Γ\GammaΓ的真实关系,代码片段如下:

const Float rangA = -ConstPi * 1.0, rangB = ConstPi * 1.0;
Float x4 = 0, x1 = 0, x2 = 0, x3 = 0, y4 = 0, y1 = 0, y2 = 0, y3 = 0;
#define XData3 x1 = rand_f_a_b(rangA,rangB),x2 = rand_f_a_b(rangA,rangB),x3 = rand_f_a_b(rangA,rangB),x4 = rand_f_a_b(rangA,rangB)
#define YData3 y1 = sin(x4),y2 = cos(x3),y3 = min(exp(x2)*0.1,1),y4 = min((1.0/(1.0+exp(-x1))),1)

1、“创造性决定一切”,设计你自己的神经网络

话不多说,我们先看一下,为了本案的问题寻找逼近映射关系,我们“设计”了如下图的网络模型(5层,4输入,4输出模型结构)来解决上述问题。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

        ///////////////////////////////////////////////////////////////////////////////
		// 自定义网络:
		// 概念说明:CL是对神经元面向对象建模的,每个神经元都应该知道自己是什么,有什么功能,到底链接了谁(类似生物神经元树突),传递到神经元核心的数据如何处理,如何激活传递处理后的数据等等。

		// 以下通过显示的定义 组装 BpnnStructDef结构,完成网络结构的自定义(这也是实现自定义网络结构的必要方法,很重要)
		// 其中最小定义单元为一个BSB结构:BSB,他描述了在本层内,同一种(即所有属性都完全相同)的神经元的属性即定义,
		// BSB结构描述了:本组内该种相同神经元的个数,传递函数类型,链接哪些上层节点(这很重要),对上层链接的节点的传入数据的处理方式(例如:线性相加、取Max、取Min、取均值,这些在卷积网络CNN池化操作中用到),
		// 权值及阈值初始化方案,是否采用共享权值(在卷积网络CNN中用到)。
		BpnnStructDef mod = { 
			// 构造第一层(该层直接接纳输入数据的各个维度):
			// 以第一个BSB为例,它表达了,该组内有6个相同神经元,传递函数为PRelu,由于没有定义对上层的链接描述,所以本组采用对上层全连接(若有需要也可自行指定链接);
			// 而本层内有4个组,每个组的神经元数量各不相同,所以本层的总神经元数量为各组神经元数量相加,即第一层有6+4+6+4=20个神经元,到此本层定义完毕;			
			{BSB(6,TF_PRelu),BSB(4,TF_LeakyRelu,{}),BSB(6,TF_PRelu),BSB(4,TF_LeakyRelu,{})},
			
			// 构造第二层:总计4组(4个BSB结构),以第1组(BSB)为例:该组共4个神经元,传递函数为Tanh,指定对上一层链接的编号为{1,3,5,7,9}的神经元
			// 注意上层链接编号是从1开始计数的,而不是0,特别注意;若没有指定了上层链接编号(类似第一层的情况),将采用对上层全连接方式。
			{BSB(4,TF_Tanh,{1,3,5,7,9}),BSB(6,TF_Sigmoid,{2,4,6,8,10}),BSB(4,TF_Tanh,{11,13,15,17,19}),BSB(6,TF_Sigmoid,{12,14,16,18,20})},
			
			// 第三层(根据需求自定义,方法同上,此处只做演示)
			{BSB(6,TF_PRelu,{1,2,3,4,5,6}),BSB(8,TF_LeakyRelu,{7,8,9,10,11,12,13,14}),BSB(6,TF_PRelu,{15,16,17,18,19,20})},
			
			// 第四层(根据需求自定义,方法同上,此处只做演示)
			{BSB(4,TF_Tanh,{1,3,5,7,9}),BSB(6,TF_Sigmoid,{2,4,6,8,10}),BSB(4,TF_Tanh,{11,13,15,17,19}),BSB(6,TF_Sigmoid,{12,14,16,18,20})},
			
			// 最后一层(输出层,该层必须被定义):由于我们的问题,输出维度必须为4,所以输出层构造的总神经元数量,也必须为4(和BSB个数无关,只和本层神经元总数有关),对上层也采用全连接
			{BSB(4,TF_PRelu)},

		};// 至此,我们完成一个4输出,5层网络层的神经网络结构定义。后续只需要将该mod选入bp中,通过buildNet即可生成自定义网络。
		
		// 结论:以简单的BSB单元构造网络,通过自由灵活的组合,可实现很多复杂的网络结构,后续将看到类似:标准及非标卷积层,softmax分类器,Inception结构,残差网络ResNet等均可以构造。
		///////////////////////////////////////////////////////////////////////////////

2、“知错就改”,模型自调学习率,修复计算

本案代码中,我们不小心把学习率设得太大了。

Float learnRate = 100.1; // 学习率设为100太大,详见附录1:完整代码
Float tagEr = 0.0001, movment = 0.8;
bp.setParam(learnRate, tagEr, movment);   

模型训练过程梯度爆炸。但是,没关系,CL自动将过大的学习率调整到合适的初始大小,并且在调整前,弹出如下提示窗,征询用户意见,自行选择是否调整还是退出。若该提示框出现后,用户一直等待,则10秒后,系统自动关闭提示按No继续执行。
在这里插入图片描述

[Waring]: Times= 1'st.The model automatically adjust learning rate ( 100.1 -> 29.0672 ) and retry!
[Waring]: Times= 2'st.The model automatically adjust learning rate ( 29.0672 -> 6.89407 ) and retry!
[Waring]: Times= 3'st.The model automatically adjust learning rate ( 6.89407 -> 1.01794 ) and retry!
[Waring]: Times= 4'st.The model automatically adjust learning rate ( 1.01794 -> 0.115428 ) and retry!
[Waring]: Times= 5'st.The model automatically adjust learning rate ( 0.115428 -> 0.0279845 ) and retry!

3、“过程信息输出”,定义自己的过程监控器

案例1案例2中我们使用了,一个内置的过程监控器,他提供回调函数指针给需要监控的过程,主要监控buildNet构造网络和train训练网络两个耗时的过程。

//代码片段
Bpnn::CallBackExample bk; //Bpnn::CallBackExample是一个实现了回显功能的监控器
BPNN_CALLBACK_MAKE(vcb2, Bpnn::CallBackExample::print);//BPNN_CALLBACK_MAKE是一个构造监控回调函数指针的宏

bp.buildNet(vcb2, &bk); //监控器传入方法,监控构造过程
bp.train(0, 0, 0, vcb2, &bk);//监控训练过程
		// 方式1: 
		// 定义静态函数指针Bpnn::CbFunStatic pMonit,通过lambda快速创建一个监视器
		Bpnn::CbFunStatic pMonit = [](Int c, Int max, PCStr inf) { 
			// c表示当前信息编号id,max表示总信息数,inf为信息字符串;
			// 当c < 0 时候,代表传入一个提示信息;当c和max都>=0时,表示传入了一个过程信息;
			if (c < 0) {
				cout << endl                      << "[Info ]" << inf;
			}
			else {
				if (c == 1) { cout << endl        << "[Start]" << inf; }
				else if (c == max) { cout << endl << "[Done ]" << inf; }
			}
		};
		
		bp.buildNet(pMonit);//监控回调指针传入
		bp.train(0, 0, 0, pMonit);
//案例3:多输入混合逼近

[Start]Net is building hide layer.
[Done ]Net is building hide layer.
[Info ]Net construct completed. Neurons: 84, layers: 5.
[Start]Net is checking share link range.
[Done ]Net is checking share link range.
[Start]Net is training.
[Info ][Waring]: Times= 1'st.The model automatically adjust learning rate ( 100.1 -> 14.6937 ) and retry!
[Start]Net is checking share link range.
[Done ]Net is checking share link range.
[Start]Net is training.
[Info ][Waring]: Times= 2'st.The model automatically adjust learning rate ( 14.6937 -> 3.87114 ) and retry!
[Start]Net is checking share link range.
[Done ]Net is checking share link range.
[Start]Net is training.
[Info ][Waring]: Times= 3'st.The model automatically adjust learning rate ( 3.87114 -> 1.05257 ) and retry!
[Start]Net is checking share link range.
[Done ]Net is checking share link range.
[Start]Net is training.
[Info ][Waring]: Times= 4'st.The model automatically adjust learning rate ( 1.05257 -> 0.115818 ) and retry!
[Start]Net is checking share link range.
[Done ]Net is checking share link range.
[Start]Net is training.
[Info ][Waring]: Times= 5'st.The model automatically adjust learning rate ( 0.115818 -> 0.0183949 ) and retry!
[Start]Net is checking share link range.
[Done ]Net is checking share link range.
[Start]Net is training.
[Done ]Net is training.
[Info ]Net training epoch completed.
Epoch 1:总耗时:27.1134秒,本次:27.1134秒,CorrectRate = 55.07 %, Er = 6.78812e-05

[Start]Net is training.
[Done ]Net is training.
[Info ]Net training epoch completed.
Epoch 2:总耗时:42.0107秒,本次:14.8973秒,CorrectRate = 66.40 %, Er = 9.02233e-05

[Start]Net is training.
[Done ]Net is training.
[Info ]Net training epoch completed.
Epoch 3:总耗时:55.4777秒,本次:13.467秒,CorrectRate = 70.40 %, Er = 7.65581e-05

github资源地址:[Release-x86/x64]

上一篇:轻量级C++神经网络应用库CreativeLus:2、分类问题。案例:空间点在平面上2分类。
下一篇:轻量级C++神经网络应用库CreativeLus:4、CNN卷积神经网络。案例:(MNIST)手写数字识别。


附录:

附1:完整代码

#include <stdio.h>
#include <string>
#include <vector>
#include <map>
#include "CreativeLus.h"

using namespace cl;

int main() {
	printf("\n\n//案例3:多输入混合逼近\n");
	string pathTag = ("D:\\Documents\\Desktop\\example_02_multi_approach\\"); //输出目录

	const Float rangA = -ConstPi * 1.0, rangB = ConstPi * 1.0;
	Float x4 = 0, x1 = 0, x2 = 0, x3 = 0, y4 = 0, y1 = 0, y2 = 0, y3 = 0;
#define XData3 x1 = rand_f_a_b(rangA,rangB),x2 = rand_f_a_b(rangA,rangB),x3 = rand_f_a_b(rangA,rangB),x4 = rand_f_a_b(rangA,rangB)
#define YData3 y1 = sin(x4),y2 = cos(x3),y3 = min(exp(x2)*0.1,1),y4 = min((1.0/(1.0+exp(-x1))),1)
#define LDataIn3  x1,x2,x3,x4
#define LDataOut3 y1,y2,y3,y4

	Bpnn bp;
	Float learnRate = 100.1;
	Float tagEr = 0.0001, movment = 0.8;
	if (!bp.readBpnnFormFile((pathTag + "model.txt").c_str())) {  //读取模型结果文件,若存在就不再训练了,直接预测(在此仅作演示,非必须)
		// 第一步:构造数据集-------------------------------
		BpnnSamSets sams;
		BpnnSamSets tags;
		size_t nPtSi = 3000;
		for (size_t i = 0; i < nPtSi; i++) {
			XData3; YData3; sams.addSample({ LDataIn3 }, { LDataOut3 });
			XData3; YData3; tags.addSample({ LDataIn3 }, { LDataOut3 });
		}
		sams.writeToFile((pathTag + "sams.txt").c_str());
		tags.writeToFile((pathTag + "tags.txt").c_str());


		//第二部:构造网络------------------------------

		///////////////////////////////////////////////////////////////////////////////
		// 自定义网络:
		// 概念说明:CL是对神经元面向对象建模的,每个神经元都应该知道自己是什么,有什么功能,到底链接了谁(类似生物神经元树突),传递到神经元核心的数据如何处理,如何激活传递处理后的数据等等。

		// 以下通过显示的定义 组装 BpnnStructDef结构,完成网络结构的自定义(这也是实现自定义网络结构的必要方法,很重要)
		// 其中最小定义单元为一个BSB结构:BSB他描述了在本层内,同一种(即所有属性都完全相同)的神经元的定义,
		// BSB描述了:本组内该种相同神经元的个数,传递函数类型,链接哪些上层节点(这很重要),对上层链接的节点的传入数据的处理方式(例如:线性相加、取Max、取Min、取均值,这些在卷积网络CNN池化操作中用到),
		// 权值及阈值初始化方案,是否采用共享权值(在卷积网络CNN中用到)。
		BpnnStructDef mod = { 
			// 构造第一层(该层直接接纳输入数据的各个维度):
			// 以第一个BSB为例,它表达了,该组内有6个相同神经元,传递函数为PRelu,由于没有定义对上层的链接描述,所以本组采用对上层全连接(若有需要也可自行指定链接);
			// 而本层内有4个组,每个组的神经元数量各不相同,所以本层的总神经元数量为各组神经元数量相加,即第一层有6+4+6+4=20个神经元,到此本层定义完毕;
			{BSB(6,TF_PRelu),BSB(4,TF_LeakyRelu,{}),BSB(6,TF_PRelu),BSB(4,TF_LeakyRelu,{})},

			// 构造第二层:总计4组(4个BSB结构),以第1组(BSB)为例:该组共4个神经元,传递函数为Tanh,指定对上一层链接的编号为{1,3,5,7,9}的神经元
			// 注意上层链接编号是从1开始计数的,而不是0,特别注意;若没有指定了上层链接编号(类似第一层的情况),将采用对上层全连接方式。
			{BSB(4,TF_Tanh,{1,3,5,7,9}),BSB(6,TF_Sigmoid,{2,4,6,8,10}),BSB(4,TF_Tanh,{11,13,15,17,19}),BSB(6,TF_Sigmoid,{12,14,16,18,20})},
			// 第三层(根据需求自定义,方法同上,此处只做演示)
			{BSB(6,TF_PRelu,{1,2,3,4,5,6}),BSB(8,TF_LeakyRelu,{7,8,9,10,11,12,13,14}),BSB(6,TF_PRelu,{15,16,17,18,19,20})},
			// 第四层(根据需求自定义,方法同上,此处只做演示)
			{BSB(4,TF_Tanh,{1,3,5,7,9}),BSB(6,TF_Sigmoid,{2,4,6,8,10}),BSB(4,TF_Tanh,{11,13,15,17,19}),BSB(6,TF_Sigmoid,{12,14,16,18,20})},
			// 最后一层(输出层,该层必须被定义):由于我们的问题,输出维度必须为4,所以输出层构造的总神经元数量,也必须为4(和BSB个数无关,只和本层神经元总数有关),对上层也采用全连接
			{BSB(4,TF_PRelu)},

		};// 至此,我们完成一个4输出,5层网络层的神经网络结构定义。后续只需要将该mod选入bp中,通过buildNet即可生成自定义网络。
		
		// 结论:以简单的BSB单元构造网络,通过自由灵活的组合,可实现很多复杂的网络结构,后续将看到类似:标准及非标卷积层,softmax分类器,Inception结构,残差网络ResNet等均可以构造。
		///////////////////////////////////////////////////////////////////////////////

		mod.writeToFile((pathTag + "NnStructDef.txt").c_str());//结构定义可输出,保存为文本文件,可自行打开查看内容		

		
		bp.setName("多输入混合逼近");
		bp.setSampSets(sams);
		bp.setCorrectRateEvaluationModel(0.985, &tags,tags.size() / 4,true);

		bp.setStructure(mod); //注意:将结构定义对象在buildNet前选入模型,设置模型构造方案(很重要)
		//{{{
		bp.setLayer(3, 28); //由于使用了BpnnStructDef自定义网络结构,该方法设置的层和节点数据将会失效并忽略(在此仅作演示,并无实际意义)
		bp.setTransFunc(TF_Sigmoid, TF_PRelu); //由于使用了BpnnStructDef自定义网络结构,该方法设置的各层传递函数将会失效并忽略(在此仅作演示,并无实际意义)
		//}}}

		bp.setSampleBatchCounts(40, true);  //设定一批次样本数量,采用随机抽取
		bp.setParam(learnRate, tagEr, movment);   
		bp.setMultiThreadSupport(true);	   //采用多线程加速。(CPU要求最低为4核)
		bp.setMaxTimes(sams.size() * 20);
		bp.openGraphFlag(true);
		Bpnn::CallBackExample bk;
		BPNN_CALLBACK_MAKE(vcb2, Bpnn::CallBackExample::print);//构造监控回调函数指针
		bp.buildNet(vcb2, &bk);
		bp.exportGraphNetStruct((pathTag + "NnStruct.bmp").c_str());//构造网络后,输出网络结构到bmp图片文件

		CLTick tick, tick2, tick3; 
		bool rt = false; Int epoch = 0;
		while (!rt) {
			rt = bp.train(0, 0, 0, vcb2, &bk);
			printf(("Epoch %d:总耗时:%g秒,本次:%g秒,CorrectRate = %.2f %%, Er = %g \n\n"), ++epoch, tick.getSpendTime(), tick2.getSpendTime(true),
				bp.getSavedCorrectRate() * 100.0, bp.getEr());
			if (epoch == 1 || rt == true || tick3.getSpendTime() > 20.0) {
				bp.showGraphParam(0); //当为0显示全部数据
				bp.showGraphNetStruct(true, 800, 1);
				tick3.timingStart();
			}
		};
		if (rt) { //将训练完成的模型输出
			bp.writeBpnnToFile((pathTag + "model.txt").c_str()); //保存模型
		}
		bp.exportGraphNetStruct((pathTag + "NnStructDetail.bmp").c_str(), true); 
		bp.exportGraphEr((pathTag + "NnEr.bmp").c_str()); // 输出保存损失曲线图
		bp.exportGraphCorrectRate((pathTag + "NnCr.bmp").c_str()); // 输出保存正确率曲线图
		bp.detachExtend();//释放训练扩展占用内存
	}
	VLF threadBuf;
	bp.makeIndependentDataBuf(threadBuf);//构造独立数据区
	for (size_t i = 0; i < 5; i++)
	{
		XData3;	YData3;	VLF vIn = { LDataIn3 };	VLF vtag = { LDataOut3 };VLF vout;
		Float Er = bp.predictWithIndependentData(threadBuf.data(), vIn, &vout, &vtag);
		printf("   \n输入值 = { ");
		for (size_t i = 0; i < vIn.size(); i++)
			printf("%f, ", vIn[i]);
		printf(" } \n预测值 = { ");
		for (size_t i = 0; i < vout.size(); i++)
			printf("%f, ", vout[i]);
		printf(" } \n真实值 = { ");
		for (size_t i = 0; i < vtag.size(); i++)
			printf("%f, ", vtag[i]);
		printf(" } \n损失   =   %f, %s %g %s\n\n", Er, Er <= tagEr ? "<=":">", tagEr, Er < tagEr ? "         预测正确!" : "         误差过大,预测失败!!!");
	}
	return system("pause");
}

附2:训练过程输出

//案例3:多输入混合逼近
Net construct completed. Neurons: 84, layers: 5.
[Waring]: Times= 1'st.The model automatically adjust learning rate ( 100.1 -> 29.0672 ) and retry!
[Waring]: Times= 2'st.The model automatically adjust learning rate ( 29.0672 -> 6.89407 ) and retry!
[Waring]: Times= 3'st.The model automatically adjust learning rate ( 6.89407 -> 1.01794 ) and retry!
[Waring]: Times= 4'st.The model automatically adjust learning rate ( 1.01794 -> 0.115428 ) and retry!
[Waring]: Times= 5'st.The model automatically adjust learning rate ( 0.115428 -> 0.0279845 ) and retry!
Net training epoch completed.
Epoch 1:总耗时:16.1289秒,本次:16.1289秒,CorrectRate = 83.60 %, Er = 4.15041e-05

Net training epoch completed.
Epoch 2:总耗时:23.3815秒,本次:7.25264秒,CorrectRate = 94.00 %, Er = 3.22477e-05

Net training epoch completed.
Epoch 3:总耗时:29.8695秒,本次:6.48801秒,CorrectRate = 96.93 %, Er = 2.45293e-05

Net training epoch completed.
Epoch 4:总耗时:36.3577秒,本次:6.4882秒,CorrectRate = 97.73 %, Er = 4.07435e-05

Net training epoch completed with achieve accuracy. CorrectRate(98.53%) >= TagCorrectRate(98.50%)
Epoch 5:总耗时:38.3055秒,本次:1.94778秒,CorrectRate = 98.53 %, Er = 2.6788e-05


输入值 = { 1.435037, 0.907074, 0.424992, -0.815886,  }
预测值 = { -0.728387, 0.911280, 0.252407, 0.806843,  }
真实值 = { -0.728333, 0.911042, 0.247706, 0.807685,  }
损失   =   0.000011, <= 0.0001          预测正确!


输入值 = { 2.043433, -0.558324, 1.185989, -0.567262,  }
预测值 = { -0.535745, 0.376521, 0.057957, 0.885794,  }
真实值 = { -0.537325, 0.375381, 0.057217, 0.885282,  }
损失   =   0.000002, <= 0.0001          预测正确!


输入值 = { -0.058070, 2.371945, -1.143344, 0.415305,  }
预测值 = { 0.406086, 0.411661, 0.995162, 0.485440,  }
真实值 = { 0.403469, 0.414554, 1.000000, 0.485487,  }
损失   =   0.000019, <= 0.0001          预测正确!


输入值 = { 0.931005, -1.908752, -1.386756, -2.818051,  }
预测值 = { -0.318844, 0.182217, 0.014959, 0.718451,  }
真实值 = { -0.317927, 0.183003, 0.014827, 0.717279,  }
损失   =   0.000001, <= 0.0001          预测正确!


输入值 = { 2.984331, -1.796760, -2.753757, -0.121948,  }
预测值 = { -0.118123, -0.923810, 0.018038, 0.951676,  }
真实值 = { -0.121646, -0.925730, 0.016584, 0.951861,  }
损失   =   0.000009, <= 0.0001          预测正确!

请按任意键继续. . .

在这里插入图片描述
在这里插入图片描述

夜雨清狂 发布了9 篇原创文章 · 获赞 1 · 访问量 637 私信 关注

标签:CreativeLus,自定义,逼近,bp,轻量级,TF,Net,BSB,神经元
来源: https://blog.csdn.net/carlclouder/article/details/104124092