dropout和l2正则化项可以同时加吗

1228人阅读
TensorFlow(20)
原文转自:
本文是&中第三章的一部分,讲/中常用的正则化方法。(本文会不断补充)
正则化方法:防止过拟合,提高泛化能力
在训练数据不够多时,或者overtraining时,常常会导致overfitting(过拟合)。其直观的表现如下图所示,随着训练过程的进行,模型复杂度增加,在training data上的error渐渐减小,但是在验证集上的error却反而渐渐增大——因为训练出来的网络过拟合了训练集,对训练集外的数据却不work。
为了防止overfitting,可以用的方法有很多,下文就将以此展开。有一个概念需要先说明,在机器学习算法中,我们常常将原始数据集分为三部分:training data、validation data,testing data。这个validation data是什么?它其实就是用来避免过拟合的,在训练过程中,我们通常用它来确定一些超参数(比如根据validation data上的accuracy来确定early stopping的epoch大小、根据validation data确定learning rate等等)。那为啥不直接在testing
data上做这些呢?因为如果在testing data做这些,那么随着训练的进行,我们的网络实际上就是在一点一点地overfitting我们的testing data,导致最后得到的testing accuracy没有任何参考意义。因此,training data的作用是计算梯度更新权重,validation data如上所述,testing data则给出一个accuracy以判断网络的好坏。
避免过拟合的方法有很多:early stopping、数据集扩增(Data augmentation)、正则化(Regularization)包括L1、L2(L2 regularization也叫weight decay),dropout。
L2 regularization(权重衰减)
L2正则化就是在代价函数后面再加上一个正则化项:
C0代表原始的代价函数,后面那一项就是L2正则化项,它是这样来的:所有参数w的平方的和,除以训练集的样本大小n。λ就是正则项系数,权衡正则项与C0项的比重。另外还有一个系数1/2,1/2经常会看到,主要是为了后面求导的结果方便,后面那一项求导会产生一个2,与1/2相乘刚好凑整。
L2正则化项是怎么避免overfitting的呢?我们推导一下看看,先求导:
可以发现L2正则化项对b的更新没有影响,但是对于w的更新有影响:
在不使用L2正则化时,求导结果中w前系数为1,现在w前面系数为 1-ηλ/n ,因为η、λ、n都是正的,所以 1-ηλ/n小于1,它的效果是减小w,这也就是权重衰减(weight decay)的由来。当然考虑到后面的导数项,w最终的值可能增大也可能减小。
另外,需要提一下,对于基于mini-batch的随机梯度下降,w和b更新的公式跟上面给出的有点不同:
对比上面w的更新公式,可以发现后面那一项变了,变成所有导数加和,乘以η再除以m,m是一个mini-batch中样本的个数。
到目前为止,我们只是解释了L2正则化项有让w“变小”的效果,但是还没解释为什么w“变小”可以防止overfitting?一个所谓“显而易见”的解释就是:更小的权值w,从某种意义上说,表示网络的复杂度更低,对数据的拟合刚刚好(这个法则也叫做奥卡姆剃刀),而在实际应用中,也验证了这一点,L2正则化的效果往往好于未经正则化的效果。当然,对于很多人(包括我)来说,这个解释似乎不那么显而易见,所以这里添加一个稍微数学一点的解释(引自知乎):
过拟合的时候,拟合函数的系数往往非常大,为什么?如下图所示,过拟合,就是拟合函数需要顾忌每一个点,最终形成的拟合函数波动很大。在某些很小的区间里,函数值的变化很剧烈。这就意味着函数在某些小区间里的导数值(绝对值)非常大,由于自变量值可大可小,所以只有系数足够大,才能保证导数值很大。
而正则化是通过约束参数的范数使其不要太大,所以可以在一定程度上减少过拟合情况。
L1 regularization
在原始的代价函数后面加上一个L1正则化项,即所有权重w的绝对值的和,乘以λ/n(这里不像L2正则化项那样,需要再乘以1/2,具体原因上面已经说过。)
同样先计算导数:
上式中sgn(w)表示w的符号。那么权重w的更新规则为:
比原始的更新规则多出了η * λ * sgn(w)/n这一项。当w为正时,更新后的w变小。当w为负时,更新后的w变大——因此它的效果就是让w往0靠,使网络中的权重尽可能为0,也就相当于减小了网络复杂度,防止过拟合。
另外,上面没有提到一个问题,当w为0时怎么办?当w等于0时,|W|是不可导的,所以我们只能按照原始的未经正则化的方法去更新w,这就相当于去掉η*λ*sgn(w)/n这一项,所以我们可以规定sgn(0)=0,这样就把w=0的情况也统一进来了。(在编程的时候,令sgn(0)=0,sgn(w&0)=1,sgn(w&0)=-1)
L1、L2正则化是通过修改代价函数来实现的,而Dropout则是通过修改神经网络本身来实现的,它是在训练网络时用的一种技巧(trike)。它的流程如下:
假设我们要训练上图这个网络,在训练开始时,我们随机地“删除”一半的隐层单元,视它们为不存在,得到如下的网络:
保持输入输出层不变,按照BP算法更新上图神经网络中的权值(虚线连接的单元不更新,因为它们被“临时删除”了)。
以上就是一次迭代的过程,在第二次迭代中,也用同样的方法,只不过这次删除的那一半隐层单元,跟上一次删除掉的肯定是不一样的,因为我们每一次迭代都是“随机”地去删掉一半。第三次、第四次……都是这样,直至训练结束。
以上就是Dropout,它为什么有助于防止过拟合呢?可以简单地这样解释,运用了dropout的训练过程,相当于训练了很多个只有半数隐层单元的神经网络(后面简称为“半数网络”),每一个这样的半数网络,都可以给出一个分类结果,这些结果有的是正确的,有的是错误的。随着训练的进行,大部分半数网络都可以给出正确的分类结果,那么少数的错误分类结果就不会对最终结果造成大的影响。
更加深入地理解,可以看看Hinton和Alex两牛2012的论文《ImageNet Classification with Deep Convolutional Neural Networks》
数据集扩增(data augmentation)
“有时候不是因为算法好赢了,而是因为拥有更多的数据才赢了。”
不记得原话是哪位大牛说的了,hinton?从中可见训练数据有多么重要,特别是在深度学习方法中,更多的训练数据,意味着可以用更深的网络,训练出更好的模型。
既然这样,收集更多的数据不就行啦?如果能够收集更多可以用的数据,当然好。但是很多时候,收集更多的数据意味着需要耗费更多的人力物力,有弄过人工标注的同学就知道,效率特别低,简直是粗活。
所以,可以在原始数据上做些改动,得到更多的数据,以图片数据集举例,可以做各种变换,如:
将原始图片旋转一个小角度
添加随机噪声
一些有弹性的畸变(elastic distortions),论文《Best practices for convolutional neural networks applied to visual document analysis》对MNIST做了各种变种扩增。
截取(crop)原始图片的一部分。比如DeepID中,从一副人脸图中,截取出了100个小patch作为训练数据,极大地增加了数据集。感兴趣的可以看《Deep learning face representation from predicting 10,000 classes》.
更多数据意味着什么?
用50000个MNIST的样本训练SVM得出的accuracy94.48%,用5000个MNIST的样本训练NN得出accuracy为93.24%,所以更多的数据可以使算法表现得更好。在机器学习中,算法本身并不能决出胜负,不能武断地说这些算法谁优谁劣,因为数据对算法性能的影响很大。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:262957次
积分:3260
积分:3260
排名:第9815名
原创:72篇
转载:31篇
评论:100条
阅读:3211
阅读:5746
阅读:8939
阅读:88075
(1)(1)(4)(3)(6)(1)(4)(1)(3)(3)(8)(6)(3)(8)(3)(1)(3)(1)(3)(3)(1)(3)(2)(3)(4)(3)(7)(1)(4)(4)(1)(4)(1)你正在使用的浏览器版本过低,将不能正常浏览和使用知乎。基于Keras/Python的深度学习模型Dropout正则项 - 推酷
基于Keras/Python的深度学习模型Dropout正则项
作者:Jason Brownlee
翻译:赵屹华
责编:周建丁(投稿请联系)
dropout技术是神经网络和深度学习模型的一种简单而有效的正则化方式。
本文将向你介绍dropout正则化技术,并且教你如何在Keras中用Python将其应用于你的模型。
读完本文之后,你将了解:
dropout正则化的原理
如何在输入层使用dropout
如何在隐藏层使用dropout
如何针对具体问题对dropout调优
神经网络的Dropout正则化
Dropout是Srivastava等人在2014年的一篇论文中提出的一种针对神经网络模型的正则化方法
Dropout的做法是在训练过程中随机地忽略一些神经元。这些神经元被随机地“抛弃”了。也就是说它们在正向传播过程中对于下游神经元的贡献效果暂时消失了,反向传播时该神经元也不会有任何权重的更新。
随着神经网络模型不断地学习,神经元的权值会与整个网络的上下文相匹配。神经元的权重针对某些特征进行调优,具有一些特殊化。周围的神经元则会依赖于这种特殊化,如果过于特殊化,模型会因为对训练数据过拟合而变得脆弱不堪。神经元在训练过程中的这种依赖于上下文的现象被称为复杂的协同适应(complex co-adaptations)。
你可以想象一下,如果在训练过程中随机丢弃网络的一部分,那么其它神经元将不得不介入,替代缺失神经元的那部分表征,为预测结果提供信息。人们认为这样网络模型可以学到多种相互独立的内部表征。
这么做的效果就是,网络模型对神经元特定的权重不那么敏感。这反过来又提升了模型的泛化能力,不容易对训练数据过拟合。
Keras的Dropout 正则化
Dropout的实现很简单,在每轮权重更新时随机选择一定比例(比如20%)的节点抛弃。Keras的Dropout也是这么实现的。Dropout技术只在模型训练的阶段使用,在评估模型性能的时候不需使用。
接下来我们看看Dropout在Keras中的一些不同用法。
本例子使用了
。这是一个二分类问题,目的是根据声呐的回声来正确地区分岩石和矿区。这个数据集非常适合神经网络模型,因为所有的输入都是数值型的,且具有相同的量纲。
数据集可以从
下载。然后把声呐数据集放在当前工作路径下,文件命名为sonar.csv。
我们会用scikit-learn来评价模型质量,为了更好地挑拣出结果的差异,采用了十折交叉验证(10-fold cross validation)方法。
每条数据有60个输入值和1个输出值,输入值在送入模型前做了归一化。基准的神经网络模型有两个隐藏层,第一层有60个节点,第二层有30个。使用了随机梯度下降的方法来训练模型,选用了较小的学习率和冲量。
完整的基准模型代码如下所示。
import numpy
import pandas
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.wrappers.scikit_learn import KerasClassifier
from keras.constraints import maxnorm
from keras.optimizers import SGD
from sklearn.cross_validation import cross_val_score
from sklearn.preprocessing import LabelEncoder
from sklearn.cross_validation import StratifiedKFold
from sklearn.preprocessing import StandardScaler
from sklearn.grid_search import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.grid_search import GridSearchCV
# fix random seed for reproducibility
numpy.random.seed(seed)
# load dataset
dataframe = pandas.read_csv(&sonar.csv&, header=None)
dataset = dataframe.values
# split into input (X) and output (Y) variables
X = dataset[:,0:60].astype(float)
Y = dataset[:,60]
# encode class values as integers
encoder = LabelEncoder()
encoder.fit(Y)
encoded_Y = encoder.transform(Y)
# baseline
def create_baseline():
# create model
model = Sequential()
model.add(Dense(60, input_dim=60, init='normal',
activation='relu'))
model.add(Dense(30, init='normal', activation='relu'))
model.add(Dense(1, init='normal', activation='sigmoid'))
# Compile model
sgd = SGD(lr=0.01, momentum=0.8, decay=0.0, nesterov=False)
pile(loss='binary_crossentropy', optimizer=sgd, metrics=['accuracy'])
return model
numpy.random.seed(seed)
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(build_fn=create_baseline, nb_epoch=300, batch_size=16, verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(y=encoded_Y, n_folds=10, shuffle=True, random_state=seed)
results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print(&Accuracy: %.2f%% (%.2f%%)& % (results.mean()*100, results.std()*100))
运行代码,分类的准确率大概为82%。
Accuracy: 82.68% (3.90%)
在可见层使用Dropout
Dropout可用于输入神经元,即可见层。
在下面这个例子里,我们在输入(可见层)和第一个隐藏层之间加入一层Dropout。丢弃率设为20%,就是说每轮迭代时每五个输入值就会被随机抛弃一个。
另外,正如Dropout那篇论文中所推荐的,每个隐藏层的权重值都做了限制,确保权重范数的最大值不超过3。在构建模型层的时候,可以通过设置Dense Class的W_constraint参数实现。
学习率提高了一个数量级,冲量增加到0.9。这也是那篇Dropout论文的原文中所推荐的做法。
顺着上面基准模型的例子,下面的代码是包含输入层dropout的网络模型。
# dropout in the input layer with weight constraint
def create_model1():
# create model
model = Sequential()
model.add(Dropout(0.2, input_shape=(60,)))
model.add(Dense(60, init='normal', activation='relu', W_constraint=maxnorm(3)))
model.add(Dense(30, init='normal', activation='relu', W_constraint=maxnorm(3)))
model.add(Dense(1, init='normal', activation='sigmoid'))
# Compile model
sgd = SGD(lr=0.1, momentum=0.9, decay=0.0, nesterov=False)
pile(loss='binary_crossentropy', optimizer=sgd, metrics=['accuracy'])
return model
numpy.random.seed(seed)
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(build_fn=create_model1, nb_epoch=300, batch_size=16, verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(y=encoded_Y, n_folds=10, shuffle=True, random_state=seed)
results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print(&Accuracy: %.2f%% (%.2f%%)& % (results.mean()*100, results.std()*100))
运行这段代码,分类准确率完美地提升到了86%。
Accuracy: 86.04% (6.33%)
在隐藏层使用Dropout
Dropout也可用于模型内的隐藏层节点。
下面这个例子里,Dropout被用于两个隐藏层之间和隐藏层与输出层之间。丢弃率同样设为20%,且使用权重限制。
# dropout in hidden layers with weight constraint
def create_model2():
# create model
model = Sequential()
model.add(Dense(60, input_dim=60, init='normal', activation='relu', W_constraint=maxnorm(3)))
model.add(Dropout(0.2))
model.add(Dense(30, init='normal', activation='relu', W_constraint=maxnorm(3)))
model.add(Dropout(0.2))
model.add(Dense(1, init='normal', activation='sigmoid'))
# Compile model
sgd = SGD(lr=0.1, momentum=0.9, decay=0.0, nesterov=False)
pile(loss='binary_crossentropy', optimizer=sgd, metrics=['accuracy'])
return model
numpy.random.seed(seed)
estimators = []
estimators.append(('standardize', StandardScaler()))
estimators.append(('mlp', KerasClassifier(build_fn=create_model2, nb_epoch=300, batch_size=16, verbose=0)))
pipeline = Pipeline(estimators)
kfold = StratifiedKFold(y=encoded_Y, n_folds=10, shuffle=True, random_state=seed)
results = cross_val_score(pipeline, X, encoded_Y, cv=kfold)
print(&Accuracy: %.2f%% (%.2f%%)& % (results.mean()*100, results.std()*100))
我们观察到,对于这个问题以及所设置的模型配置参数,在隐藏层使用dropout并不能提升模型效果。事实上,效果反而比基准更差。
有可能需要增加训练迭代次数,或者是更多地调优学习率。
Accuracy: 82.16% (6.16%)
使用Dropout的小技巧
提出Dropout的那篇论文提供了一些在标准机器学习问题上得到的实践性结论。这些结论在dropout的实际应用中会带来帮助。
通常丢弃率控制在20%~50%比较好,可以从20%开始尝试。如果比例太低则起不到效果,比例太高则会导致模型的欠学习。
在大的网络模型上应用。当dropout用在较大的网络模型时更有可能得到效果的提升,模型有更多的机会学习到多种独立的表征。
在输入层(可见层)和隐藏层都使用dropout。在每层都应用dropout被证明会取得好的效果。
增加学习率和冲量。把学习率扩大10~100倍,冲量值调高到0.9~0.99.
限制网络模型的权重。大的学习率往往导致大的权重值。对网络的权重值做最大范数正则化等方法被证明会提升效果。
有关Dropout的更多资源
下面这些资料也是关于dropout在神经网络和深度学习模型中应用。
通过本文,我们讨论了dropout正则化技术在深度学习模型中的应用。你应该掌握了:
dropout的含义和原理
如何在自己的深度学习模型中使用dropout
使用dropout的小技巧
如果你对dropout或者对本文有任何问题,请留言。
将于8月26-27日在京举行,AAAI主席,多位院士,MIT、微软、大疆、百度、滴滴专家领衔全球技术领袖和产业先锋打造国内人工智能前沿平台,6大主题报告,人机交互、机器学习、模式识别、产业实战相关4大专题论坛,1000+高质量参会嘉宾。 门票限时六折优惠中 。
已发表评论数()
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见
正文不准确
标题不准确
排版有问题
主题不准确
没有分页内容
图片无法显示
视频无法显示
与原文不一致基于Dropout正则化的汉语框架语义角色识别
中文信息学报
&&&&&&&ISSN && CN 11-2325/N&& CODEN ZXXHAU
&& |& &&&&&
: 147-154&&&&
信息抽取与文本挖掘
引用本文: &&
王瑞波;李济洪;李国臣;杨耀文. 基于Dropout正则化的汉语框架语义角色识别[J]. 中文信息学报, ): 147-154.
WANG R LI J LI G YANG Yaowen. Chinese FrameNet Semantic Role Identification Based on Dropout Regularization. , ): 147-154.
基于Dropout正则化的汉语框架语义角色识别
王瑞波1,2,李济洪1,李国臣3,杨耀文4
1. 山西大学 软件学院,山西 太原 030006;2. 山西大学 计算机与信息技术学院,山西 太原 030006;3. 太原工业学院 计算机科学系,山西 太原 030008;4. 山西大学 数学科学学院,山西 太原 030006
Chinese FrameNet Semantic Role Identification Based on Dropout Regularization
WANG Ruibo1,2, LI Jihong1, LI Guochen3, YANG Yaowen4
1. School of Software, Shanxi University, Taiyuan, Shanxi 030006, C2. School of Computer and Information Technology, Shanxi University, Taiyuan, Shanxi 030006, C3. Computer Science Department, Taiyuan Institute of Technology, Taiyuan, Shanxi 030008, C4. School of Mathematic Sciences, Shanxi University, Taiyuan, Shanxi 030006, China
[1] Fillmore C J, Baker C F. Frame semantics for text understanding[C]//Proceedings of WordNet and Other Lexical Resources Workshop, NAACL. 2001.[2] 李济洪, 王瑞波, 王蔚林, 等. 汉语框架语义角色的自动标注[J]. 软件学报, ): 597-611.[3] 宋毅君, 王瑞波, 李济洪, 等. 基于条件随机场的汉语框架语义角色自动标注[J]. 中文信息学报, ): 36-47. [4] Collobert R, Weston J, Bottou L, et al. Natural language processing (almost) from scratch[J]. The Journal of Machine Learning Research, 93-2537.[5] Turian J, Ratinov L, Bengio Y. Word representations: a simple and general method for semi-supervised learning[C]//Proceedings of the 48th annual meeting of the association for computational linguistics. Association for Computational Linguistics, 4.[6] 刘挺, 车万翔, 李生. 基于最大熵分类器的语义角色标注[J]. 软件学报, ): 565-573.[7] Pradhan S, Ward W, Hacioglu K, et al. Shallow Semantic Parsing using Support Vector Machines[C]//Proceedings of HLT-NAACL. 0.[8] Zhou J, Xu W. End-to-end learning of semantic role labeling using recurrent neural networks[C]//Proceedings of the Annual Meeting of the Association for Computational Linguistics. 2015.[9] Hong S, Noh H, Han B. Decoupled deep neural network for semi-supervised semantic segmentation[C]//Proceedings of Advances in Neural Information Processing Systems. -1503.[10] Shi L, Mihalcea R. Putting pieces together: Combining FrameNet, VerbNet and WordNet for robust semantic parsing[M]//Computational linguistics and intelligent text processing. Springer Berlin Heidelberg, 1.[11] 邵艳秋, 穗志方, 吴云芳. 基于词汇语义特征的中文语义角色标注研究[J]. 中文信息学报, ): 3-11.[12] Mikolov T, Chen K, Corrado G, et al. Efficient estimation of word representations in vector space[J]. arXiv preprint arXiv: , 2013.[13] Pennington J, Socher R, Manning C D. Glove: Global Vectors for Word Representation[C]//Proceedings of EMNLP. 32-1543.[14] 李国臣, 党帅兵, 王瑞波, 等. 基于字的分布表征的汉语基本块识别[J]. 中文信息学报, ): 18-25.[15] 李国臣, 王瑞波, 李济洪. 基于条件随机场模型的汉语功能块自动标注[J]. 计算机研究与发展, ): 336-343.[16] Srivastava N, Hinton G, Krizhevsky A, et al. Dropout: A simple way to prevent neural networks from overfitting[J]. The Journal of Machine Learning Research, ): .[17] Yu W, Ruibo W, Huichen J, et al. Blocked 3× 2 cross-validated t-test for comparing supervised classification learning algorithms[J]. Neural computation, ): 208-235.[18] Mikolov T, Karafiát M, Burget L, et al. Recurrent neural network based language model[C]//Proceedings of INTERSPEECH. .[19] Che W, Zhang M, Aw A, et al. Using a Hybrid Convolution Tree Kernel for Semantic Role Labeling[J]. ACM Transactions on Asian Language Information Processing, ).
郝晓燕,刘伟,李茹,刘开瑛. [J]. 中文信息学报, ): 96-100.
版权所有 &
《》编辑部
地址:北京市海淀区中关村南四街4号 邮编:100190 电话:010- E-mail:cips@
本系统由设计开发作者:寒小阳
时间:2016年1月。
出处:http://blog.csdn.net/han_xiaoyang/article/details/
声明:版权所有,转载请联系作者并注明出处
在前一节当中我们讨论了神经静态的部分:包括神经网络结构、神经元类型、数据部分、损失函数部分等。这个部分我们集中讲讲的部分,主要是训练的事情,集中在实际工程实践训练过程中要注意的一些点,如何找到最合适的参数。
1.1 关于梯度检验
之前的博文我们提到过,我们需要比对数值梯度和解析法求得的梯度,实际工程中这个过程非常容易出错,下面提一些小和注意点:
使用中心化公式,这一点我们之前也说过,使用如下的数值梯度计算公式:
df(x)dx=f(x+h)?f(x?h)2h(use instead)
df(x)dx=f(x+h)?f(x)h(bad, do not use)
即使看似上面的形式有着2倍的计算量,但是如果你有兴趣用把公式中的f(x+h)和f(x?h)展开的话,你会发现上面公式出错率大概是O(h2)级别的,而下面公式则是O(h),注意到h是很小的数,因此显然上面的公式要精准得多。
使用相对误差做比较,这是实际工程中需要提到的另外一点,在我们得到数值梯度f′n和解析梯度f′a之后,我们如何去比较两者?第一反应是作差|f′a?f′n|对吧,或者顶多求一个平方。但是用绝对值是不可靠的,假如两个梯度的绝对值都在1.0左右,那么我们可以认为1e-4这样一个差值是非常小的,但是如果两个梯度本身就是1e-4级别的,那这个差值就相当大了。所以我们考虑相对误差:
∣f′a?f′n∣max(∣f′a∣,∣f′n∣)
加max项的原因很简单:整体形式变得简单和对称。再提个小醒,别忘了避开分母中两项都为0的情况。OK,对于相对误差而言:
相对误差&1e-2意味着你的实现肯定是有问题的
1e-2&相对误差&1e-4,你会有点担心
1e-4&相对误差,基本是OK的,但是要注意极端情况(使用tanh或者softmax时候出现kinks)那还是太大
1e-7&相对误差,放心大胆使用
哦,对对,还有一点,随着神经网络层数增多,相对误差是会增大的。这意味着,对于10层的神经网络,其实相对误差也许在1e-2级别就已经是可以正常使用的了。
使用双精度浮点数。如果你使用单精度浮点数计算,那你的实现可能一点问题都没有,但是相对误差却很大。实际工程中出现过,从单精度切到双精度,相对误差立马从1e-2降到1e-8的情况。
要留意浮点数的范围。一篇很好的是What Every Computer Scientist Should Know About Floating-Point Arithmetic。我们得保证计算时,所有的数都在浮点数的可计算范围内,太小的值(比如h)会带来计算上的问题。
Kinks。它指的是一种会导致数值梯度和解析梯度不一致的情况。会出现在使用ReLU或者类似的神经单元上时,对于很小的负数,比如x=-1e-6,因为x&0,所以解析梯度是绝对为0的,但是对于数值梯度而言,加入你计算f(x+h),取的h&1e-6,那就跳到大于0的部分了,这样数值梯度就一定和解析梯度不一样了。而且这个并不是极端情况哦,对于一个像CIFAR-10这样级别的数据集,因为有50000个样本,会有450000个max(0,x),会出现很多的kinks。
不过我们可以监控max里的2项,比较大的那项如果存在跃过0的情况,那就要注意了。
设定步长h要小心。h肯定不能特别大,这个大家都知道对吧。但我并不是说h要设定的非常小,其实h设定的非常小也会有问题,因为h太小可能会有精度问题。很有意思的是,有时候在实际情况中h如果从非常小调为1e-4或者1e-6反倒会突然计算变得正常。
不要让正则化项盖过数据项。有时候会出现这个问题,因为损失函数是数据损失部分与正则化部分的求和。因此要特别注意正则化部分,你可以想象下,如果它盖过了数据部分,那么主要的梯度来源于正则化项,那这样根本就做不到正常的梯度回传和参数迭代更新。所以即使在检查数据部分的实现是否正确,也得先关闭正则化部分(系数λ设为0),再检查。
注意dropout和其他参数。在检查数值梯度和解析梯度的时候,如果不把dropout和其他参数都『关掉』的话,两者之间是一定会有很大差值的。不过『关掉』它们的负面影响是,没有办法检查这些部分的梯度是否正确。所以,一个合理的方式是,在计算f(x+h)和f(x?h)之前,都随机初始化x,然后再计算解析梯度。
关于只检查几个维度。在实际情况中,梯度可能有上百万维参数。因此每个维度都检查一遍就不太现实了,一般都是只检查一些维度,然后假定其他的维度也都正确。要小心一点:要保证这些维度的每个参数都检查对比过了。
1.2 训练前的检查工作
在开始训练之前,我们还得做一些检查,来确保不会运行了好一阵子,才发现计算代价这么大的训练其实并不正确。
在初始化之后看一眼loss。其实我们在用很小的随机数初始化神经网络后,第一遍计算loss可以做一次检查(当然要记得把正则化系数设为0)。以CIFAR-10为例,如果使用Softmax分类器,我们预测应该可以拿到值为2.302左右的初始loss(因为10个类别,初始概率应该都未0.1,Softmax损失是-log(正确类别的概率):-ln(0.1)=2.302)。
加回正则项,接着我们把正则化系数设为正常的小值,这时候再算损失/loss,应该比刚才要大一些。
试着去拟合一个小的数据集。最后一步,也是很重要的一步,在对大数据集做训练之前,我们可以先训练一个小的数据集(比如20张),然后看看你的神经网络能够做到0损失/loss(当然,是指的正则化系数为0的情况下),因为如果神经网络实现是正确的,在无正则化项的情况下,完全能够过拟合这一小部分的数据。
1.3 训练过程中的监控
开始训练之后,我们可以通过监控一些指标来了解训练的状态。我们还记得有一些参数是我们认为敲定的,比如学习率,比如正则化系数。
损失/loss随每轮完整迭代后的变化
下面这幅图表明了不同的学习率下,我们每轮完整迭代(这里的一轮完整迭代指的是所有的样本都被过了一遍,因为随机梯度下降中batch size的大小设定可能不同,因此我们不选每次mini-batch迭代为周期)过后的loss应该呈现的变化状况:
合适的学习率可以保证每轮完整训练之后,loss都减小,且能在一段时间后降到一个较小的程度。太小的学习率下loss减小的速度很慢,如果太激进,设置太高的学习率,开始的loss减小速度非常可观,可是到了某个程度之后就不再下降了,在离最低点一段距离的地方反复,无法下降了。下图是实际训练CIFAR-10的时候,loss的变化情况:
大家可能会注意到上图的曲线有一些上下跳动,不稳定,这和随机梯度下降时候设定的batch size有关系。batch size非常小的情况下,会出现很大程度的不稳定,如果batch size设定大一些,会相对稳定一点。
训练集/验证集上的准确度
然后我们需要跟踪一下训练集和验证集上的准确度状况,以判断分类器所处的状态(过拟合程度如何):
随着时间推进,训练集和验证集上的准确度都会上升,如果训练集上的准确度到达一定程度后,两者之间的差值比较大,那就要注意一下,可能是过拟合现象,如果差值不大,那说明状况良好。
权重:权重更新部分 的比例
最后一个需要留意的量是权重更新幅度和当前权重幅度的壁纸。注意哦,是权重更新部分,不一定是计算出来的梯度哦(比如训练用的vanilla sgd,那这个值就是梯度和学习率的乘积)。最好对于每组参数都独立地检查这个比例。我们没法下定论,但是在之前的工程实践中,一个合适的比例大概是1e-3。如果你得到的比例比这个值小很多,那么说明学习率设定太低了,反之则是设定太高了。
每一层的 激励/梯度值 分布
如果初始化不正确,那整个训练过程会越来越慢,甚至直接停掉。不过我们可以很容易发现这个问题。体现最明显的数据是每一层的激励和梯度的方差(波动状况)。举个例子说,如果初始化不正确,很有可能从前到后逐层的激励方差变化是如下的状况:
# 我们用标准差为0.01均值为0的高斯分布值来初始化权重(这不合理)
Layer 0: Variance: 1.
Layer 1: Variance: 3.
Layer 2: Variance: 1.
Layer 3: Variance: 5.
Layer 4: Variance: 2.
Layer 5: Variance: 3.
Layer 6: Variance: 3.
Layer 7: Variance: 6.
Layer 8: Variance: 6.
大家看一眼上述的数值,就会发现,从前往后,激励值波动逐层降得非常厉害,这也就意味着反向算法中,计算回传梯度的时候,梯度都要接近0了,因此参数的迭代更新几乎就要衰减没了,显然不太靠谱。我们按照上一讲中提到的方式正确初始化权重,再逐层看激励/梯度值的方差,会发现它们的方差衰减没那么厉害,近似在一个级别:
# 重新正确设定权重:
Layer 0: Variance: 1.
Layer 1: Variance: 7.
Layer 2: Variance: 6.
Layer 3: Variance: 8.
Layer 4: Variance: 6.
Layer 5: Variance: 4.
Layer 6: Variance: 3.
Layer 7: Variance: 3.
Layer 8: Variance: 2.
再看逐层的激励波动情况,你会发现即使到最后一层,网络也还是『活跃』的,意味着反向传播中回传的梯度值也是够的,神经网络是一个积极learning的状态。
首层的可视化
最后再提一句,如果神经网络是用在图像相关的问题上,那么把首层的特征和数据画出来(可视化)可以帮助我们了解训练是否正常:
上图的左右是一个正常和不正常情况下首层特征的可视化对比。左边的图中特征噪点较多,图像很『浑浊』,预示着可能训练处于『病态』过程:也许是学习率设定不正常,或者正则化系数设定太低了,或者是别的原因,可能神经网络不会收敛。右边的图中,特征很平滑和干净,同时相互间的区分度较大,这表明训练过程比较正常。
1.4 关于参数更新部分的注意点
当我们确信解析梯度实现正确后,那就该在后向传播算法中使用它更新权重参数了。就单参数更新这个部分,也是有讲究的:
说起来,神经网络的最优化这个子话题在深度学习研究领域还真是很热。下面提一下大神们的论文中提到的方法,很多在实际应用中还真是很有效也很常用。
1.4.1 随机梯度下降与参数更新
这是最简单的参数更新方式,拿到梯度之后,乘以设定的学习率,用现有的权重减去这个部分,得到新的权重参数(因为梯度表示变化率最大的增大方向,减去这个值之后,损失函数值才会下降)。记x为权重参数向量x,而梯度为dx,然后我们设定学习率为learning_rate,则最简单的参数更新大家都知道:
# Vanilla update
x += - learning_rate * dx
当然learning_rate是我们自己敲定的一个超变量值(在该更新方法中是全程不变的),而且数学上可以保证,当学习率足够低的时候,经这个过程迭代后,损失函数不会增加。
Momentum update
这是上面参数更新方法的一种小小的优化,通常说来,在深层次的神经网络中,收敛效率更高一些(速度更快)。这种参数更新方式源于物理学角度的优化
# 物理动量角度启发的参数更新
v = mu * v - learning_rate * dx # 合入一部分附加速度
x += v # 更新参数
这里v是初始化为0的一个值,mu是我们敲定的另外一个超变量(最常见的设定值为0.9,物理含义和摩擦力系数相关),一个比较粗糙的理解是,(随机)梯度下降可以看做从山上下山到山底的过程,这种方式,相当于在下山的过程中,加上了一定的摩擦阻力,消耗掉一小部分动力系统的能量,这样会比较高效地在山底停住,而不是持续震荡。对了,其实我们也可以用交叉验证来选择最合适的mu值,一般我们会从[0.5, 0.9, 0.95, 0.99]里面选出最合适的。
Nesterov Momentum
这是momentum update的一个不同的版本,最近也用得很火。咳咳,据称,这种参数更新方法,有更好的凸函数和凸优化理论基础,而实际中的收敛效果也略优于momentum update。
此处的深层次原理,博主表示智商有点捉急…有兴趣的同学可以看看以下的2个材料:
Yoshua Bengio大神的Advances in optimizing Recurrent s3.5节
Ilya Sutskever’s thesis7.2节
它的思想对应着如下的代码:
x_ahead = x + mu * v
# 考虑到这个时候的x已经有一些变化了
v = mu * v - learning_rate * dx_ahead
工程上更实用的一个版本是:
v_prev = v # 当前状态先起来
v = mu * v - learning_rate * dx # 依旧按照Momentum update的方式更新
x += -mu * v_prev + (1 + mu) * v # 新的更新方式
1.4.2 衰减学习率
在实际训练过程中,随着训练过程推进,逐渐衰减学习率是很有必要的。我们继续回到下山的场景中,刚下山的时候,可能离最低点很远,那我步子迈大一点也没什么关系,可是快到山脚了,我还激进地大步飞奔,一不小心可能就迈过去了。所以还不如随着下山过程推进,逐步减缓一点点步伐。不过这个『火候』确实要好好把握,衰减太慢的话,最低段震荡的情况依旧;衰减太快的话,整个系统下降的『动力』衰减太快,很快就下降不动了。下面提一些常见的学习率衰减方式:
步伐衰减:这是很常见的一个衰减模式,每过一轮完整的训练周期(所有的图片都过了一遍)之后,学习率下降一些。比如比较常见的一个衰减率可能是每20轮完整训练周期,下降10%。不过最合适的值还真是依问题不同有变化。如果你在训练过程中,发现交叉验证集上呈现很高的错误率,还一直不下降,你可能就可以考虑考虑调整一下(衰减)学习率了。
指数级别衰减:数学形式为α=α0e?kt,其中α0,k是需要自己敲定的超参数,t是迭代轮数。
1/t衰减:有着数学形式为α=α0/(1+kt)的衰减模式,其中α0,k是需要自己敲定的超参数,t是迭代轮数。
实际工程实践中,大家还是更倾向于使用步伐衰减,因为它包含的超参数少一些,计算简单一些,可解释性稍微高一点。
1.4.3 二次迭代方法
最优化问题里还有一个非常有名的牛顿法,它按照如下的方式进行迭代更新参数:
x←x?[Hf(x)]?1?f(x)
这里的Hf(x)是Hessian矩阵,是函数的二阶偏微分。而?f(x)和梯度下降里看到的一样,是一个梯度向量。直观理解是Hessian矩阵描绘出了损失函数的曲度,因此能让我们更高效地迭代和靠近最低点:乘以Hessian矩阵进行参数迭代会让在曲度较缓的地方,会用更激进的步长更新参数,而在曲度很陡的地方,步伐会放缓一些。因此相对一阶的更新算法,在这点上它还是有很足的优势的。
比较尴尬的是,实际深度学习过程中,直接使用二次迭代的方法并不是很实用。原因是直接计算Hessian矩阵是一个非常耗时耗资源的过程。举个例子说,一个一百万参数的神经网络的Hessian矩阵维度为[0000],算下来得占掉3725G的内存。当然,我们有L-BFGS这种近似Hessian矩阵的算法,可以解决内存问题。但是L-BFGS一般在全部数据集上计算,而不像我们用的mini-batch SGD一样在小batch小batch上迭代。现在有很多人在努力研究这个问题,试图让L-BFGS也能以mini-batch的方式稳定迭代更新。但就目前而言,大规模数据上的深度学习很少用到L-BFGS或者类似的二次迭代方法,倒是随机梯度下降这种简单的算法被广泛地使用着。
感兴趣的同学可以参考以下文献:
On Optimization Methods for Deep Learning:2011年的论文比较随机梯度下降和L-BFGS
Large Scale Distributed Deep Networks: brain组的论文,比较随机梯度下降和L-BFGS在大规模分布式优化上的差别。
SFO算法试图结合随机梯度下降和L-BFGS的优势。
1.4.4 逐参更新学习率
到目前为止大家看到的学习率更新方式,都是全局使用同样的学习率。调整学习率是一件很费时同时也容易出错的事情,因此大家一直希望有一种学习率自更新的方式,甚至可以细化到逐参数更新。现在确实有一些这种方法,其中大多数还需要额外的超参数设定,优势是在大多数超参数设定下,效果都比使用写死的学习率要好。下面稍微提一下常见的自适应方法(原谅博主底子不够,没办法深入数学细节讲解):
Adagrad是Duchi等在论文Adaptive Subgradient Methods for Online Learning and Stochastic Optimization中提出的自适应学习率算法。简单代码实现如下:
# 假定梯度为dx,参数向量为x
cache += dx**2
x += - learning_rate * dx / np.sqrt(cache + 1e-8)
其中变量cache有着和梯度一样的维度,然后我们用这个变量持续累加梯度平方。之后这个值被用作参数更新步骤中的归一化。这种方法的好处是,对于高梯度的权重,它们的有效学习率被降低了;而小梯度的权重迭代过程中学习率提升了。而分母开根号这一步非常重要,不开根号的效果远差于开根号的情况。平滑参数1e-8避免了除以0的情况。
RMSprop是一种非常有效,然后好像还没有被公开发布的自适应学习率更新方法。有意思的是,现在使用这个方法的人,都引用的大神Geoff Hinton的coursera课程第6节的slide第29页。RMSProp方法对Adagrad算法做了一个简单的优化,以减缓它的迭代强度,它开方的部分cache做了一个平滑处理,大致的示意代码如下:
cache = decay_rate * cache + (1 - decay_rate) * dx**2
x += - learning_rate * dx / np.sqrt(cache + 1e-8)
这里的decay_rate是一个手动敲定的超参数,我们通常会在[0.9, 0.99, 0.999]中取值。需要特别注意的是,x+=这个累加的部分和Adagrad是完全一样的,但是cache本身是迭代变化的。
另外的方法还有:
Matthew Zeiler提出的Adadelta
Adam: A Method for Stochastic Optimization
Unit Tests for Stochastic Optimization
下图是上述提到的多种参数更新方法下,损失函数最优化的示意图:
1.5 超参数的设定与优化
神经网络的训练过程中,不可避免地要和很多超参数打交道,这是我们需要手动设定的,大致包括:
初始学习率
学习率衰减程度
正则化系数/强度(包括l2正则化强度,dropout比例)
对于大的深层次神经网络而言,我们需要很多的时间去训练。因此在此之前我们花一些时间去做超参数搜索,以确定最佳设定是非常有必要的。最直接的方式就是在框架实现的过程中,设计一个会持续变换超参数实施优化,并记录每个超参数下每一轮完整训练迭代下的验证集状态和效果。
未完待续…

我要回帖

更多关于 l2正则化 的文章

 

随机推荐