欢迎关注“
计算机视觉研究院
”
计算机视觉研究院专栏
作者:Edison_G
前言 向往深度学习技术,可是深度学习框架太难学怎么办?百度倾心打造飞桨框架高层API,零基础也能轻松上手深度学习,一起来看看吧?
另:文末有福利,一定要看完
高层API,What
深度学习作为人工智能时代的核心技术,近年来无论学术、还是工业领域,均发挥着愈加重要的作用。然而,深度学习理论太难学,开发过程太复杂,又将许多人拒之于深度学习的门外。
为了简化深度学习的学习过程、降低深度学习的开发难度,百度飞桨框架历经近一年的打磨,不断地优化深度学习API,并针对开发者的使用场景进行封装,在飞桨框架的最新版本中,推出了高低融合、科学统一的飞桨全新 API 体系。
飞桨框架将API分为两种,基础API和高层API。用制作披萨举例,一般有两种方法:一种是我们准备好面粉、牛奶、火腿等食材,精心加工后,就能制作出美味的披萨;而第二种则是我们买商家预烤制的披萨饼,以及调好的馅料,直接加热就可以吃到披萨了。
那么这两种方法有什么区别呢?采用方法一,自己准备食材,可以随心所欲的搭配料理,制作酱料,从而满足我们的不同口味,但是,这更适合「老司机」,如果是新人朋友,很有可能翻车;而方法二,用商家预烤制的披萨饼与馅料,直接加热就可以非常快速的完成披萨的制作,而且味道会有保障;但是,相比于方法一,我们会少一些口味的选择。
用框架来类比,基础API对应方法一,高层API对应方法二。使用基础API,我们可以随心所欲的搭建自己的深度学习模型,不会受到任何限制;而使用方法二,我们可以很快的实现模型,但是可能会少一些自主性。
但是,与制作披萨不同的是,飞桨框架可以做到真正的「鱼与熊掌」可以兼得。因为高层API本身不是一个独立的体系,它完全可以和基础API互相配合使用,做到高低融合,使用起来会更加便捷。使我们在开发过程中,既可以享受到基础API的强大,又可以兼顾高层API的快捷。
高层API,All
飞桨框架高层API的全景图如下:
从图中可以看出,飞桨框架高层API由五个模块组成,分别是数据加载、模型组建、模型训练、模型可视化和高阶用法。针对不同的使用场景,飞桨框架提供了不同高层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(784, 512),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(512, 10))
# 模型封装,用 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.save('model_path')
从示例可以看出,在数据预处理、数据加载、模型组网、模型训练、模型评估、模型保存等场景,高层API均可以通过1~3 行代码实现。相比传统方法动辄几十行的代码量,高层API只需要十来行代码,就能轻松完成一个MNIST分类器的实现。以极少的代码就能达到与基础API同样的效果,大幅降低了深度学习的学习门槛。
如果是初次学习深度学习框架,使用飞桨高层API,可以「凡尔赛」说出「好烦哦,飞桨高层API怎么这么快就完成开发了,我还想多写几行代码呢!」
高层API,How
接下来以CV任务为例,简单介绍飞桨高层API在不同场景下的使用方法。
本示例的完整代码可以在AI Studio上获取,无需准备任何软硬件环境即可直接在线运行代码,https://aistudio.baidu.com/aistudio/projectdetail/1243085
一、数据预处理与数据加载
对于数据加载,在一些典型的任务中,我们完全可以使用飞桨框架内置的数据集,完成数据的加载。飞桨框架将常用的数据集作为领域API,集成在paddle.vision.datasets 目录中,包含了CV领域中常见的MNIST、Cifar、Flowers等数据集。
而在数据预处理场景中,飞桨框架提供了20多种常见的图像预处理 API,方便我们快速实现数据增强,如实现图像的色调、对比度、饱和度、大小等各种数字图像处理的方法。图像预处理API集成在paddle.vision.transforms目录中,使用起来非常方便。只需要先创建一个数据预处理的transform,在其中存入需要进行的数据预处理方法,然后在数据加载的过程中,将transform作为参数传入即可。
此外,如果我们需要加载自己的数据集,使用飞桨框架标准数据定义与数据加载API paddle.io.Dataset与paddle.io.DataLoader,就可以「一键」完成数据集的定义与数据的加载。这里通过一个案例来展示如何利用Dataset定义数据集,示例如下:
from paddle.io import Dataset
class MyDataset(Dataset):
"""
步骤一:继承 paddle.io.Dataset 类
"""
def __init__(self):
"""
步骤二:实现构造函数,定义数据读取方式,划分训练和测试数据集
"""
super(MyDataset, self).__init__()
self.data = [
['traindata1', 'label1'],
['traindata2', 'label2'],
['traindata3', 'label3'],
['traindata4', '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()
print('=============train dataset=============')
for data, label in train_dataset:
print(data, label)
只需要按照上述规范的四个步骤,我们就实现了一个自己的数据集。然后,将train_dataset作为参数,传入到DataLoader中,即可获得一个数据加载器,完成训练数据的加载。
【Tips:对于数据集的定义,飞桨框架同时支持 map-style 和 iterable-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)
)
2、SubClass
使用SubClass进行组网的实现如下:
# SubClass 方式组网
class Mnist(nn.Layer):
def __init__(self):
super(Mnist, self).__init__()
self.flatten = nn.Flatten()
self.linear_1 = nn.Linear(784, 512)
self.linear_2 = nn.Linear(512, 10)
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目录下,包含了常见的vgg系列、resnet系列等模型。使用方式如下:
import paddle
from paddle.vision.models import resnet18
# 方式一: 一行代码直接使用
resnetresnet = resnet18()
# 方式二: 作为主干网络进行二次开发
class FaceNet(paddle.nn.Layer):
def __init__(self, num_keypoints=15, pretrained=False):
super(FaceNet, self).__init__()
self.backbone = resnet18(pretrained)
self.outLayer1 = paddle.nn.Linear(1000, 512)
self.outLayer2 = paddle.nn.Linear(512, num_keypoints*2)
def forward(self, inputs):
out = self.backbone(inputs)
out = self.outLayer1(out)
out = self.outLayer2(out)
return out
三、模型可视化
在我们完成模型的构建后,有时还需要可视化模型的网络结构与参数,只要我们用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]] [32, 784] 0
Linear-5 [[32, 784]] [32, 512] 401,920
ReLU-3 [[32, 512]] [32, 512] 0
Dropout-3 [[32, 512]] [32, 512] 0
Linear-6 [[32, 512]] [32, 10] 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}
Model.summary不仅会给出每一层网络的形状,还会给出每层网络的参数量与模型的总参数量,非常方便直观的就可以看到模型的全部信息。
四、模型训练
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, 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()):
model.predict_batch([data[0]])
五、高阶用法
除此之外,飞桨高层API还支持一些高阶的玩法,如自定义Loss、自定义Metric、自定义Callback等等。
自定义Loss是指有时我们会遇到特定任务的Loss计算方式在框架既有的Loss接口中不存在,或算法不符合自己的需求,那么期望能够自己来进行Loss的自定义。
自定义Metric和自定义Loss的场景一样,如果遇到一些想要做个性化实现的操作时,我们也可以来通过框架完成自定义的评估计算方法。
自定义Callback则是可以帮助我们收集训练时的一些参数以及数据,由于Model.fit()封装了训练过程,如果我们需要保存训练时的loss、metric等信息,则需要通过callback 参数收集这部分信息。
更多更丰富的玩法,可以扫描关注文末的二维码获取
高层API,Next
上文以CV 任务为例,介绍了飞桨框架高层API的使用指南。后续,飞桨框架还计划推出NLP领域专用的数据预处理模块,如对数据进行padding、获取数据集词表等;在组网方面,也会实现NLP领域中组网专用的API,如组网相关的 sequence_mask,评估指标相关的BLEU等;最后,针对NLP领域中的神器transformer,我们也会对其进行特定的优化;待这些功能上线后,我们会第一时间告诉大家,敬请期待吧!
高层API,Where
看完前面飞桨高层API的使用介绍,是不是有种跃跃欲试的冲动呀?
体验方式一:在线体验
无需准备任何软硬件环境,直接访问以下地址,即可在线跑代码看效果:https://aistudio.baidu.com/aistudio/projectdetail/1243085
体验方式二:本地体验
如果你还想在自己本地电脑上体验,那需要确保本地电脑上已成功安装飞桨开源框架 2.0。
下面介绍飞桨开源框架2.0的安装方法,可以参考下面的命令,直接使用pip安装。安装后,就可以开始使用高层API。
# CPU 版
$ pip3 install paddlepaddle==2.0.0rc0 -i https://mirror.baidu.com/pypi/simple
# GPU 版
$ pip3 install paddlepaddle_gpu==2.0.0rc0 -i https://mirror.baidu.com/pypi/simple
/End.
我们开创“计算机视觉协会”知识星球一年有余,也得到很多同学的认可,我们定时会推送实践型内容与大家分享,在星球里的同学可以随时提问,随时提需求,我们都会及时给予回复及给出对应的答复。
如果想加入我们“计算机视觉研究院”,请扫二维码加入我们。我们会按照你的需求将你拉入对应的学习群!
计算机视觉研究院主要涉及深度学习领域,主要致力于人脸检测、人脸识别,多目标检测、目标跟踪、图像分割等研究方向。研究院接下来会不断分享最新的论文算法新框架,我们这次改革不同点就是,我们要着重”研究“。之后我们会针对相应领域分享实践过程,让大家真正体会摆脱理论的真实场景,培养爱动手编程爱动脑思考的习惯!
后台回复“飞桨代码”
获取源码下载地址
计算机视觉研究院
长按扫描二维码关注我们