利用随机前馈神经网络生成图像观察网络复杂度

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/happynear/article/details/46583811

零、声明

这是一篇失败的工作,我低估了batch normalization里scale/shift的作用。细节在第四节,请大家引以为戒。

一、前言

  关于神经网络的作用有一个解释:它是一个万能函数逼近器。通过BP算法调整权重,在理论上神经网络可以近似出任意的函数。
  当然,要近似出来的函数的复杂度肯定不能超过神经网络的表达能力,不然就会产生欠拟合的现象。而一个网络能承载的函数复杂度通常与隐层节点个数和深度有关。
  本篇文章通过一种可视化的方法,来直观地表示出一个神经网络得表达能力。

二、算法

  值得注意的是,神经网络所拟合的函数,是关于输入的连续函数。如果输入是图像的2维坐标,输出是3维的RGB颜色的话,那颜色就是坐标的连续函数,这一点是图像是否美观的一个很重要的标准。如果我们随机生成神经网络的权重,通过生成的图像的复杂程度,就能够大致看出一个网络能够表达出多么复杂的函数。

以下是生成图像的代码(基于DeepLearnToolBox,地址:https://github.com/happynear/DeepLearnToolbox):

layers = randi(10,1,10)+10;%隐层节点数,从[10 20]随机采样

nn = nnsetup([2 layers 3]);%构建网络
nn.activation_function              = 'sigm';%隐层激活函数
nn.output = 'sigm';%输出层激活函数
nn.useBatchNormalization            = 0;%是否使用Batch Normalization

output_h = 600;%高
output_w = 800;%宽

[I,J] = ind2sub([output_h,output_w],(1:output_h*output_w)');%得到坐标
I = (I - output_h / 2) / output_h * 2;%归一化一下
J = (J - output_w / 2) / output_w * 2;

nn = nnff(nn,[I J],zeros(size(I,1),3));%前馈神经网络

output = nn.a{length(nn.a)};
output = zscore(output);%做一下归一化,如果输出层激活函数为sigm,此步可省略。
output = reshape(output,[output_h,output_w,3]);

imshow(uint8(output*100+128));%显示图像

  这里有三个地方可以设置,隐层节点数,神经网络中使用的激活函数,是否采用Batch Normalization。关于Batch Normalization,请参考我之前的一篇博客(链接
  这里加入Batch Normalization是基于如下考虑:由于我们并不是使用一个训练好的神经网络,即使对输入做了归一化,这个网络的权重可能还是不适合我们使用的这种输入形式,不仅是梯度会产生弥散现象,前馈神经网络的中也会有类似的现象发生,导致网络过早饱和。在每层都做一次归一化,可以最大程度上减小该现象的发生。

三、结果

ReLU+Batch Normalization:
ReLU+BN
ReLU,无Batch Normalization:
ReLU
Sigmoid+Batch Normalization:
Sigm+BN
Sigmoid,无Batch Normalization:
Sigm
以上图像均为10个隐层的神经网络生成,不同层数生成的图像请读者自行运行代码并观察。

  可以看到:
  1、ReLU+Batch Normalization所表达的函数最为复杂。
  2、Sigmoid+Batch Normalization生成的图像有较多的相同颜色的区域,相对来说不如ReLU的表达能力强,有些甚至不如ReLU无BN条件下生成的图像复杂。
  3、使用Sigmoid函数而不采用Batch Normalization算法时,函数图像都较为简单,通过观察每层的输出可以看到,最后几层的节点响应值几乎相等,此时网络实际上已经退化成一个简单的无隐层网络。

生成的图像可以通过以下链接下载:
http://pan.baidu.com/s/1hqtkoug

四、后记

  由于隔壁教研室的老师办了个深度学习的Call for Paper,所以我又把这个博客拿出来想做更深入的研究。我之前在想,如果继续把Leaky Relu、MaxOut等结构拿来跑一下,然后再把这个扩展一下,变成CNN的形式(这个算法可以看作是一个2channel的线性图像,经过多个1x1conv之后,输出3channel的图像),把VGG、Inception、NIN等结构拿来跑跑,妥妥的是一篇好文章啊。。
  然而在码了一堆代码之后,发现了一个重大bug,我在Batch Normalization层中,只考虑了scale的影响,而忘记了另一个关键因素:shift,这一项其实对网络所表达的函数具有更大影响。
  请注意到上方Sigmoid+Batch Normalization生成的图像中,所有的线条似乎都指向了图像的中点。这是因为如果设置shift=0的话,由于Sigmoid关于(0,0.5)奇对称,所以在BN之后,所表达的函数必定为奇函数。多个奇函数的嵌套可能是奇函数也可能是偶函数,但都具备了某种对称性,大大限制了神经网络的发挥。所以shift也必须进行赋值来打破这种对称性。
  在对shift进行随机赋值之后,Sigmoid+BN生成的图像就变成了这个样子(隐藏层数5):
  这里写图片描述
  从这张图上我们已经很难说它和ReLU生成的图像的复杂度有多大的区别了,因此这个方法仅仅只能生成好看的图像而已,然而这并没有什么卵用。
  
  进一步分析,实际上在原图中有大量的复杂图像的可能性都被收缩到中间那一个小点中了,比如把shift的随机范围调小一点,就会生成中间很复杂,而周围仍呈放射状的图形(隐藏层数8):
  这里写图片描述
  看来这篇paper是发不出了,请允许我做一个悲伤地表情。。

猜你喜欢

转载自blog.csdn.net/happynear/article/details/46583811