2
雷锋网 AI科技评论按:本文作者陈仲铭,雷锋网 AI科技评论获其授权发布。
为什么我的CNN网络模型训练出来的东西总是过度拟合?已经改了很多次参数都不行,到底是样本有问题还是网络模型定义有问题?问题在哪来?
CNN网络模型中的每一层学习的是些什么特征?为什么有的人说第一层卷积核提取的是边缘信息特征,有的人却说第一层卷积核提取的是颜色特征?到底是两者都有还是什么回事?
CNN网络可不可以减掉几层然后保持相同的精度和损失率呢?减掉几层可以减少网络参数,本来我的GPU显存不是很大,太大的网络塞不下,不想重新买GPU只能减层,有没有大神可以教教我怎么操作啊?
很多时候我们会遇到上面的问题,然后道听途说地开始给别人瞎粑粑吹吹牛皮。在这之前,连我自己都不知道VGG16网络最后两层block5 conv2,block5 conv3训练到最后几乎没有太多的纹理特征。你知道吗?不知道的话可以跟我一起学习学习,我也是个初学者,在国内CNN还没有太流行起来之前就开始跳坑,到现在蹲坑已深。弃坑 ing。。。。
最近为自己所在的公司写了一个工具来分析训练出来的CNN网络模型,如下图所示:从第一张图我们可以看到这张图有一些goldfish金鱼的模型,好像有很多条鱼尾巴,然后中间有好多鳞片,一张很奇怪的图像,但是别嫌弃这张图像,因为假设你把这张图像扔到经过ImageNet数据集训练过的VGGNet模型,出来为goldfish的概率绝对是99%的,你多试100次看是不是都为goldfish。
同理,第2张图,第3张图,都是这样的结果,连我学文科的妹纸看到这些图就yaya叫,这是什么,好恶心,怎么第3张有点像鸟但是有不是鸟的呢。对,我们搞神经网络的就喜欢这些恶心的图片。越恶心特征越明显。
通过梯度上升获得可视化卷积图
假设人工合成的可视化卷积核图为 x,我们希望这张合成图 x 能够使其对应的神经元(卷积核)具有最高的激活值。所得到的这张合成图像就是该卷基层的卷积核“想要看到的”或者“正在寻找的纹理特征”。也就是说我们希望找到一张图像经过CNN网络,传播到指定的卷积核的时候,这张图片可以使得该卷积核的得分最高。
为了合成这一张图片,我们开始从一张带有随机噪声的图像开始,每个像素值随机选取一种颜色。
接下来,我们使用这张噪声图作为CNN网络的输入向前传播,然后取得其在网络中第 i 层 j 个卷积核的激活 a_ij(x),然后做一个反向传播计算 delta a_i(x)/delta x 的梯度,最后我们把该噪声图的卷积核梯度来更新噪声图。目标是希望通过改变每个像素的颜色值以增加对该卷积核的激活,这里就使用了梯度上升法:
其中 itselong 为梯度上升的学习率。不断重复上述过程,直到图像 x 能够让第 i 层第 j 个卷积核具有较高的激活值。
对于具体的实现我们需要定义一个损失函数,该损失函数将用于最大化某个指定卷积核的激活值。以该损失函数作为优化目标,我们可以了解到底什么样的图片才可以使得这个卷积核取得更好的激活值。
现在我们使用Keras的后端来完成这个损失函数, gradients(loss, variables)为返回loss函数关于variables的梯度。
start_time = time.time()
# The loss is the activation of the neuron for the chosen class
loss = layer_output[0, class_index]
# we compute the gradient of the input picture wrt this loss
grads = K.gradients(loss, input_img)[0]
# this function returns the loss and grads given the input picture
# also add a flag to disable the learning phase (in our case dropout)
iterate = K.function([input_img, K.learning_phase()], [loss, grads])
np.random.seed(1337) # for reproducibility
# we start from a gray image with some random noise
input_img_data = np.random.normal(0, 10, (1,) + model.input_shape[1:])
# (1,) for batch axis
注意这里有个小小的trick——对计算出来的梯度进行了L2正则化操作,使得梯度不会过小或过大,其带来的好处是使梯度上升的过程平滑进行。
后端函数function用传递来的参数实例化一个Keras的Function类返回。这相当于Function的对象当作函数来使用,相当于重载了括号运算符,如outputs = self.train_function(inputs)。
根据刚刚定义的损失函数iterate_fun,现在可以根据梯度上升对卷积核的激活值进行梯度上升计算。
# we run gradient ascent for 1000 steps
for i in range(1000):
loss_value, grads_value = iterate([input_img_data, 0]) # 0 for test phase
input_img_data += grads_value * learning_rate # Apply gradient to image
print('Current loss value:', loss_value)
# decode the resulting input image and add it to the list
img = deprocess(input_img_data[0])
kept_images.append((img, loss_value))
end_time = time.time()
print('Filter %d processed in %ds' % (class_index, end_time - start_time))
最后输出的图像如下图所示,这是鱼头?还是羊头?不看ImageNet的连接我也不知道是什么头。不过这样的方法是我们让输入的图像尽量与卷积核希望看到的东西一样,通过该层卷积核的损失和梯度进行上升补充,对输入的原图进行填充细节,最后得到可视化卷积核图。
可视化所有卷积核图
可视化卷积核是本例子最有趣的部分,也是笔者最喜欢的部分。通过分析CNN网络模型中的卷积核,我们将会看到每一层卷积核到底提取的是什么样的内容、纹理、特征。当我们深入了解CNN模型提取特征背后的意义,就可以有足够信心去修改卷积神经网络CNN的参数。
下面我们将会利用已经训练好的VGG16网络模型,来系统地可视化各个网络层的各个卷积核,看看CNN是对输入进行逐层分解提取特征的到底都是些什么。
最后的执行结果如下图所示,Block1_Conv1的卷积核主要完成如颜色、方向等编码,到了Block2_Conv2的卷积核明显比Block1_Conv1多了更多的纹理和不同的纹理方向,所表达的颜色也更加丰富多样,并且在边缘处可以看到有部分凹凸表现。
随着VGG16网络模型继续深入,这些颜色和方向与基本的纹理进行组合,逐渐生成特殊纹理。当进入Block3_Conv1后,方向和颜色的表现开始变少,开始出现更加复杂的纹理特征(圆形、螺旋形、多边形、波浪等形状组合),到了Block5_Conv1后可以清晰看到其纹理更加特别,卷积核随着网络空间信息的增长而出现了更加精细和复杂的特征。
卷积核变得越来越复杂,因为他们开始纳入越来越大的空间范围信息中,所呈现和表达的信息进一步丰富。
细心的读者经过实际的尝试之后或许会发现:在同一卷积层中会出现少量的可视化卷积核是空白或者相同,这意味着该卷积核对后续的操作并没有产生实际的作用,可以通过Dropout这些卷积核以减少网络的计算量和减少过度拟合的可能性。
另外,也会有部分可视化卷积核可以通过旋转平移,获得另外一个可视化卷积核。这是一个很有趣的研究方向,我们或许可以通过寻找一种旋转不变性的方法来潜在地代替网络层中的其他卷积核,从而压缩卷积核的数量。惊讶的是即使对于级别相对高的滤波器,如Block4_Conv1中,通过旋转、平移获得相同的可视化卷积核仍然成立。
可是到了block5 conv3的最后,经过代码实践之后,我们会发现512个卷积核里面只有65个卷积核loss不为0,也就是其余的卷积核已经不能再继续提取高维纹理特征信息了,这是怎么一回事?为什么没有了呢?这也是ResNet或者GoogleNet对于VGGNet的改进,可能有时候CNN网络不是每一个卷积核(神经元)都能参与网络的计算,发挥作用,提取到高维特征。下图为ResNet的跳层方式,因为VGG的block5 conv3、block5 conv2开始出现大量没有用的卷积核,而block4 conv3却有很多有用的信息可以向后传递。
有了对CNN网络模型的可视化分析,我们更加深一步地理解卷积神经网络CNN的具体操作过程,具体提取的是什么样的纹理特征信息。如果你有精力,还可以自己动手指定dropout的神经元,查看自己的卷积神经网络为什么会过度拟合,可以怎么修剪网络。这是一个很有趣的过程,也是一个考验人类耐心的过程。
卷积神经网络CNN的两个主要作用是:1)把输入的视觉空间图像,解耦成分层次的卷积核组合。2)通过分层次的卷积核把输入的数据映射到不同的几何空间。
有的人会宣称卷积神经网络CNN通过分层思想对输入的图像进行解耦,这一过程模拟了人类视觉皮层,因此才能获得如此精妙的效果。可事实真的是这样吗?从科学的角度来看,这并不意味着我们真的在某种程度上解决了计算机视觉的问题,我们只是使用了数学的方法对输入的图像数据进行抽样和几何空间映射。即使科学是这么解释,但究其竟并没有反驳视觉皮层不是这种工作方式。
深度学习虽然不能表达真正的智力,但毋庸置疑的是其预测效果如此惊人,以至于在近年来没有其他算法可以比拟,甚至在某些情况下超过了人类的预测精度!我们不期待着算法学习我们的思考方式,而应去拥抱数学,用其特殊的方式去为人类服务,继续发现、继续创造、继续在模拟数字领域领跑!
参考文献:
Yosinski__2015__ICML_DL__Understanding_Neural_Networks_Through_Deep_Visualization
How convolutional neural networks see the world
地址:https://blog.keras.io/how-convolutional-neural-networks-see-the-world.html
雷锋网 AI科技评论。
雷峰网版权文章,未经授权禁止转载。详情见转载须知。