使用飞桨PaddlePaddle高层API完成分类网络


最近参加飞桨的公开课,快速上手深度学习https://aistudio.baidu.com/aistudio/course/introduce/6771
PaddlePaddle开发文档

深度学习万能公式

在这里插入图片描述

数据处理

paddle.io.Dataset
paddle.io.DataLoader

组网和网络配置

paddle.prepare

训练和评估

model.fit
model.evaluate

预测

model.predict

数据集

使用cifar100数据集,完成对100个类别的分类。

导入相关库

import paddle
import numpy as np

数据准备

直接使用高层API内置的数据集Cifar100

// 首先查看一下数据集中的数据内容和标签
train = paddle.vision.datasets.Cifar100(mode='train')
print(train[0])
plt.imshow(train[0][0])
print('标签:',train[0][1])

在这里插入图片描述
正式准备训练集和验证集,并且使用高级API内置的数据增强方法对数据进行预处理。

import paddle.vision.transforms as T

# 训练数据集
train_dataset = paddle.vision.datasets.Cifar100(mode='train', transform=T.ToTensor())

# 验证数据集
eval_dataset = paddle.vision.datasets.Cifar100(mode='test', transform=T.ToTensor())
# 输出数据集大小
print('训练数据集大小为: ', len(train_dataset))
print('验证数据集大小为:', len(eval_dataset))

在这里插入图片描述

sample = train_dataset[0]
print('图片尺寸:',sample[0].shape)
print('图片标签:',sample[1])

在这里插入图片描述

模型搭建

论文地址:https://arxiv.org/abs/1804.06882

"""
一种面向实时应用的精简CNN架构,轻量级 backbone
基于DenseNet的轻量化网络变体,主要面向移动端部署。
"""
import paddle
import paddle.nn as nn
import paddle.nn.functional as F
import numpy as np
from paddle.fluid.dygraph import to_variable
import layers

class Conv_bn_relu(nn.Layer):
    def __init__(self,in_channels,out_channels,kernel_size=3,stride=1,padding=1,use_relu=True):
        super(Conv_bn_relu, self).__init__()
        self.use_relu = use_relu
        if self.use_relu:
            self.convs = nn.Sequential(
                nn.Conv2D(in_channels,out_channels,kernel_size,stride,padding),
                nn.BatchNorm2D(out_channels),
                nn.ReLU()
            )
        else:
            self.convs = nn.Sequential(
                nn.Conv2D(in_channels,out_channels,kernel_size,stride,padding),
                nn.BatchNorm2D(out_channels)
            )

    def forward(self, x):
        out = self.convs(x)
        return out

""" Stem Block:实现输入图像空间维度的第一次降采样(stride=2)和通道数的增加 """
class StemBlock(nn.Layer):
    """ 尺寸缩小1/4,通道为32 """
    def __init__(self,in_channels=3,out_channels=32):
        super(StemBlock, self).__init__()

        self.stem_1 = Conv_bn_relu(in_channels,out_channels,kernel_size=3,stride=2,padding=1)
        self.stem_2a = Conv_bn_relu(out_channels,int(out_channels/2),1,1,0)
        self.stem_2b = Conv_bn_relu(int(out_channels/2),out_channels,3,2,1)
        self.stem_2p = nn.MaxPool2D(kernel_size=2,stride=2)
        self.stem_3 = Conv_bn_relu(out_channels*2,out_channels,1,1,0)

    def forward(self, x):
        stem_1_out = self.stem_1(x)  # 通道增加,尺寸缩小

        # 左分支,先1*1改变通道数,再3*3缩小尺寸
        stem_2a_out = self.stem_2a(stem_1_out)
        stem_2b_out = self.stem_2b(stem_2a_out)

        # 右分支,使用最大池化缩小尺寸
        stem_2p_out = self.stem_2p(stem_1_out)

        # Concat两个分支
        out = self.stem_3(paddle.concat([stem_2b_out,stem_2p_out],1))

        return out  # 尺寸缩小1/4,通道32

""" Two-Way Dense Layer:由两路分别捕捉不同尺度感受野信息的网络分支构成 """
class DenseBlock(nn.Layer):
    """ 分为左右两路提取特征 最后进行concat 尺寸不变 最终通道加倍 两个分支输出通道为growth_rate"""
    def __init__(self,inp,inter_channel,growth_rate):
        super(DenseBlock, self).__init__()

        self.cb1_a = Conv_bn_relu(inp,inter_channel,1,1,0)
        self.cb1_b = Conv_bn_relu(inter_channel,growth_rate,3,1,1)

        self.cb2_a = Conv_bn_relu(inp,inter_channel,1,1,0)
        self.cb2_b = Conv_bn_relu(inter_channel,growth_rate,3,1,1)
        self.cb2_c = Conv_bn_relu(growth_rate,growth_rate,3,1,1)

    def forward(self, x):
        cb1_a_out = self.cb1_a(x)
        cb1_b_out = self.cb1_b(cb1_a_out)

        cb2_a_out = self.cb2_a(x)
        cb2_b_out = self.cb2_b(cb2_a_out)
        cb2_c_out = self.cb2_c(cb2_b_out)

        out = paddle.concat([x,cb1_b_out,cb2_c_out],1)

        return out

class TransitionBlock(nn.Layer):
    """ 过渡层:输入与输出通道数保持一致 """
    def __init__(self,inp,outp,with_pooling=True):
        super(TransitionBlock, self).__init__()

        if with_pooling:  # 池化缩小尺寸
            self.tb = nn.Sequential(
                Conv_bn_relu(inp,outp,1,1,0),
                nn.AvgPool2D(kernel_size=2,stride=2)
            )
        else:  # 没有池化则尺寸不变
            self.tb = Conv_bn_relu(inp,outp,1,1,0)

    def forward(self, x):
        out = self.tb(x)
        return out

class PeleeNet(nn.Layer):
    """ 组网开始 分类网络Backbone 轻量级架构 """
    def __init__(self,num_classes=1000,
                 num_init_features=32,
                 growthRate=32,
                 nDenseBlocks=[3,4,8,6],  # 每一个阶段DenseBlock的数量
                 bottleneck_width=[1,2,4,4]):
        super(PeleeNet, self).__init__()

        self.stage = nn.Sequential()
        self.num_classes = num_classes
        self.num_init_features = num_init_features

        inter_channel = list()
        total_filter = list()
        dense_inp = list()

        self.half_growth_rate = int(growthRate / 2)  # 16

        # building stemblock
        self.stage.add_sublayer(name='stage_0',sublayer=StemBlock(3,num_init_features))

        for i,b_w in enumerate(bottleneck_width): # bottleneck_width=[1,2,4,4]

            inter_channel.append(int(self.half_growth_rate * b_w / 4) * 4)

            if i==0:
                total_filter.append(num_init_features + growthRate * nDenseBlocks[i])
                dense_inp.append(self.num_init_features)
            else:
                total_filter.append(total_filter[i-1] + growthRate * nDenseBlocks[i])
                dense_inp.append(total_filter[i-1])

            if i == len(nDenseBlocks)-1:
                with_pooling = False
            else:
                with_pooling = True

            # building middle stageblock
            self.stage.add_sublayer('stage_{}'.format(i+1),self._make_dense_transition(dense_inp[i],
                                                                                       total_filter[i],
                                                                                       inter_channel[i],
                                                                                       nDenseBlocks[i],
                                                                                       with_pooling=with_pooling))

        # building classifier
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(total_filter[len(nDenseBlocks)-1],self.num_classes)
        )

    def _make_dense_transition(self,dense_inp,total_filter,inter_channel,ndenseblocks,with_pooling=True):
        """
        dense_inp:输入通道数
        total_filter:总输出通道数
        inter_channel:中间通道数
        ndenseblocks:稠密连接块的个数
        with_pooling:池化操作缩小尺寸
        """
        layers = []
        for i in range(ndenseblocks):
            layers.append(DenseBlock(dense_inp,inter_channel,self.half_growth_rate))
            dense_inp += self.half_growth_rate * 2  # 对了

        # Transition layer without Conpression
        layers.append(TransitionBlock(dense_inp,total_filter,with_pooling))

        return nn.Sequential(*layers)

    def forward(self, x):

        x = self.stage(x)
        # print(self.stage.children())
        # s = list(self.stage.children())
        # print(len(s))

        # global ave pooling
        x = F.avg_pool2d(x,kernel_size=7)
        x = paddle.fluid.layers.reshape(x,[x.shape[0],-1])
        x = self.classifier(x)

        out = F.log_softmax(x,1)

        return out

net = PeleeNet(num_classes=1000)
peleenet = list(net.stage.children())

if __name__ == '__main__':
    data = np.random.rand(1,3,224,224).astype(np.float32)
    data = to_variable(data)

    # model = StemBlock()
    # out = model(data)
    # print(out.shape)
    #
    # model1 = DenseBlock(inp=32,inter_channel=64,growth_rate=16)
    # out1 = model1(out)
    # print(out1.shape)
    #
    # model2 = TransitionBlock(inp=64,outp=128)
    # out2 = model2(out1)
    # print(out2.shape)
    model = PeleeNet(num_classes=1000)
    model.eval()
    out = model(data)
    print(out.shape)

    model_info = paddle.summary(model,(1,3,224,224))
    print(model_info)
import peleenet
pelee_network = peleenet.PeleeNet(num_classes=100)

模型可视化,使用paddle.Model接口对模型进行封装。

model = paddle.Model(pelee_network)
model.summary((-1, 3, 32, 32))

在这里插入图片描述

模型训练与调优

model.prepare(paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters()),
              paddle.nn.CrossEntropyLoss(),
              paddle.metric.Accuracy())  
model.fit(train_dataset,
          eval_dataset,
          epochs=20,
          batch_size=64,
          verbose=1)
// 模型存储
checkpoints_path = './checkpoints/models'
model.save(checkpoints_path,train=False)
// 模型存储两个文件,分别为.pdmodel和.pdiparams文件,可用于模型部署。

在这里,笔者对模型训练了20个epoch,精度为:

result = model.evaluate(eval_dataset, verbose=1)
print(result)

在这里插入图片描述
可以看到,模型在训练集的精度很高,但是验证集精度不够,发生了训练集的过拟合,可以对数据集以及模型结构进行进一步优化,达到验证集的精度提高。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值