如何创建网络的连接、数据和损失函数
1. 学习到的权重 W W W相当于图像模板
2. 评分函数:原始图像数据到类别分值的映射
损失函数:量化预测值和真实值的差距,越小越好。利用梯度最优化,求出损失函数值最小时的参数(权重W)
损失函数包含两个部分:数据损失和正则化损失
数据损失:数据损失是对所有样本的数据损失求平均
3. 正则化
目的:向某些特定的权重 W W W添加一些偏好,对其他权重不添加,以此来消除模糊性(即避免不同的权重均使损失函数最小,求出了多个W图像模板);
note:效果是让所有权重w在参数更新过程中逐渐向着0变化,变得小而均匀化
实现方式:是向损失函数增加一个正则化惩罚(regularization penalty)R(W)部分。最常用的正则化惩罚是L2范式,L2范式通过对W所有参数进行逐元素的平方惩罚来抑制大数值的权重(遇到大数值,求得的损失函数变大了)
4. 最优化
目的:使损失函数最小,并记录下此时的权重,即为训练结果
损失函数可视化:直观感受损失函数的值
5. 小批量数据梯度下降简化最优化问题
训练集中的数据都是相关的,小批量数据的梯度就是对整个数据集梯度的一个近似,一个典型的小批量包含256个例子
6. 反向传播
利用链式求导法则,简化求梯度计算,也是为了简化最优化问题
对前向传播变量进行缓存:在计算反向传播时,前向传播过程中得到的一些中间变量非常有用。在实际操作中,最好代码实现对于这些中间变量的缓存,这样在反向传播的时候也能用上它们。如果这样做过于困难,也可以(但是浪费计算资源)重新计算它们。
加法门单元:把输出的梯度相等地分发给它所有的输入;
取最大值门单元:对梯度做路由;
乘法门单元:相对不容易解释。它的局部梯度就是输入值,但是是相互交换之后的,然后根据链式法则乘以输出值的梯度。
note:注意一种比较特殊的情况,如果乘法门单元的其中一个输入非常小,而另一个输入非常大,那么乘法门的操作将会不是那么直观:它将会把大的梯度分配给小的输入,把小的梯度分配给大的输入;
输入数据的大小对于权重梯度的大小有影响,这就是为什么数据预处理关系重大,它即使只是有微小变化,也会产生巨大影响!!!
7. 神经网络算法和线性分类算法不同(非线性函数即激活函数的作用)
神经网络算法: s = W 2 m a x ( 0 , W 1 x ) s=W_2max(0,W_1x)\quad s=W2max(0,W1x),定义了评分函数的新形式
其中 W 1 W_1 W1的含义是这样的:举个例子来说,它可以是一个[100x3072]的矩阵,其作用是将图像转化为一个100维的过渡向量。函数max(0,-)即ReLu函数是非线性的,它会作用到每个元素。最终,矩阵 W 2 W_2 W2的尺寸是[10x100],因此将得到10个数字,这10个数字可以解释为是分类的评分。
注意非线性函数在计算上是至关重要的,如果略去这一步,那么两个矩阵将会合二为一,对于分类的评分计算将重新变成关于输入的线性函数。这个非线性函数就是改变的关键点(与线性分类不同的点)。
参数 W 1 , W 2 W_1,W_2 W1,W2将通过随机梯度下降来学习到,他们的梯度在反向传播过程中,通过链式法则来求导计算得出。
8. 激活函数(ReLu函数)
每个神经元都对它的输入和权重进行点积,然后加上偏差,最后使用非线性函数(或称为激活函数)
常用激活函数:
ReLu:ReLu神经元会出现死亡,再也不能被激活,尤其当学习率设置过大时,40%都会死亡
Leaky ReLU:解决ReLu的死亡问题;ReLU中当x<0时,函数值为0。而Leaky ReLU则是给出一个很小的负数梯度值,比如0.01
9. 神经网络结构
层数:不包含输入层;
全连接层:后一层神经元与前一层所有神经元皆有连接;
输出层:不需要激活函数,因为表示分类评分,没必要加;
尺寸标准:神经元个数和参数个数;
10. 防止神经网络过拟合
L2正则化、dropout、输入噪音;
不要使用减少神经元数目来解决正则化,相反应该增大网络和加大正则化强度:小网络更难使用梯度下降等局部方法来进行训练,虽然小型网络的损失函数的局部极小值更少,也比较容易收敛到这些局部极小值,但是这些最小值一般都很差,损失值很高,一不小心就略过了。。。
note:正则化强度是控制神经网络过拟合的好方法,增大λ会减小过拟合层度
包括数据预处理,权重初始化和损失函数
X -= np.mean(X)
实现,也可以在3个颜色通道上分别操作。最常用均值减法,它对数据中每个独立特征减去平均值,从几何上可以理解为在每个维度上都将数据云的中心都迁移到原点。对于图像可以用X -= np.mean(X)
实现,也可以在3个颜色通道上分别操作。X /= np.std(X, axis=0)
。第二种方法是对每个维度都做归一化,使得每个维度的最大和最小值是1和-1。这个预处理操作只有在确信不同的输入特征有不同的数值范围(或计量单位)时才有意义# 假设输入数据矩阵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)
Xrot = np.dot(X,U) # 对数据去相关性
Xrot_reduced = np.dot(X, U[:,:100]) # Xrot_reduced 变成 [N x 100]
# 对数据进行白化操作:
# 除以特征值
Xwhite = Xrot / np.sqrt(S + 1e-5)
w = np.random.randn(n) / sqrt(n)
,其中randn函数
是基于零均值和标准差的一个高斯分布;w = np.random.randn(n) * sqrt(2.0/n)
。这个形式是神经网络算法使用ReLU神经元时的当前最佳推荐。w = np.random.randn(n) * sqrt(2.0/n)
来进行权重初始化;神经网络学习参数和搜索最优超参数的过程
理论上将进行梯度检查很简单,就是简单地把解析梯度和数值计算梯度进行比较。然而从实际操作层面上来说,这个过程更加复杂且容易出错。下面是一些提示、技巧和需要仔细注意的事情:
在进行费时费力的最优化之前,最好进行一些合理性检查:
-ln(0.1)=2.302
。对于Weston Watkins SVM,假设所有的边界都被越过(因为所有的分值都近似为零),所以损失值是9(因为对于每个错误分类,边界值是1)。如果没看到这些损失值,那么初始化中就可能有问题。周期(epochs):x轴通常都是表示周期(epochs)单位,该单位衡量了在训练中每个样本数据都被观察过次数的期望(一个周期意味着每个样本数据都被观察过了一次)。相较于迭代次数(iterations),一般更倾向跟踪周期,这是因为迭代次数与数据的批尺寸(batchsize)有关,而批尺寸的设置又可以是任意的。
训练期间第一个要跟踪的数值就是损失值,它在前向传播时对每个独立的批数据进行计算。下图展示的是随着损失值随时间的变化,尤其是曲线形状会给出关于学习率设置的情况:
左图展示了不同的学习率的效果。过低的学习率导致算法的改善是线性的。高一些的学习率会看起来呈几何指数下降,更高的学习率会让损失值很快下降,但是接着就停在一个不好的损失值上(绿线)。这是因为最优化的“能量”太大,参数在混沌中随机震荡,不能最优化到一个很好的点上。
右图显示了一个典型的随时间变化的损失函数值,在CIFAR-10数据集上面训练了一个小的网络,这个损失函数值曲线看起来比较合理(虽然可能学习率有点小,但是很难说),而且指出了批数据的数量可能有点太小(因为损失值的噪音很大)。
损失函数震荡:损失值的震荡程度和批尺寸(batch size)有关,当批尺寸为1,震荡会相对较大。当批尺寸就是整个数据集时震荡就会最小,因为每个梯度更新都是单调地优化损失函数(除非学习率设置得过高)。
在训练分类器的时候,需要跟踪的第二重要的数值是验证集和训练集的准确率。这个图表能够展现知道模型过拟合的程度:
在训练集准确率和验证集准确率中间的空隙指明了模型过拟合的程度。在图中,蓝色的验证集曲线显示相较于训练集,验证集的准确率低了很多,这就说明模型有很强的过拟合。遇到这种情况,就应该增大正则化强度(更强的L2权重惩罚,更多的随机失活等)或收集更多的数据。另一种可能就是验证集曲线和训练集曲线如影随形,这种情况说明你的模型容量还不够大:应该通过增加参数数量让模型容量更大些。
最后一个应该跟踪的量是权重中更新值的数量和全部值的数量之间的比例。注意:是更新的,而不是原始梯度(比如,在普通sgd中就是梯度乘以学习率)。需要对每个参数集的更新比例进行单独的计算和跟踪。一个经验性的结论是这个比例应该在1e-3左右。如果更低,说明学习率可能太小,如果更高,说明学习率可能太高。下面是具体例子:
#假设参数向量为W,其梯度向量为dW
param_scale = np.linalg.norm(W.ravel())
update = -learning_rate*dW # 简单SGD更新
update_scale = np.linalg.norm(update.ravel())
W += update # 实际更新
print update_scale / param_scale # 要得到1e-3左右
相较于跟踪最大和最小值,有研究者更喜欢计算和跟踪梯度的范式及其更新。这些矩阵通常是相关的,也能得到近似的结果。
一个不正确的初始化可能让学习过程变慢,甚至彻底停止。还好,这个问题可以比较简单地诊断出来。其中一个方法是输出网络中所有层的激活数据和梯度分布的柱状图。直观地说,就是如果看到任何奇怪的分布情况,那都不是好兆头。比如,对于使用tanh的神经元,我们应该看到激活数据的值在整个[-1,1]区间中都有分布。如果看到神经元的输出全部是0,或者全都饱和了往-1和1上跑,那肯定就是有问题了。
第一层可视化:
最后,如果数据是图像像素数据,那么把第一层特征可视化会有帮助:
将神经网络第一层的权重可视化的例子。左图中的特征充满了噪音,这暗示了网络可能出现了问题:网络没有收敛,学习率设置不恰当,正则化惩罚的权重过低。右图的特征不错,平滑,干净而且种类繁多,说明训练过程进行良好。
x += -learning_rate * dx
v = mu * v - learning_rate * dx #与速度融合
x += v # 与位置融合
v 0 v_0 v0=0;
mu:设为[0.5,0.9,0.95,0.99]
中的一个,和学习率随着时间退火(下文有讨论)类似,动量随时间变化的设置有时能略微改善最优化的效果,其中动量在学习过程的后阶段会上升。一个典型的设置是刚开始将动量设为0.5而在后面的多个周期(epoch)中慢慢提升到0.99。
3. Nesterov动量
当参数向量位于某个位置x时,观察上面的动量更新公式可以发现,动量部分(忽视带梯度的第二个部分)会通过mu * v稍微改变参数向量。因此,如果要计算梯度,那么可以将未来的近似位置x + mu * v看做是“向前看”,这个点在我们一会儿要停止的位置附近。因此,计算x + mu * v的梯度而不是“旧”位置x的梯度就有意义了。
Nesterov动量。既然我们知道动量将会把我们带到绿色箭头指向的点,我们就不要在原点(红色点)那里计算梯度了。使用Nesterov动量,我们就在这个“向前看”的地方计算梯度。
代码:
x_ahead = x + mu * v
# 计算dx_ahead(在x_ahead处的梯度,而不是在x处的梯度)
v = mu * v - learning_rate * dx_ahead
x += v
然而在实践中,人们更喜欢和普通SGD或上面的动量方法一样简单的表达式。通过对x_ahead = x + mu * v
使用变量变换进行改写是可以做到的,然后用x_ahead而不是x来表示上面的更新。也就是说,实际存储的参数向量总是向前一步的那个版本。x_ahead的公式(将其重新命名为x)就变成了:
v_prev = v # 存储备份
v = mu * v - learning_rate * dx # 速度更新保持不变
x += -mu * v_prev + (1 + mu) * v # 位置更新变了形式
前面讨论的所有方法都是对学习率进行全局地操作,并且对所有的参数都是一样的。学习率调参是很耗费计算资源的过程,所以很多工作投入到发明能够适应性地对学习率调参的方法,甚至是逐个参数适应学习率调参。
# 假设有梯度和参数向量x
cache += dx**2
x += - learning_rate * dx / (np.sqrt(cache) + eps)
cache = decay_rate * cache + (1 - decay_rate) * dx**2
x += - learning_rate * dx / (np.sqrt(cache) + eps)
m = beta1*m + (1-beta1)*dx
v = beta2*v + (1-beta2)*(dx**2)
x += - learning_rate * m / (np.sqrt(v) + eps)
训练一个神经网络会遇到很多超参数设置。神经网络最常用的设置有:
1. 初始学习率
2. 学习率衰减方式(例如一个衰减常量)
3. 正则化强度(L2惩罚,随机失活强度)
一些额外的调参技巧:
1. 利用辅助程序记录调参过程:持续地随机设置参数然后进行最优化,对每个周期后验证集的准确率进行监控,然后向文件系统写下一个模型的记录点(记录点中有各种各样的训练统计数据,比如随着时间的损失值变化等),这个文件系统最好是可共享的。在文件名中最好包含验证集的算法表现,这样就能方便地查找和排序了。
2. 使用一个合理的验证集而不是交叉验证方式
3. 超参数范围:在对数尺度上进行超参数搜索。例如,一个典型的学习率应该看起来是这样:learning_rate = 10 ** uniform(-6, 1)
。也就是说,我们从标准分布中随机生成了一个数字,然后让它成为10的阶数。对于正则化强度,可以采用同样的策略。直观地说,这是因为学习率和正则化强度都对于训练的动态进程有乘的效果。例如:当学习率是0.001的时候,如果对其固定地增加0.01,那么对于学习进程会有很大影响。然而当学习率是10的时候,影响就微乎其微了。这就是因为学习率乘以了计算出的梯度。因此,比起加上或者减少某些值,思考学习率的范围是乘以或者除以某些值更加自然。但是有一些参数(比如随机失活)还是在原始尺度上进行搜索(例如:dropout=uniform(0,1))。
4. 对于边界上的最优值要小心
5. 从粗到细地分阶段搜索
6. 贝叶斯超参数最优化
在实践的时候,有一个总是能提升神经网络几个百分点准确率的办法,就是在训练的时候训练几个独立的模型,然后在测试的时候平均它们预测结果。集成的模型数量增加,算法的结果也单调提升(但提升效果越来越少)。还有模型之间的差异度越大,提升效果可能越好。进行集成有以下几种方法:
训练一个神经网络需要: