当前位置:   article > 正文

卷积神经网络(CNN)代码实现(MNIST)解析_cnn超参数训练代码(三维图像)

cnn超参数训练代码(三维图像)

http://blog.csdn.net/fengbingchun/article/details/50814710中给出了CNN的简单实现,这里对每一步的实现作个说明:

共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.

         代码段如下:

  1. #define num_map_input_CNN 1 //输入层map个数
  2. #define num_map_C1_CNN 6 //C1层map个数
  3. #define num_map_S2_CNN 6 //S2层map个数
  4. #define num_map_C3_CNN 16 //C3层map个数
  5. #define num_map_S4_CNN 16 //S4层map个数
  6. #define num_map_C5_CNN 120 //C5层map个数
  7. #define num_map_output_CNN 10 //输出层map个数
  8. #define len_weight_C1_CNN 150 //C1层权值数,(5*5*1)*6=150
  9. #define len_bias_C1_CNN 6 //C1层阈值数,6
  10. #define len_weight_S2_CNN 6 //S2层权值数,1*6=6
  11. #define len_bias_S2_CNN 6 //S2层阈值数,6
  12. #define len_weight_C3_CNN 2400 //C3层权值数,(5*5*6)*16=2400
  13. #define len_bias_C3_CNN 16 //C3层阈值数,16
  14. #define len_weight_S4_CNN 16 //S4层权值数,1*16=16
  15. #define len_bias_S4_CNN 16 //S4层阈值数,16
  16. #define len_weight_C5_CNN 48000 //C5层权值数,(5*5*16)*120=48000
  17. #define len_bias_C5_CNN 120 //C5层阈值数,120
  18. #define len_weight_output_CNN 1200 //输出层权值数,(1*120)*10=1200
  19. #define len_bias_output_CNN 10 //输出层阈值数,10
  20. #define num_neuron_input_CNN 1024 //输入层神经元数,(32*32)*1=1024
  21. #define num_neuron_C1_CNN 4704 //C1层神经元数,(28*28)*6=4704
  22. #define num_neuron_S2_CNN 1176 //S2层神经元数,(14*14)*6=1176
  23. #define num_neuron_C3_CNN 1600 //C3层神经元数,(10*10)*16=1600
  24. #define num_neuron_S4_CNN 400 //S4层神经元数,(5*5)*16=400
  25. #define num_neuron_C5_CNN 120 //C5层神经元数,(1*1)*120=120
  26. #define num_neuron_output_CNN 10 //输出层神经元数,(1*1)*10=10

         权值、偏置初始化:

(1)、权值使用函数uniform_real_distribution均匀分布初始化,tiny-cnn中每次初始化权值数值都相同,这里作了调整,使每次初始化的权值均不同。每层权值初始化大小范围都不一样;

(2)、所有层的偏置均初始化为0.

         代码段如下:

  1. double CNN::uniform_rand(double min, double max)
  2. {
  3. //static std::mt19937 gen(1);
  4. std::random_device rd;
  5. std::mt19937 gen(rd());
  6. std::uniform_real_distribution<double> dst(min, max);
  7. return dst(gen);
  8. }
  9. bool CNN::uniform_rand(double* src, int len, double min, double max)
  10. {
  11. for (int i = 0; i < len; i++) {
  12. src[i] = uniform_rand(min, max);
  13. }
  14. return true;
  15. }
  16. bool CNN::initWeightThreshold()
  17. {
  18. srand(time(0) + rand());
  19. const double scale = 6.0;
  20. double min_ = -std::sqrt(scale / (25.0 + 150.0));
  21. double max_ = std::sqrt(scale / (25.0 + 150.0));
  22. uniform_rand(weight_C1, len_weight_C1_CNN, min_, max_);
  23. for (int i = 0; i < len_bias_C1_CNN; i++) {
  24. bias_C1[i] = 0.0;
  25. }
  26. min_ = -std::sqrt(scale / (4.0 + 1.0));
  27. max_ = std::sqrt(scale / (4.0 + 1.0));
  28. uniform_rand(weight_S2, len_weight_S2_CNN, min_, max_);
  29. for (int i = 0; i < len_bias_S2_CNN; i++) {
  30. bias_S2[i] = 0.0;
  31. }
  32. min_ = -std::sqrt(scale / (150.0 + 400.0));
  33. max_ = std::sqrt(scale / (150.0 + 400.0));
  34. uniform_rand(weight_C3, len_weight_C3_CNN, min_, max_);
  35. for (int i = 0; i < len_bias_C3_CNN; i++) {
  36. bias_C3[i] = 0.0;
  37. }
  38. min_ = -std::sqrt(scale / (4.0 + 1.0));
  39. max_ = std::sqrt(scale / (4.0 + 1.0));
  40. uniform_rand(weight_S4, len_weight_S4_CNN, min_, max_);
  41. for (int i = 0; i < len_bias_S4_CNN; i++) {
  42. bias_S4[i] = 0.0;
  43. }
  44. min_ = -std::sqrt(scale / (400.0 + 3000.0));
  45. max_ = std::sqrt(scale / (400.0 + 3000.0));
  46. uniform_rand(weight_C5, len_weight_C5_CNN, min_, max_);
  47. for (int i = 0; i < len_bias_C5_CNN; i++) {
  48. bias_C5[i] = 0.0;
  49. }
  50. min_ = -std::sqrt(scale / (120.0 + 10.0));
  51. max_ = std::sqrt(scale / (120.0 + 10.0));
  52. uniform_rand(weight_output, len_weight_output_CNN, min_, max_);
  53. for (int i = 0; i < len_bias_output_CNN; i++) {
  54. bias_output[i] = 0.0;
  55. }
  56. return true;
  57. }

2.        加载MNIST数据:

关于MNIST的介绍可以参考:http://blog.csdn.net/fengbingchun/article/details/49611549

使用MNIST库作为训练集和测试集,训练样本集为60000个,测试样本集为10000个。

(1)、MNIST库中图像原始大小为28*28,这里缩放为32*32,数据取值范围为[-1,1],扩充值均取-1,作为输入层输入数据。

代码段如下:

  1. static void readMnistImages(std::string filename, double* data_dst, int num_image)
  2. {
  3. const int width_src_image = 28;
  4. const int height_src_image = 28;
  5. const int x_padding = 2;
  6. const int y_padding = 2;
  7. const double scale_min = -1;
  8. const double scale_max = 1;
  9. std::ifstream file(filename, std::ios::binary);
  10. assert(file.is_open());
  11. int magic_number = 0;
  12. int number_of_images = 0;
  13. int n_rows = 0;
  14. int n_cols = 0;
  15. file.read((char*)&magic_number, sizeof(magic_number));
  16. magic_number = reverseInt(magic_number);
  17. file.read((char*)&number_of_images, sizeof(number_of_images));
  18. number_of_images = reverseInt(number_of_images);
  19. assert(number_of_images == num_image);
  20. file.read((char*)&n_rows, sizeof(n_rows));
  21. n_rows = reverseInt(n_rows);
  22. file.read((char*)&n_cols, sizeof(n_cols));
  23. n_cols = reverseInt(n_cols);
  24. assert(n_rows == height_src_image && n_cols == width_src_image);
  25. int size_single_image = width_image_input_CNN * height_image_input_CNN;
  26. for (int i = 0; i < number_of_images; ++i) {
  27. int addr = size_single_image * i;
  28. for (int r = 0; r < n_rows; ++r) {
  29. for (int c = 0; c < n_cols; ++c) {
  30. unsigned char temp = 0;
  31. file.read((char*)&temp, sizeof(temp));
  32. data_dst[addr + width_image_input_CNN * (r + y_padding) + c + x_padding] = (temp / 255.0) * (scale_max - scale_min) + scale_min;
  33. }
  34. }
  35. }
  36. }

(2)、对于Label,输出层有10个节点,对应位置的节点值设为0.8,其它节点设为-0.8,作为输出层数据。

代码段如下:

  1. static void readMnistLabels(std::string filename, double* data_dst, int num_image)
  2. {
  3. const double scale_max = 0.8;
  4. std::ifstream file(filename, std::ios::binary);
  5. assert(file.is_open());
  6. int magic_number = 0;
  7. int number_of_images = 0;
  8. file.read((char*)&magic_number, sizeof(magic_number));
  9. magic_number = reverseInt(magic_number);
  10. file.read((char*)&number_of_images, sizeof(number_of_images));
  11. number_of_images = reverseInt(number_of_images);
  12. assert(number_of_images == num_image);
  13. for (int i = 0; i < number_of_images; ++i) {
  14. unsigned char temp = 0;
  15. file.read((char*)&temp, sizeof(temp));
  16. data_dst[i * num_map_output_CNN + temp] = scale_max;
  17. }
  18. }static void readMnistLabels(std::string filename, double* data_dst, int num_image)
  19. {
  20. const double scale_max = 0.8;
  21. std::ifstream file(filename, std::ios::binary);
  22. assert(file.is_open());
  23. int magic_number = 0;
  24. int number_of_images = 0;
  25. file.read((char*)&magic_number, sizeof(magic_number));
  26. magic_number = reverseInt(magic_number);
  27. file.read((char*)&number_of_images, sizeof(number_of_images));
  28. number_of_images = reverseInt(number_of_images);
  29. assert(number_of_images == num_image);
  30. for (int i = 0; i < number_of_images; ++i) {
  31. unsigned char temp = 0;
  32. file.read((char*)&temp, sizeof(temp));
  33. data_dst[i * num_map_output_CNN + temp] = scale_max;
  34. }
  35. }

3.        前向传播:主要计算每层的神经元值;其中C1层、C3层、C5层操作过程相同;S2层、S4层操作过程相同。

(1)、输入层:神经元数为(32*32)*1=1024。

(2)、C1层:神经元数为(28*28)*6=4704,分别用每一个5*5的卷积图像去乘以32*32的图像,获得一个28*28的图像,即对应位置相加再求和,stride长度为1;一共6个5*5的卷积图像,然后对每一个神经元加上一个阈值,最后再通过tanh激活函数对每一神经元进行运算得到最终每一个神经元的结果。

激活函数的作用:它是用来加入非线性因素的,解决线性模型所不能解决的问题,提供网络的非线性建模能力。如果没有激活函数,那么该网络仅能够表达线性映射,此时即便有再多的隐藏层,其整个网络跟单层神经网络也是等价的。因此也可以认为,只有加入了激活函数之后,深度神经网络才具备了分层的非线性映射学习能力。

代码段如下:

  1. double CNN::activation_function_tanh(double x)
  2. {
  3. double ep = std::exp(x);
  4. double em = std::exp(-x);
  5. return (ep - em) / (ep + em);
  6. }
  7. bool CNN::Forward_C1()
  8. {
  9. init_variable(neuron_C1, 0.0, num_neuron_C1_CNN);
  10. for (int o = 0; o < num_map_C1_CNN; o++) {
  11. for (int inc = 0; inc < num_map_input_CNN; inc++) {
  12. int addr1 = get_index(0, 0, num_map_input_CNN * o + inc, width_kernel_conv_CNN, height_kernel_conv_CNN, num_map_C1_CNN * num_map_input_CNN);
  13. int addr2 = get_index(0, 0, inc, width_image_input_CNN, height_image_input_CNN, num_map_input_CNN);
  14. int addr3 = get_index(0, 0, o, width_image_C1_CNN, height_image_C1_CNN, num_map_C1_CNN);
  15. const double* pw = &weight_C1[0] + addr1;
  16. const double* pi = data_single_image + addr2;
  17. double* pa = &neuron_C1[0] + addr3;
  18. for (int y = 0; y < height_image_C1_CNN; y++) {
  19. for (int x = 0; x < width_image_C1_CNN; x++) {
  20. const double* ppw = pw;
  21. const double* ppi = pi + y * width_image_input_CNN + x;
  22. double sum = 0.0;
  23. for (int wy = 0; wy < height_kernel_conv_CNN; wy++) {
  24. for (int wx = 0; wx < width_kernel_conv_CNN; wx++) {
  25. sum += *ppw++ * ppi[wy * width_image_input_CNN + wx];
  26. }
  27. }
  28. pa[y * width_image_C1_CNN + x] += sum;
  29. }
  30. }
  31. }
  32. int addr3 = get_index(0, 0, o, width_image_C1_CNN, height_image_C1_CNN, num_map_C1_CNN);
  33. double* pa = &neuron_C1[0] + addr3;
  34. double b = bias_C1[o];
  35. for (int y = 0; y < height_image_C1_CNN; y++) {
  36. for (int x = 0; x < width_image_C1_CNN; x++) {
  37. pa[y * width_image_C1_CNN + x] += b;
  38. }
  39. }
  40. }
  41. for (int i = 0; i < num_neuron_C1_CNN; i++) {
  42. neuron_C1[i] = activation_function_tanh(neuron_C1[i]);
  43. }
  44. return true;
  45. }

(3)、S2层:神经元数为(14*14)*6=1176,对C1中6个28*28的特征图生成6个14*14的下采样图,相邻四个神经元分别乘以同一个权值再进行相加求和,再求均值即除以4,然后再加上一个阈值,最后再通过tanh激活函数对每一神经元进行运算得到最终每一个神经元的结果。

代码段如下:

  1. bool CNN::Forward_S2()
  2. {
  3. init_variable(neuron_S2, 0.0, num_neuron_S2_CNN);
  4. double scale_factor = 1.0 / (width_kernel_pooling_CNN * height_kernel_pooling_CNN);
  5. assert(out2wi_S2.size() == num_neuron_S2_CNN);
  6. assert(out2bias_S2.size() == num_neuron_S2_CNN);
  7. for (int i = 0; i < num_neuron_S2_CNN; i++) {
  8. const wi_connections& connections = out2wi_S2[i];
  9. neuron_S2[i] = 0;
  10. for (int index = 0; index < connections.size(); index++) {
  11. neuron_S2[i] += weight_S2[connections[index].first] * neuron_C1[connections[index].second];
  12. }
  13. neuron_S2[i] *= scale_factor;
  14. neuron_S2[i] += bias_S2[out2bias_S2[i]];
  15. }
  16. for (int i = 0; i < num_neuron_S2_CNN; i++) {
  17. neuron_S2[i] = activation_function_tanh(neuron_S2[i]);
  18. }
  19. return true;
  20. }

(4)、C3层:神经元数为(10*10)*16=1600,C3层实现方式与C1层完全相同,由S2中的6个14*14下采样图生成16个10*10特征图,对于生成的每一个10*10的特征图,是由6个5*5的卷积图像去乘以6个14*14的下采样图,然后对应位置相加求和,然后对每一个神经元加上一个阈值,最后再通过tanh激活函数对每一神经元进行运算得到最终每一个神经元的结果。

也可按照Y.Lecun给出的表进行计算,即对于生成的每一个10*10的特征图,是由n个5*5的卷积图像去乘以n个14*14的下采样图,其中n是小于6的,即不完全连接。这样做的原因:第一,不完全的连接机制将连接的数量保持在合理的范围内。第二,也是最重要的,其破坏了网络的对称性。由于不同的特征图有不同的输入,所以迫使他们抽取不同的特征。

代码段如下:

  1. // connection table [Y.Lecun, 1998 Table.1]
  2. #define O true
  3. #define X false
  4. static const bool tbl[6][16] = {
  5. O, X, X, X, O, O, O, X, X, O, O, O, O, X, O, O,
  6. O, O, X, X, X, O, O, O, X, X, O, O, O, O, X, O,
  7. O, O, O, X, X, X, O, O, O, X, X, O, X, O, O, O,
  8. X, O, O, O, X, X, O, O, O, O, X, X, O, X, O, O,
  9. X, X, O, O, O, X, X, O, O, O, O, X, O, O, X, O,
  10. X, X, X, O, O, O, X, X, O, O, O, O, X, O, O, O
  11. };
  12. #undef O
  13. #undef X
  14. bool CNN::Forward_C3()
  15. {
  16. init_variable(neuron_C3, 0.0, num_neuron_C3_CNN);
  17. for (int o = 0; o < num_map_C3_CNN; o++) {
  18. for (int inc = 0; inc < num_map_S2_CNN; inc++) {
  19. if (!tbl[inc][o]) continue;
  20. int addr1 = get_index(0, 0, num_map_S2_CNN * o + inc, width_kernel_conv_CNN, height_kernel_conv_CNN, num_map_C3_CNN * num_map_S2_CNN);
  21. int addr2 = get_index(0, 0, inc, width_image_S2_CNN, height_image_S2_CNN, num_map_S2_CNN);
  22. int addr3 = get_index(0, 0, o, width_image_C3_CNN, height_image_C3_CNN, num_map_C3_CNN);
  23. const double* pw = &weight_C3[0] + addr1;
  24. const double* pi = &neuron_S2[0] + addr2;
  25. double* pa = &neuron_C3[0] + addr3;
  26. for (int y = 0; y < height_image_C3_CNN; y++) {
  27. for (int x = 0; x < width_image_C3_CNN; x++) {
  28. const double* ppw = pw;
  29. const double* ppi = pi + y * width_image_S2_CNN + x;
  30. double sum = 0.0;
  31. for (int wy = 0; wy < height_kernel_conv_CNN; wy++) {
  32. for (int wx = 0; wx < width_kernel_conv_CNN; wx++) {
  33. sum += *ppw++ * ppi[wy * width_image_S2_CNN + wx];
  34. }
  35. }
  36. pa[y * width_image_C3_CNN + x] += sum;
  37. }
  38. }
  39. }
  40. int addr3 = get_index(0, 0, o, width_image_C3_CNN, height_image_C3_CNN, num_map_C3_CNN);
  41. double* pa = &neuron_C3[0] + addr3;
  42. double b = bias_C3[o];
  43. for (int y = 0; y < height_image_C3_CNN; y++) {
  44. for (int x = 0; x < width_image_C3_CNN; x++) {
  45. pa[y * width_image_C3_CNN + x] += b;
  46. }
  47. }
  48. }
  49. for (int i = 0; i < num_neuron_C3_CNN; i++) {
  50. neuron_C3[i] = activation_function_tanh(neuron_C3[i]);
  51. }
  52. return true;
  53. }

(5)、S4层:神经元数为(5*5)*16=400,S4层实现方式与S2层完全相同,由C3中16个10*10的特征图生成16个5*5下采样图,相邻四个神经元分别乘以同一个权值再进行相加求和,再求均值即除以4,然后再加上一个阈值,最后再通过tanh激活函数对每一神经元进行运算得到最终每一个神经元的结果。

代码段如下:

  1. bool CNN::Forward_S4()
  2. {
  3. double scale_factor = 1.0 / (width_kernel_pooling_CNN * height_kernel_pooling_CNN);
  4. init_variable(neuron_S4, 0.0, num_neuron_S4_CNN);
  5. assert(out2wi_S4.size() == num_neuron_S4_CNN);
  6. assert(out2bias_S4.size() == num_neuron_S4_CNN);
  7. for (int i = 0; i < num_neuron_S4_CNN; i++) {
  8. const wi_connections& connections = out2wi_S4[i];
  9. neuron_S4[i] = 0.0;
  10. for (int index = 0; index < connections.size(); index++) {
  11. neuron_S4[i] += weight_S4[connections[index].first] * neuron_C3[connections[index].second];
  12. }
  13. neuron_S4[i] *= scale_factor;
  14. neuron_S4[i] += bias_S4[out2bias_S4[i]];
  15. }
  16. for (int i = 0; i < num_neuron_S4_CNN; i++) {
  17. neuron_S4[i] = activation_function_tanh(neuron_S4[i]);
  18. }
  19. return true;
  20. }

(6)、C5层:神经元数为(1*1)*120=120,也可看为全连接层,C5层实现方式与C1、C3层完全相同,由S4中16个5*5下采样图生成120个1*1特征图,对于生成的每一个1*1的特征图,是由16个5*5的卷积图像去乘以16个5*5的下采用图,然后相加求和,然后对每一个神经元加上一个阈值,最后再通过tanh激活函数对每一神经元进行运算得到最终每一个神经元的结果。

代码段如下:

  1. bool CNN::Forward_C5()
  2. {
  3. init_variable(neuron_C5, 0.0, num_neuron_C5_CNN);
  4. for (int o = 0; o < num_map_C5_CNN; o++) {
  5. for (int inc = 0; inc < num_map_S4_CNN; inc++) {
  6. int addr1 = get_index(0, 0, num_map_S4_CNN * o + inc, width_kernel_conv_CNN, height_kernel_conv_CNN, num_map_C5_CNN * num_map_S4_CNN);
  7. int addr2 = get_index(0, 0, inc, width_image_S4_CNN, height_image_S4_CNN, num_map_S4_CNN);
  8. int addr3 = get_index(0, 0, o, width_image_C5_CNN, height_image_C5_CNN, num_map_C5_CNN);
  9. const double *pw = &weight_C5[0] + addr1;
  10. const double *pi = &neuron_S4[0] + addr2;
  11. double *pa = &neuron_C5[0] + addr3;
  12. for (int y = 0; y < height_image_C5_CNN; y++) {
  13. for (int x = 0; x < width_image_C5_CNN; x++) {
  14. const double *ppw = pw;
  15. const double *ppi = pi + y * width_image_S4_CNN + x;
  16. double sum = 0.0;
  17. for (int wy = 0; wy < height_kernel_conv_CNN; wy++) {
  18. for (int wx = 0; wx < width_kernel_conv_CNN; wx++) {
  19. sum += *ppw++ * ppi[wy * width_image_S4_CNN + wx];
  20. }
  21. }
  22. pa[y * width_image_C5_CNN + x] += sum;
  23. }
  24. }
  25. }
  26. int addr3 = get_index(0, 0, o, width_image_C5_CNN, height_image_C5_CNN, num_map_C5_CNN);
  27. double *pa = &neuron_C5[0] + addr3;
  28. double b = bias_C5[o];
  29. for (int y = 0; y < height_image_C5_CNN; y++) {
  30. for (int x = 0; x < width_image_C5_CNN; x++) {
  31. pa[y * width_image_C5_CNN + x] += b;
  32. }
  33. }
  34. }
  35. for (int i = 0; i < num_neuron_C5_CNN; i++) {
  36. neuron_C5[i] = activation_function_tanh(neuron_C5[i]);
  37. }
  38. return true;
  39. }

(7)、输出层:神经元数为(1*1)*10=10,为全连接层,输出层中的每一个神经元均是由C5层中的120个神经元乘以相对应的权值,然后相加求和;然后对每一个神经元加上一个阈值,最后再通过tanh激活函数对每一神经元进行运算得到最终每一个神经元的结果。

代码段如下:

  1. bool CNN::Forward_output()
  2. {
  3. init_variable(neuron_output, 0.0, num_neuron_output_CNN);
  4. for (int i = 0; i < num_neuron_output_CNN; i++) {
  5. neuron_output[i] = 0.0;
  6. for (int c = 0; c < num_neuron_C5_CNN; c++) {
  7. neuron_output[i] += weight_output[c * num_neuron_output_CNN + i] * neuron_C5[c];
  8. }
  9. neuron_output[i] += bias_output[i];
  10. }
  11. for (int i = 0; i < num_neuron_output_CNN; i++) {
  12. neuron_output[i] = activation_function_tanh(neuron_output[i]);
  13. }
  14. return true;
  15. }

4.        反向传播:主要计算每层权值和偏置的误差以及每层神经元的误差;其中输入层、S2层、S4层操作过程相同;C1层、C3层操作过程相同。

(1)、输出层:计算输出层神经元误差;通过mse损失函数的导数函数和tanh激活函数的导数函数来计算输出层神经元误差,即a、已计算出的输出层神经元值减去对应label值,b、1.0减去输出层神经元值的平方,c、a与c的乘积和。

损失函数作用:在统计学中损失函数是一种衡量损失和错误(这种损失与”错误地”估计有关)程度的函数。损失函数在实践中最重要的运用,在于协助我们通过过程的改善而持续减少目标值的变异,并非仅仅追求符合逻辑。在深度学习中,对于损失函数的收敛特性,我们期望是当误差越大的时候,收敛(学习)速度应该越快。成为损失函数需要满足两点要求:非负性;预测值和期望值接近时,函数值趋于0.

代码段如下:

  1. double CNN::loss_function_mse_derivative(double y, double t)
  2. {
  3. return (y - t);
  4. }
  5. void CNN::loss_function_gradient(const double* y, const double* t, double* dst, int len)
  6. {
  7. for (int i = 0; i < len; i++) {
  8. dst[i] = loss_function_mse_derivative(y[i], t[i]);
  9. }
  10. }
  11. double CNN::activation_function_tanh_derivative(double x)
  12. {
  13. return (1.0 - x * x);
  14. }
  15. double CNN::dot_product(const double* s1, const double* s2, int len)
  16. {
  17. double result = 0.0;
  18. for (int i = 0; i < len; i++) {
  19. result += s1[i] * s2[i];
  20. }
  21. return result;
  22. }
  23. bool CNN::Backward_output()
  24. {
  25. init_variable(delta_neuron_output, 0.0, num_neuron_output_CNN);
  26. double dE_dy[num_neuron_output_CNN];
  27. init_variable(dE_dy, 0.0, num_neuron_output_CNN);
  28. loss_function_gradient(neuron_output, data_single_label, dE_dy, num_neuron_output_CNN); // 损失函数: mean squared error(均方差)
  29. // delta = dE/da = (dE/dy) * (dy/da)
  30. for (int i = 0; i < num_neuron_output_CNN; i++) {
  31. double dy_da[num_neuron_output_CNN];
  32. init_variable(dy_da, 0.0, num_neuron_output_CNN);
  33. dy_da[i] = activation_function_tanh_derivative(neuron_output[i]);
  34. delta_neuron_output[i] = dot_product(dE_dy, dy_da, num_neuron_output_CNN);
  35. }
  36. return true;
  37. }

(2)、C5层:计算C5层神经元误差、输出层权值误差、输出层偏置误差;通过输出层神经元误差乘以输出层权值,求和,结果再乘以C5层神经元的tanh激活函数的导数(即1-C5层神经元值的平方),获得C5层每一个神经元误差;通过输出层神经元误差乘以C5层神经元获得输出层权值误差;输出层偏置误差即为输出层神经元误差。

代码段如下:

  1. bool CNN::muladd(const double* src, double c, int len, double* dst)
  2. {
  3. for (int i = 0; i < len; i++) {
  4. dst[i] += (src[i] * c);
  5. }
  6. return true;
  7. }
  8. bool CNN::Backward_C5()
  9. {
  10. init_variable(delta_neuron_C5, 0.0, num_neuron_C5_CNN);
  11. init_variable(delta_weight_output, 0.0, len_weight_output_CNN);
  12. init_variable(delta_bias_output, 0.0, len_bias_output_CNN);
  13. for (int c = 0; c < num_neuron_C5_CNN; c++) {
  14. // propagate delta to previous layer
  15. // prev_delta[c] += current_delta[r] * W_[c * out_size_ + r]
  16. delta_neuron_C5[c] = dot_product(&delta_neuron_output[0], &weight_output[c * num_neuron_output_CNN], num_neuron_output_CNN);
  17. delta_neuron_C5[c] *= activation_function_tanh_derivative(neuron_C5[c]);
  18. }
  19. // accumulate weight-step using delta
  20. // dW[c * out_size + i] += current_delta[i] * prev_out[c]
  21. for (int c = 0; c < num_neuron_C5_CNN; c++) {
  22. muladd(&delta_neuron_output[0], neuron_C5[c], num_neuron_output_CNN, &delta_weight_output[0] + c * num_neuron_output_CNN);
  23. }
  24. for (int i = 0; i < len_bias_output_CNN; i++) {
  25. delta_bias_output[i] += delta_neuron_output[i];
  26. }
  27. return true;
  28. }

(3)、S4层:计算S4层神经元误差、C5层权值误差、C5层偏置误差;通过C5层权值乘以C5层神经元误差,求和,结果再乘以S4层神经元的tanh激活函数的导数(即1-S4神经元的平方),获得S4层每一个神经元误差;通过S4层神经元乘以C5层神经元误差,求和,获得C5层权值误差;C5层偏置误差即为C5层神经元误差。

代码段如下:

  1. bool CNN::Backward_S4()
  2. {
  3. init_variable(delta_neuron_S4, 0.0, num_neuron_S4_CNN);
  4. init_variable(delta_weight_C5, 0.0, len_weight_C5_CNN);
  5. init_variable(delta_bias_C5, 0.0, len_bias_C5_CNN);
  6. // propagate delta to previous layer
  7. for (int inc = 0; inc < num_map_S4_CNN; inc++) {
  8. for (int outc = 0; outc < num_map_C5_CNN; outc++) {
  9. int addr1 = get_index(0, 0, num_map_S4_CNN * outc + inc, width_kernel_conv_CNN, height_kernel_conv_CNN, num_map_S4_CNN * num_map_C5_CNN);
  10. int addr2 = get_index(0, 0, outc, width_image_C5_CNN, height_image_C5_CNN, num_map_C5_CNN);
  11. int addr3 = get_index(0, 0, inc, width_image_S4_CNN, height_image_S4_CNN, num_map_S4_CNN);
  12. const double* pw = &weight_C5[0] + addr1;
  13. const double* pdelta_src = &delta_neuron_C5[0] + addr2;
  14. double* pdelta_dst = &delta_neuron_S4[0] + addr3;
  15. for (int y = 0; y < height_image_C5_CNN; y++) {
  16. for (int x = 0; x < width_image_C5_CNN; x++) {
  17. const double* ppw = pw;
  18. const double ppdelta_src = pdelta_src[y * width_image_C5_CNN + x];
  19. double* ppdelta_dst = pdelta_dst + y * width_image_S4_CNN + x;
  20. for (int wy = 0; wy < height_kernel_conv_CNN; wy++) {
  21. for (int wx = 0; wx < width_kernel_conv_CNN; wx++) {
  22. ppdelta_dst[wy * width_image_S4_CNN + wx] += *ppw++ * ppdelta_src;
  23. }
  24. }
  25. }
  26. }
  27. }
  28. }
  29. for (int i = 0; i < num_neuron_S4_CNN; i++) {
  30. delta_neuron_S4[i] *= activation_function_tanh_derivative(neuron_S4[i]);
  31. }
  32. // accumulate dw
  33. for (int inc = 0; inc < num_map_S4_CNN; inc++) {
  34. for (int outc = 0; outc < num_map_C5_CNN; outc++) {
  35. for (int wy = 0; wy < height_kernel_conv_CNN; wy++) {
  36. for (int wx = 0; wx < width_kernel_conv_CNN; wx++) {
  37. int addr1 = get_index(wx, wy, inc, width_image_S4_CNN, height_image_S4_CNN, num_map_S4_CNN);
  38. int addr2 = get_index(0, 0, outc, width_image_C5_CNN, height_image_C5_CNN, num_map_C5_CNN);
  39. int addr3 = get_index(wx, wy, num_map_S4_CNN * outc + inc, width_kernel_conv_CNN, height_kernel_conv_CNN, num_map_S4_CNN * num_map_C5_CNN);
  40. double dst = 0.0;
  41. const double* prevo = &neuron_S4[0] + addr1;
  42. const double* delta = &delta_neuron_C5[0] + addr2;
  43. for (int y = 0; y < height_image_C5_CNN; y++) {
  44. dst += dot_product(prevo + y * width_image_S4_CNN, delta + y * width_image_C5_CNN, width_image_C5_CNN);
  45. }
  46. delta_weight_C5[addr3] += dst;
  47. }
  48. }
  49. }
  50. }
  51. // accumulate db
  52. for (int outc = 0; outc < num_map_C5_CNN; outc++) {
  53. int addr2 = get_index(0, 0, outc, width_image_C5_CNN, height_image_C5_CNN, num_map_C5_CNN);
  54. const double* delta = &delta_neuron_C5[0] + addr2;
  55. for (int y = 0; y < height_image_C5_CNN; y++) {
  56. for (int x = 0; x < width_image_C5_CNN; x++) {
  57. delta_bias_C5[outc] += delta[y * width_image_C5_CNN + x];
  58. }
  59. }
  60. }
  61. return true;
  62. }

(4)、C3层:计算C3层神经元误差、S4层权值误差、S4层偏置误差;通过S4层权值乘以S4层神经元误差,求和,结果再乘以C3层神经元的tanh激活函数的导数(即1-S4神经元的平方),然后再乘以1/4,获得C3层每一个神经元误差;通过C3层神经元乘以S4神经元误差,求和,再乘以1/4,获得S4层权值误差;通过S4层神经元误差求和,来获得S4层偏置误差。

代码段如下:

  1. bool CNN::Backward_C3()
  2. {
  3. init_variable(delta_neuron_C3, 0.0, num_neuron_C3_CNN);
  4. init_variable(delta_weight_S4, 0.0, len_weight_S4_CNN);
  5. init_variable(delta_bias_S4, 0.0, len_bias_S4_CNN);
  6. double scale_factor = 1.0 / (width_kernel_pooling_CNN * height_kernel_pooling_CNN);
  7. assert(in2wo_C3.size() == num_neuron_C3_CNN);
  8. assert(weight2io_C3.size() == len_weight_S4_CNN);
  9. assert(bias2out_C3.size() == len_bias_S4_CNN);
  10. for (int i = 0; i < num_neuron_C3_CNN; i++) {
  11. const wo_connections& connections = in2wo_C3[i];
  12. double delta = 0.0;
  13. for (int j = 0; j < connections.size(); j++) {
  14. delta += weight_S4[connections[j].first] * delta_neuron_S4[connections[j].second];
  15. }
  16. delta_neuron_C3[i] = delta * scale_factor * activation_function_tanh_derivative(neuron_C3[i]);
  17. }
  18. for (int i = 0; i < len_weight_S4_CNN; i++) {
  19. const io_connections& connections = weight2io_C3[i];
  20. double diff = 0;
  21. for (int j = 0; j < connections.size(); j++) {
  22. diff += neuron_C3[connections[j].first] * delta_neuron_S4[connections[j].second];
  23. }
  24. delta_weight_S4[i] += diff * scale_factor;
  25. }
  26. for (int i = 0; i < len_bias_S4_CNN; i++) {
  27. const std::vector<int>& outs = bias2out_C3[i];
  28. double diff = 0;
  29. for (int o = 0; o < outs.size(); o++) {
  30. diff += delta_neuron_S4[outs[o]];
  31. }
  32. delta_bias_S4[i] += diff;
  33. }
  34. return true;
  35. }

(5)、S2层:计算S2层神经元误差、C3层权值误差、C3层偏置误差;通过C3层权值乘以C3层神经元误差,求和,结果再乘以S2层神经元的tanh激活函数的导数(即1-S2神经元的平方),获得S2层每一个神经元误差;通过S2层神经元乘以C3层神经元误差,求和,获得C3层权值误差;C3层偏置误差即为C3层神经元误差和。

代码段如下:

  1. bool CNN::Backward_S2()
  2. {
  3. init_variable(delta_neuron_S2, 0.0, num_neuron_S2_CNN);
  4. init_variable(delta_weight_C3, 0.0, len_weight_C3_CNN);
  5. init_variable(delta_bias_C3, 0.0, len_bias_C3_CNN);
  6. // propagate delta to previous layer
  7. for (int inc = 0; inc < num_map_S2_CNN; inc++) {
  8. for (int outc = 0; outc < num_map_C3_CNN; outc++) {
  9. if (!tbl[inc][outc]) continue;
  10. int addr1 = get_index(0, 0, num_map_S2_CNN * outc + inc, width_kernel_conv_CNN, height_kernel_conv_CNN, num_map_S2_CNN * num_map_C3_CNN);
  11. int addr2 = get_index(0, 0, outc, width_image_C3_CNN, height_image_C3_CNN, num_map_C3_CNN);
  12. int addr3 = get_index(0, 0, inc, width_image_S2_CNN, height_image_S2_CNN, num_map_S2_CNN);
  13. const double *pw = &weight_C3[0] + addr1;
  14. const double *pdelta_src = &delta_neuron_C3[0] + addr2;;
  15. double* pdelta_dst = &delta_neuron_S2[0] + addr3;
  16. for (int y = 0; y < height_image_C3_CNN; y++) {
  17. for (int x = 0; x < width_image_C3_CNN; x++) {
  18. const double* ppw = pw;
  19. const double ppdelta_src = pdelta_src[y * width_image_C3_CNN + x];
  20. double* ppdelta_dst = pdelta_dst + y * width_image_S2_CNN + x;
  21. for (int wy = 0; wy < height_kernel_conv_CNN; wy++) {
  22. for (int wx = 0; wx < width_kernel_conv_CNN; wx++) {
  23. ppdelta_dst[wy * width_image_S2_CNN + wx] += *ppw++ * ppdelta_src;
  24. }
  25. }
  26. }
  27. }
  28. }
  29. }
  30. for (int i = 0; i < num_neuron_S2_CNN; i++) {
  31. delta_neuron_S2[i] *= activation_function_tanh_derivative(neuron_S2[i]);
  32. }
  33. // accumulate dw
  34. for (int inc = 0; inc < num_map_S2_CNN; inc++) {
  35. for (int outc = 0; outc < num_map_C3_CNN; outc++) {
  36. if (!tbl[inc][outc]) continue;
  37. for (int wy = 0; wy < height_kernel_conv_CNN; wy++) {
  38. for (int wx = 0; wx < width_kernel_conv_CNN; wx++) {
  39. int addr1 = get_index(wx, wy, inc, width_image_S2_CNN, height_image_S2_CNN, num_map_S2_CNN);
  40. int addr2 = get_index(0, 0, outc, width_image_C3_CNN, height_image_C3_CNN, num_map_C3_CNN);
  41. int addr3 = get_index(wx, wy, num_map_S2_CNN * outc + inc, width_kernel_conv_CNN, height_kernel_conv_CNN, num_map_S2_CNN * num_map_C3_CNN);
  42. double dst = 0.0;
  43. const double* prevo = &neuron_S2[0] + addr1;
  44. const double* delta = &delta_neuron_C3[0] + addr2;
  45. for (int y = 0; y < height_image_C3_CNN; y++) {
  46. dst += dot_product(prevo + y * width_image_S2_CNN, delta + y * width_image_C3_CNN, width_image_C3_CNN);
  47. }
  48. delta_weight_C3[addr3] += dst;
  49. }
  50. }
  51. }
  52. }
  53. // accumulate db
  54. for (int outc = 0; outc < len_bias_C3_CNN; outc++) {
  55. int addr1 = get_index(0, 0, outc, width_image_C3_CNN, height_image_C3_CNN, num_map_C3_CNN);
  56. const double* delta = &delta_neuron_C3[0] + addr1;
  57. for (int y = 0; y < height_image_C3_CNN; y++) {
  58. for (int x = 0; x < width_image_C3_CNN; x++) {
  59. delta_bias_C3[outc] += delta[y * width_image_C3_CNN + x];
  60. }
  61. }
  62. }
  63. return true;
  64. }

(6)、C1层:计算C1层神经元误差、S2层权值误差、S2层偏置误差;通过S2层权值乘以S2层神经元误差,求和,结果再乘以C1层神经元的tanh激活函数的导数(即1-C1神经元的平方),然后再乘以1/4,获得C1层每一个神经元误差;通过C1层神经元乘以S2神经元误差,求和,再乘以1/4,获得S2层权值误差;通过S2层神经元误差求和,来获得S4层偏置误差。

代码段如下:

  1. bool CNN::Backward_C1()
  2. {
  3. init_variable(delta_neuron_C1, 0.0, num_neuron_C1_CNN);
  4. init_variable(delta_weight_S2, 0.0, len_weight_S2_CNN);
  5. init_variable(delta_bias_S2, 0.0, len_bias_S2_CNN);
  6. double scale_factor = 1.0 / (width_kernel_pooling_CNN * height_kernel_pooling_CNN);
  7. assert(in2wo_C1.size() == num_neuron_C1_CNN);
  8. assert(weight2io_C1.size() == len_weight_S2_CNN);
  9. assert(bias2out_C1.size() == len_bias_S2_CNN);
  10. for (int i = 0; i < num_neuron_C1_CNN; i++) {
  11. const wo_connections& connections = in2wo_C1[i];
  12. double delta = 0.0;
  13. for (int j = 0; j < connections.size(); j++) {
  14. delta += weight_S2[connections[j].first] * delta_neuron_S2[connections[j].second];
  15. }
  16. delta_neuron_C1[i] = delta * scale_factor * activation_function_tanh_derivative(neuron_C1[i]);
  17. }
  18. for (int i = 0; i < len_weight_S2_CNN; i++) {
  19. const io_connections& connections = weight2io_C1[i];
  20. double diff = 0.0;
  21. for (int j = 0; j < connections.size(); j++) {
  22. diff += neuron_C1[connections[j].first] * delta_neuron_S2[connections[j].second];
  23. }
  24. delta_weight_S2[i] += diff * scale_factor;
  25. }
  26. for (int i = 0; i < len_bias_S2_CNN; i++) {
  27. const std::vector<int>& outs = bias2out_C1[i];
  28. double diff = 0;
  29. for (int o = 0; o < outs.size(); o++) {
  30. diff += delta_neuron_S2[outs[o]];
  31. }
  32. delta_bias_S2[i] += diff;
  33. }
  34. return true;
  35. }

(7)、输入层:计算输入层神经元误差、C1层权值误差、C1层偏置误差;通过C1层权值乘以C1层神经元误差,求和,结果再乘以输入层神经元的tanh激活函数的导数(即1-输入层神经元的平方),获得输入层每一个神经元误差;通过输入层层神经元乘以C1层神经元误差,求和,获得C1层权值误差;C1层偏置误差即为C1层神经元误差和。

  1. bool CNN::Backward_input()
  2. {
  3. init_variable(delta_neuron_input, 0.0, num_neuron_input_CNN);
  4. init_variable(delta_weight_C1, 0.0, len_weight_C1_CNN);
  5. init_variable(delta_bias_C1, 0.0, len_bias_C1_CNN);
  6. // propagate delta to previous layer
  7. for (int inc = 0; inc < num_map_input_CNN; inc++) {
  8. for (int outc = 0; outc < num_map_C1_CNN; outc++) {
  9. int addr1 = get_index(0, 0, num_map_input_CNN * outc + inc, width_kernel_conv_CNN, height_kernel_conv_CNN, num_map_C1_CNN);
  10. int addr2 = get_index(0, 0, outc, width_image_C1_CNN, height_image_C1_CNN, num_map_C1_CNN);
  11. int addr3 = get_index(0, 0, inc, width_image_input_CNN, height_image_input_CNN, num_map_input_CNN);
  12. const double* pw = &weight_C1[0] + addr1;
  13. const double* pdelta_src = &delta_neuron_C1[0] + addr2;
  14. double* pdelta_dst = &delta_neuron_input[0] + addr3;
  15. for (int y = 0; y < height_image_C1_CNN; y++) {
  16. for (int x = 0; x < width_image_C1_CNN; x++) {
  17. const double* ppw = pw;
  18. const double ppdelta_src = pdelta_src[y * width_image_C1_CNN + x];
  19. double* ppdelta_dst = pdelta_dst + y * width_image_input_CNN + x;
  20. for (int wy = 0; wy < height_kernel_conv_CNN; wy++) {
  21. for (int wx = 0; wx < width_kernel_conv_CNN; wx++) {
  22. ppdelta_dst[wy * width_image_input_CNN + wx] += *ppw++ * ppdelta_src;
  23. }
  24. }
  25. }
  26. }
  27. }
  28. }
  29. for (int i = 0; i < num_neuron_input_CNN; i++) {
  30. delta_neuron_input[i] *= activation_function_identity_derivative(data_single_image[i]/*neuron_input[i]*/);
  31. }
  32. // accumulate dw
  33. for (int inc = 0; inc < num_map_input_CNN; inc++) {
  34. for (int outc = 0; outc < num_map_C1_CNN; outc++) {
  35. for (int wy = 0; wy < height_kernel_conv_CNN; wy++) {
  36. for (int wx = 0; wx < width_kernel_conv_CNN; wx++) {
  37. int addr1 = get_index(wx, wy, inc, width_image_input_CNN, height_image_input_CNN, num_map_input_CNN);
  38. int addr2 = get_index(0, 0, outc, width_image_C1_CNN, height_image_C1_CNN, num_map_C1_CNN);
  39. int addr3 = get_index(wx, wy, num_map_input_CNN * outc + inc, width_kernel_conv_CNN, height_kernel_conv_CNN, num_map_C1_CNN);
  40. double dst = 0.0;
  41. const double* prevo = data_single_image + addr1;//&neuron_input[0]
  42. const double* delta = &delta_neuron_C1[0] + addr2;
  43. for (int y = 0; y < height_image_C1_CNN; y++) {
  44. dst += dot_product(prevo + y * width_image_input_CNN, delta + y * width_image_C1_CNN, width_image_C1_CNN);
  45. }
  46. delta_weight_C1[addr3] += dst;
  47. }
  48. }
  49. }
  50. }
  51. // accumulate db
  52. for (int outc = 0; outc < len_bias_C1_CNN; outc++) {
  53. int addr1 = get_index(0, 0, outc, width_image_C1_CNN, height_image_C1_CNN, num_map_C1_CNN);
  54. const double* delta = &delta_neuron_C1[0] + addr1;
  55. for (int y = 0; y < height_image_C1_CNN; y++) {
  56. for (int x = 0; x < width_image_C1_CNN; x++) {
  57. delta_bias_C1[outc] += delta[y * width_image_C1_CNN + x];
  58. }
  59. }
  60. }
  61. return true;
  62. }

5.        更新各层权值、偏置:通过之前计算的各层权值、各层权值误差;各层偏置、各层偏置误差以及学习率来更新各层权值和偏置。

代码段如下:

  1. void CNN::update_weights_bias(const double* delta, double* e_weight, double* weight, int len)
  2. {
  3. for (int i = 0; i < len; i++) {
  4. e_weight[i] += delta[i] * delta[i];
  5. weight[i] -= learning_rate_CNN * delta[i] / (std::sqrt(e_weight[i]) + eps_CNN);
  6. }
  7. }
  8. bool CNN::UpdateWeights()
  9. {
  10. update_weights_bias(delta_weight_C1, E_weight_C1, weight_C1, len_weight_C1_CNN);
  11. update_weights_bias(delta_bias_C1, E_bias_C1, bias_C1, len_bias_C1_CNN);
  12. update_weights_bias(delta_weight_S2, E_weight_S2, weight_S2, len_weight_S2_CNN);
  13. update_weights_bias(delta_bias_S2, E_bias_S2, bias_S2, len_bias_S2_CNN);
  14. update_weights_bias(delta_weight_C3, E_weight_C3, weight_C3, len_weight_C3_CNN);
  15. update_weights_bias(delta_bias_C3, E_bias_C3, bias_C3, len_bias_C3_CNN);
  16. update_weights_bias(delta_weight_S4, E_weight_S4, weight_S4, len_weight_S4_CNN);
  17. update_weights_bias(delta_bias_S4, E_bias_S4, bias_S4, len_bias_S4_CNN);
  18. update_weights_bias(delta_weight_C5, E_weight_C5, weight_C5, len_weight_C5_CNN);
  19. update_weights_bias(delta_bias_C5, E_bias_C5, bias_C5, len_bias_C5_CNN);
  20. update_weights_bias(delta_weight_output, E_weight_output, weight_output, len_weight_output_CNN);
  21. update_weights_bias(delta_bias_output, E_bias_output, bias_output, len_bias_output_CNN);
  22. return true;
  23. }

6.        测试准确率是否达到要求或已达到循环次数:依次循环3至5中操作,根据训练集数量,每循环60000次时,通过计算的权值和偏置,来对10000个测试集进行测试,如果准确率达到0.985或者达到迭代次数上限100次时,保存权值和偏置。

代码段如下:

  1. bool CNN::train()
  2. {
  3. out2wi_S2.clear();
  4. out2bias_S2.clear();
  5. out2wi_S4.clear();
  6. out2bias_S4.clear();
  7. in2wo_C3.clear();
  8. weight2io_C3.clear();
  9. bias2out_C3.clear();
  10. in2wo_C1.clear();
  11. weight2io_C1.clear();
  12. bias2out_C1.clear();
  13. calc_out2wi(width_image_C1_CNN, height_image_C1_CNN, width_image_S2_CNN, height_image_S2_CNN, num_map_S2_CNN, out2wi_S2);
  14. calc_out2bias(width_image_S2_CNN, height_image_S2_CNN, num_map_S2_CNN, out2bias_S2);
  15. calc_out2wi(width_image_C3_CNN, height_image_C3_CNN, width_image_S4_CNN, height_image_S4_CNN, num_map_S4_CNN, out2wi_S4);
  16. calc_out2bias(width_image_S4_CNN, height_image_S4_CNN, num_map_S4_CNN, out2bias_S4);
  17. calc_in2wo(width_image_C3_CNN, height_image_C3_CNN, width_image_S4_CNN, height_image_S4_CNN, num_map_C3_CNN, num_map_S4_CNN, in2wo_C3);
  18. calc_weight2io(width_image_C3_CNN, height_image_C3_CNN, width_image_S4_CNN, height_image_S4_CNN, num_map_C3_CNN, num_map_S4_CNN, weight2io_C3);
  19. calc_bias2out(width_image_C3_CNN, height_image_C3_CNN, width_image_S4_CNN, height_image_S4_CNN, num_map_C3_CNN, num_map_S4_CNN, bias2out_C3);
  20. calc_in2wo(width_image_C1_CNN, height_image_C1_CNN, width_image_S2_CNN, height_image_S2_CNN, num_map_C1_CNN, num_map_C3_CNN, in2wo_C1);
  21. calc_weight2io(width_image_C1_CNN, height_image_C1_CNN, width_image_S2_CNN, height_image_S2_CNN, num_map_C1_CNN, num_map_C3_CNN, weight2io_C1);
  22. calc_bias2out(width_image_C1_CNN, height_image_C1_CNN, width_image_S2_CNN, height_image_S2_CNN, num_map_C1_CNN, num_map_C3_CNN, bias2out_C1);
  23. int iter = 0;
  24. for (iter = 0; iter < num_epochs_CNN; iter++) {
  25. std::cout << "epoch: " << iter + 1;
  26. for (int i = 0; i < num_patterns_train_CNN; i++) {
  27. data_single_image = data_input_train + i * num_neuron_input_CNN;
  28. data_single_label = data_output_train + i * num_neuron_output_CNN;
  29. Forward_C1();
  30. Forward_S2();
  31. Forward_C3();
  32. Forward_S4();
  33. Forward_C5();
  34. Forward_output();
  35. Backward_output();
  36. Backward_C5();
  37. Backward_S4();
  38. Backward_C3();
  39. Backward_S2();
  40. Backward_C1();
  41. Backward_input();
  42. UpdateWeights();
  43. }
  44. double accuracyRate = test();
  45. std::cout << ", accuray rate: " << accuracyRate << std::endl;
  46. if (accuracyRate > accuracy_rate_CNN) {
  47. saveModelFile("E:/GitCode/NN_Test/data/cnn.model");
  48. std::cout << "generate cnn model" << std::endl;
  49. break;
  50. }
  51. }
  52. if (iter == num_epochs_CNN) {
  53. saveModelFile("E:/GitCode/NN_Test/data/cnn.model");
  54. std::cout << "generate cnn model" << std::endl;
  55. }
  56. return true;
  57. }
  58. double CNN::test()
  59. {
  60. int count_accuracy = 0;
  61. for (int num = 0; num < num_patterns_test_CNN; num++) {
  62. data_single_image = data_input_test + num * num_neuron_input_CNN;
  63. data_single_label = data_output_test + num * num_neuron_output_CNN;
  64. Forward_C1();
  65. Forward_S2();
  66. Forward_C3();
  67. Forward_S4();
  68. Forward_C5();
  69. Forward_output();
  70. int pos_t = -1;
  71. int pos_y = -2;
  72. double max_value_t = -9999.0;
  73. double max_value_y = -9999.0;
  74. for (int i = 0; i < num_neuron_output_CNN; i++) {
  75. if (neuron_output[i] > max_value_y) {
  76. max_value_y = neuron_output[i];
  77. pos_y = i;
  78. }
  79. if (data_single_label[i] > max_value_t) {
  80. max_value_t = data_single_label[i];
  81. pos_t = i;
  82. }
  83. }
  84. if (pos_y == pos_t) {
  85. ++count_accuracy;
  86. }
  87. Sleep(1);
  88. }
  89. return (count_accuracy * 1.0 / num_patterns_test_CNN);
  90. }

7.        对输入的图像数据进行识别:载入已保存的权值和偏置,对输入的数据进行识别,过程相当于前向传播。

代码段如下:

  1. int CNN::predict(const unsigned char* data, int width, int height)
  2. {
  3. assert(data && width == width_image_input_CNN && height == height_image_input_CNN);
  4. const double scale_min = -1;
  5. const double scale_max = 1;
  6. double tmp[width_image_input_CNN * height_image_input_CNN];
  7. for (int y = 0; y < height; y++) {
  8. for (int x = 0; x < width; x++) {
  9. tmp[y * width + x] = (data[y * width + x] / 255.0) * (scale_max - scale_min) + scale_min;
  10. }
  11. }
  12. data_single_image = &tmp[0];
  13. Forward_C1();
  14. Forward_S2();
  15. Forward_C3();
  16. Forward_S4();
  17. Forward_C5();
  18. Forward_output();
  19. int pos = -1;
  20. double max_value = -9999.0;
  21. for (int i = 0; i < num_neuron_output_CNN; i++) {
  22. if (neuron_output[i] > max_value) {
  23. max_value = neuron_output[i];
  24. pos = i;
  25. }
  26. }
  27. return pos;
  28. }

GitHub: https://github.com/fengbingchun/NN_Test

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/140567
推荐阅读
相关标签
  

闽ICP备14008679号