12

飞桨开源框架2.0,带你走进全新高层API,十行代码搞定深度学习模型开发

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MjM5MDE2NDU2MA%3D%3D&%3Bmid=2654481607&%3Bidx=1&%3Bsn=a3ac35ba85cb47919928cbd4dd962508
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

深度学习作为人工智能时代的核心技术,近年来无论学术、还是工业领域,均发挥着愈加重要的作用。而对于深度学习项目开发来说,选择使用哪个框架非常重要,一个合适的框架往往能起到事半功倍的作用。百度飞桨作为行业的领军者,为了进一步降低深度学习的学习门槛,提升开发效率,推出高层API。此API具有高低融合、科学统一、完备易用、兼容历史版本四大特点,更贴近开发者的使用习惯,可谓是实现了真正意义上的快速上手,友好开发。

高层API,Why

通常情况下,要完成一个典型的深度学习任务,往往需要经过下图的步骤:

qQvyie2.png!mobile

如果使用当前主流深度学习框架实现以上步骤,每一步都涉及到大量的概念,同时每一步的实现都需要编写大量的代码。这需要开发者花费大量的时间来学习其中的概念和使用方法,还需要开发者投入非常多的时间在深度学习任务的代码实现上。特别是在深度学习任务中,数据的处理与加载是一件非常”折磨”开发者的事情,无论是数据预处理部分,亦或者是数据的加载部分,往往带来大量的工作与未知的错误。

以上种种困难,能否用简洁而优雅的方式处理呢?

答案是:可以!

什么是高层API

为了简化深度学习的学习过程、降低深度学习的开发效率,飞桨框架历经几个月的迭代,不断的对飞桨框架API进行优化和开发者使用场景进行封装,终于推出了飞桨高层API。

BFFnyqf.png!mobile

飞桨高层API是飞桨框架推出的快速实现深度学习模型的API,旨在帮助开发者更快更好的完成深度学习模型的学习和开发。

简单来说,飞桨API分为两类,一类是基础API,另一类是高层API。拿制作披萨举例,一般有两种方法:一种是我们准备好面粉、牛奶、火腿等食材,经过我们精心加工,就能制作出美味的披萨;而第二种则是我们买商家预烤制的披萨饼,以及调好的馅料,我们直接加热就可以吃到披萨了。

那么这两种方法有什么区别呢?采用方法一,自己准备食材,可以随心所欲的搭配料理,制作酱料,从而满足我们的不同口味,但是,这更适合”老司机”,如果是新人朋友,很有可能翻车;而方法二,我们用商家预烤制的披萨饼与馅料,直接加热即可,可以非常快速的完成披萨的制作;但是,相比于方法一,我们会少一些披萨的选择。

那么,用框架来类比,飞桨框架基础API对应方法一,飞桨框架高层API对应方法二。使用基础API,我们可以随心所欲的搭建自己的深度学习模型,不会受到任何限制;而使用方法二,我们可以很快的实现模型,达到自己想要的效果,缺点是少了一些自主性。但是,与制作披萨不同的是,飞桨框架可以做到真正的”鱼与熊掌”可以兼得,我们在飞桨框架中实现了API的高低融合,使我们的开发者既可以享受到基础API的强大,又可以兼顾高层API的快捷。

高层API的特点

首先,这次升级并不是简单的优化或者修改了几个API,而是让所有API变得更加体系化。飞桨基于对开发者使用习惯的洞察,以及对深度学习技术本身的理解和应用实践,对已有的API进行了整理和优化,使得API做到了更加科学和一致,贴合开发者的使用习惯。

其次,为了帮助开发者实现低代码快速建模,我们提供了更适合低代码编程的高层API,比如数据增强,或者是建立数据流水线等等API,这样就能够帮助开发者进一步简化工作流程;此外,一些非常经典的模型结构,我们也将其封装成了高层API,提供给开发者直接使用。高层API本身不是一个独立的体系,它完全可以和基础API互相配合使用,做到高低融合,从而使用起来会更加便捷。

然后还有一个点是广大老开发者最为关心的问题,那就是兼容性,这一点大家可以放心,我们实现了对历史版本的完全兼容,不用担心已有的模型会出现不能使用的情况,我们的老开发者依然可以使用原来的API进行模型开发,同时我们会配备非常完善的教程,引导开发者去根据自己的偏好顺利升级到新版本的API。

高层API全景图

飞桨高层API的全景图如下:

63qqMjE.png!mobile

从上图中可以看出,目前飞桨高层API由五个模块组成,分别是数据加载、模型组建、模型训练、模型可视化和高阶用法。我们先通过一个深度学习中经典的手写数字分类任务,来简单了解飞桨高层API。然后在详细的介绍每个模块中所包含的API。

import paddle

from paddle.vision.transforms  import Compose, Normalize

from paddle.vision.datasets  import MNIST

import paddle.nn  as nn 

# 数据预处理,这里用到了归一化

transform = Compose([Normalize(mean=[ 127.5 ],

std=[ 127.5 ],

data_format= 'CHW' )])

# 数据加载,在训练集上应用数据预处理的操作

train_dataset = paddle.vision.datasets.MNIST(mode= 'train' , transform=transform)

test_dataset = paddle.vision.datasets.MNIST(mode= 'test' , transform=transform)

# 模型组网

mnist = nn.Sequential(

nn.Flatten(),

nn.Linear( 784512 ),

nn.ReLU(),

nn.Dropout( 0.2 ),

nn.Linear( 51210 )

)

# 模型封装,用Model类封装

model = paddle.Model(mnist)

# 模型配置:为模型训练做准备,设置优化器,损失函数和精度计算方式

model.prepare(optimizer=paddle.optimizer.Adam(parameters=model.parameters()),

loss=nn.CrossEntropyLoss(),

metrics=paddle.metric.Accuracy())

# 模型训练,

model.fit(train_dataset,

epochs= 10 ,

batch_size= 64 ,

verbose= 1 )

# 模型评估,

model.evaluate(test_dataset, verbose= 1 )

# 模型保存,

'model_path'

)

从上面的示例可以看出,飞桨框架高层API对数据预处理、数据加载、模型组网、模型训练、模型评估、模型保存等都进行了封装,每一步均可以通过1~3行代码实现。相比传统方法动辄几十行的代码量,高层API只需要十来行代码,就能轻松完成一个MNIST分类器的训练、评估与保存。开发者以极少的代码就能达到与基础API同样的效果,大大提高了模型开发效率;同时还方便开发者更专注于深度学习技术的核心:即模型算法的实现与调优,而不是在其它的方面。

在一些场景下,如初次学习深度学习框架时,使用飞桨高层API,可以骄傲地说出"当你用传统框架还在写数据加载时,我用飞桨高层API已经开始训练模型了"!

JFjYBr2.gif!mobile

高层API详解

下面以CV任务为例,详细介绍如何使用飞桨高层API完成模型的构建、训练、保存等全流程操作。

本示例的完整代码可以在AI Studio上获取,无需准备任何软硬件环境即可直接在线运行代码,相当方便哦:

https://aistudio.baidu.com/aistudio/projectdetail/1216442

数据预处理与数据加载

对于数据预处理与数据加载,飞桨框架提供了许多API,列表如下:

  1. 飞桨框架内置数据集:paddle.vision.datasets内置包含了许多CV领域相关的数据集,直接调用API即可使用;

  2. 飞桨框架数据预处理:paddle.vision.transforms飞桨框架对于图像预处理的方式,可以快速完成常见的图像预处理的方式,如调整色调、对比度,图像大小等;

  3. 飞桨框架数据加载:paddle.io.Dataset与paddle.io.DataLoader飞桨框架标准数据加载方式,可以”一键”完成数据的批加载与异步加载;

1.飞桨框架内置数据集

首先,飞桨框架将常用的数据集作为领域API对开发者开放,对应API所在目录为paddle.vision.datasets,包含的数据集如下所示:

视觉相关数据集:['DatasetFolder', 'ImageFolder', 'MNIST', 'Flowers', 'Cifar10', 'Cifar100', 'VOC2012']

飞桨提供的数据集API包含计算机视觉领域中常见的数据集,完全可以满足我们在数据集方面的需求。

2. 飞桨框架预处理方法

飞桨框架提供了20多种数据集预处理的接口,方便开发者快速实现数据增强,比如实现图像的色调、对比度、饱和度、大小等各种数字图像处理的方法。预处理API集中在 paddle.vision.transforms 目录下。

这些数据预处理方法使用起来非常方便。只需要先创建一个数据预处理的transform,在其中存入需要进行的数据预处理方法,然后在数据加载的过程中,将transform作为参数传入即可。

随机处理与未随机处理的对比图如下:

m2YfIn.png!mobile

3. 飞桨框架数据加载

paddle.io.Dataset与paddle.io.DataLoader是飞桨框架标准数据定义与数据加载方式,这俩“兄弟”相互使用,可以”一键”完成数据集的定义与数据的批加载或异步加载。

首先,我们来看一下自定义数据集。除了飞桨框架内置的数据集之外,更多的时候我们需要自己使用已有的相关数据来定义数据集,那么这里我们通过一个案例来了解如何进行数据集的定义,飞桨为开发者提供了paddle.io.Dataset基类,让开发者通过类的集成来快速实现数据集定义。示例如下:

from paddle.io  import Dataset

class MyDataset (Dataset) :

"""

步骤一:继承paddle.io.Dataset类

def __init__ (self, mode= 'train' ) :

"""

步骤二:实现构造函数,定义数据读取方式,划分训练和测试数据集

super(MyDataset, self).__init__()

if mode ==  'train' :

self.data = [

[ 'traindata1''label1' ],

[ 'traindata2''label2' ],

[ 'traindata3''label3' ],

[ 'traindata4''label4' ],

]

else :

self.data = [

[ 'testdata1''label1' ],

[ 'testdata2''label2' ],

[ 'testdata3''label3' ],

[ 'testdata4''label4' ],

]

def __getitem__ (self, index) :

"""

步骤三:实现__getitem__方法,定义指定index时如何获取数据,并返回单条数据(训练数据,对应的标签)

data = self.data[index][ 0 ]

label = self.data[index][ 1 ]

return data, label

def __len__ (self) :

"""

步骤四:实现__len__方法,返回数据集总数目

return len(self.data)

# 测试定义的数据集

train_dataset = MyDataset(mode= 'train' )

val_dataset = MyDataset(mode= 'test' )

print( '=============train dataset=============' )

for data, label  in train_dataset:

print(data, label)

print( '=============evaluation dataset=============' )

for data, label  in val_dataset:

print(data, label)

通过上述的方法,我们就实现了一个自己的数据集。然后,将train_dataset 与 val_dataset 作为参数,传入到DataLoader中,即可获得一个数据加载器,完成训练数据的加载。

PS:对于数据集的定义,飞桨框架同时支持map-style和interable-style两种类型的数据集定义,只需要分别继承paddle.io.Dataset和paddle.io.IterableDataset即可。

网络构建

在网络构建模块,飞桨高层API与基础API保持完全的一致,都使用paddle.nn下的API进行组网。这也是尽可能的减少需要暴露的概念,从而提升框架的易学性。飞桨框架  paddle.nn 目录下包含了所有与模型组网相关的API,如卷积相关的 Conv1D、Conv2D、Conv3D,循环神经网络相关的 RNN、LSTM、GRU 等。

对于组网方式,飞桨框架统一支持 Sequential 或 SubClass 的方式进行模型的组建。我们根据实际的使用场景,来选择最合适的组网方式。如针对顺序的线性网络结构我们可以直接使用 Sequential ,相比于 SubClass ,Sequential 可以快速的完成组网。

如果是一些比较复杂的网络结构,我们可以使用 SubClass 定义的方式来进行模型代码编写,在 __init__ 构造函数中进行 Layer 的声明,在 forward 中使用声明的 Layer 变量进行前向计算。通过这种方式,我们可以组建更灵活的网络结构。

下面就来分别介绍一下 Sequential 与 SubClass 的组网方式。

1. Sequential 的组网方式

使用 Sequential 进行组网的实现如下:

# Sequential形式组网

mnist = nn.Sequential(

nn.Flatten(),

nn.Linear(784, 512),

nn.ReLU(),

nn.Dropout(0.2),

nn.Linear(512, 10)

)

对于线性的网络模型,我们只需要按网络模型的结构顺序,一层一层的加到Sequential  后面即可,非常快速就可以完成模型的组建。

2. SubClass 的组网方式

使用 SubClass 进行组网的实现如下:

# SubClass方式组网

class Mnist ( nn . Layer ):

def __init__ ( self ) :

super (Mnist,  self ).__init_ _ ()

self .flatten = nn.Flatten()

self .linear_1 = nn.Linear( 784512 )

self .linear_2 = nn.Linear( 51210 )

self .relu = nn.ReLU()

self .dropout = nn.Dropout( 0 . 2 )

def forward ( self , inputs) :

y =  self .flatten(inputs)

y =  self .linear_1(y)

y =  self .relu(y)

y =  self .dropout(y)

y =  self .linear_2(y)

return

y

上述的SubClass 组网的结果与Sequential 组网的结果完全一致,可以明显看出,使用SubClass 组网会比使用Sequential 更复杂一些。不过,这带来的是网络模型结构的灵活性。我们可以设计不同的网络模型结构来应对不同的场景。

3. 飞桨框架内置模型

除了自定义模型结构外,飞桨框架还”贴心”的内置了许多模型,真正的一行代码实现深度学习模型。目前,飞桨框架内置的模型都是CV领域领域的模型,都在paddle.vision.models目录下,具体包含如下的模型:

视觉相关模型:['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', 'resnet152', 'VGG', 'vgg11', 'vgg13', 'vgg16', 'vgg19', 'MobileNetV1', 'mobilenet_v1', 'MobileNetV2', 'mobilenet_v2', 'LeNet']

模型训练

1. 使用高层API在全部数据集上进行训练

过去常常困扰深度学习开发者的一个问题是,模型训练的代码过于复杂,常常要写好多步骤,才能正确的使程序运行起来,冗长的代码使许多开发者望而却步。

现在,飞桨高层API将训练、评估与预测API都进行了封装,直接使用Model.prepare()、Model.fit()、Model.evaluate()、Model.predict()完成模型的训练、评估与预测。

对比传统框架动辄一大块的训练代码。使用飞桨高层API,可以在3-5行内,完成模型的训练,极大的简化了开发的代码量,对初学者开发者非常友好。具体代码如下:

# 将网络结构用 Model类封装成为模型

model = paddle.Model(mnist)

# 为模型训练做准备,设置优化器,损失函数和精度计算方式

model.prepare(optimizer=paddle.optimizer.Adam(parameters=model.parameters()),

loss=paddle.nn.CrossEntropyLoss(),

metrics=paddle.metric.Accuracy())

# 启动模型训练,指定训练数据集,设置训练轮次,设置每次数据集计算的批次大小,设置日志格式

model.fit(train_dataset,

epochs=10,

batch_size=64,

verbose=1)

# 启动模型评估,指定数据集,设置日志格式

model.evaluate(test_dataset, verbose=1)

# 启动模型测试,指定测试集 

Model.predict(test_dataset)

2. 使用高层API在一个批次的数据集上训练、验证与测试

有时我们需要对数据按batch进行取样,然后完成模型的训练与验证,这时,可以使用 train_batch、eval_batch、predict_batch 完成一个批次上的训练、验证与测试,具体如下:

# 模型封装,用Model类封装

model = paddle.Model(mnist)

# 模型配置:为模型训练做准备,设置优化器,损失函数和精度计算方式

model.prepare(optimizer=paddle.optimizer.Adam(parameters=model.parameters()),

loss=nn.CrossEntropyLoss(),

metrics=paddle.metric.Accuracy())

# 构建训练集数据加载器

train_loader = paddle.io.DataLoader(train_dataset, places=paddle.CPUPlace(), batch_size= 64 , shuffle= True )

# 使用train_batch 完成训练

for batch_id, data  in enumerate(train_loader()):

model.train_batch([data[ 0 ]],[data[ 1 ]])

# 构建测试集数据加载器

test_loader = paddle.io.DataLoader(test_dataset, places=paddle.CPUPlace(), batch_size= 64 , shuffle= True )

# 使用 eval_batch 完成验证

for batch_id, data  in enumerate(test_loader()):

model.eval_batch([data[ 0 ]],[data[ 1 ]])

# 使用 predict_batch 完成预测

for batch_id, data  in enumerate(test_loader()):

0

]])

以上是飞桨高层API在常见任务中的使用方式,可以快速高效的完成模型的训练。

高阶用法

除此之外,飞桨高层API还支持一些高阶的玩法,如自定义Loss、自定义Metric、自定义Callback等。

1. 自定义Loss

有时我们会遇到特定任务的Loss计算方式在框架既有的Loss接口中不存在,或算法不符合自己的需求,那么期望能够自己来进行Loss的自定义,我们介绍一下如何进行Loss的自定义操作,首先来看下面的代码:

class SelfDefineLoss (paddle.nn.Layer) :

"""

1. 继承paddle.nn.Layer

def __init__ (self) :

"""

2. 构造函数根据自己的实际算法需求和使用需求进行参数定义即可

super(SelfDefineLoss, self).__init__()

def forward (self, input, label) :

"""

3. 实现forward函数,forward在调用时会传递两个参数:input和label

- input:单个或批次训练数据经过模型前向计算输出结果

- label:单个或批次训练数据对应的标签数据

接口返回值是一个Tensor,根据自定义的逻辑加和或计算均值后的损失

# 使用Paddle中相关API自定义的计算逻辑

# output = xxxxx

# return output

那么了解完代码层面如果编写自定义代码后我们看一个实际的例子,下面是在图像分割示例代码中写的一个自定义Loss,主要是想使用自定义的softmax计算维度。

class SoftmaxWithCrossEntropy ( paddle . nn . Layer ):

def __init__ ( self ) :

super (SoftmaxWithCrossEntropy,  self ).__init_ _ ()

def forward ( self , input, label) :

loss = F.softmax_with_cross_entropy(input,

label,

return_softmax=False,

axis= 1 )

return

paddle.mean(loss)

2. 自定义metric

和Loss一样,如果遇到一些想要做个性化实现的操作时,我们也可以来通过框架完成自定义的评估计算方法,由于这部分代码量过大,我们在后面的文章再来描述。对这部分内容感兴趣也可以到我们的官网文档中查看。

3. 自定义执行过程回调

这里我们简单介绍自定义Callback。fit接口的callback参数支持我们传一个Callback类实例,用来在每个epoch训练和每个batch训练前后进行调用,以此收集训练过程中的一些数据和参数,或者实现一些自定义操作。如对于模型保存而言,正常情况下,fit只会保存模型最后一次迭代的参数。然而实际情况中,往往我们需要保存多个模型,从中选择效果最好的那个。

这时,我们可以通过框架预定义的ModelCheckpoint回调函数,可以在fit训练模型时自动存储每轮训练得到的模型。

有时,我们需要保存模型训练过程中loss下降的信息,绘成图来分析网络模型的优化过程,这个在高层API中该如何实现呢?这时就需要上文提到的自定义Callback,我们只需要自定义与loss相关的Callback,然后保存loss信息,最后将其转化为图片即可。具体的实现过程如下:

# 自定义Callback 记录训练过程中的loss信息

class LossCallback ( paddle . callbacks . Callback ):

def on_train_begin ( self , logs={}) :

# 在fit前 初始化losses,用于保存每个batch的loss结果

self .losses = []

def on_train_batch_end ( self , step, logs={}) :

# 每个batch训练完成后调用,把当前loss添加到losses中

self .losses.append(logs.get( 'loss' ))

# 初始化一个loss_log 的实例,然后将其作为参数传递给fit

loss_log = LossCallback()

model.fit(train_dataset,

epochs= 10 ,

batch_size= 64 ,

callbacks=loss_log,

verbose= 1 )

# loss信息都保存在 loss_log.losses 中,可视化后得到下图

Y7RfUnN.png!mobile

通过这样的方式,我们就可以将训练过程中的loss信息保存,然后可视化得到loss下降的曲线,根据loss下降的曲线,对模型进行下一步的优化迭代。

PS:除了loss外,飞桨框架还可以保存 “metrics” 等信息。

模型可视化

在我们完成模型的构建后,有时还需要可视化模型的网络结构与训练过程,来直观的了解深度学习模型与训练过程,方便我们更好地优化模型。飞桨框架高层API提供了一系列相关的API,来帮助我们可视化模型与训练过程,就让我们来看一下吧。

在飞桨框架中,对于我们组网的模型,只要我们用Model进行模型的封装后,只需要调用 model.summary 即可实现网络模型的可视化,具体如下:

mnist = nn.Sequential(

nn.Flatten(),

nn.Linear(784, 512),

nn.ReLU(),

nn.Dropout(0.2),

nn.Linear(512, 10)

)

# 模型封装,用Model类封装

model = paddle.Model(mnist)

model.summary()

其输出如下:

---------------------------------------------------------------------------

Layer ( type )       Input Shape          Output Shape         Param #    

===========================================================================

Flatten -795 [[32, 1, 28, 28]] [ 32784 ]               0

Linear -5 [[32, 784]] [ 32512 ]            401 , 920

ReLU -3 [[32, 512]] [ 32512 ]               0

Dropout -3 [[32, 512]] [ 32512 ]               0

Linear -6 [[32, 512]] [ 3210 ]             5 , 130

===========================================================================

Total params:  407 , 050

Trainable params:  407 , 050

Non-trainable params:  0

---------------------------------------------------------------------------

Input size (MB):  0.10

Forward/backward pass size (MB):  0.57

Params size (MB):  1.55

Estimated Total Size (MB):  2.22

---------------------------------------------------------------------------

{ 'total_params'407050'trainable_params'

407050

}

不仅会给出每一层网络的形状,还会给出每层网络的参数量与模型的总参数量,非常方便直观的就可以看到模型的全部信息。

上文以CV任务为例,介绍了飞桨框架高层API的使用指南。后续,飞桨框架还计划推出NLP领域专用的数据预处理模块,如对数据进行padding、获取数据集词表等;在组网方面,也会实现NLP领域中组网专用的API,如组网相关的sequence_mask,评估指标相关的BLEU等;最后,针对NLP领域中的神器transformer,我们也会对其进行特定的优化;待这些功能上线后,我们会第一时间告诉大家,敬请期待吧~

总结一下

综上所述,大家不难发现,百度飞桨提供的高层API,不仅提供了完备易用的API体系,还在兼容历史版本的API的同时,给开发者提供了更加科学、高低融合的全新API体系,让开发者进行深度学习应用的开发变得更加容易。

体验一把

看完前面飞桨高层API的使用介绍,是不是有种跃跃欲试的冲动呀?

体验方式一:在线体验

无需准备任何软硬件环境,直接访问以下地址,即可在线跑代码看效果:

https://aistudio.baidu.com/aistudio/projectdetail/1243085

体验方式二:本地体验

如果你还想在自己本地电脑上体验,那需要确保本地电脑上已成功安装飞桨开源框架2.0。

下面介绍飞桨开源框架2.0的安装方法,可以参考下面的命令,直接使用pip安装。安装后,就可以开始使用高层API啦。

# CPU版

$ pip3 install paddlepaddle== 2.0.0 rc0 

-i https: //mirror.baidu.com/pypi/simple

# GPU版

$ pip3 install paddlepaddle_gpu== 2.0.0

//mirror.baidu.com/pypi/simple

写在最后

本次飞桨开源框架2.0高层API相当简单易用了。更难得的是,百度飞桨开源框架近期还会针对高层API推出一系列教程——跟着雨哥学AI,该课程由多位资深飞桨工程师精心打造,真的是诚意满满。对于刚上手深度学习的人来说,这就是最佳的学习材料。不管是自学,还是用于开发,都是极好的。

教程链接:

https://aistudio.baidu.com/aistudio/personalcenter/thirdview/564527

也可以扫描下方的二维码,了解教程。

长按扫码

——推荐阅读——

百度网盘小程序互转工具:wx2正式开源!

深度学习推荐方法

飞桨推出异构参数服务器架构,异构硬件高效组合,训练速度提升65%以上

百度开源Server-Agent:一种高性能、高效率的任务调度执行引擎


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK