深度学习中的数学与技巧(1):BN之利用随机前馈神经网络生成图像观察网络复杂度

本文通过可视化方法展示神经网络的表达能力,对比不同配置下(如激活函数类型和BatchNormalization的使用与否)生成图像的复杂度,以此评估网络的函数逼近性能。



一、前言

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

二、算法

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

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


   
  1. layers = randi(10,1,10)+10;%隐层节点数,从[10 20]随机采样
  2. nn = nnsetup([2 layers 3]);%构建网络
  3. nn.activation_function = ‘sigm’;%隐层激活函数
  4. nn.output = ‘sigm’;%输出层激活函数
  5. nn.useBatchNormalization = 0;%是否使用Batch Normalization
  6. output_h = 600;%高
  7. output_w = 800;%宽
  8. [I,J] = ind2sub([output_h,output_w],(1:output_h*output_w)’);%得到坐标
  9. I = (I - output_h / 2) / output_h * 2;%归一化一下
  10. J = (J - output_w / 2) / output_w * 2;
  11. nn = nnff(nn,[I J],zeros(size(I,1),3));%前馈神经网络
  12. output = nn.a{length(nn.a)};
  13. output = zscore(output);%做一下归一化,如果输出层激活函数为sigm,此步可省略。
  14. output = reshape(output,[output_h,output_w,3]);
  15. 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): 
  这里写图片描述

### 深度学习中的卷积神经网络基础 #### 卷积神经网络(CNN)原理 卷积神经网络是一种专门用于处理具有网格状拓扑数据的前馈神经网络,最常应用于图像识别和分类任务。其核心思想在于通过局部感知野和权值共享来减少计算复杂度并提取空间特征[^1]。 - **卷积层**:这是 CNN 的基本构建模块之一,负责从输入数据中提取特征。卷积操作通过对输入矩阵应用一组可学习的滤波器实现,这些滤波器可以检测边缘、纹理或其他视觉模式。 - **池化层**:通常紧跟在卷积层之后,用于降低特征图的空间维度,从而减少参数数量和计算量。常用的池化方式包括最大池化和平均池化[^2]。 - **全连接层**:位于网络的最后一部分,将前面提取到的高维特征映射到类别标签上。这一层的作用类似于传统神经网络的功能。 - **激活函数**:ReLU 是目前广泛使用的非线性激活函数,它能够有效缓解梯度消失问题,并加速收敛过程[^3]。 #### 常见的应用场景 卷积神经网络因其强大的特征提取能力,在多个领域得到了广泛应用: - 图像分类目标检测:如 AlexNet, VGG, ResNet 等经典架构均基于 CNN 设计而成; - 自然语言处理:尽管 RNN 和 Transformer 更为主流,但在某些特定任务中仍可见到 CNN 的身影; - 医疗影像分析:利用 CNN 对 X 光片、MRI 扫描结果进行疾病诊断等[^4]; #### 面试可能涉及的经典题目 以下是围绕卷积神经网络的一些典型面试考察点及其解答思路: 1. **什么是感受野?如何增大它的范围而不增加额外开销** - 受感野定义为单个像素位置处所覆盖的实际区域大小。可以通过堆叠更多层数或者采用膨胀卷积技术间接扩大受感野而无需显著提升资源消耗。 2. **解释一下 Inception 结构的设计理念是什么样的?** - Google 提出的 Inception Module 主要目的是探索最优尺度组合的同时保持高效运算效率。具体做法是在同一层内部平行设置多种尺寸核大小的操作选项供选择使用。 3. **Batch Normalization 起到了哪些作用呢 ?** - BN 技术不仅可以加快训练速度还能起到一定正则效果防止过拟合现象发生 。另外也有助于缓解 Internal Covariate Shift 问题改善优化性能表现. ```python import torch.nn as nn class SimpleCNN(nn.Module): def __init__(self): super(SimpleCNN, self).__init__() self.conv_layer = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=(3, 3)) self.pooling_layer = nn.MaxPool2d(kernel_size=(2, 2), stride=2) self.fc_layer = nn.Linear(64 * (image_height//2) * (image_width//2), num_classes) def forward(self, x): x = self.conv_layer(x) x = nn.functional.relu(x) x = self.pooling_layer(x) x = x.view(-1, 64 * (image_height//2) * (image_width//2)) # Flatten the tensor output = self.fc_layer(x) return output ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值