CS231n(六)Neural Nets Notes2

神经网络就是进行了一系列的线性映射与非线性激活函数交织的运算。

数据预处理

这里有三个常用的符号:数据矩阵X,假设其尺寸是[N x D]N是数据样本的数量,D是数据的维度)。

1.均值减法(Mean subtraction是预处理最常用的形式。它对数据中每个独立特征减去平均值,从几何上可以理解为在每个维度上都将数据云的中心都迁移到原点。在numpy中,该操作可以通过代码X -= np.mean(X, axis=0)实现。而对于图像,更常用的是对所有像素都减去一个值,可以用X -= np.mean(X)实现,也可以在3个颜色通道上分别操作。

2.归一化(Normalization是指将数据的所有维度都归一化,使其数值范围都近似相等。有两种方法,一是对数据做零中心化(zero-centered)处理,然后每个维度都除以其标准差,实现代码为X /= np.std(X, axis=0)。第二种方法是对每个维度都做归一化,使得每个维度的最大和最小值是1和-1。

公式:clip_image004,其中clip_image006为所有样本数据的均值,clip_image008为所有样本数据的标准差。

下面是预处理的一般流程:

左边:原始的2维输入数据。

中间:在每个维度上都减去平均值后得到零中心化数据,现在数据云是以原点为中心的。

右边:每个维度都除以其标准差来调整其数值范围。

3.PCA和白化(Whitening是另一种预处理形式。先对数据进行零中心化处理,然后计算协方差矩阵,它展示了数据中的相关结构性。    

实现代码:

# 假设输入数据矩阵X的尺寸为[N x D]
X -= np.mean(X, axis = 0) # 对数据进行零中心化(重要)
cov = np.dot(X.T, X) / X.shape[0] # 得到数据的协方差矩阵

其中协方差矩阵的第(i,j)个元素是数据第i个和第j个维度的协方差,也就是该矩阵的对角线上的元素是方差。协方差矩阵是对称的是半正定的,我们可以对数据协方差矩阵进行奇异值分解SVD预算。

实现代码:

U,S,V = np.linalg.svd(cov)

其中U的是特征向量(可以看作是标准正交基向量),S是包含奇异值的一维数组(S中的元素是特征值的平方)。为了去除数据的相关性,我们将已经零中心化处理过的原始数据投影到特征基准上:

Xrot = np.dot(X,U) # 对数据去相关性

如果计算Xrot的协方差矩阵,将会看到它是对角对称的。np.linalg.svd的一个良好性质是在它的返回值U中,特征向量是按照特征值的大小排列的。我们可以利用这个性质来对数据降维,只要使用前面的小部分特征向量,丢弃掉那些包含的数据没有方差的维度。 这个操作也被称为主成分分析( Principal Component Analysis 简称PCA)降维:

Xrot_reduced = np.dot(X, U[:,:100]) # Xrot_reduced 变成 [N x 100]

经过上面的操作,将原始的数据集的大小由[N x D]降到了[N x 100],留下了数据中包含最大方差的100个维度。通常使用PCA降维过的数据训练线性分类器和神经网络会达到非常好的性能效果,同时还能节省时间和存储器空间。

白化(whitening):白化操作的输入是特征基准上的数据,然后对每个维度除以其特征值来对数值范围进行归一化。该变换的几何解释是:如果数据服从多变量的高斯分布,那么经过白化后,数据的分布将会是一个均值为零,且协方差相等的矩阵。该操作的代码如下:

# 对数据进行白化操作:
# 除以特征值 
Xwhite = Xrot / np.sqrt(S + 1e-5)

其中分母添加了1e-5(或一个更小的常量)来防止分母为0,作用是解决变换过程中会夸大数据中的噪声。在实际操作中,这个问题可以用更强的平滑来解决(例如:采用比1e-5更大的值)。

左边是二维的原始数据。

中间:经过PCA操作的数据。可以看出数据首先是零中心的,然后变换到了数据协方差矩阵的基准轴上。这样就对数据进行了解相关(协方差矩阵变成对角阵)。

右边:每个维度都被特征值调整数值范围,将数据协方差矩阵变为单位矩阵。从几何上看,就是对数据在各个方向上拉伸压缩,使之变成服从高斯分布的一个数据点分布。

实践操作。在这个笔记中提到PCA和白化主要是为了介绍的完整性,实际上在卷积神经网络中并不会采用这些变换。然而对数据进行零中心化操作还是非常重要的,对每个像素进行归一化也很常见。很重要的一点是:任何预处理策略(比如数据均值)都只能在训练集数据上进行计算,算法训练完毕后再应用到验证集或者测试集上。应该先分成训练/验证/测试集,只是从训练集中求图片平均值,然后各个集(训练/验证/测试集)中的图像再减去这个平均值。

 

权重初始化:

路是:如果神经元刚开始的时候是随机且不相等的,那么它们将计算出不同的更新,并将自身变成整个网络的不同部分。

做法:小随机数初始化。权重初始值要非常接近0但又不能等于0。

实现:W = 0.01 * np.random.randn(D,H)其中randn函数是基于零均值和标准差的一个高斯分布(正态)来生成随机数的。

注意:使用1/sqrt(n)校准方差。上面的做法有一个问题:随着输入数据的增长,随机初始化的神经元的输入数据的分布中方差也在增大。我们可以除以输入数量的平方根来调整数值范围,这样神经元输出的方差就归一到1。

建议做法:经元的权重向量初始化为:w = np.random.randn(n) / sqrt(n) 其中n就是输入数据的数量。这样就保证了网络中所有神经元起始时有近似同样的输出分布。针对ReLU神经元的特殊初始化,并给出结论:网络中神经元的方差应该是2.0/n。代码为w = np.random.randn(n) * sqrt(2.0/n)

偏置(biases)的初始化通常将偏置初始化为0,这是因为随机小数值权重矩阵已经打破了对称性。对于ReLU非线性激活函数,有研究人员喜欢使用如0.01这样的小数值常量作为所有偏置的初始值,这是因为他们认为这样做能让所有的ReLU单元一开始就激活,这样就能保存并传播一些梯度。然而,这样做是不是总是能提高算法性能并不清楚(有时候实验结果反而显示性能更差),所以通常还是使用0来初始化偏置参数

批量归一化(Batch Normalization)。在每一层的wx+b和f(wx+b)之间加一个归一化(将wx+b归一化成:均值为0,方差为1(标准高斯分布))

批量归一化,其做法是让激活数据在训练开始前通过一个网络,网络处理数据使其服从标准高斯分布。因为归一化是一个简单可求导的操作,所以上述思路是可行的。在实现层面,应用这个技巧通常意味着全连接层(或者是卷积层,后续会讲)与激活函数之间添加一个BatchNorm层。在实践中,使用了批量归一化的网络对于不好的初始值有更强的鲁棒性。最后一句话总结:批量归一化可以理解为在网络的每一层之前都做预处理,只是这种操作以另一种方式与网络集成在了一起

这个方法可以进一步加速收敛,因此学习率可以适当增大,加快训练速度;过拟合现象可以得倒一定程度的缓解,所以可以不用Dropout或用较低的Dropout,而且可以减小L2正则化系数,训练速度又再一次得到了提升。即Batch Normalization可以降低我们对正则化的依赖程度。
现在的深度神经网络基本都会用到Batch Normalization。 

正则化 Regularization

防止过拟合。

L1正则化是一个相对常用的正则化方法。对于每个w我们都向目标函数增加一个\lambda|w|。L1正则化有一个有趣的性质,它会让权重向量在最优化的过程中变得稀疏(即非常接近0)。一般说来L2正则化都会比L1正则化效果好。

L2正则化可能是最常用的正则化方法了。可以通过惩罚目标函数中所有参数的平方将其实现。即对于网络中的每个权重w,向目标函数中增加一个\frac{1}{2}\lambda w^2,其中\lambda是正则化强度。前面这个\frac{1}{2}很常见,是因为加上\frac{1}{2}后,该式子关于w梯度就是\lambda w而不是2\lambda w了。L2正则化可以直观理解为它对于大数值的权重向量进行严厉惩罚,倾向于更加分散的权重向量。使网络更倾向于使用所有输入特征,而不是严重依赖输入特征中某些小部分特征。需要注意在梯度下降和参数更新的时候,使用L2正则化意味着所有的权重都以w += -lambda * W向着0线性下降。 

L1和L2正则化也可以进行组合\lambda_1|w|+\lambda_2w^2,这也被称作Elastic net regularizaton

最大范式约束(Max norm constraints)这种形式的正则化是给每个神经元中权重向量的量级设定上限,并使用投影梯度下降来确保这一约束。在实践中,与之对应的是参数更新方式不变,然后要求神经元中的权重向量\overrightarrow{w}必须满足||\overrightarrow{w}||_2<c这一条件,一般c值为3或者4。有研究者发文称在使用这种正则化方法时效果更好。这种正则化还有一个良好的性质,即使在学习率设置过高的时候,网络中也不会出现数值“爆炸”,这是因为它的参数更新始终是被限制着的。

随机失活(Dropout)是一个简单又极其有效的正则化方法。该方法由Srivastava在论文Dropout: A Simple Way to Prevent Neural Networks from Overfitting中提出的,与L1正则化,L2正则化和最大范式约束等方法互为补充。在训练的时候,随机失活的实现方法是让神经元以超参数p的概率被激活或者被设置为0。

别看dropout之后的网络只是原网络的一个 子网络 ,复杂度不比原网络。但由于每个神经元的dropout是 随机dropout,因此每一轮都相当于在一个 新的 子网络上训练。那么最终得到的模型便是 无数个 子网络 共同训练 的成果,效果自然会更好。

然后麻烦也来了,训练阶段的每个神经元要事先添加一道概率流程:

对应的公式变化如下如下:

  • 没有dropout的神经网络:

  • 拥有dropout的神经网络:

下面是Tensorflow代码实现:

# coding=utf-8

import tensorflow as tf
import numpy as np

inputs = np.random.uniform(-10, 10, size=[3, 3])
features = tf.placeholder_with_default(input=inputs, shape=[3, 3])

# 随机失活
output_dropout = tf.nn.dropout(features, keep_prob=0.5)

with tf.Session() as sess:
    print ("\nfeatures :\n{}".format(sess.run(features)))
    print ("\n----------\n")
    print ("\ndropout :\n{}".format(sess.run(output_dropout)))
features :
[[-7.75502709 -3.78393841  2.47710937]
 [-6.84196585  1.61396282  7.89847326]
 [-3.37708497 -5.77882292 -8.40472124]]

----------


dropout :
[[ -0.          -0.           4.95421875]
 [-13.6839317    0.          15.79694652]
 [ -0.         -11.55764583  -0.        ]]

正则化实践:通过交叉验证获得一个全局使用的L2正则化强度是比较常见的。在使用L2正则化的同时在所有层后面使用随机失活也很常见。p值一般默认设为0.5,也可能在验证集上调参。

损失函数

失函数的正则化损失部分,它可以看做是对模型复杂程度的某种惩罚。损失函数的第二个部分是数据损失它是一个有监督学习问题,用于衡量分类算法的预测结果(即分类评分)和真实标签结果之间的一致性。数据损失是对所有样本的数据损失求平均。也就是说,L=\frac{1}{N}\sum_iL_i中,N是训练集数据的样本数。让我们把神经网络中输出层的激活函数简写为f=f(x_i;W),在实际中你可能需要解决以下几类问题:

1.分类问题是我们一直讨论的。在该问题中,假设有一个装满样本的数据集,每个样本都有一个唯一的正确标签(是固定分类标签之一)。在这类问题中,一个最常见的损失函数就是SVM(是Weston Watkins 公式):

\displaystyle L_i=\sum_{j\not=y_i}max(0,f_j-f_{y_i}+1)

第二个常用的损失函数是Softmax分类器,它使用交叉熵损失:

\displaystyle L_i=-log(\frac{e^{f_{y_i}}}{\sum_je^{f_j}})

类别数目巨大当标签集非常庞大(例如字典中的所有英语单词,或者ImageNet中的22000种分类),就需要使用分层Softmax(Hierarchical Softmax了(参考文献)。分层softmax将标签分解成一个树。每个标签都表示成这个树上的一个路径,这个树的每个节点处都训练一个Softmax分类器来在左和右分枝之间做决策。

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

L_i=||f-y_i||^2_2

之所以在目标函数中要进行平方,是因为梯度算起来更加简单。因为平方是一个单调运算,所以不用改变最优参数。L1范式则是要将每个维度上的绝对值加起来:

L_i=||f-y_i||_1=\sum_j|f_j-(y_i)_j|

在上式中,如果有多个数量被预测了,就要对预测的所有维度的预测求和,即\sum_j。观察第i个样本的第j维,用\delta_{ij}表示预测值与真实值之间的差异。关于该维度的梯度(也就是\partial L_i/\partial f_j)能够轻松地通过被求导为L2范式的\delta_{ij}sign(\delta_{ij})。这就是说,评分值的梯度要么与误差中的差值直接成比例,要么是固定的并从差值中继承sign。

总结:

  • 推荐的预处理操作是对数据的每个特征都进行零中心化,然后将其数值范围都归一化到[-1,1]范围之内。
  • 使用标准差为\sqrt{2/n}的高斯分布来初始化权重,其中n是输入的神经元数。例如用numpy可以写作:w = np.random.randn(n) * sqrt(2.0/n)
  • 使用L2正则化和随机失活的倒置版本。
  • 使用批量归一化。
  • 讨论了在实践中可能要面对的不同任务,以及每个任务对应的常用损失函数。

猜你喜欢

转载自blog.csdn.net/qq_40755643/article/details/82932683