卷积神经网络(CNN)代码实现(MNIST)解析共7层:依次为输入层、C1层、S2层、C3层、S4层、C5层、输出层,C代表卷积层(特征提取),S代表降采样层或池化层(Pooling),输出层为全连接层。1.各层权值、偏置(阈值)初始化:各层权值、偏置个数计算如下:(1)、输入层:预处理后的32*32图像数据,无权值和偏置;(2)、C1层:卷积窗大小5*5,输出特征图数量6,卷积窗种类1*6=6,输出特征图大小28*28,因此可训练参数(权值+偏置):(5*5*1)*6+6=150+6;(3)、S2层:卷积窗大小2*2,输出下采样图数量6,卷积窗种类6,输出下采样图大小14*14,因此可训练参数(权值+偏置):1*6+6=6+6;(4)、C3层:卷积窗大小5*5,输出特征图数量16,卷积窗种类6*16=96,输出特征图大小10*10,因此可训练参数(权值+偏置):(5*5*6)*16+16=2400+16;(5)、S4层:卷积窗大小2*2,输出下采样图数量16,卷积窗种类16,输出下采样图大小5*5,因此可训练参数(权值+偏置):1*16+16=16+16;(6)、C5层:卷积窗大小5*5,输出特征图数量120,卷积窗种类16*120=1920,输出特征图大小1*1,因此可训练参数(权值+偏置):(5*5*16)*120+120=48000+120;(7)、输出层:卷积窗大小1*1,输出特征图数量10,卷积窗种类120*10=1200,输出特征图大小1*1,因此可训练参数(权值+偏置):(1*120)*10+10=1200+10.代码段如下:[cpp]viewplaincopy#definenum_map_input_CNN1//输入层map个数#definenum_map_C1_CNN6//C1层map个数#definenum_map_S2_CNN6//S2层map个数#definenum_map_C3_CNN16//C3层map个数#definenum_map_S4_CNN16//S4层map个数#definenum_map_C5_CNN120//C5层map个数#definenum_map_output_CNN10//输出层map个数#definelen_weight_C1_CNN150//C1层权值数,(5*5*1)*6=150#definelen_bias_C1_CNN6//C1层阈值数,6#definelen_weight_S2_CNN6//S2层权值数,1*6=6#definelen_bias_S2_CNN6//S2层阈值数,6#definelen_weight_C3_CNN2400//C3层权值数,(5*5*6)*16=2400#definelen_bias_C3_CNN16//C3层阈值数,16#definelen_weight_S4_CNN16//S4层权值数,1*16=16#definelen_bias_S4_CNN16//S4层阈值数,16#definelen_weight_C5_CNN48000//C5层权值数,(5*5*16)*120=48000#definelen_bias_C5_CNN120//C5层阈值数,120#definelen_weight_output_CNN1200//输出层权值数,(1*120)*10=1200#definelen_bias_output_CNN10//输出层阈值数,10#definenum_neuron_input_CNN1024//输入层神经元数,(32*32)*1=1024#definenum_neuron_C1_CNN4704//C1层神经元数,(28*28)*6=4704#definenum_neuron_S2_CNN1176//S2层神经元数,(14*14)*6=1176#definenum_neuron_C3_CNN1600//C3层神经元数,(10*10)*16=1600#definenum_neuron_S4_CNN400//S4层神经元数,(5*5)*16=400#definenum_neuron_C5_CNN120//C5层神经元数,(1*1)*120=120#definenum_neuron_output_CNN10//输出层神经元数,(1*1)*10=10权值、偏置初始化:(1)、权值使用函数uniform_real_distribution均匀分布初始化,tiny-cnn中每次初始化权值数值都相同,这里作了调整,使每次初始化的权值均不同。每层权值初始化大小范围都不一样;(2)、所有层的偏置均初始化为0.代码段如下:[cpp]viewplaincopydoubleCNN::uniform_rand(doublemin,doublemax){//staticstd::mt19937gen(1);std::random_devicerd;std::mt19937gen(rd());std::uniform_real_distributiondoubledst(min,max);returndst(gen);}boolCNN::uniform_rand(double*src,intlen,doublemin,doublemax){for(inti=0;ilen;i++){src[i]=uniform_rand(min,max);}returntrue;}boolCNN::initWeightThreshold(){srand(time(0)+rand());constdoublescale=6.0;doublemin_=-std::sqrt(scale/(25.0+150.0));doublemax_=std::sqrt(scale/(25.0+150.0));uniform_rand(weight_C1,len_weight_C1_CNN,min_,max_);for(inti=0;ilen_bias_C1_CNN;i++){bias_C1[i]=0.0;}min_=-std::sqrt(scale/(4.0+1.0));max_=std::sqrt(scale/(4.0+1.0));uniform_rand(weight_S2,len_weight_S2_CNN,min_,max_);for(inti=0;ilen_bias_S2_CNN;i++){bias_S2[i]=0.0;}min_=-std::sqrt(scale/(150.0+400.0));max_=std::sqrt(scale/(150.0+400.0));uniform_rand(weight_C3,len_weight_C3_CNN,min_,max_);for(inti=0;ilen_bias_C3_CNN;i++){bias_C3[i]=0.0;}min_=-std::sqrt(scale/(4.0+1.0));max_=std::sqrt(scale/(4.0+1.0));uniform_rand(weight_S4,len_weight_S4_CNN,min_,max_);for(inti=0;ilen_bias_S4_CNN;i++){bias_S4[i]=0.0;}min_=-std::sqrt(scale/(400.0+3000.0));max_=std::sqrt(scale/(400.0+3000.0));uniform_rand(weight_C5,len_weight_C5_CNN,min_,max_);for(inti=0;ilen_bias_C5_CNN;i++){bias_C5[i]=0.0;}min_=-std::sqrt(scale/(120.0+10.0));max_=std::sqrt(scale/(120.0+10.0));uniform_rand(weight_output,len_weight_output_CNN,min_,max_);for(inti=0;ilen_bias_output_CNN;i++){bias_output[i]=0.0;}returntrue;}2.加载MNIST数据:关于MNIST的介绍可以参考:使用MNIST库作为训练集和测试集,训练样本集为60000个,测试样本集为10000个。(1)、MNIST库中图像原始大小为28*28,这里缩放为32*32,数据取值范围为[-1,1],扩充值均取-1,作为输入层输入数据。代码段如下:[cpp]viewplaincopystaticvoidreadMnistImages(std::stringfilename,double*data_dst,intnum_image){constintwidth_src_image=28;constintheight_src_image=28;constintx_padding=2;constinty_padding=2;constdoublescale_min=-1;constdoublescale_max=1;std::ifstreamfile(filename,std::ios::binary);assert(file.is_open());intmagic_number=0;intnumber_of_images=0;intn_rows=0;intn_cols=0;file.read((char*)&magic_number,sizeof(magic_number));magic_number=reverseInt(magic_number);file.read((char*)&number_of_images,sizeof(number_of_images));number_of_images=reverseInt(number_of_images);assert(number_of_images==num_image);file.read((char*)&n_rows,sizeof(n_rows));n_rows=reverseInt(n_rows);file.read((char*)&n_cols,sizeof(n_cols));n_cols=reverseInt(n_cols);assert(n_rows==height_src_image&&n_cols==width_src_image);intsize_single_image=width_image_input_CNN*height_image_input_CNN;for(inti=0;inumber_of_images;++i){intaddr=size_single_image*i;for(intr=0;rn_rows;++r){for(intc=0;cn_cols;++c){unsignedchartemp=0;file.read((char*)&temp,sizeof(temp));data_dst[addr+width_image_input_CNN*(r+y_padding)+c+x_padding]=(temp/255.0)*(scale_max-scale_min)+scale_min;}}}}(2)、对于Label,输出层有10个节点,对应位置的节点值设为0.8,其它节点设为-0.8,作为输出层数据。代码段如下:[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片staticvoidreadMnistLabels(std::stringfilename,double*data_dst,intnum_image){constdoublescale_max=0.8;std::ifstreamfile(filename,std::ios::binary);asser