深度学习之构建网络

文章目录

  • 构建网络
    • 前言
    • 卷积层
    • 汇聚层
    • 全连接层
    • 激活函数
    • 权重初始化
    • 正则化
    • 损失函数
      • 一般的分类问题
      • 类别数目巨大
      • 属性(Attribute)分类
      • 回归问题
      • 结构化预测(structured prediction)
    • 实际建议

构建网络

https://www.yuque.com/lart/ml-newer/

版本号
作者
时间
改动内容
0.1
Lart
2018年10月17日
创建文档
0.2
Lart
2018年10月18日10:59:28
调整部分内容

前言

以经典的AlexNet网络为例,介绍构建网络的细节.
先给出AlexNet的一些参数:
  • 卷积层:5层
  • 全连接层:3层
  • 深度:8层
  • 参数个数:60M
  • 神经元个数:650k
  • 分类数目:1000类

卷积层

卷积层在计算的时候看上去像是在做卷积运算,实际上是一种等价的理解方式.
对于卷积层的理解有两种看待的角度:
  1. 每一个卷积核(可移动的对整个图像进行滑动内积),都对应一个卷积层的深度切片,每个卷积层都有多个深度切片,也就是对应多个卷积核,多次不同的卷积计算.最终得到的特征图的输出的深度是和卷积层的切片数目(深度)一致.
  2. 每一个卷积层切片对应的运算,不再看做一个单独的核在滑动卷积,而是看作具有多个神经元的一个网络层,每个神经元都有自己的局部感受野,只计算自己的那一部分.所有这一切片上的神经元的运算结果的结合就是得到的一层特征图.

由于卷积层的 参数共享 设定,导致两种理解是等价的. 而实际运算中,是以第一种理解更为直观,所以多用第一种理解方式来解释问题.

在计算卷积核的卷积时,每一次卷积运算都会有一个偏置项,这一点到时更贴合第2点的理解,每一个神经元的输入连接都有一个偏置项.


注意

  • 关于感受野: 在卷积神经网络中,感受野的定义是 卷积神经网络每一层输出的特征图(feature map)上的像素点在原始图像上映射的区域大小。
  • 关于参数共享: 如果一个特征在计算某个空间位置(x, y)的时候有用,那么它在计算另一个不同位置(x2, y2)的时候也有用。基于这个假设,可以显著地减少参数数量。
  • 关于滤波器深度: 每个滤波器在空间上(宽度和高度)都比较小,但是深度和输入数据一致

    有一种设定是不同深度,这会导致更为复杂一些的情况.
    沿着深度方向排列、感受野相同的神经元集合称为深度列(depth column),也有人使用纤维(fibre)来称呼它们。

  • 关于卷积核移动的步长: 一般是1,但是有些时候会有所变化.

    最近一个研究(Fisher Yu和Vladlen Koltun的论文)给卷积层引入了一个新的叫扩张(dilation)的超参数。到目前为止,我们只讨论了卷积层滤波器是连续的情况。但是,让滤波器中元素之间有间隙也是可以的,这就叫做扩张。换句话说,操作中存在1的间隙。在某些设置中,扩张卷积与正常卷积结合起来非常有用,因为在很少的层数内更快地汇集输入图片的大尺度特征。

  • 关于卷积操作的反向传播: (同时对于数据和权重)还是一个卷积(但是是和空间上翻转的滤波器)。

每个卷积核对应的输出数据体在空间上的尺寸可以通过 输入数据体尺寸(W),卷积层中神经元的 感受野尺寸(F)步长(S)零填充的数量(P) 的函数来计算。(这里假设输入数组的空间形状是正方形,即高度和宽度相等)

输出数据体的空间尺寸为

应该使用 小尺寸滤波器(比如3x3或最多5x5),使用步长。

还有一点非常重要,就是对输入数据进行零填充,这样卷积层就不会改变输入数据在空间维度上的尺寸。比如,当,那就使用来保持输入尺寸。当,一般对于任意,当的时候能保持输入尺寸。如果必须使用更大的滤波器尺寸(比如7x7之类),通常只用在第一个面对原始图像的卷积层上。

直观说来,最好选择带有小滤波器的卷积层组合,而不是用一个带有大的滤波器的卷积层。
前者可以表达出输入数据中更多个强力特征,使用的参数也更少
唯一的不足是,在进行 反向传播时,中间的卷积层可能会导致占用更多的内存

汇聚层

通常,在 连续的卷积层之间会周期性地插入 一个汇聚层。

它的作用是 逐渐降低数据体的空间尺寸,这样的话就能减少网络中参数的数量,使得计算资源耗费变少,也能有效控制过拟合

在大型网络中,一般担心的是过拟合的问题,而欠拟合关心的少些。

汇聚层使用MAX操作对输入数据体的每一个深度切片独立进行操作,改变它的空间尺寸。

在实践中,最大汇聚层通常只有两种形式:一种是也叫重叠汇聚(overlapping pooling),另一个更常用的是对更大感受野进行汇聚需要的汇聚尺寸也更大,而且往往对网络有破坏性。

除了最大汇聚,汇聚单元还可以使用其他的函数,比如平均汇聚(average pooling)L-2范式汇聚(L2-norm pooling)。平均汇聚历史上比较常用,但是现在已经很少使用了。因为实践证明,最大汇聚的效果比平均汇聚要好。

负责对输入数据的空间维度进行降采样。最常用的设置是用2x2感受野(即)的最大值汇聚,步长为2()。


注意

  • 最大汇聚的反向传播就是对于 max 操作的一个求导问题,在向前传播经过汇聚层的时候,通常会把池中最大元素的索引记录下来(有时这个也叫作道岔(switches)),这样在反向传播的时候梯度的路由就很高效。
  • 很多人不喜欢汇聚操作,认为可以不使用它。比如在Striving for Simplicity: The All Convolutional Net一文中,提出使用一种只有重复的卷积层组成的结构,抛弃汇聚层。通过在卷积层中使用更大的步长来降低数据体的尺寸。有发现认为,在训练一个良好的生成模型时,弃用汇聚层也是很重要的。比如变化自编码器(VAEs:variational autoencoders)和生成性对抗网络(GANs:generative adversarial networks)。现在看起来,未来的卷积网络结构中,可能会很少使用甚至不使用汇聚层。

全连接层

在全连接层中,神经元对于前一层中的所有激活数据是全部连接的,这个常规神经网络中一样。它们的激活可以先用矩阵乘法,再加上偏差。


注意

  • 对于任一个卷积层,都存在一个能实现和它一样的前向传播函数的全连接层。权重矩阵是一个巨大的矩阵,除了某些特定块(这是因为有局部连接),其余部分都是零。而在其中大部分块中,元素都是相等的(因为参数共享)。
  • 任何全连接层都可以被转化为卷积层。比如,一个的全连接层,输入数据体的尺寸是,这个全连接层可以被等效地看做一个的卷积层。换句话说,就是将滤波器的尺寸设置为和输入数据体的尺寸一致了。因为只有一个单独的深度列覆盖并滑过输入数据体,所以输出将变成,这个结果就和使用初始的那个全连接层一样了。

在两种变换中,将全连接层转化为卷积层在实际运用中更加有用。


补充

卷积神经网络最常见的形式就是将一些卷积层和ReLU层放在一起,其后紧跟汇聚层,然后重复如此直到图像在空间上被缩小到一个足够小的尺寸,在某个地方过渡成全连接层也较为常见。最后的全连接层得到输出,比如分类评分等。换句话说,最常见的卷积神经网络结构如下:

INPUT -> [[CONV -> RELU]*N -> POOL?]*M -> [FC -> RELU]*K -> FC

其中*__指的是重复次数,POOL?指的是一个可选的汇聚层。其中__N >=0,通常N<=3,M>=0,K>=0,通常K<3。

例如,下面是一些常见的网络结构规律:

  • INPUT -> FC,实现一个线性分类器,此处__N = M = K = 0__。
  • INPUT -> CONV -> RELU -> FC
  • INPUT -> [CONV -> RELU -> POOL]*2 -> FC -> RELU -> FC。此处在每个汇聚层之间有一个卷积层。
  • INPUT -> [CONV -> RELU -> CONV -> RELU -> POOL]*3 -> [FC -> RELU]2 -> FC 。此处每个汇聚层前有两个卷积层,这个思路适用于更大更深的网络,因为在执行具有破坏性的汇聚操作前,多重的卷积层可以从输入数据中学习到更多的复杂特征。

激活函数

激活函数的理论作用是使神经网络成为一个普适近似器。

实际建议

在同一个网络中混合使用不同类型的神经元是非常少见的,虽然没有什么根本性问题来禁止这样做。

ReLU非线性函数。注意设置好学习率,或许可以监控你的网络中死亡的神经元占的比例。
如果单元死亡问题困扰你,就试试Leaky ReLU或者Maxout,不要再用sigmoid了。
也可以试试tanh,但是其效果应该不如ReLU或者Maxout。

权重初始化

在训练完毕后,虽然不知道网络中每个权重的最终值应该是多少,但如果数据经过了恰当的归一化的话,就可以假设所有权重数值中大约一半为正数,一半为负数。
这样,一个听起来蛮合理的想法就是把这些权重的初始值都设为0吧,因为在期望上来说0是最合理的猜测。这个做法错误的!
因为如果网络中的每个神经元都计算出同样的输出,然后它们就会在反向传播中计算出同样的梯度,从而进行同样的参数更新。换句话说,如果权重被初始化为同样的值,神经元之间就失去了不对称性的源头

  1. 小随机数初始化: 权重初始值要非常接近0又不能等于0。解决方法就是将权重初始化为很小的数值,以此来打破对称性。其思路是:如果神经元刚开始的时候是随机且不相等的,那么它们将计算出不同的更新,并将自身变成整个网络的不同部分。高斯分布/均匀分布一般都可以,差别不大.
    注意 并不是小数值一定会得到好的结果。例如,一个神经网络的层中的权重值很小,那么在反向传播的时候就会计算出非常小的梯度(因为梯度与权重值是成比例的)。这就会很大程度上减小反向传播中的“梯度信号”,在深度网络中,就会出现问题。
  2. 偏置参数初始化: 通常还是使用0来初始化偏置参数
  3. 批量归一化: 在神经网络中使用批量归一化已经变得非常常见。在实践中,使用了批量归一化的网络对于不好的初始值有更强的鲁棒性批量归一化可以理解为在网络的每一层之前都做预处理,只是这种操作以另一种方式与网络集成在了一起.

实际建议 当前的推荐是使用ReLU激活函数,并且使用w = np.random.randn(n) * sqrt(2.0/n)来进行权重初始化.网络中可以使用批量归一化来动态处理权重.

正则化

主要目的是为了防止网络过拟合.

主要分为两类.

一类是作为损失函数的正则化损失部分, 常见的是L范数.

  • L2正则化: L2正则化可以直观理解为它对于大数值的权重向量进行严厉惩罚,倾向于更加分散的权重向量。 可以通过惩罚目标函数中所有参数的平方将其实现。即对于网络中的每个权重,向目标函数中增加一个,其中是正则化强度。前面这个很常见,是因为加上后,该式子关于梯度就是而不是了。
  • L1正则化: 是另一个相对常用的正则化方法。对于每个我们都向目标函数增加一个L1正则化有一个有趣的性质,它会让权重向量在最优化的过程中变得稀疏(即非常接近0)。

L1正则化的神经元最后使用的是它们最重要的输入数据的稀疏子集,同时对于噪音输入则几乎是不变的了。
L2正则化中的权重向量大多是分散的小数字。

实际建议 如果不是特别关注某些明确的特征选择,一般说来L2正则化都会比L1正则化效果好。

L1和L2正则化也可以进行组合:,这也被称作Elastic net regularizaton。

另外一类是网络传递中的操作, 如随机失活.

  • 使用dropout可以使得部分节点失活,可以起到简化神经网络结构的作用,从而起到正则化的作用。
  • 因为dropout是使得神经网络的节点随机失活,这样会让神经网络在训练的时候不会使得某一个节点权重过大。因为该节点输入的特征可能会被清除,所以神经网络的节点不能依赖任何输入的特征。
  • dropout最终会产生收缩权重的平方范数的效果,来压缩权重,达到类似于L2正则化的效果。
  1. 普通随机失活
""" 普通版随机失活: 不推荐实现 """
p = 0.5 # 激活神经元的概率. p值更高 = 随机失活更弱,越容易保留神经元

def train_step(X):
  	""" X中是输入数据 """

 	# 3层neural network的前向传播
  	H1 = np.maximum(0, np.dot(W1, X) + b1) # 第一层输出
  	U1 = np.random.rand(*H1.shape) < p # 第一个随机失活遮罩,要失活的位置就是0,也就是值大于p的要失活
  	H1 *= U1 # drop!
  	H2 = np.maximum(0, np.dot(W2, H1) + b2) # 第二层输出
  	U2 = np.random.rand(*H2.shape) < p # 第二个随机失活遮罩
  	H2 *= U2 # drop!
  	out = np.dot(W3, H2) + b3 # 第三层输出 = 第三层输入
  	# 反向传播:计算梯度... (略)
  	# 进行参数更新... (略)

def predict(X):
  	# 前向传播时模型集成
  	H1 = np.maximum(0, np.dot(W1, X) + b1) * p # 注意:激活数据要乘以p
  	H2 = np.maximum(0, np.dot(W2, H1) + b2) * p # 注意:激活数据要乘以p
  	out = np.dot(W3, H2) + b3

注意 在训练的时候,要随机失活,但是在预测的时候,不进行随机失活,但是对于两个隐层的输出都要乘以,调整其数值范围.

  1. 反向随机失活
""" 
反向随机失活: 推荐实现方式.
在训练的时候drop和调整数值范围,测试时不做任何事.
"""
p = 0.5 # 激活神经元的概率. p值更高 = 随机失活更弱

def train_step(X):
  	# 3层neural network的前向传播
  	H1 = np.maximum(0, np.dot(W1, X) + b1)
 	U1 = (np.random.rand(*H1.shape) < p) / p # 第一个随机失活遮罩. 注意/p!
  	H1 *= U1 # drop!
  	H2 = np.maximum(0, np.dot(W2, H1) + b2)
  	U2 = (np.random.rand(*H2.shape) < p) / p # 第二个随机失活遮罩. 注意/p!
  	H2 *= U2 # drop!
  	out = np.dot(W3, H2) + b3

  # 反向传播:计算梯度... (略)
  # 进行参数更新... (略)

def predict(X):
    # 前向传播时模型集成
    H1 = np.maximum(0, np.dot(W1, X) + b1) # 不用数值范围调整了
    H2 = np.maximum(0, np.dot(W2, H1) + b2)
    out = np.dot(W3, H2) + b3

注意 在训练的时候,要随机失活,并除以对应的概率,但是在预测的时候,不进行随机失活,对于两个隐层的输出不再需要乘以.

实际建议

  • 通过交叉验证获得一个全局使用的L2正则化强度是比较常见的。
  • 在使用L2正则化的同时在所有层后面使用随机失活也很常见。

值一般默认设为0.5,也可能在验证集上调参。

损失函数

我们已经讨论过损失函数的正则化损失部分,它可以看做是对模型复杂程度的某种惩罚。

损失函数的第二个部分是数据损失,它是一个有监督学习问题,用于衡量分类算法的预测结果(即分类评分)和真实标签结果之间的一致性。数据损失是对所有样本的数据损失求平均。

一般的分类问题

在该问题中,假设有一个装满样本的数据集,每个样本都有一个唯一的正确标签(是固定分类标签之一)。

  • 一个最常见的损失函数就是SVM(是Weston Watkins 公式):.有些学者的论文中指出平方折叶损失(即使用)算法的结果会更好.
  • 第二个常用的损失函数是Softmax分类器,它使用交叉熵损失:

类别数目巨大

当标签集非常庞大(例如字典中的所有英语单词,或者ImageNet中的22000种分类),就需要使用 分层Softmax(Hierarchical Softmax 了(参考文献)。

分层softmax将标签分解成一个树。每个标签都表示成这个树上的一个路径,这个树的每个节点处都训练一个Softmax分类器来在左和右分枝之间做决策。树的结构对于算法的最终结果影响很大,而且一般需要具体问题具体分析。

属性(Attribute)分类

上面两个损失公式的前提,都是假设每个样本只有一个正确的标签。但是如果是一个二值向量,每个样本可能有,也可能没有某个属性,而且属性之间__并不相互排斥__呢?

比如在Instagram上的图片,就可以看成是被一个巨大的标签集合中的某个子集打上标签,一张图片上可能有多个标签。

在这种情况下,一个明智的方法是为每个属性创建一个独立的二分类的分类器。
一个方法: 针对每个分类的二分类器会采用下面的公式:.
上式中,求和是对所有分类,的值为1或者-1,具体根据__第i个样本是否被第j个属性打标签__而定,当该类别__被正确预测并展示__的时候,分值向量为正,其余情况为负。
也就是说,第i个样本是(1)否(-1)被第j个属性打标签时,又是(+)否(-)被预测为该类别,结果为负时,就计算损失.即被打了标签,却没有预测为该类,没打标签却被预测为该类时就计算损失.

另一种方法: 对每种属性训练一个独立的逻辑回归分类器。
然后损失函数最大化这个对数似然函数,问题可以简化为:

回归问题

是预测实数的值的问题,比如预测房价,预测图片中某个东西的长度等。对于这种问题,通常是计算预测值和真实值之间的损失。然后用L2平方范式或L1范式度量差异。

L2范式 :;
L1范式 则是要将每个维度上的绝对值加起来:

注意 L2损失比起较为稳定的Softmax损失来,其 最优化过程要困难很多

  • 直观而言,它需要网络具备一个特别的性质,即对于每个输入(和增量)都要输出一个确切的正确值。而在Softmax中就不是这样,每个评分的准确值并不是那么重要:只有当它们量级适当的时候,才有意义
  • L2损失鲁棒性不好,因为异常值可以导致很大的梯度。

所以在面对一个回归问题时,先考虑将输出变成二值化是否真的不够用。例如,如果对一个产品的星级进行预测,使用5个独立的分类器来对1-5星进行打分的效果一般比使用一个回归损失要好很多。分类还有一个额外优点,就是能给出关于回归的输出的分布,而不是一个简单的毫无把握的输出值。

实际建议 如果确信分类不适用,那么使用L2损失吧,但是一定要谨慎:L2非常脆弱,在网络中使用随机失活(尤其是在L2损失层的上一层)不是好主意

当面对一个回归任务,首先考虑是不是必须这样。一般而言,尽量把你的输出变成二分类,然后对它们进行分类,从而变成一个分类问题。

结构化预测(structured prediction)

结构化损失是指 标签可以是任意的结构 ,例如图表、树或者其他复杂物体的情况。通常这种情况还会 假设结构空间非常巨大,不容易进行遍历 。结构化SVM背后的基本思想就是 在正确的结构 和得分最高的非正确结构之间画出一个边界

解决这类问题,并不是像解决一个简单无限制的最优化问题那样使用梯度下降就可以了,而是需要设计一些特殊的解决方案,这样可以有效利用对于结构空间的特殊简化假设。

实际建议

  1. 需要记住的是:不应该因为害怕出现过拟合而使用小网络。相反,应该进尽可能使用大网络,然后使用正则化技巧来控制过拟合。
  2. 在构建卷积神经网络结构时,最大的瓶颈是内存瓶颈。要注意三种内存占用来源:
    1. 来自中间数据体尺寸:卷积神经网络中的每一层中都有激活数据体的原始数值,以及损失函数对它们的梯度(和激活数据体尺寸一致)。通常,大部分激活数据都是在网络中靠前的层中(比如第一个卷积层)。在训练时,这些数据需要放在内存中,因为 反向传播 的时候还会用到。但是在 _测试时可以聪明点:让网络在 测试运行时候每层都只存储当前的激活数据,然后丢弃前面层的激活数据,这样就能减少巨大的激活数据量
    2. 来自参数尺寸:即整个网络的参数的数量,在反向传播时它们的梯度值,以及使用momentum、Adagrad或RMSProp等方法进行最优化时的每一步计算缓存。因此,存储参数向量的内存通常需要在参数向量的容量基础上乘以3或者更多。
    3. 卷积神经网络实现还有各种零散的内存占用,比如成批的训练数据,扩充的数据等等。
      如果你的网络工作得不好,一个常用的方法是降低批尺寸(batch size),因为 绝大多数的内存都是被激活数据消耗掉 了。

你可能感兴趣的:(深度学习)