github资源地址:[Release-x86/x64]
下一篇:轻量级C++神经网络应用库CreativeLus:2、分类问题。案例:空间点在平面上2分类。
CreativeLus库,又名“创造性逻辑元”,简称CL,是基于反馈式神经网络(BP网络)模型理论基础开发的C++神经网络库。希望在尽可能的丰富功能前提下,让使用者付出极低的代价。尤其是学生、研究人员或小型应用,在机器学习,深度学习,神经网络应用等领域能有一种快速的应用可选方案。
作为超轻量级应用库,它区别于TensorFlow、PyTorch等强大而复杂的系统工具或库,但它同样简单易用,而且有效。能处理常规逼近,分类问题,还能构建自定义的各类网络模型,包括:复杂节点网络,卷积网络(cnn),残差网络(ResNet),循环网络(RNN)等等,用于解决实际的诸如图像识别,语义识别等问题。它的小范围可视化输出,能让神经网络初学者在理论学习中,有对神经网络工作原理的感官认知,具有一定启发性。
由于开发力量有限,所以目前这个库只用于Windows平台,并在不断成长中,由衷希望得到大家的支持和宝贵的意见!
序号 | 内容 |
---|---|
1 | Windows平台,基于C++语言的,面向对象神经网络库。 |
2 | 多线程支持,让你的多核CPU飞起来。(GPU加速暂不支持,完善中) |
3 | 拒绝臃肿,作为轻量级的库,代码简洁易于使用。适用于学习,科研,和部分小型化产品。 |
4 | 快速的可视化输出,过程监控及网络结构呈献。可加深对神经网络的理解。 |
5 | 具有灵活自如的,快捷的,创造性的多种建模方式。发挥你的创造力。 |
6 | 对象是由内核(用于发布)和训练层(用于训练)组成,他们相互独立。节约内存。 |
该工具库在Windows平台下以静态链编dll方式使用,包含三个部分文件:
头文件会根据版本不同,连同lib和dll文件一同发布。
从_x64或_x86文件后缀可看出,你需要根据你的系统是64位或32位选择对应的库文件,暂时未提供debug版本的库文件。
1.首先,在任何需要调用该类库中对象的源文件中引入头文件
#include "CreativeLus.h"
2.其次,项目在编译时链接静态库lib文件,方法有两种。
第一种:通过显示的在源文件代码中申明编译器链接静态库
#ifdef _WIN64 #pragma comment(lib,"CreativeLus_x64.lib") //文件路径自行调整修改 #else #pragma comment(lib,"CreativeLus_x86.lib") #endif
第二种:将CreativeLus_x64.dll,CreativeLus_x86.dll文件放入生成的应用程序目录,或系统目录,或到IDE(开发环境)的链接库搜索目录下面,这要根据你使用IDE情况而定。具体方法自行百度.。
3.最后,将CreativeLus_x64.dll,CreativeLus_x86.dll文件放入生成的应用程序所在的同目录下,或Windows系统目录C:\Windows\System32下,或环境变量指定的搜索目录下。
github资源地址:[Release-x86/x64]
千言万语不如代码来得真实!首先我们就以一个最朴实无华的例子,sin函数逼近,借助代码我们徐徐展开,看看他能做些什么!
问题:我们在x轴,取定义域[-Π,Π]之间的随机输入,通过y=f(x)=sin(x),得到一定数量的训练样本对和测试样本对。通过构建的bp神经网络,在一定次数的训练后,若达到任务目标(即误差精度达到目标或测试集样本带入模型预测正确率满足目标要求),则模型训练完成,此时,随机生成(也可人为指定)多个单点测试输入数据,加入模型预测,观察结果与真实结果之间的差距(是否满足精度要求或是正确分类),评价模型的有效性(即预测能力)。
代码如下:
#include <stdio.h> #include <math.h> #include <vector> #include "CreativeLus.h" #ifdef _WIN64 #pragma comment(lib,"CreativeLus_x64.lib") #else #pragma comment(lib,"CreativeLus_x86.lib") #endif using namespace cl; int main() {
printf("\nCL控件信息:\n"); //只是做一个控件信息显示,不是必须的步骤 for (auto& i : getBpnnToolInfo())cout << i.first << " = " << i.second << endl; printf("\n\n//案例1:sin函数逼近测试"); printf("\n\n//第一步:以下生成原始数据集---------------------------------------\n"); const Float rangA = -ConstPi * 1.0, rangB = ConstPi * 1.0; // Float x1, y1; #define XData x1 = rand_f_a_b(rangA,rangB) // rand_f_a_b是一个生成a,b之间范围随机数的Api,精度远高于rand() #define YData y1 = ::sin(x1) #define LDataIn x1 #define LDataOut y1 BpnnSamSets trainData;//定义训练数据集 BpnnSamSets checkData;//定义测试数据集 size_t nPtSi = 2020; //样本数量2020(至于为什么设2020,纪念一下今年抗战医务一线战士们) for (size_t i = 0; i < nPtSi; i++) {
XData; YData; trainData.addSample({
LDataIn }, {
LDataOut }); //取1/4数量的测试集数据 if (i < nPtSi / 4) {
XData; YData; checkData.addSample({
LDataIn }, {
LDataOut }); } } printf("\n\n//第二步:以下构造神经网络---------------------------------------\n"); Bpnn::CallBackExample bpnnMonitor;//生成一个过程监控器 BPNN_CALLBACK_MAKE(pMonitCbFunc, Bpnn::CallBackExample::print);//用其中的成员函数作为外显回调函数 Float learnRate = 0.1, tagEr = 0.0005, movment = 0.8; Bpnn bp; bp.setName("sin函数逼近") //给网络取个中文名(可省略) .setLayer(2, 5) //设置2隐藏层,每次5个神经元。(输出层神经元无需设置,由传入的训练样本维度确定) .setParam(learnRate, tagEr, movment) //设置 学习率,误差精度,动量惯性系数(一个避免陷入局部极小值的设定,也可增加训练过程稳定性) .setTransFunc(TF_Sigmoid, TF_PRelu) //隐藏层采用S函数,为保证输出域是正负无穷,输出层采用PRelu函数 .setSampSets(trainData) //设置训练集到网络 .setCorrectRateEvaluationModel(0.999, &checkData) //设置网络目标要求正确率99.9%,测试数据集,0表示采用测试集全部数据 .setSampleBatchCounts(1, false) //设置每一步训练时,样本采用的批次数量(可省略) .buildNet(pMonitCbFunc, &bpnnMonitor); //构建网络,不需要监控可以传入nullptr printf("\n\n//第三步:以下开始训练网络---------------------------------------\n"); CLTick tick, tick2;//CLTick类源码详文章末尾代码,此处计算耗时的代码不是必须的,可自行调整修改或删掉 Bool rt = false; bp.openGraphFlag(true); //打开训练数据流记录 do {
//此处采用循环,保证模型训练持续,直到收敛为止 rt = bp.setMaxTimes(trainData.size() * 10).train(nullptr, nullptr, nullptr, pMonitCbFunc, &bpnnMonitor); printf(("总耗时:%g秒,本次:%g秒,正确率 = %.2f %%, 当前误差 = %g\n"), tick.getSpendTime(), tick2.getSpendTime(true), bp.getSavedCorrectRate() * 100.0, bp.getEr()); bp.showGraphParam(); //显示训练结果曲线(前面必须先打开训练数据流记录开关) bp.showGraphNetStruct(true, 800, 1); //显示网络结构,参数为true表示权值结构图一起显示 } while (rt == false); printf("\n\n//第四步:以下做随机测试,验证网络预测能力---------------------------------------\n"); const size_t checkTimes = 5; //做5次随机检测,检查模型有效性 for (size_t i = 0; i < checkTimes; i++) {
XData; YData; VLF vIn = {
LDataIn }; VLF vtag = {
LDataOut }; VLF vout; auto Er = bp.predict(vIn, &vout, &vtag); printf_s(" \n输入值 = { "); for (size_t i = 0; i < vIn.size(); i++) printf_s(("%f, "), vIn[i]); printf_s(" } \n预测值 = { "); for (size_t i = 0; i < vout.size(); i++) printf_s(("%f, "), vout[i]); printf_s(" } \n真实值 = { "); for (size_t i = 0; i < vtag.size(); i++) printf_s(("%f, "), vtag[i]); printf_s(" } \n损失 = %f, < %g %s\n\n", Er, tagEr,Er < tagEr ? " 预测正确!":" 结果错误!"); } getchar(); return 1; }
网络模型结构图:这是一个有3层11个神经元的网络结构(2隐藏层,每层5个神经元),由于y=sin(x),即输入输出维度均为1。
网络各节点链接及权重分布:颜色越重,越粗表示该节点的链接权重绝对值越大。可以直观的观察网络。
误差曲线:随着训练次数的增加,模型误差在逐渐减小,模型开始收敛。模型在运行71988次训练后达到训练目标。
正确率曲线:随着训练次数的增加,模型误差减小同时,模型对传入的测试数据正确率开始逐渐提高。模型在运行71988次训练后达到训练目标,正确率达到100%。
输出信息:
CL控件信息: Author = Cailuo CharacterSet = gbk CharacterWidth = MuiltChar/Ascii Company = Cailuo CompileDate = Jan 30 2020 CompileEnvironment = Microsoft Visual Studio 2019 (v142) CompileLanguage = C++17 CompileTime = 02:22:06 Country = China Debug = Release IsDoublePrecision = false Language = Chinese Level = 0 OriginalExtensionName = dll OriginalFilename = CreativeLus_x64 Platform = x64 Region = Shenzhen RuntimeLib = MT SupportGpuAcceleration = false SupportMultiThreading = true System = Windows SystemMinimumSupport = Windows Nt Version = 1.0.2 //案例1:sin函数逼近测试 //第一步:以下生成原始数据集--------------------------------------- //第二步:以下构造神经网络--------------------------------------- Net construct completed. Neurons: 11, layers: 3. //第三步:以下开始训练网络--------------------------------------- Net training epoch completed. 总耗时:0.秒,本次:0.秒,正确率 = 24.00 %, 当前误差 = 0.00 Net training epoch completed. 总耗时:0.秒,本次:0.秒,正确率 = 97.00 %, 当前误差 = 2.78476e-05 Net training epoch completed. 总耗时:1.57773秒,本次:1.08466秒,正确率 = 99.80 %, 当前误差 = 1.23399e-05 Net training epoch completed with achieve accuracy. CorrectRate(100.00%) >= TagCorrectRate(99.90%) 总耗时:2.49737秒,本次:0.秒,正确率 = 100.00 %, 当前误差 = 3.94404e-06 //第四步:以下做随机测试,验证网络预测能力--------------------------------------- 输入值 = {
0., } 预测值 = {
0., } 真实值 = {
0., } 损失 = 0.000036, < 0.0005 预测正确! 输入值 = {
-0.023569, } 预测值 = {
-0.044516, } 真实值 = {
-0.023567, } 损失 = 0.000219, < 0.0005 预测正确! 输入值 = {
-2., } 预测值 = {
-0., } 真实值 = {
-0., } 损失 = 0.000006, < 0.0005 预测正确! 输入值 = {
-0., } 预测值 = {
-0., } 真实值 = {
-0., } 损失 = 0.000042, < 0.0005 预测正确! 输入值 = {
-1., } 预测值 = {
-1.006636, } 真实值 = {
-0., } 损失 = 0.000023, < 0.0005 预测正确! 请按任意键继续. . .
2 输出层采用PRelu函数目的,替换成Sigmod函数或者PureLin函数是否可行?
3 调整网络隐藏层数和神经元数量,对模型的影响?
4 调整每一步训练时,同一批次拟合训练使用的样本数量,对模型的影响?
下一篇:轻量级C++神经网络应用库CreativeLus:2、分类问题。案例:空间点在平面上2分类。
#include "windows.h" #ifndef __CL_TICK_DEF__ #define __CL_TICK_DEF__ //高精度计时器类 class CLTick {
protected: LARGE_INTEGER lis; LARGE_INTEGER lie; LARGE_INTEGER Freg; public: CLTick() {
timingStart(); } //开始计时 CLTick& timingStart() {
QueryPerformanceFrequency(&Freg); QueryPerformanceCounter(&lis); return *this; } //取得从计时开始到当前的时间 double getSpendTime(bool saveToStart = false) {
QueryPerformanceCounter(&lie); double rt = double(lie.QuadPart - lis.QuadPart) / double(Freg.QuadPart); if (saveToStart)lis = lie; return rt; } }; #endif`