Jeremy Howard是fast.ai的联合创始人,目前在Kaggle的植物幼苗分类竞赛中获得了第105名,但他的成绩下降得很快。因为,他自己的学生击败了他。这群学生的名字现在已经在整个Kaggle的排行榜的顶部了。
那些让新手在短短几周内实施世界级算法的秘诀是什么呢?他们是怎样甩开经验丰富的深度学习从业者的呢?让我用分十个简单的步骤告诉你。
文章阅读说明(总之就是让你继续读)
在我们开始之前,你应该知道需要访问GPU才能有效地运行fast.ai内容。就我而言,我一直在使用FloydHub。经过对其他基于云的解决方案的大量实验和研究后,我发现FloydHub是在云GPU上训练深度学习模型的最佳且最简单的方法。我喜欢能够在FloydHub上的项目中轻松跟踪我的实验,并且可以轻易可视化和筛选最佳模型。他们还拥有最简单的解决方案来管理(并自动版本化)你的数据集,你将了解到,在任何DL项目中,这些解决方案都是非常有价值的。
好的,让我们开始吧。
1.使用Fast.ai库
from fast.ai import *
fast.ai库不仅是让新手快速实施深度学习的工具包,而且是当前最佳实践的强大而便捷的来源。每当fast.ai团队(以及他们的AI研究人员和合作者网络)发现一篇特别有趣的论文时,他们就会在各种数据集上进行测试,并找出优化它的方法。如果它们成功,它将在库中实现,并且用户可以快速访问该技术。
最后,生成一个功能强大的工具箱,包括快速访问最佳实践,例如带重启的随机梯度下降(Stochastic Gradient Descent with restarts),差分学习率(Differential Learning rates)和 test-time augmentation(更不用说更多)。
下面将介绍这些技术中的每一种,我们将展示如何使用fast.ai库快速实现它们。该库是基于PyTorch构建。
要开始使用FloydHub上的库,请查看安装说明。
安装:https://docs.floydhub.com/getstarted/get_started/
2.不要只使用一个学习率,使用多个
差异学习率意味着在训练期间更高层的变化比更深层要大。在预先存在的架构之上构建深度学习模型是一种经过验证的可以在计算机视觉任务中产生更好结果的方法。大多数这些架构(即Resnet,VGG,初始......)都是在ImageNet上进行训练的,根据数据与ImageNet上的图像的相似度,这些权重需要或多或少地进行修改。在修改这些权重时,模型的最后几层通常需要进行最大的修改,而已经训练好的且检测基本特征(例如边缘和轮廓)的较深的层则需要更少的修改。
首先,要使用fast.ai库获得预训练的模型,代码:
from fastai.conv_learner import *
# import library for creating learning object for convolutional #networks
model = VVG16()
# assign model to resnet, vgg, or even your own custom model
PATH = './folder_containing_images'
data = ImageClassifierData.from_paths(PATH)
# create fast ai data object, in this method we use from_paths where
# inside PATH each image class is separated into different folders
learn = ConvLearner.pretrained(model, data, precompute=True)
# create a learn object to quickly utilise state of the art
# techniques from the fast ai library
通过现在已经创建的学习对象,我们可以解决通过快速冻结第一层来精细调优最后一层的问题:
learn.freeze()
# freeze layers up to the last one, so weights will not be updated.
learning_rate = 0.1
learn.fit(learning_rate, epochs=3)
# train only the last layer for a few epochs
一旦最后一层产生了良好的结果,我们实施差异学习率以改变较低层。较低层期望较小的改变,因此最好将每个学习率设置为比上一个低10倍:
learn.unfreeze()
# set requires_grads to be True for all layers, so they can be updated
learning_rate = [0.001, 0.01, 0.1]
# learning rate is set so that deepest third of layers have a rate of 0.001, # middle layers have a rate of 0.01, and final layers 0.1.
learn.fit(learning_rate, epochs=3)
# train model for three epoch with using differential learning rates
3.如何找到合适的学习率
学习率是训练神经网络最重要的超参数,但直到最近才确定它的价值。莱斯利•史密斯(Leslie Smith)在论文中偶然发现了答案;一个相对冷门的发现,直到它被fast.ai课程推广。
论文:https://arxiv.org/abs/1506.01186
在这种方法中,我们进行试运行并使用较低的学习率训练神经网络,但每个批次以指数方式增加它。使用以下代码:
learn.lr_find()
# run on learn object where learning rate is increased exponentially
learn.sched.plot_lr()
# plot graph of learning rate against iterations
每次迭代,学习率都呈指数增长
同时,为学习率的每个值记录损失。然后我们根据学习率绘制损失:
learn.sched.plot()
# plots the loss against the learning rate
找出损失仍在减少但尚未稳定的地方。
通过找到学习率最高且损失仍在下降的值来确定最佳学习率,在上述情况下,该值将为0.01。
4.余弦退火
随着每次随机梯度下降(SGD),网络应该越来越接近损失的全局最小值。因为它越来越接近这个最小值,因此学习率应该变得更小以使你的算法不会超调,而是尽可能接近这个全局最小值。余弦退火通过降低余弦函数控制的学习率来解决这个问题,如下图所示。
随着我们增加x,我们看到余弦(x)随着这个波浪形减少。
从上面的图中,我们看到随着我们增加x,余弦值首先缓慢下降,然后变快,然后再稍微慢一些。这种降低模式与学习率很好地集合,在计算效率方面产生很好的结果。
learn.fit(0.1, 1)
# Calling learn fit automatically takes advantage of cosine annealing
该技术在使用learn.fit()时由fast ai库自动实现。上面的代码将使我们的学习率在整个周期内降低,如下图所示。
学习率在一个需要200次迭代的周期内降低
然后,我们甚至可以更进一步,引入重启
5.带重启的随机梯度下降
在训练期间,梯度下降可能会陷入局部最小值而非全局最小值。
梯度下降可能会陷入局部最小值
通过突然提高学习率,梯度下降可以“跳出”局部最小值并找到通向全局最小值的路。这样做被称为带重启的随机梯度下降(SGDR),这一想法在Loshchilov和Hutter的论文中被证明是非常有效。
fast.ai库也会自动为你处理SGDR。在调用learn.fit(learning_rate, epochs)时,学习率在每个纪元的开始处重置为你作为参数输入的初始值,然后在余弦退火中如上所述在周期上再次减小。
在每个周期之后,学习率恢复到其原始值。
每次学习率下降到最小点(上图中每100次迭代),我们称之为循环(cycle)。
cycle_len = 1
# decide how many epochs it takes for the learning rate to fall to
# its minimum point. In this case, 1 epoch
cycle_mult=2
# at the end of each cycle, multiply the cycle_len value by 2
learn.fit(0.1, 3, cycle_len=2, cycle_mult=2)
# in this case there will be three restarts. The first time with
# cycle_len of 1, so it will take 1 epoch to complete the cycle.
# cycle_mult=2 so the next cycle with have a length of two epochs,
# and the next four.
每个循环需要两倍于上一循环完成的迭代数
使用这些参数以及使用差分学习率是让fast.ai用户在图像分类问题上表现良好的关键技术。
Cycle_mult
并
cycle_len
进行了讨论:http://forums.fast.ai/t/understanding-cycle-len-and-cycle-mult/9413/8
6. 为你的激活函数赋予“性格”
Softmax喜欢只挑选一件事。Sigmoid想知道你在-1和1之间的位置,超出这些值就不再关心你增加了多少。Relu是一名俱乐部保镖,他不会让负数进门。
以这种方式讨论激活函数似乎很傻,但给它们赋予“性格”可以确保不将它们用于错误的任务。正如杰里米·霍华德所指出的那样,即使是学术论文也经常使用softmax进行多类分类,而且在我研究DL的短暂时间里,也曾在博客和论文中看到过它被错误地使用。
7.迁移学习在NLP中非常有效
正如使用预训练的模型已被证明在计算机视觉中非常有效一样,自然语言处理(NLP)模型可以从此受益。
在fast.ai 的第4课中,杰里米·霍华德建立了一个模型,通过迁移学习来确定IMDB评论是积极的还是消极的。效果立竿见影,他所达到的准确性超过了Bradbury等人在论文中提出的所有先前的努力。
现有的架构提供最先进的NLP结果。
成功的秘诀在于首先训练模型以获得对语言的一些理解,然后再使用这种预训练的模型作为模型的一部分来分析情绪。
为了创建第一模型,训练递归神经网络(RNN)以预测文本序列中的下一个单词。这就是众所周知的语言建模。一旦网络经过高度准确的训练,其对每个单词的编码就会传递给用于情感分析的新模型。
在该示例中,我们看到该语言模型与模型集成以执行情感分析,而同样的方法可以用于从翻译到数据提取的任何NLP任务。
再次,同样的原理也适用于计算机视觉,在这里冻结(freezing )和使用差分学习率可以产生更好的结果。
代码:https://github.com/fastai/fastai/blob/master/courses/dl1/lesson4-imdb.ipynb
8.深度学习可以在处理结构化数据方面挑战机器学习
Fast.ai展示了在结构化数据上快速生成出色结果的技术,无需借助特征工程或应用领域特定知识。
他们的库充分利用了PyTorch的嵌入功能,允许将分类变量快速转换为嵌入矩阵。
他们展示的技术相对简单,只需将分类变量转换为数字,然后为每个值分配嵌入向量:
一周中的每一天都嵌入了四个值。
与创建虚拟变量(即执行独热编码)的传统方法相比,这样做的好处是可以用四个数字代替一个数字,因此我们获得更高的维度和更丰富的关系。
在Rossman Kaggle竞赛中,本节课展示的实现获得了第三名。
代码:https://github.com/fastai/fastai/blob/master/courses/dl1/lesson3-rossman.ipynb
9.尺寸,dropout和TTA
4月30日,fast.ai团队在Imagenet和CIFAR10分类上赢得了DAWNBench竞赛(由斯坦福大学主办)。在Jeremy将自己的成功归功于fast.ai库中的一些额外功能。
其中之一是,两年前Geoffrey Hinton在提出的Dropout概念。尽管它最初受欢迎,但在最近的计算机视觉论文中,它似乎有些被忽视了。然而,PyTorch使其实现变得异常简单,并且在fast.ai的支持下,它比以往更容易。
空格表示由Dropout函数敲除的激活。
Dropout克服了过拟合问题,因此在CIFAR10这样一个相对较小的数据集上获胜是非常重要的。在创建学习对象时,fast ai会自动执行dropout,但可以使用ps变量进行更改,如下所示:
learn = ConvLearner.pretrained(model, data, ps=0.5, precompute=True)
# creates a dropout of 0.5 (i.e. half the activations) on test dataset.
# This is automatically turned off for the validation set
他们用于解决过拟合和提高准确性的另一种非常简单有效的方法是对较小的图像尺寸进行训练,然后再增加尺寸再次训练相同的模型。
# create a data object with images of sz * sz pixels
def get_data(sz):
tmfs = tfms_from_model(model, sz)
# tells what size images should be, additional transformations such
# image flips and zooms can easily be added here too
data = ImageClassifierData.from_paths(PATH, tfms=tfms)
# creates fastai data object of create size
return data
learn.set_data(get_data(299))
# changes the data in the learn object to be images of size 299
# without changing the model.
learn.fit(0.1, 3)
# train for a few epochs on larger versions of images, avoiding overfitting
最后一种可以将精确度提高1%或2%的技术是TTA(test time augmentation)。这涉及捕捉原始图像的一系列不同版本(例如裁剪不同区域,或更改缩放)并通过模型传递它们。然后计算不同版本的平均输出,并将其作为图像的最终输出分数。可以通过运行learn.TTA()来调用它。
preds, target = learn.TTA()
这种技术很有效,因为原始的图像裁剪可能会漏掉一个必要的特征。为模型提供图片的多个版本并取平均值会降低这种影响。
10.创造力是关键
fast.ai团队不仅在DAWNBench比赛中以最快的速度赢得了大奖,而且这些算法也因为最便宜的运行而获得了奖励。这里要吸取的教训是,创建成功的DL应用程序不仅仅是在这个问题上抛弃大量的GPU,而是一个关于创造力、直觉和创新的问题。
本文中讨论的大多数突破实际上就是有人想以不同的方式解决问题的时候诞生的。
不要以为你不能挑战谷歌的大牛或者大型的GPU。事实上,时你可以把约束看成是一种祝福;毕竟,需要是发明之母。