文章目录

  • 前言
  • 一、基本函数定义
    • 1.高低配置电脑一些不同
    • 2.数字转换成字符串
    • 3.将两个字符串相连
  • 二、数据加载
    • 1.结构体设计
    • 2.读取图像
    • 3.读取标签
    • 4.将结构体图像转为图片文件
  • 三、矩阵计算函数设计
    • 1.结构体设计及变量宏定义
    • 2.矩阵函数设计
      • 1.矩阵翻转180度
      • 2.互相关操作
      • 3. 卷积操作
      • 4. 上采样
      • 5. 矩阵扩边
      • 6. 矩阵缩边
      • 7. 保存矩阵
      • 8.矩阵相加
      • 9.矩阵系数相乘
      • 10.矩阵元素求和
    • 3.卷积函数设计
      • 1.卷积结构体设计
      • 2.初始化卷积层
      • 3.初始化采样层
      • 4.初始化输出层
      • 5.激活函数
      • 6.前向传播
      • 7.模型暴露和保存
  • 四、模型构建
  • 五、main函数
    • 1.训练
    • 2.测试
  • 总结

前言

本文实现使用c++底层实现深度学习框架,主要使用结构体。
本文简单搭建简单的图像分类的网络结构


一、基本函数定义

本次训练用到的基本数据处理函数

1.高低配置电脑一些不同

英特尔处理器和其他低端机用户必须翻转头字节。


int ReverseInt(int i)
{  unsigned char ch1, ch2, ch3, ch4;  ch1 = i & 255;  ch2 = (i >> 8) & 255;  ch3 = (i >> 16) & 255;  ch4 = (i >> 24) & 255;  return((int) ch1 << 24) + ((int)ch2 << 16) + ((int)ch3 << 8) + ch4;
}

2.数字转换成字符串

char* intTochar(int i)
{int itemp=i;int w=0;while(itemp>=10){itemp=itemp/10;w++;}char* ptr=(char*)malloc((w+2)*sizeof(char));ptr[w+1]='\0';int r; // 余数while(i>=10){r=i%10;i=i/10;        ptr[w]=(char)(r+48);w--;}ptr[w]=(char)(i+48);return ptr;
}

3.将两个字符串相连

char * combine_strings(char *a, char *b)
{char *ptr;int lena=strlen(a),lenb=strlen(b);int i,l=0;ptr = (char *)malloc((lena+lenb+1) * sizeof(char));for(i=0;i<lena;i++)ptr[l++]=a[i];for(i=0;i<lenb;i++)ptr[l++]=b[i];ptr[l]='\0';return(ptr);
}

二、数据加载

本文例子使用MNIST数据集,本模块主要设计数据集加载代码

1.结构体设计

typedef struct MinstImg{int c;           // 图像宽int r;           // 图像高float** ImgData; // 图像数据二维动态数组
}MinstImg;typedef struct MinstImgArr{int ImgNum;        // 存储图像的数目MinstImg* ImgPtr;  // 存储图像数组指针
}*ImgArr;              // 存储图像数据的数组typedef struct MinstLabel{int l;            // 输出标记的长float* LabelData; // 输出标记数据
}MinstLabel;typedef struct MinstLabelArr{int LabelNum;MinstLabel* LabelPtr;
}*LabelArr;              // 存储图像标记的数组

2.读取图像

ImgArr read_Img(const char* filename)
{FILE  *fp=NULL;fp=fopen(filename,"rb");if(fp==NULL)printf("打开文件失败\n");assert(fp);int magic_number = 0;  int number_of_images = 0;  int n_rows = 0;  int n_cols = 0;  //从文件中读取sizeof(magic_number) 个字符到 &magic_number  fread((char*)&magic_number,sizeof(magic_number),1,fp); magic_number = ReverseInt(magic_number);  //获取训练或测试image的个数number_of_images fread((char*)&number_of_images,sizeof(number_of_images),1,fp);  number_of_images = ReverseInt(number_of_images);    //获取训练或测试图像的高度Heigh  fread((char*)&n_rows,sizeof(n_rows),1,fp); n_rows = ReverseInt(n_rows);                  //获取训练或测试图像的宽度Width  fread((char*)&n_cols,sizeof(n_cols),1,fp); n_cols = ReverseInt(n_cols);  //获取第i幅图像,保存到vec中 int i,r,c;// 图像数组的初始化ImgArr imgarr=(ImgArr)malloc(sizeof(MinstImgArr));imgarr->ImgNum=number_of_images;imgarr->ImgPtr=(MinstImg*)malloc(number_of_images*sizeof(MinstImg));for(i = 0; i < number_of_images; ++i)  {  imgarr->ImgPtr[i].r=n_rows;imgarr->ImgPtr[i].c=n_cols;imgarr->ImgPtr[i].ImgData=(float**)malloc(n_rows*sizeof(float*));for(r = 0; r < n_rows; ++r)      {imgarr->ImgPtr[i].ImgData[r]=(float*)malloc(n_cols*sizeof(float));for(c = 0; c < n_cols; ++c){ unsigned char temp = 0;  fread((char*) &temp, sizeof(temp),1,fp); imgarr->ImgPtr[i].ImgData[r][c]=(float)temp/255.0;}  }    }fclose(fp);return imgarr;
}

3.读取标签

LabelArr read_Lable(const char* filename)// 读入图像
{FILE  *fp=NULL;fp=fopen(filename,"rb");if(fp==NULL)printf("打开文件失败\n");assert(fp);int magic_number = 0;  int number_of_labels = 0; int label_long = 10;//从文件中读取sizeof(magic_number) 个字符到 &magic_number  fread((char*)&magic_number,sizeof(magic_number),1,fp); magic_number = ReverseInt(magic_number);  //获取训练或测试image的个数number_of_images fread((char*)&number_of_labels,sizeof(number_of_labels),1,fp);  number_of_labels = ReverseInt(number_of_labels);    int i,l;// 图像标记数组的初始化LabelArr labarr=(LabelArr)malloc(sizeof(MinstLabelArr));labarr->LabelNum=number_of_labels;labarr->LabelPtr=(MinstLabel*)malloc(number_of_labels*sizeof(MinstLabel));for(i = 0; i < number_of_labels; ++i)  {  labarr->LabelPtr[i].l=10;labarr->LabelPtr[i].LabelData=(float*)calloc(label_long,sizeof(float));unsigned char temp = 0;  fread((char*) &temp, sizeof(temp),1,fp); labarr->LabelPtr[i].LabelData[(int)temp]=1.0;    }fclose(fp);return labarr;
}

4.将结构体图像转为图片文件

将图像数据保存成文件

void save_Img(ImgArr imgarr,char* filedir)
{int img_number=imgarr->ImgNum;int i,r;for(i=0;i<img_number;i++){const char* filename=combine_strings(filedir,combine_strings(intTochar(i),".gray"));FILE  *fp=NULL;fp=fopen(filename,"wb");if(fp==NULL)printf("保存图像文件失败\n");assert(fp);for(r=0;r<imgarr->ImgPtr[i].r;r++)fwrite(imgarr->ImgPtr[i].ImgData[r],sizeof(float),imgarr->ImgPtr[i].c,fp);fclose(fp);}
}

三、矩阵计算函数设计

1.结构体设计及变量宏定义

#define full 0
#define same 1
#define valid 2
typedef struct Mat2DSize{int c; // 列(宽)int r; // 行(高)
}nSize;

2.矩阵函数设计

1.矩阵翻转180度

float** rotate180(float** mat, nSize matSize)// 矩阵翻转180度
{int i,c,r;int outSizeW=matSize.c;int outSizeH=matSize.r;float** outputData=(float**)malloc(outSizeH*sizeof(float*));for(i=0;i<outSizeH;i++)outputData[i]=(float*)malloc(outSizeW*sizeof(float));for(r=0;r<outSizeH;r++)for(c=0;c<outSizeW;c++)outputData[r][c]=mat[outSizeH-r-1][outSizeW-c-1];return outputData;
}

2.互相关操作

关于卷积和相关操作的输出选项
这里共有三种选择:full、same、valid,分别表示
full指完全,操作后结果的大小为inSize+(mapSize-1)
same指同输入相同大小
valid指完全操作后的大小,一般为inSize-(mapSize-1)大小,其不需要将输入添0扩大。

float** correlation(float** map,nSize mapSize,float** inputData,nSize inSize,int type)// 互相关
{// 这里的互相关是在后向传播时调用,类似于将Map反转180度再卷积// 为了方便计算,这里先将图像扩充一圈// 这里的卷积要分成两拨,偶数模板同奇数模板int i,j,c,r;int halfmapsizew;int halfmapsizeh;if(mapSize.r%2==0&&mapSize.c%2==0){ // 模板大小为偶数halfmapsizew=(mapSize.c)/2; // 卷积模块的半瓣大小halfmapsizeh=(mapSize.r)/2;}else{halfmapsizew=(mapSize.c-1)/2; // 卷积模块的半瓣大小halfmapsizeh=(mapSize.r-1)/2;}// 这里先默认进行full模式的操作,full模式的输出大小为inSize+(mapSize-1)int outSizeW=inSize.c+(mapSize.c-1); // 这里的输出扩大一部分int outSizeH=inSize.r+(mapSize.r-1);float** outputData=(float**)malloc(outSizeH*sizeof(float*)); // 互相关的结果扩大了for(i=0;i<outSizeH;i++)outputData[i]=(float*)calloc(outSizeW,sizeof(float));// 为了方便计算,将inputData扩大一圈float** exInputData=matEdgeExpand(inputData,inSize,mapSize.c-1,mapSize.r-1);for(j=0;j<outSizeH;j++)for(i=0;i<outSizeW;i++)for(r=0;r<mapSize.r;r++)for(c=0;c<mapSize.c;c++){outputData[j][i]=outputData[j][i]+map[r][c]*exInputData[j+r][i+c];}for(i=0;i<inSize.r+2*(mapSize.r-1);i++)free(exInputData[i]);free(exInputData);nSize outSize={outSizeW,outSizeH};switch(type){ // 根据不同的情况,返回不同的结果case full: // 完全大小的情况return outputData;case same:{float** sameres=matEdgeShrink(outputData,outSize,halfmapsizew,halfmapsizeh);for(i=0;i<outSize.r;i++)free(outputData[i]);free(outputData);return sameres;}case valid:{float** validres;if(mapSize.r%2==0&&mapSize.c%2==0)validres=matEdgeShrink(outputData,outSize,halfmapsizew*2-1,halfmapsizeh*2-1);elsevalidres=matEdgeShrink(outputData,outSize,halfmapsizew*2,halfmapsizeh*2);for(i=0;i<outSize.r;i++)free(outputData[i]);free(outputData);return validres;}default:return outputData;}
}

3. 卷积操作

float** cov(float** map,nSize mapSize,float** inputData,nSize inSize,int type) // 卷积操作
{// 卷积操作可以用旋转180度的特征模板相关来求float** flipmap=rotate180(map,mapSize); //旋转180度的特征模板float** res=correlation(flipmap,mapSize,inputData,inSize,type);int i;for(i=0;i<mapSize.r;i++)free(flipmap[i]);free(flipmap);return res;
}

4. 上采样

这个是矩阵的上采样(等值内插),upc及upr是内插倍数

float** UpSample(float** mat,nSize matSize,int upc,int upr)
{ int i,j,m,n;int c=matSize.c;int r=matSize.r;float** res=(float**)malloc((r*upr)*sizeof(float*)); // 结果的初始化for(i=0;i<(r*upr);i++)res[i]=(float*)malloc((c*upc)*sizeof(float));for(j=0;j<r*upr;j=j+upr){for(i=0;i<c*upc;i=i+upc)// 宽的扩充for(m=0;m<upc;m++)res[j][i+m]=mat[j/upr][i/upc];for(n=1;n<upr;n++)      //  高的扩充for(i=0;i<c*upc;i++)res[j+n][i]=res[j][i];}return res;
}

5. 矩阵扩边

给二维矩阵边缘扩大,增加addw大小的0值边

float** matEdgeExpand(float** mat,nSize matSize,int addc,int addr)
{ // 向量边缘扩大int i,j;int c=matSize.c;int r=matSize.r;float** res=(float**)malloc((r+2*addr)*sizeof(float*)); // 结果的初始化for(i=0;i<(r+2*addr);i++)res[i]=(float*)malloc((c+2*addc)*sizeof(float));for(j=0;j<r+2*addr;j++){for(i=0;i<c+2*addc;i++){if(j<addr||i<addc||j>=(r+addr)||i>=(c+addc))res[j][i]=(float)0.0;elseres[j][i]=mat[j-addr][i-addc]; // 复制原向量的数据}}return res;
}

6. 矩阵缩边

给二维矩阵边缘缩小,擦除shrinkc大小的边

float** matEdgeShrink(float** mat,nSize matSize,int shrinkc,int shrinkr)
{ // 向量的缩小,宽缩小addw,高缩小addhint i,j;int c=matSize.c;int r=matSize.r;float** res=(float**)malloc((r-2*shrinkr)*sizeof(float*)); // 结果矩阵的初始化for(i=0;i<(r-2*shrinkr);i++)res[i]=(float*)malloc((c-2*shrinkc)*sizeof(float));for(j=0;j<r;j++){for(i=0;i<c;i++){if(j>=shrinkr&&i>=shrinkc&&j<(r-shrinkr)&&i<(c-shrinkc))res[j-shrinkr][i-shrinkc]=mat[j][i]; // 复制原向量的数据}}return res;
}

7. 保存矩阵

void savemat(float** mat,nSize matSize,const char* filename)
{FILE  *fp=NULL;fp=fopen(filename,"wb");if(fp==NULL)printf("write file failed\n");int i;for(i=0;i<matSize.r;i++)fwrite(mat[i],sizeof(float),matSize.c,fp);fclose(fp);
}

8.矩阵相加

void addmat(float** res, float** mat1, nSize matSize1, float** mat2, nSize matSize2)// 矩阵相加
{int i,j;if(matSize1.c!=matSize2.c||matSize1.r!=matSize2.r)printf("ERROR: Size is not same!");for(i=0;i<matSize1.r;i++)for(j=0;j<matSize1.c;j++)res[i][j]=mat1[i][j]+mat2[i][j];
}

9.矩阵系数相乘

void multifactor(float** res, float** mat, nSize matSize, float factor)// 矩阵乘以系数
{int i,j;for(i=0;i<matSize.r;i++)for(j=0;j<matSize.c;j++)res[i][j]=mat[i][j]*factor;
}

10.矩阵元素求和

float summat(float** mat,nSize matSize) // 矩阵各元素的和
{float sum=0.0;int i,j;for(i=0;i<matSize.r;i++)for(j=0;j<matSize.c;j++)sum=sum+mat[i][j];return sum;
}

3.卷积函数设计

1.卷积结构体设计

#define AvePool 0
#define MaxPool 1
#define MinPool 2// 卷积层
typedef struct convolutional_layer{int inputWidth;   //输入图像的宽int inputHeight;  //输入图像的长int mapSize;      //特征模板的大小,模板一般都是正方形int inChannels;   //输入图像的数目int outChannels;  //输出图像的数目// 关于特征模板的权重分布,这里是一个四维数组// 其大小为inChannels*outChannels*mapSize*mapSize大小// 这里用四维数组,主要是为了表现全连接的形式,实际上卷积层并没有用到全连接的形式// 这里的例子是DeapLearningToolboox里的CNN例子,其用到就是全连接float**** mapData;     //存放特征模块的数据float**** dmapData;    //存放特征模块的数据的局部梯度float* basicData;   //偏置,偏置的大小,为outChannelsbool isFullConnect; //是否为全连接bool* connectModel; //连接模式(默认为全连接)// 下面三者的大小同输出的维度相同float*** v; // 进入激活函数的输入值float*** y; // 激活函数后神经元的输出// 输出像素的局部梯度float*** d; // 网络的局部梯度,δ值
}CovLayer;// 采样层 pooling
typedef struct pooling_layer{int inputWidth;   //输入图像的宽int inputHeight;  //输入图像的长int mapSize;      //特征模板的大小int inChannels;   //输入图像的数目int outChannels;  //输出图像的数目int poolType;     //Pooling的方法float* basicData;   //偏置float*** y; // 采样函数后神经元的输出,无激活函数float*** d; // 网络的局部梯度,δ值
}PoolLayer;// 输出层 全连接的神经网络
typedef struct nn_layer{int inputNum;   //输入数据的数目int outputNum;  //输出数据的数目float** wData; // 权重数据,为一个inputNum*outputNum大小float* basicData;   //偏置,大小为outputNum大小// 下面三者的大小同输出的维度相同float* v; // 进入激活函数的输入值float* y; // 激活函数后神经元的输出float* d; // 网络的局部梯度,δ值bool isFullConnect; //是否为全连接
}OutLayer;typedef struct cnn_network{int layerNum;CovLayer* C1;PoolLayer* S2;CovLayer* C3;PoolLayer* S4;OutLayer* O5;float* e; // 训练误差float* L; // 瞬时误差能量
}CNN;typedef struct train_opts{int numepochs; // 训练的迭代次数float alpha; // 学习速率
}CNNOpts;

2.初始化卷积层

CovLayer* initCovLayer(int inputWidth,int inputHeight,int mapSize,int inChannels,int outChannels)
{CovLayer* covL=(CovLayer*)malloc(sizeof(CovLayer));covL->inputHeight=inputHeight;covL->inputWidth=inputWidth;covL->mapSize=mapSize;covL->inChannels=inChannels;covL->outChannels=outChannels;covL->isFullConnect=true; // 默认为全连接// 权重空间的初始化,先行再列调用,[r][c]int i,j,c,r;srand((unsigned)time(NULL));covL->mapData=(float****)malloc(inChannels*sizeof(float***));for(i=0;i<inChannels;i++){covL->mapData[i]=(float***)malloc(outChannels*sizeof(float**));for(j=0;j<outChannels;j++){covL->mapData[i][j]=(float**)malloc(mapSize*sizeof(float*));for(r=0;r<mapSize;r++){covL->mapData[i][j][r]=(float*)malloc(mapSize*sizeof(float));for(c=0;c<mapSize;c++){float randnum=(((float)rand()/(float)RAND_MAX)-0.5)*2; covL->mapData[i][j][r][c]=randnum*sqrt((float)6.0/(float)(mapSize*mapSize*(inChannels+outChannels)));}}}}// 权重梯度变化covL->dmapData=(float****)malloc(inChannels*sizeof(float***));for(i=0;i<inChannels;i++){covL->dmapData[i]=(float***)malloc(outChannels*sizeof(float**));for(j=0;j<outChannels;j++){covL->dmapData[i][j]=(float**)malloc(mapSize*sizeof(float*));for(r=0;r<mapSize;r++){covL->dmapData[i][j][r]=(float*)calloc(mapSize,sizeof(float));}}}covL->basicData=(float*)calloc(outChannels,sizeof(float));int outW=inputWidth-mapSize+1;int outH=inputHeight-mapSize+1;covL->d=(float***)malloc(outChannels*sizeof(float**));covL->v=(float***)malloc(outChannels*sizeof(float**));covL->y=(float***)malloc(outChannels*sizeof(float**));for(j=0;j<outChannels;j++){covL->d[j]=(float**)malloc(outH*sizeof(float*));covL->v[j]=(float**)malloc(outH*sizeof(float*));covL->y[j]=(float**)malloc(outH*sizeof(float*));for(r=0;r<outH;r++){covL->d[j][r]=(float*)calloc(outW,sizeof(float));covL->v[j][r]=(float*)calloc(outW,sizeof(float));covL->y[j][r]=(float*)calloc(outW,sizeof(float));}}return covL;
}

3.初始化采样层

PoolLayer* initPoolLayer(int inputWidth,int inputHeight,int mapSize,int inChannels,int outChannels,int poolType)
{PoolLayer* poolL=(PoolLayer*)malloc(sizeof(PoolLayer));poolL->inputHeight=inputHeight;poolL->inputWidth=inputWidth;poolL->mapSize=mapSize;poolL->inChannels=inChannels;poolL->outChannels=outChannels;poolL->poolType=poolType; poolL->basicData=(float*)calloc(outChannels,sizeof(float));int outW=inputWidth/mapSize;int outH=inputHeight/mapSize;int j,r;poolL->d=(float***)malloc(outChannels*sizeof(float**));poolL->y=(float***)malloc(outChannels*sizeof(float**));for(j=0;j<outChannels;j++){poolL->d[j]=(float**)malloc(outH*sizeof(float*));poolL->y[j]=(float**)malloc(outH*sizeof(float*));for(r=0;r<outH;r++){poolL->d[j][r]=(float*)calloc(outW,sizeof(float));poolL->y[j][r]=(float*)calloc(outW,sizeof(float));}}return poolL;
}

4.初始化输出层

OutLayer* initOutLayer(int inputNum,int outputNum)
{OutLayer* outL=(OutLayer*)malloc(sizeof(OutLayer));outL->inputNum=inputNum;outL->outputNum=outputNum;outL->basicData=(float*)calloc(outputNum,sizeof(float));outL->d=(float*)calloc(outputNum,sizeof(float));outL->v=(float*)calloc(outputNum,sizeof(float));outL->y=(float*)calloc(outputNum,sizeof(float));// 权重的初始化outL->wData=(float**)malloc(outputNum*sizeof(float*)); // 输入行,输出列int i,j;srand((unsigned)time(NULL));for(i=0;i<outputNum;i++){outL->wData[i]=(float*)malloc(inputNum*sizeof(float));for(j=0;j<inputNum;j++){float randnum=(((float)rand()/(float)RAND_MAX)-0.5)*2; // 产生一个-1到1的随机数outL->wData[i][j]=randnum*sqrt((float)6.0/(float)(inputNum+outputNum));}}outL->isFullConnect=true;return outL;
}

5.激活函数

float activation_Sigma(float input,float bas) // sigma激活函数
{float temp=input+bas;return (float)1.0/((float)(1.0+exp(-temp)));
}

6.前向传播

void cnntrain(CNN* cnn,  ImgArr inputData,LabelArr outputData,CNNOpts opts,int trainNum)
{// 学习训练误差曲线cnn->L=(float*)malloc(trainNum*sizeof(float));int e;for(e=0;e<opts.numepochs;e++){int n=0;for(n=0;n<trainNum;n++){printf("%d\n",n);cnnff(cnn,inputData->ImgPtr[n].ImgData);  // 前向传播,这里主要计算各cnnbp(cnn,outputData->LabelPtr[n].LabelData); // 后向传播,这里主要计算各神经元的误差梯度char* filedir="./data/";const char* filename=combine_strings(filedir,combine_strings(intTochar(n),".cnn"));savecnndata(cnn,filename,inputData->ImgPtr[n].ImgData);cnnapplygrads(cnn,opts,inputData->ImgPtr[n].ImgData); // 更新权重cnnclear(cnn);// 计算并保存误差能量float l=0.0;int i;for(i=0;i<cnn->O5->outputNum;i++)l=l+cnn->e[i]*cnn->e[i];if(n==0)cnn->L[n]=l/(float)2.0;elsecnn->L[n]=cnn->L[n-1]*0.99+0.01*l/(float)2.0;}}
}

7.模型暴露和保存


// 保存cnn
void savecnn(CNN* cnn, const char* filename)
{FILE  *fp=NULL;fp=fopen(filename,"wb");if(fp==NULL)printf("write file failed\n");int i,j,r;// C1的数据for(i=0;i<cnn->C1->inChannels;i++)for(j=0;j<cnn->C1->outChannels;j++)for(r=0;r<cnn->C1->mapSize;r++)fwrite(cnn->C1->mapData[i][j][r],sizeof(float),cnn->C1->mapSize,fp);fwrite(cnn->C1->basicData,sizeof(float),cnn->C1->outChannels,fp);// C3网络for(i=0;i<cnn->C3->inChannels;i++)for(j=0;j<cnn->C3->outChannels;j++)for(r=0;r<cnn->C3->mapSize;r++)fwrite(cnn->C3->mapData[i][j][r],sizeof(float),cnn->C3->mapSize,fp);fwrite(cnn->C3->basicData,sizeof(float),cnn->C3->outChannels,fp);// O5输出层for(i=0;i<cnn->O5->outputNum;i++)fwrite(cnn->O5->wData[i],sizeof(float),cnn->O5->inputNum,fp);fwrite(cnn->O5->basicData,sizeof(float),cnn->O5->outputNum,fp);fclose(fp);
}
// 导入cnn的数据
void importcnn(CNN* cnn, const char* filename)
{FILE  *fp=NULL;fp=fopen(filename,"rb");if(fp==NULL)printf("write file failed\n");int i,j,c,r;// C1的数据for(i=0;i<cnn->C1->inChannels;i++)for(j=0;j<cnn->C1->outChannels;j++)for(r=0;r<cnn->C1->mapSize;r++)for(c=0;c<cnn->C1->mapSize;c++){float* in=(float*)malloc(sizeof(float));fread(in,sizeof(float),1,fp);cnn->C1->mapData[i][j][r][c]=*in;}for(i=0;i<cnn->C1->outChannels;i++)fread(&cnn->C1->basicData[i],sizeof(float),1,fp);// C3网络for(i=0;i<cnn->C3->inChannels;i++)for(j=0;j<cnn->C3->outChannels;j++)for(r=0;r<cnn->C3->mapSize;r++)for(c=0;c<cnn->C3->mapSize;c++)fread(&cnn->C3->mapData[i][j][r][c],sizeof(float),1,fp);for(i=0;i<cnn->C3->outChannels;i++)fread(&cnn->C3->basicData[i],sizeof(float),1,fp);// O5输出层for(i=0;i<cnn->O5->outputNum;i++)for(j=0;j<cnn->O5->inputNum;j++)fread(&cnn->O5->wData[i][j],sizeof(float),1,fp);for(i=0;i<cnn->O5->outputNum;i++)fread(&cnn->O5->basicData[i],sizeof(float),1,fp);fclose(fp);
}

四、模型构建

void cnnsetup(CNN* cnn,nSize inputSize,int outputSize)
{cnn->layerNum=5;nSize inSize;int mapSize=5;inSize.c=inputSize.c;inSize.r=inputSize.r;cnn->C1=initCovLayer(inSize.c,inSize.r,5,1,6);inSize.c=inSize.c-mapSize+1;inSize.r=inSize.r-mapSize+1;cnn->S2=initPoolLayer(inSize.c,inSize.r,2,6,6,AvePool);inSize.c=inSize.c/2;inSize.r=inSize.r/2;cnn->C3=initCovLayer(inSize.c,inSize.r,5,6,12);inSize.c=inSize.c-mapSize+1;inSize.r=inSize.r-mapSize+1;cnn->S4=initPoolLayer(inSize.c,inSize.r,2,12,12,AvePool);inSize.c=inSize.c/2;inSize.r=inSize.r/2;cnn->O5=initOutLayer(inSize.c*inSize.r*12,outputSize);cnn->e=(float*)calloc(cnn->O5->outputNum,sizeof(float));
}

五、main函数

1.训练

LabelArr trainLabel=read_Lable("./data/train-labels.idx1-ubyte");ImgArr trainImg=read_Img("./data/train-images.idx3-ubyte");LabelArr testLabel=read_Lable("./data/t10k-labels.idx1-ubyte");ImgArr testImg=read_Img("./data/t10k-images.idx3-ubyte");nSize inputSize={testImg->ImgPtr[0].c,testImg->ImgPtr[0].r};int outSize=testLabel->LabelPtr[0].l;// CNN结构的初始化CNN* cnn=(CNN*)malloc(sizeof(CNN));cnnsetup(cnn,inputSize,outSize);// CNN训练CNNOpts opts;opts.numepochs=1;opts.alpha=1.0;int trainNum=55000;cnntrain(cnn,trainImg,trainLabel,opts,trainNum);printf("train finished!!\n");savecnn(cnn,"./data/minst.cnn");// 保存训练误差FILE  *fp=NULL;fp=fopen("./data/cnnL.ma","wb");if(fp==NULL)printf("write file failed\n");fwrite(cnn->L,sizeof(float),trainNum,fp);fclose(fp);

2.测试

// CNN测试importcnn(cnn,"minst.cnn");int testNum=10000;float incorrectRatio=0.0;incorrectRatio=cnntest(cnn,testImg,testLabel,testNum);printf("test finished!!\n");

总结

CNN源码

c++ 实现深度学习网络结构【附源码】相关推荐

  1. 深度学习框架Caffe源码解析

    作者:薛云峰(https://github.com/HolidayXue),主要从事视频图像算法的研究, 本文来源微信公众号:深度学习大讲堂.  原文:深度学习框架Caffe源码解析  欢迎技术投稿. ...

  2. java中batch基础_详解Spring batch 入门学习教程(附源码)

    详解Spring batch 入门学习教程(附源码) 发布时间:2020-09-08 00:28:40 来源:脚本之家 阅读:99 作者:achuo Spring batch 是一个开源的批处理框架. ...

  3. 深度学习03-sklearn.LinearRegression 源码学习

    在上次的代码重写中使用了sklearn.LinearRegression 类进行了线性回归之后猜测其使用的是常用的梯度下降+反向传播算法实现,所以今天来学习它的源码实现.但是在看到源码的一瞬间突然有种 ...

  4. 使用RNN神经网络自动生成名字 (不使用深度学习框架,源码)

    本文讲解在不使用深度学习框架的情况下,构建一个基本的RNN神经网络来进行名字自动生成.RNN模型请看下面的三张图片.本文主要讲解数据集以及输入模型的数据格式. 数据集和可执行的源码下载地址:https ...

  5. [WinForm]Dundas Chart控件学习(附源码)

    1.Dundas公司简介 加拿大的一家公司,专业做图表展现的,很牛,据说现在被Microsoft收购了.官网地址:http://www.dundas.com/ 2.Chart基本要素 3.最简单的柱状 ...

  6. 2022年新出的Python学习神器(附源码)

    大家好,今天给大家隆重介绍一下我的朋友俊欣,目前就职于魔都的一家互联网初创公司,有着丰富地海外留学经验,并且还去过20多个国家游学.旅游,而他的公众号:关于数据分析与可视化,已经累积了120+篇的原创 ...

  7. 深度学习实战-从源码解密AlphGo Zero背后基本原理

    (本文由深度学习与NLP编译) DeepMind在强化学习领域具有非常重要的作用,其创造了举世震惊的AI智能AlphaGo,以及后来的AlphaGo Zero.这是第一个在19 x 19棋盘上打败人类 ...

  8. 图像纹理合成及纹理传输算法学习(附源码)。

    有2到3年没有逛CodeProject了,上班一时无聊,就翻翻这个比较有名的国外网站,在其Articles » Multimedia » General Graphics » Graphics一栏看到 ...

  9. 前海征信“好信杯”大数据算法竞赛 - HM队【附源码】 原创 2017-06-17 高铭 科赛Kesci 赛题回顾 自2006年Hinton等人提出“深度学习”概念至今,深度学习在海量数据的挖

    前海征信"好信杯"大数据算法竞赛 - H&M队[附源码] 原创 2017-06-17 高铭 科赛Kesci 赛题回顾 自2006年Hinton等人提出"深度学习& ...

  10. 【附源码】计算机毕业设计JAVA智友少儿编程学习平台

    [附源码]计算机毕业设计JAVA智友少儿编程学习平台 目运行 环境项配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(In ...

最新文章

  1. #pragma pack 内存对齐
  2. 【青少年编程】【Scratch】04 事件模块
  3. windows RabbitMq 安装
  4. 【NLP】bert4vec:一个基于预训练的句向量生成工具
  5. CSS美化网页元素大全
  6. UML小结以及基于领域模型的系统设计初步
  7. 普通二本的辛酸Android面试之路,满满干货指导
  8. leetcode1277. 统计全为 1 的正方形子矩阵(dp)
  9. 多生产者-多消费者问题
  10. UBIFS - UBI File-System
  11. 【待解决】使用JUnit时报错java.lang
  12. nginx 如何处理请求系列3-server_name指令
  13. java jni编译_从源码编译Android系统的Java类库和JNI动态库的方法
  14. R|数据处理|list的转化与转置
  15. 金融计量学第一次实验:eviews做多元线性回归分析
  16. mysql概念模型中的3种基本联系_数据库建模三步骤:概念模型
  17. 【IoT】产品设计:OEM、ODM、EMS 的区别是什么?
  18. linux sed尾行符号,用sed流编辑器处理特殊符号
  19. C# winfrom 在button按钮上显示箭头
  20. MACBOOK快捷键输入

热门文章

  1. 搜苹果ipad版_iPad抠图比PC更给力 iPad版PS的自动抠图神了
  2. 关于火狐浏览器开发者工具使用
  3. Android设置横屏
  4. python计算波峰波谷值的方法(极值点)
  5. 电力六氟化硫SF6气体监测仪器无线lora传输
  6. Centos7上Hadoop 3.3.1的高可用HA安装过程
  7. 国家二级c语言程序设计技巧,计算机等级考试二级C语言程序设计技巧.doc
  8. 首先定义一个Point (点)类,包含属性x,y(x,y为坐标点),方法有setPoint、getX、getY和OprintInfo...python编程题练习
  9. 一门改考三门!西安电子科技大学网络与信息安全学院
  10. 4万高考冒名顶替事件_高考生冒名顶替上大学事件内幕调查