0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

图像分类问题为例,带你领略fastai这一高层抽象框架惊人的简洁性

zhKF_jqr_AI 来源:未知 作者:李倩 2018-11-05 15:36 次阅读

大半个月前,fast.ai在博客上宣布fastai 1.0版发布,之后很快在GitHub上发布了1.0.5测试版,半个月前发布1.0.6正式版。由于刚发布不久,网上关于fastai 1.0的教程极少,因此,我们编写了这篇入门教程,以一个简单的图像分类问题(异形与铁血战士)为例,带你领略fastai这一高层抽象框架惊人的简洁性。

注意,fastai最近的更新节奏很疯狂:

好在按照语义化版本的规矩,动的都是修订号,所以这篇教程在这些版本上应该都适用。不过,以防万一,给出我们使用的版本号供参考:

fastai 1.0.15

pytorch-nightly-cpu 1.0.0.dev20181014

安装

建议通过conda安装。如果用的是最近的Nvidia显卡,可以安装GPU版本:

conda install -c pytorch pytorch-nightly cuda92

conda install -c fastai torchvision-nightly

conda install -c fastai fastai

显卡不给力的话,可以安装CPU版本:

conda install -c pytorch pytorch-nightly-cpu

conda install -c fastai torchvision-nightly-cpu

conda install -c fastai fastai

不用担心,在迁移学习的加持下,本教程中的模型,即使是性能较低的机器,不到一小时也可训练完毕。当然,如果使用GPU版,就更快了,几分钟搞定。

当然也可以通过pip安装,甚至直接从源代码编译,详见官方仓库的README。

注意,不管是GPU版本,还是CPU版本,都需要python 3.6以上,如果是GPU版本,还需要正确配置显卡驱动。

安装好之后,我们可以通过以下语句引入fastai:

import fastai

from fastai import *

from fastai.vision import *

严格来说最后两行并不必要,不过,加载模块中的所有定义,可以使代码看起来更简洁,所以我们这里就加上了。

图像分类示例

MNIST

深度学习入门的经典例子是MNIST手写数字分类,实际上fastai的官方文档开篇就是MNIST的例子:

path = untar_data(URLs.MNIST_SAMPLE)

data = ImageDataBunch.from_folder(path)

learn = create_cnn(data, models.resnet18, metrics=accuracy)

learn.fit(1)

只有四行!事实上,其中还有两行是加载数据,然后最后一行是训练,真正搭建模型只用一行!如果你接触过其他深度学习框架,也许立刻就会意识到fastai恐怖的简洁性。反正我第一次看到的反应就是:“我靠!这也行!?”

不过MNIST分类实在是太过经典,几乎每篇入门教程都用,说不定有些人已经看吐了。而且,黑白手写数字分类,在当前背景下,太过古老,体现不了近年来深度学习在计算机视觉领域的突飞猛进。所以,我们还是换一个酷炫一点的例子吧。

异形大战铁血战士

换个什么例子呢?最近上映了一部新的《铁血战士》,我们不如做个铁血战士和异形的分类器吧(以前还有部《异形大战铁血战士》,不妨假想一下,铁血战士的HUD依靠神经网络区分敌我)。

图片来源: frolic.media

数据集

要做这样一个分类器,首先需要数据集。这很简单,网络上铁血战士和异形的图片太多了。不过,在自己动手搜集图片之前,我们先检查下有没有人做过类似的工作。

这里安利下Google的数据集搜索,找数据集很方便:https://toolbox.google.com/datasetsearch/

用“alien predator”一搜,还真有,第一个结果就是Kaggle上的Alien vs. Predator images:

这些图像是通过Google图像搜索搜集的JPEG缩略图(约250×250像素),训练集、验证集的每个分类各有347、100张图像样本。

从Kaggle下载数据后,我们将validation文件夹改名为valid,得到以下的目录结构:

|-- train

|-- alien

|-- predator

|-- valid

|-- alien

|-- predator

这一目录结构符合fastai组织数据的惯例。

数据预处理

之前MNIST的例子中,我们是这样加载数据的:

path = untar_data(URLs.MNIST_SAMPLE)

data = ImageDataBunch.from_folder(path)

我们直接使用URLs.MNIST_SAMPLE,fastai会自动下载数据集并解压,这是因为MNIST是fastai的自带数据集。fastai自带了MNIST、CIFAR10、Wikitext-103等常见数据集,详见fastai官网:https://course.fast.ai/datasets

而我们要使用的是非自带数据集,所以只需像之前提到的那样,在相关路径准备好数据,然后直接调用ImageDataBunch.from_folder加载即可。

不过,上面的MNIST例子中,出于简单性考虑,没有进行预处理。这是因为MNIST图像本身比较齐整,而且MNIST非常简单,所以不做预处理也行。我们的异形和铁血战士图片则需要做一些预处理。

首先,大多数卷积神经网络的输入层形状都是28、32、64、96、224、384、512之类的数字。而数据集中的图片边长约为250像素,这就需要缩放或者裁切一下。

其次,绝大多数图像分类任务,都需要做下数据增强。一方面增加些样本,以充分利用数量有限的样本;另一方面,也是更重要的一方面,通过平移、旋转、缩放、翻转等手段,迫使模型学习图像更具概括性的特征,缓解过拟合问题。

如果你平时积累了一些不依赖框架的图像增强函数(比如,基于numpy和scipy定义),那图像增强不算太麻烦。不过,你也许好奇,fastai有没有内置图像增强的功能?

有的。实际上,上面说了一大堆,体现到代码就是一句话:

data = ImageDataBunch.from_folder('data', ds_tfms=get_transforms(), size=224)

前面MNIST的例子中,我们看到,fastai只需一个语句就可以完成加载数据集的任务,这已经足够简洁了。现在我们加上预处理,还是一个语句,只不过多了两个参数

现在我们回过头来,再看看from_folder这个方法,它根据路径参数获取数据集目录,然后根据目录结构区分训练集、验证集、分类集,根据目录名称获取样本的分类标签。这种API的设计极为简洁,避免了很多冗余的“模板代码”。类似地,fastai的ImageDataBunch类还有from_csv、from_df等方法,从CSV文件或DataFrame加载数据。

size参数指定了形状,ds_tfms指定了预处理逻辑,两个参数完成预处理工作。get_transforms()则是fastai的内置方法,提供了适用于大多数计算机视觉任务的默认数据增强方案:

以0.5的概率随机水平翻转

以0.75的概率在-10与10度之间旋转

以0.75的概率在1与1.1倍之间随机放大

以0.75的概率随机改变亮度和对比度

以0.75的概率进行随机对称扭曲

get_transforms()充分体现了fastai的高层抽象程度。fastai的使用者考虑的是我需要应用常见的数据增强方案,而不是具体要对图像进行哪些处理。

在设计模型之前,我们先简单地检查下数据加载是否成功:

data.show_batch(rows=3, figsize=(6,6))

看起来没问题。

顺便提下,如果使用GPU版本,建议再传入一个bs参数,指定下batch大小,比如bs=32,以便充分利用GPU的并行特性,加速之后的训练。

化神经网络为平常

之前提到,MNIST例子中的核心语句(指定网络架构)其实只有一行:

learn = create_cnn(data, models.resnet18, metrics=accuracy)

其实我们这个异形、铁血战士的模型架构也只需一行:

learn = create_cnn(data, models.resnet50, metrics=accuracy)

几乎和MNIST一模一样,只是把模型换成了表达力更强、更复杂的ResNet-50网络,毕竟,异形和铁血战士图像要比黑白手写数字复杂不少。

正好,提供异形、铁血战士数据集的Kaggle页面还提供了分类器的Keras实现和PyTorch实现。我们不妨把网络架构部分的代码抽出来对比一下。

首先,是以API简洁著称的Keras:

conv_base = ResNet50(

include_top=False,

weights='imagenet')

for layer in conv_base.layers:

layer.trainable = False

x = conv_base.output

x = layers.GlobalAveragePooling2D()(x)

x = layers.Dense(128, activation='relu')(x)

predictions = layers.Dense(2, activation='softmax')(x)

model = Model(conv_base.input, predictions)

optimizer = keras.optimizers.Adam()

model.compile(loss='sparse_categorical_crossentropy',

optimizer=optimizer,

metrics=['accuracy'])

然后,是以易用性著称的PyTorch:

device = torch.device("cuda:0"if torch.cuda.is_available() else"cpu")

model = models.resnet50(pretrained=True).to(device)

for param in model.parameters():

param.requires_grad = False

model.fc = nn.Sequential(

nn.Linear(2048, 128),

nn.ReLU(inplace=True),

nn.Linear(128, 2)).to(device)

criterion = nn.CrossEntropyLoss()

optimizer = optim.Adam(model.fc.parameters())

对比一下,很明显,论简洁程度,PyTorch并不比一直打广告说自己简洁的Keras差。然后,虽然写法有点不一样,但这两个框架的基本思路都是差不多的。首先指定模型的冻结部分,然后指定后续层,最后指定损失函数、优化算法、指标。

然后我们再看一遍fastai:

learn = create_cnn(data, models.resnet50, metrics=accuracy)

你大概能体会文章开头提到的惊呼“这样也行!?”的心情了吧。看起来我们只是指定了模型种类和指标,很多东西都没有呀。

实际上,如果你运行过开头提到的MNIST的代码,就会发现一个epoch就达到了98%以上的精确度。很明显,用了迁移学习,否则不会学得这么快。和Keras、PyTorch需要明确指出继承权重、预训练不同,fastai里迁移学习是默认配置。同理,后续层的层数、形状、激活函数,损失函数,优化算法,都不需要明确指定,fastai可以根据数据的形状、模型种类、指标,自动搞定这些。

fastai的口号是“makeing neural nets uncool again”(化神经网络为平常),真是名不虚传。

训练和解读

定下模型后,只需调用fit就可以开始训练了,就像MNIST例子中写的那样。

不过,这次我们打算转用fit_one_cycle方法。fit_one_cycle使用的是一种周期性学习率,从较小的学习率开始学习,缓慢提高至较高的学习率,然后再慢慢下降,周而复始,每个周期的长度略微缩短,在训练的最后部分,允许学习率比之前的最小值降得更低。这不仅可以加速训练,还有助于防止模型落入损失平面的陡峭区域,使模型更倾向于寻找更平坦的极小值,从而缓解过拟合现象。

图片来源:sgugger.github.io

这种学习率规划方案是Lesile Smith等最近一年刚提出的。fit_one_cycle体现了fastai的一个知名的特性:让最新的研究成果易于使用。

先跑个epoch看看效果:

learn.fit_one_cycle(1)

结果:

epoch train_loss valid_loss accuracy

1 0.400788 0.520693 0.835106

嗯,看起来相当不错。一般而言,先跑一个epoch是个好习惯,可以快速检查是否在编码时犯了一些低级错误(比如形状弄反之类的)。但是fastai这么简洁,这么自动化,犯低级错误的几率相应也低了不少,也让先跑一个epoch体现价值的机会少了很多。;-)

再多跑几个epoch看看:

learn.fit_one_cycle(3)

结果:

epoch train_loss valid_loss accuracy

1 0.221689 0.364424 0.888298

2 0.164495 0.209541 0.909574

3 0.132979 0.181689 0.930851

有兴趣的读者可以试着多跑几个epoch,表现应该还能提升一点。不过,我对这个精确度已经很满意了。我们可以对比下Kaggle上的PyTorch实现跑3个epoch的表现:

Epoch1/3

----------

train loss: 0.5227, acc: 0.7205

validation loss: 0.3510, acc: 0.8400

Epoch2/3

----------

train loss: 0.3042, acc: 0.8818

validation loss: 0.2759, acc: 0.8800

Epoch3/3

----------

train loss: 0.2181, acc: 0.9135

validation loss: 0.2405, acc: 0.8950

fastai的表现与之相当,但是,相比PyTorch实现需要进行的编码(已经很简洁了),我们的fastai实现可以说是毫不费力。

当然,也不能光看精确度——有的时候这会形成偏差。让我们看下混淆矩阵吧。

interp = ClassificationInterpretation.from_learner(learn)

interp.plot_confusion_matrix()

看起来很不错嘛!

再查看下最让模型头疼的样本:

interp.plot_top_losses(9, figsize=(10,10))

我们看到,这个模型还是有一定的可解释性的。我们可以猜想,人脸、亮度过暗、画风独特、头部在画幅外或较小,都可能干扰分类器的判断。如果我们想进一步提升模型的表现和概括能力,可以根据我们的猜想再收集一些样本,然后做一些针对性的试验加以验证。查明问题后,再采取相应措施加以改进。

灵活性

本文的目的是展示fastai API的简洁性和高层抽象性。不过,我们最后需要指出的一点是,fastai并没有因此放弃灵活性和可定制性。

比如,之前为了符合fastai数据集目录结构的惯例,我们在下载数据集后将validation重命名为valid。实际上,不进行重命名也完全可以,只需在调用ImageDataBunch的from_folder方法时,额外将validation传入对应的参数即可。

再比如,如果我们的数据集目录中,子目录名作为标签,但没有按训练集、验证集、测试集分开,需要随机按比例划分。另外,我们还想手动指定数据增强操作列表。那么可以这样加载数据集:

# 手动指定数据增强操作

tfms = [rotate(degrees=(-20,20)), symmetric_warp(magnitude=(-0.3,0.3))]

data = (ImageFileList.from_folder(path)

.label_from_folder() # 根据子目录决定标签

.random_split_by_pct(0.1) # 随机分割10%为验证集

.datasets(ImageClassificationDataset) # 转换为图像分类数据集

.transform(tfms, size=224) # 数据增强

.databunch()) # 最终转换

我们上面明确罗列了数据增强操作。如果我们只是需要对默认方案进行微调的话,那么get_transforms方法其实有一大堆参数可供调整,比如get_transforms(flip_vert=True, max_rotate=20)意味着我们同时进行上下翻转(默认只进行水平翻转),并且增加了旋转的角度范围(默认为-10到10)。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 图像分类
    +关注

    关注

    0

    文章

    87

    浏览量

    11838
  • 数据集
    +关注

    关注

    4

    文章

    1177

    浏览量

    24347

原文标题:fastai 1.0入门教程:如何训练简单图像分类器

文章出处:【微信号:jqr_AI,微信公众号:论智】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    基于多通道分类合成的SAR图像分类研究

    ,证明了该算法的可行和有效。【关键词】:多通道;;SAR;;图像分类;;SVM;;粒度合成【DOI】:CNKI:SUN:JYXH.0.2010-03-003【正文快照】:0引言合成
    发表于 04-23 11:52

    个“贪吃蛇”带你进入proteus的世界

    通过个贪吃蛇程序的仿真,带你领略proteus的魅力,也让你掌握基本的proteus操作起初学习proteus,从网上找了很多资料,但感觉写的都不好,从实践中掌握是最好的方法,通过网上
    发表于 11-10 20:34

    使用MXNetFashionMNIST数据集分类简洁实现

    [MXNet逐梦之旅]练习四·使用MXNetFashionMNIST数据集分类简洁实现
    发表于 05-11 15:21

    种基于图像平移的目标检测框架

    的研究方向。目前,在照明条件良好的白天场景等标准场景中,目标检测显示出了显著的效率和可靠。然而,在夜间等不利条件下,目标检测的准确明显下降。造成这一问题的主要原因之是缺乏足够的夜
    发表于 08-31 07:43

    课程预告丨12月15日官方直播带你领略ArkUI的声明式开发范式之美

    HarmonyOS系列课程的第五期“Hello Codelabs:分布式新闻客户端实战(eTS)”线上直播来啦!华为软件开发工程师Jacky将带你领略ArkUI的声明式开发范式之美,讲解如何使用eTS语言
    发表于 12-10 17:52

    PowerVR框架:PVRApi Vulkan和OpenGL ES抽象

    Vulkan和OpenGL ES抽象层 • PowerVR框架:使用PVRApi编写可移植的Vulkan和OpenGL ES 3.0/3.1 • PowerVR框架:使用PVRAssets加载场景、纹理和着
    发表于 02-09 18:52 643次阅读
     PowerVR<b class='flag-5'>框架</b>:PVRApi Vulkan和OpenGL ES<b class='flag-5'>抽象</b>层

    基于双稀疏正则的图像集距离学习框架DSRID

    随着视频采集和网络传输技术的快速发展以及个人移动终端设备的广泛使用,大量图像数据以集合形式存在,集合内在结构的复杂性使得如何度量集合间距离成为图像分类的一个关键问题.为了解决这一问题
    发表于 12-26 19:06 3次下载

    电机控制硬件抽象层(HAL)

    硬件抽象层( Hardware Abstraction Layer,HAL)为更高层(例如:应用程序框架和客户应用程序等等)提供基于API函数的服务,允许更高层独立于实际的硬件细节执行
    发表于 03-21 16:38 3次下载

    简单好上手的图像分类教程!

    简单好上手的图像分类教程!构建图像分类模型的一个突破是发现卷积神经网络(CNN)可以用来逐步地提取图像内容的更
    的头像 发表于 05-31 16:36 7974次阅读
    简单好上手的<b class='flag-5'>图像</b><b class='flag-5'>分类</b>教程!

    人工智能引发的图像分类算法

    作者:Quenton Hall,赛灵思公司工业、视觉、医疗及科学市场的 AI 系统架构师 在上一篇文章中,我们简要介绍了更高层次的问题,这些问题为优化加速器的需求奠定了基础。作为一个尖锐的问题提醒
    的头像 发表于 11-16 16:40 3608次阅读
    人工智能引发的<b class='flag-5'>图像</b><b class='flag-5'>分类</b>算法

    线路简洁性能极优越的准甲类电路

    线路简洁性能极优越的准甲类电路说明。
    发表于 04-12 09:38 6次下载

    fastai深度学习实践库

    fastai.zip
    发表于 04-19 14:26 0次下载
    <b class='flag-5'>fastai</b>深度学习实践库

    OpenHarmony工作委员会PMC委员万承臻带你领略OpenHarmony3.1从内核到框架

    OpenHarmony工作委员会PMC委员万承臻带你领略OpenHarmony3.1从内核到框架 OpenHarmony各版本不断迭代不断完善;今天以“共建新技术、开拓新领域”作为
    的头像 发表于 04-25 17:12 1931次阅读
    OpenHarmony工作委员会PMC委员万承臻<b class='flag-5'>带你</b><b class='flag-5'>领略</b>OpenHarmony3.1从内核到<b class='flag-5'>框架</b>

    电机控制硬件抽象层(HAL)用户指南

    硬件抽象层(Hardware Abstraction Layer,HAL)为更高层(例如:应用程序框架和客户应用程序等等)提供基于API函数的服务,允许更高层独立于实际的硬件细节执行面
    发表于 09-22 08:31 0次下载
    电机控制硬件<b class='flag-5'>抽象</b>层(HAL)用户指南

    传感器抽象框架有哪些

    传感器抽象框架是一种用于开发和管理传感器网络的软件架构。它提供了一种抽象和整合的方式来处理传感器节点、传感器数据和传感器网络的通信。本文将详细介绍传感器抽象
    的头像 发表于 12-28 14:08 232次阅读