CNN 的代码出了问题, 这里把调拭过程展示出来, 希望对读者有用.
网络训练好后, 对不同的输入产生了相同的输出. 例如, 预测值总是 7.
将训练过程的预测值打印, 发现同一批的输出都是相同的, 但不同批的输出可能不同. 网络是在每批数据 forward 和 backPropagation 之后进行 updateParameters, 因此, 预测值主要由网络确定.
进一步打印每次预测的分布 (长度为 10 的向量), 发现不同的输入导致的预测有细微的差别. 因此, 输入对输出还是有一定影响, 但缺点是影响不够.
本节通过 4 个方面进行程序的测试, 以找到问题.
超参数有如下几个方面:
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后, 并不是每批输出一致, 但成片的输出相同情况还是没有缓解.
当训练数据比例为 0.2 出了该问题, 将其改为 0.6, 让我们见证奇迹吧: 该问题消失!
把训练数据比例增大到 0.6, 识别正确率达到 0.785. 虽然还没有非常高, 但已经可以接受了.
这说明: 没有足够的训练数据, 是无法得到有准确率高的网络!
我刚拉开架势, 就把问题找出来了, 是不是有点秃然?
说不出心情的分割线
后面又出问题啦,准确率提不上去. 所以只有用特殊数据 (如全设置为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;
导致上一轮不同图的结果无法堆叠.
要先做单元测试, 再做集成测试, 最后做系统测试. 这样才靠谱!