深度学习C++代码配套教程(5附1: CNN代码调拭)

CNN 的代码出了问题, 这里把调拭过程展示出来, 希望对读者有用.

1. 问题

网络训练好后, 对不同的输入产生了相同的输出. 例如, 预测值总是 7.
将训练过程的预测值打印, 发现同一批的输出都是相同的, 但不同批的输出可能不同. 网络是在每批数据 forward 和 backPropagation 之后进行 updateParameters, 因此, 预测值主要由网络确定.
进一步打印每次预测的分布 (长度为 10 的向量), 发现不同的输入导致的预测有细微的差别. 因此, 输入对输出还是有一定影响, 但缺点是影响不够.

2. 测试

本节通过 4 个方面进行程序的测试, 以找到问题.

2.1 超参数设置

超参数有如下几个方面:

2.1.1 ALPHA, LAMBDA

CNN Java 代码里面, ALPHA = 0.85, LAMBDA = 0. 这个设置比较奇怪, 和我相像的不同. 与这两个超参数相关的代码为:

void MfCnnLayer::updateKernels()
{
     
    int tempNumLastMap = lastLayer->getNumOutMaps();
    bool tempFirst = true;

    for (int j = 0; j < numOutMaps; j++)
    {
     
        for (int i = 0; i < tempNumLastMap; i++)
        {
     
            tempFirst = true;
            for (int r = 0; r < batchSize; r++)
            {
     
                currentErrors = getErrorsAt(r, j);
                if (tempFirst)
                {
     
                    tempFirst = false;
                    deltaKernel->convolutionValidToMe(lastLayer->getOutMapAt(r, i), currentErrors);
                }
                else
                {
     
                    singleDeltaKernel->convolutionValidToMe(lastLayer->getOutMapAt(r, i), currentErrors);
                    deltaKernel->addToMe(deltaKernel, singleDeltaKernel);
                }//Of if
            }//Of for r

            double tempValue;
            deltaKernel->timesValueToMe(ALPHA/batchSize);
 
            currentKernel = getKernelAt(i, j);
            currentKernel->timesValueToMe(1 - LAMBDA * ALPHA);
            currentKernel->addToMe(currentKernel, deltaKernel);

            setKernelAt(i, j, currentKernel);
        }//Of for i
    }//Of for j
}//Of updateKernels

按我的理解, ALPHA 应该是一个比较小的数, 如 0.01, 类似于 ANN 的 rate. 将其改为0.1后, 并不是每批输出一致, 但成片的输出相同情况还是没有缓解.

2.1.2 层数, kernelSize, batchSize

2.1.3 训练数据量

当训练数据比例为 0.2 出了该问题, 将其改为 0.6, 让我们见证奇迹吧: 该问题消失!

2.2 基础模块

2.3 初始化

2.4 算法流程

3. 修改

把训练数据比例增大到 0.6, 识别正确率达到 0.785. 虽然还没有非常高, 但已经可以接受了.
这说明: 没有足够的训练数据, 是无法得到有准确率高的网络!

4. 小结

我刚拉开架势, 就把问题找出来了, 是不是有点秃然?


说不出心情的分割线
后面又出问题啦,准确率提不上去. 所以只有用特殊数据 (如全设置为1) 做集成测试. 最后找出 bug.
贴一下代码吧:

void MfFullCnn::integratedTest()
{
     
    //Test the digit recognition data.
    MfFullCnn* tempCnn = new MfFullCnn(1);
    MfSize* tempImageSize = new MfSize(6, 6);
    MfSize* tempConvolutionSize = new MfSize(3, 3);
    MfSize* tempSampleSize = new MfSize(2, 2);
    int tempNumClasses = 2;

    //Four layers
    tempCnn->addLayer(INPUT_LAYER, -1, tempImageSize);
    tempCnn->addLayer(CONVOLUTION_LAYER, 2, tempConvolutionSize);
    tempCnn->addLayer(SAMPLING_LAYER, -1, tempSampleSize);
    tempCnn->addLayer(OUTPUT_LAYER, tempNumClasses, nullptr);

    //Setup.
    tempCnn->setup();

    printf("After setup\r\n");

    //Now load the data for training/testing.
    MfDoubleMatrix* tempTrainingX = new MfDoubleMatrix(1, 36);
    for(int i = 0; i < 18; i ++) {
     
        tempTrainingX->setValue(0, i, 0);
    }
    for(int i = 18; i < 36; i ++) {
     
        tempTrainingX->setValue(0, i, 1);
    }
    MfIntArray* tempTrainingY = new MfIntArray(1);
    tempTrainingY->fill(1);
    tempCnn->initializeRandomArray(tempTrainingX->getRows());

    printf("before training\r\n");
    tempCnn->train(tempTrainingX, tempTrainingY);

    printf("After training:\r\n\r\n");
    tempCnn->outputKernelsToFile();

    //double tempAccuracy = tempCnn->test(tempTestingX, tempTestingY);

    free(tempCnn);
}//Of integratedTest

另外, 还将 kernel 初始化为全 1.
最后找出的 bug 在
MfCnnLayer::setConvolutionOutput()
少了一句
tempEmpty = false;
导致上一轮不同图的结果无法堆叠.

5. 继续小结

要先做单元测试, 再做集成测试, 最后做系统测试. 这样才靠谱!

你可能感兴趣的:(深度学习C++代码,机器学习,c++)