@nrailgun
2016-07-03T22:27:13.000000Z
字数 3046
阅读 3862
论文笔记
第一阶段阅读论文,大约两天大体阅读完论文 Deep Convolutional Network Cascade for Facial Point Detection。感觉还是比较缺乏论文阅读经验,但是比以前快了很多。主要阅读论文 intro、method 和 modeling。作者的思维比较奇特,把损失函数放在 experiment 部分,我找了好久没找到。
寻找同学帮助,发现代码变化太快,旧经验有些跟不上。尝试瞎猫撞死耗子的策略,瞎写,显然不能成功,浪费 4+ 天时间。
静下心,阅读 Caffe 框架代码,C++ 不熟悉,读 C++ 代码没经验。硬着头皮读,不得法,痛苦,事倍功半;晚上,脑子突然灵光一闪,读继承关系和头文件(因为 C 的头文件只是函数声明,而宏定义可以临时找),有效,思路清晰。一天时间写完代码。
计算结果 J(theta)
是 NaN
,天!各种 Debug,找了 3+ 天,代码中没发现和数值相关的 BUG。也许是数值计算方法的问题?不能,这是框架写的代码。后来发是学习率调太大。之前已经调小过,无效,没想到还要继续调小。
再次验证,收敛失败。这是什么原因?后来发现是 loss 的设计存在问题,没有做 normalization。细节在这里。收敛成功。
最直接的方法当然是输出坐标点,可惜一点都不直观。写了一堆代码用来在图上做各种标记,代码混乱成翔(后来抽取到 我的 github)。从输出来看,总体位置还算靠谱,但是效果并不好,虽然还没量化,但是我知道肯定不好,不用算了。
我天真的觉得是 overfit 了。在 train set 做个测试,underfit。
突然想到会不会是 BUG 导致所有输出都是一样的?检查了输出的坐标,虽然比较相近,但是确实是不同的,所以应该不是 BUG。这说明,没有提取到足够的 inter-face 特征?这个地方要想想。
其实我没有完全按照论文实现。论文结构太复杂,存在 3 个级联,第一个级联有 3 个网络做平均。作者的说法是第一个级联最重要,所以我只实现了第一个级联的其中一个网络。有可能没有完全按照结构设计导致的问题?
另外一个思路是,用一个 kernel 去扫描 5 个不同的面部特征真的靠谱么?我也许试试五个 kernel?一时间没有思路为何 underfit。先做一个 left eye 试试吧。我快速 10000 轮迭代做出个 model,输出一百个图像来人力比较,第一印象是单点的确比多点要准确。看一下损失:
左眼坐标网络损失:
I0920 16:27:52.355422 11912 solver.cpp:294] Iteration 10000, Testing net (#0)
I0920 16:27:52.606781 11912 solver.cpp:343] Test net output #0: loss = 0.00317065 (* 1 = 0.00317065 loss)
脸部坐标网络损失:
I0920 16:19:16.628931 48513 solver.cpp:294] Iteration 10000, Testing net (#0)
I0920 16:19:16.885615 48513 solver.cpp:343] Test net output #0: loss = 0.0263838 (* 1 = 0.0263838 loss)
从数据来看,确实只做一个坐标效果会比较好。
我想我一定是弄错论文某些细节了,我决定重新读一遍这篇论文。重新阅读论文过程中,我产生了新的想法。
之前的 network 只有一个 kernel,而作者提出的方案是 locally weight-sharing,开始的时候不甚明了,直接忽略了。我看了 Convolutional Neural Networks 突然想通了 weight-sharing 指的是什么意思。
Convolutional layers consist of a rectangular grid of neurons. It requires that the previous layer also be a rectangular grid of neurons. Each neuron takes inputs from a rectangular section of the previous layer; the weights for this rectangular section are the same for each neuron in the convolutional layer. Thus, the convolutional layer is just an image convolution of the previous layer, where the weights specify the convolution filter.
Fully-Connected layer 的输入和输出的尺寸都是固定的,每一个神经元对应一个输出。根据 Andrew NG 的 UFLDL,我产生了这种观念:卷积层只有一个神经元,这个神经元就是 kernel,是 weight 的矩阵。我突然发觉这种观念可能有问题。
我现在的理解是这样的,不知道有没有错。一般看到,kernel 只有一个,而 convolution layer 的输入是没有大小限制的,而输出也固定,根据图像大小产生不同大小的 feature map。如果换个角度看,convolution layer 是一个
输入区域似乎也和我想的不一样,原文的 input range 是脸部区域 + 一个百分比...,我觉得我当时没看到是不是不太合适....
看完我大概是怎么一个思路:
根据上次阅读论文的想法,先测试左眼的检测,进行数据清洗,只截取左眼。结果发现,虽然输出的 cost 变小了,但是看图片依然是 underfit。
进行 64 样本的测试,发现依旧是 underfit,这实在诡异,难道是 Bug?那么测试 1 样本试试,还是不准。输出预测值和坐标试试....咦,数据绩小,所以几乎完美重合,那么打印怎么会有那么巨大的位置错误,这只能解释为我的程序哪里写错了。
理理思路,程序的逻辑大概是:
很可能是某个数值忘记加减,导致的偏移。检查输出值后发现,输出的训练坐标和预测坐标是几乎相同的。这就说明:
测试后发现,确实是自己 normalize 监督信号的时候把监督信号弄错了。在选取 sub-region 时候并不需要 normalize 坐标,只有 resize 才需要,大脑故障了。调整后发现,无论是损失函数,还是肉眼观察结果,比起之前大大靠谱,非常好。