【深度学习项目三】ResNet50多分类任务【十二生肖分类】

  1. 卷积神经网络简介

    1.1 AlexNet

    贡献:

    • 引入ReLU作为激活函数
    • Dropout层
    • Max Pooling
    • GPU加速
    • 数据增强(截取、水平翻转)

    1.2 VGG

    1.3 GoogleNet

    全连接层对输入输出大小有限制,用池化层代替没有约束。

    1.4 ResNet

    • 残差结构解决梯度消失问题,多个路径前向传播。
    • 层数改变如图左下角,主要是为了减少计算开销,既减少参数。
  2. 数据集介绍

    按照12生肖在网上”下载的12种动物照片

    训练样本量| 7,096张

    验证样本量| 639张

    测试样本量| 656张

    加载使用方式|自定义数据集

    2.1 数据标注

    数据集分为train、valid、test三个文件夹,每个文件夹内包含12个分类文件夹,每个分类文件夹内是具体的样本图片。

    .
    ├── test|train|valid
    │ ├── dog
    │ ├── dragon
    │ ├── goat
    │ ├── horse
    │ ├── monkey
    │ ├── ox
    │ ├── pig
    │ ├── rabbit
    │ ├── ratt
    │ ├── rooster
    │ ├── snake
    │ └── tiger 我们对这些样本进行一个标注处理,最终生成train.txt/valid.txt/test.txt三个数据标注文件。

”`python
config.py
all = [‘CONFIG’, ‘get’] CONFIG = {

&#39;model_save_dir&#39;: &#34;./output/zodiac&#34;,<br/>
&#39;num_classes&#39;: 12,<br/>
&#39;total_images&#39;: 7096,<br/>
&#39;epochs&#39;: 20,<br/>
&#39;batch_size&#39;: 32,<br/>
&#39;image_shape&#39;: [3, 224, 224],<br/>
&#39;LEARNING_RATE&#39;: {<br/>
    &#39;params&#39;: {<br/>
        &#39;lr&#39;: 0.00375<br/>
    }<br/>
},<br/>
&#39;OPTIMIZER&#39;: {<br/>
    &#39;params&#39;: {<br/>
        &#39;momentum&#39;: 0.9<br/>
    },<br/>
    &#39;regularizer&#39;: {<br/>
        &#39;function&#39;: &#39;L2&#39;,<br/>
        &#39;factor&#39;: 0.000001<br/>
    }<br/>
},<br/>
&#39;LABEL_MAP&#39;: [<br/>
    &#34;ratt&#34;,<br/>
    &#34;ox&#34;,<br/>
    &#34;tiger&#34;,<br/>
    &#34;rabbit&#34;,<br/>
    &#34;dragon&#34;,<br/>
    &#34;snake&#34;,<br/>
    &#34;horse&#34;,<br/>
    &#34;goat&#34;,<br/>
    &#34;monkey&#34;,<br/>
    &#34;rooster&#34;,<br/>
    &#34;dog&#34;,<br/>
    &#34;pig&#34;,<br/>
]<br/>

} def get(full_path):

for id, name in enumerate(full_path.split(&#39;.&#39;)):<br/>
    if id == 0:<br/>
        config = CONFIG

config = config[name] return config

import io
import os
from PIL import Image
from config import get

数据集根目录

DATA_ROOT = ‘signs’

标签List

LABEL_MAP = get(‘LABEL_MAP’)

标注生成函数

def generate_annotation(mode):

# 建立标注文件<br/>
with open(&#39;{}/{}.txt&#39;.format(DATA_ROOT, mode), &#39;w&#39;) as f:<br/>
    # 对应每个用途的数据文件夹,train/valid/test<br/>
    train_dir = &#39;{}/{}&#39;.format(DATA_ROOT, mode)

遍历文件夹,获取里面的分类文件夹

    for path in os.listdir(train_dir):<br/>
        # 标签对应的数字索引,实际标注的时候直接使用数字索引<br/>
        label_index = LABEL_MAP.index(path)

图像样本所在的路径

        image_path = &#39;{}/{}&#39;.format(train_dir, path)

遍历所有图像

        for image in os.listdir(image_path):<br/>
            # 图像完整路径和名称<br/>
            image_file = &#39;{}/{}&#39;.format(image_path, image)

try:

                # 验证图片格式是否ok<br/>
                with open(image_file, &#39;rb&#39;) as f_img:<br/>
                    image = Image.open(io.BytesIO(f_img.read()))<br/>
                    image.load()

if image.mode == ‘RGB’:

                        f.write(&#39;{}\t{}\n&#39;.format(image_file, label_index))<br/>
            except:<br/>
                continue

generate_annotation(‘train’) # 生成训练集标注文件
generate_annotation(‘valid’) # 生成验证集标注文件
generate_annotation(‘test’) # 生成测试集标注文件

2.2 数据集定义

接下来我们使用标注好的文件进行数据集类的定义,方便后续模型训练使用。

2.2.1 导入相关库

import paddle
import numpy as np
from config import get

HWC和CHW区别

  • C代表:输入通道数
  • H/W分别代表图片的高、宽

NCHW

  • N代表样本数

to_tensor

paddle.vision.transforms.to_tensor(pic, data_format=‘CHW’)[源代码]

将 PIL.Image 或 numpy.ndarray 转换成 paddle.Tensor。

  • 形状为 (H x W x C)的输入数据 PIL.Image 或 numpy.ndarray 转换为 (C x H x W)。 如果想保持形状不变,可以将参数 data_format 设置为 ‘HWC’。
  • 同时,如果输入的 PIL.Image 的 mode 是 (L, LA, P, I, F, RGB, YCbCr, RGBA, CMYK, 1) 其中一种,或者输入的 numpy.ndarray 数据类型是 ‘uint8’,那个会将输入数据从(0-255)的范围缩放到 (0-1)的范围。其他的情况,则保持输入不变。

2.2.2 导入数据集的定义实现

我们数据集的代码实现是在dataset.py中。

import paddle
import paddle.vision.transforms as T
import numpy as np
from config import get
from PIL import Image all = [‘ZodiacDataset’]

定义图像的大小

image_shape = get(‘image_shape’) #‘image_shape’: [3, 224, 224],
IMAGE_SIZE = (image_shape[1], image_shape[2]) class ZodiacDataset(paddle.io.Dataset):

&#34;&#34;&#34;<br/>
十二生肖数据集类的定义<br/>
&#34;&#34;&#34;

def init(self, mode=‘train’):

    &#34;&#34;&#34;<br/>
    初始化函数<br/>
    &#34;&#34;&#34;<br/>
    assert mode in [&#39;train&#39;, &#39;test&#39;, &#39;valid&#39;], &#39;mode is one of train, test, valid.&#39; #判断参数合法性

self.data = []

     &#34;&#34;&#34;<br/>
    根据不同模式选择不同的数据标注文件

“”“

    with open(&#39;signs/{}.txt&#39;.format(mode)) as f:<br/>
        for line in f.readlines():<br/>
            info = line.strip().split(&#39;\t&#39;)

if len(info) &gt; 0:

                self.data.append([info[0].strip(), info[1].strip()])#进行切分形成数组,每个数组包含图像的地址和label

if mode == ‘train’:

        self.transforms = T.Compose([<br/>
            T.RandomResizedCrop(IMAGE_SIZE),    # 随机裁剪大小,裁剪地方不同等于间接增加了数据样本 300*300-224*224<br/>
            T.RandomHorizontalFlip(0.5),        # 随机水平翻转,概率0.5,也是等于得到一个新的图像<br/>
            T.ToTensor(),                       # 数据的格式转换和标准化 HWC =&gt; CHW<br/>
            T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # 图像归一化<br/>
        ])<br/>
    else:  #评估模式:没必要进行水平翻转增加样本量了,主要是想看看效果<br/>
        self.transforms = T.Compose([<br/>
            T.Resize(256),                 # 图像大小修改<br/>
            T.RandomCrop(IMAGE_SIZE),      # 随机裁剪,<br/>
            T.ToTensor(),                  # 数据的格式转换和标准化 HWC =&gt; CHW<br/>
            T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])   # 图像归一化<br/>
        ])

def getitem(self, index):

    &#34;&#34;&#34;<br/>
    根据索引获取单个样本<br/>
    &#34;&#34;&#34;<br/>
    image_file, label = self.data[index]<br/>
    image = Image.open(image_file)<br/>

#转成RGB模式,三通道的

    if image.mode != &#39;RGB&#39;:<br/>
        image = image.convert(&#39;RGB&#39;)

image = self.transforms(image)#得到预处理后的结果 return image, np.array(label, dtype=‘int64’)#对label做个数据转换,int类型转成numpy def len(self):

    &#34;&#34;&#34;<br/>
    获取样本总数<br/>
    &#34;&#34;&#34;<br/>
    return len(self.data)<br/>

from dataset import ZodiacDataset

2.3.3 实例化数据集类

根据所使用的数据集需求实例化数据集类,并查看总样本量。

train_dataset = ZodiacDataset(mode=‘train’)
valid_dataset = ZodiacDataset(mode=‘valid’) print(‘训练数据集:{}张;验证数据集:{}张’.format(len(train_dataset), len(valid_dataset)))
3.模型选择和开发

3.1 网络构建

本次我们使用ResNet50网络来完成我们的案例实践。

1)ResNet系列网络

2)ResNet50结构

3)残差区块

4)ResNet其他版本

network = paddle.vision.models.resnet50(num_classes=get(‘num_classes’), pretrained=True)
#pretrained=True使用别人已经训练好的预训练模型进行训练网络
model = paddle.Model(network)
model.summary((-1, ) + tuple(get(‘image_shape’)))
——————————————————————————-
Layer (type) Input Shape Output Shape Param #
===============================================================================

 Conv2D-1        [[1, 3, 224, 224]]   [1, 64, 112, 112]        9,408<br/>

BatchNorm2D-1 [[1, 64, 112, 112]] [1, 64, 112, 112] 256

  ReLU-1        [[1, 64, 112, 112]]   [1, 64, 112, 112]          0<br/>
MaxPool2D-1     [[1, 64, 112, 112]]    [1, 64, 56, 56]           0<br/>
 Conv2D-3        [[1, 64, 56, 56]]     [1, 64, 56, 56]         4,096<br/>

BatchNorm2D-3 [[1, 64, 56, 56]] [1, 64, 56, 56] 256

  ReLU-2         [[1, 256, 56, 56]]    [1, 256, 56, 56]          0<br/>
 Conv2D-4        [[1, 64, 56, 56]]     [1, 64, 56, 56]        36,864<br/>

BatchNorm2D-4 [[1, 64, 56, 56]] [1, 64, 56, 56] 256

 Conv2D-5        [[1, 64, 56, 56]]     [1, 256, 56, 56]       16,384<br/>

BatchNorm2D-5 [[1, 256, 56, 56]] [1, 256, 56, 56] 1,024

 Conv2D-2        [[1, 64, 56, 56]]     [1, 256, 56, 56]       16,384<br/>

BatchNorm2D-2 [[1, 256, 56, 56]] [1, 256, 56, 56] 1,024
BottleneckBlock-1 [[1, 64, 56, 56]] [1, 256, 56, 56] 0

 Conv2D-6        [[1, 256, 56, 56]]    [1, 64, 56, 56]        16,384<br/>

BatchNorm2D-6 [[1, 64, 56, 56]] [1, 64, 56, 56] 256

  ReLU-3         [[1, 256, 56, 56]]    [1, 256, 56, 56]          0<br/>
 Conv2D-7        [[1, 64, 56, 56]]     [1, 64, 56, 56]        36,864<br/>

BatchNorm2D-7 [[1, 64, 56, 56]] [1, 64, 56, 56] 256

 Conv2D-8        [[1, 64, 56, 56]]     [1, 256, 56, 56]       16,384<br/>

BatchNorm2D-8 [[1, 256, 56, 56]] [1, 256, 56, 56] 1,024
BottleneckBlock-2 [[1, 256, 56, 56]] [1, 256, 56, 56] 0

 Conv2D-9        [[1, 256, 56, 56]]    [1, 64, 56, 56]        16,384<br/>

BatchNorm2D-9 [[1, 64, 56, 56]] [1, 64, 56, 56] 256

  ReLU-4         [[1, 256, 56, 56]]    [1, 256, 56, 56]          0<br/>
 Conv2D-10       [[1, 64, 56, 56]]     [1, 64, 56, 56]        36,864<br/>

BatchNorm2D-10 [[1, 64, 56, 56]] [1, 64, 56, 56] 256

 Conv2D-11       [[1, 64, 56, 56]]     [1, 256, 56, 56]       16,384<br/>

BatchNorm2D-11 [[1, 256, 56, 56]] [1, 256, 56, 56] 1,024
BottleneckBlock-3 [[1, 256, 56, 56]] [1, 256, 56, 56] 0

 Conv2D-13       [[1, 256, 56, 56]]    [1, 128, 56, 56]       32,768<br/>

BatchNorm2D-13 [[1, 128, 56, 56]] [1, 128, 56, 56] 512

  ReLU-5         [[1, 512, 28, 28]]    [1, 512, 28, 28]          0<br/>
 Conv2D-14       [[1, 128, 56, 56]]    [1, 128, 28, 28]       147,456<br/>

BatchNorm2D-14 [[1, 128, 28, 28]] [1, 128, 28, 28] 512

 Conv2D-15       [[1, 128, 28, 28]]    [1, 512, 28, 28]       65,536<br/>

BatchNorm2D-15 [[1, 512, 28, 28]] [1, 512, 28, 28] 2,048

 Conv2D-12       [[1, 256, 56, 56]]    [1, 512, 28, 28]       131,072<br/>

BatchNorm2D-12 [[1, 512, 28, 28]] [1, 512, 28, 28] 2,048
BottleneckBlock-4 [[1, 256, 56, 56]] [1, 512, 28, 28] 0

 Conv2D-16       [[1, 512, 28, 28]]    [1, 128, 28, 28]       65,536<br/>

BatchNorm2D-16 [[1, 128, 28, 28]] [1, 128, 28, 28] 512

  ReLU-6         [[1, 512, 28, 28]]    [1, 512, 28, 28]          0<br/>
 Conv2D-17       [[1, 128, 28, 28]]    [1, 128, 28, 28]       147,456<br/>

BatchNorm2D-17 [[1, 128, 28, 28]] [1, 128, 28, 28] 512

 Conv2D-18       [[1, 128, 28, 28]]    [1, 512, 28, 28]       65,536<br/>

BatchNorm2D-18 [[1, 512, 28, 28]] [1, 512, 28, 28] 2,048
BottleneckBlock-5 [[1, 512, 28, 28]] [1, 512, 28, 28] 0

 Conv2D-19       [[1, 512, 28, 28]]    [1, 128, 28, 28]       65,536<br/>

BatchNorm2D-19 [[1, 128, 28, 28]] [1, 128, 28, 28] 512

  ReLU-7         [[1, 512, 28, 28]]    [1, 512, 28, 28]          0<br/>
 Conv2D-20       [[1, 128, 28, 28]]    [1, 128, 28, 28]       147,456<br/>

BatchNorm2D-20 [[1, 128, 28, 28]] [1, 128, 28, 28] 512

 Conv2D-21       [[1, 128, 28, 28]]    [1, 512, 28, 28]       65,536<br/>

BatchNorm2D-21 [[1, 512, 28, 28]] [1, 512, 28, 28] 2,048
BottleneckBlock-6 [[1, 512, 28, 28]] [1, 512, 28, 28] 0

 Conv2D-22       [[1, 512, 28, 28]]    [1, 128, 28, 28]       65,536<br/>

BatchNorm2D-22 [[1, 128, 28, 28]] [1, 128, 28, 28] 512

  ReLU-8         [[1, 512, 28, 28]]    [1, 512, 28, 28]          0<br/>
 Conv2D-23       [[1, 128, 28, 28]]    [1, 128, 28, 28]       147,456<br/>

BatchNorm2D-23 [[1, 128, 28, 28]] [1, 128, 28, 28] 512

 Conv2D-24       [[1, 128, 28, 28]]    [1, 512, 28, 28]       65,536<br/>

BatchNorm2D-24 [[1, 512, 28, 28]] [1, 512, 28, 28] 2,048
BottleneckBlock-7 [[1, 512, 28, 28]] [1, 512, 28, 28] 0

 Conv2D-26       [[1, 512, 28, 28]]    [1, 256, 28, 28]       131,072<br/>

BatchNorm2D-26 [[1, 256, 28, 28]] [1, 256, 28, 28] 1,024

  ReLU-9        [[1, 1024, 14, 14]]   [1, 1024, 14, 14]          0<br/>
 Conv2D-27       [[1, 256, 28, 28]]    [1, 256, 14, 14]       589,824<br/>

BatchNorm2D-27 [[1, 256, 14, 14]] [1, 256, 14, 14] 1,024

 Conv2D-28       [[1, 256, 14, 14]]   [1, 1024, 14, 14]       262,144<br/>

BatchNorm2D-28 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 4,096

 Conv2D-25       [[1, 512, 28, 28]]   [1, 1024, 14, 14]       524,288<br/>

BatchNorm2D-25 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 4,096
BottleneckBlock-8 [[1, 512, 28, 28]] [1, 1024, 14, 14] 0

 Conv2D-29      [[1, 1024, 14, 14]]    [1, 256, 14, 14]       262,144<br/>

BatchNorm2D-29 [[1, 256, 14, 14]] [1, 256, 14, 14] 1,024

  ReLU-10       [[1, 1024, 14, 14]]   [1, 1024, 14, 14]          0<br/>
 Conv2D-30       [[1, 256, 14, 14]]    [1, 256, 14, 14]       589,824<br/>

BatchNorm2D-30 [[1, 256, 14, 14]] [1, 256, 14, 14] 1,024

 Conv2D-31       [[1, 256, 14, 14]]   [1, 1024, 14, 14]       262,144<br/>

BatchNorm2D-31 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 4,096
BottleneckBlock-9 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 0

 Conv2D-32      [[1, 1024, 14, 14]]    [1, 256, 14, 14]       262,144<br/>

BatchNorm2D-32 [[1, 256, 14, 14]] [1, 256, 14, 14] 1,024

  ReLU-11       [[1, 1024, 14, 14]]   [1, 1024, 14, 14]          0<br/>
 Conv2D-33       [[1, 256, 14, 14]]    [1, 256, 14, 14]       589,824<br/>

BatchNorm2D-33 [[1, 256, 14, 14]] [1, 256, 14, 14] 1,024

 Conv2D-34       [[1, 256, 14, 14]]   [1, 1024, 14, 14]       262,144<br/>

BatchNorm2D-34 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 4,096
BottleneckBlock-10 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 0

 Conv2D-35      [[1, 1024, 14, 14]]    [1, 256, 14, 14]       262,144<br/>

BatchNorm2D-35 [[1, 256, 14, 14]] [1, 256, 14, 14] 1,024

  ReLU-12       [[1, 1024, 14, 14]]   [1, 1024, 14, 14]          0<br/>
 Conv2D-36       [[1, 256, 14, 14]]    [1, 256, 14, 14]       589,824<br/>

BatchNorm2D-36 [[1, 256, 14, 14]] [1, 256, 14, 14] 1,024

 Conv2D-37       [[1, 256, 14, 14]]   [1, 1024, 14, 14]       262,144<br/>

BatchNorm2D-37 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 4,096
BottleneckBlock-11 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 0

 Conv2D-38      [[1, 1024, 14, 14]]    [1, 256, 14, 14]       262,144<br/>

BatchNorm2D-38 [[1, 256, 14, 14]] [1, 256, 14, 14] 1,024

  ReLU-13       [[1, 1024, 14, 14]]   [1, 1024, 14, 14]          0<br/>
 Conv2D-39       [[1, 256, 14, 14]]    [1, 256, 14, 14]       589,824<br/>

BatchNorm2D-39 [[1, 256, 14, 14]] [1, 256, 14, 14] 1,024

 Conv2D-40       [[1, 256, 14, 14]]   [1, 1024, 14, 14]       262,144<br/>

BatchNorm2D-40 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 4,096
BottleneckBlock-12 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 0

 Conv2D-41      [[1, 1024, 14, 14]]    [1, 256, 14, 14]       262,144<br/>

BatchNorm2D-41 [[1, 256, 14, 14]] [1, 256, 14, 14] 1,024

  ReLU-14       [[1, 1024, 14, 14]]   [1, 1024, 14, 14]          0<br/>
 Conv2D-42       [[1, 256, 14, 14]]    [1, 256, 14, 14]       589,824<br/>

BatchNorm2D-42 [[1, 256, 14, 14]] [1, 256, 14, 14] 1,024

 Conv2D-43       [[1, 256, 14, 14]]   [1, 1024, 14, 14]       262,144<br/>

BatchNorm2D-43 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 4,096
BottleneckBlock-13 [[1, 1024, 14, 14]] [1, 1024, 14, 14] 0

 Conv2D-45      [[1, 1024, 14, 14]]    [1, 512, 14, 14]       524,288<br/>

BatchNorm2D-45 [[1, 512, 14, 14]] [1, 512, 14, 14] 2,048

  ReLU-15        [[1, 2048, 7, 7]]     [1, 2048, 7, 7]           0<br/>
 Conv2D-46       [[1, 512, 14, 14]]     [1, 512, 7, 7]       2,359,296<br/>

BatchNorm2D-46 [[1, 512, 7, 7]] [1, 512, 7, 7] 2,048

 Conv2D-47        [[1, 512, 7, 7]]     [1, 2048, 7, 7]       1,048,576<br/>

BatchNorm2D-47 [[1, 2048, 7, 7]] [1, 2048, 7, 7] 8,192

 Conv2D-44      [[1, 1024, 14, 14]]    [1, 2048, 7, 7]       2,097,152<br/>

BatchNorm2D-44 [[1, 2048, 7, 7]] [1, 2048, 7, 7] 8,192
BottleneckBlock-14 [[1, 1024, 14, 14]] [1, 2048, 7, 7] 0

 Conv2D-48       [[1, 2048, 7, 7]]      [1, 512, 7, 7]       1,048,576<br/>

BatchNorm2D-48 [[1, 512, 7, 7]] [1, 512, 7, 7] 2,048

  ReLU-16        [[1, 2048, 7, 7]]     [1, 2048, 7, 7]           0<br/>
 Conv2D-49        [[1, 512, 7, 7]]      [1, 512, 7, 7]       2,359,296<br/>

BatchNorm2D-49 [[1, 512, 7, 7]] [1, 512, 7, 7] 2,048

 Conv2D-50        [[1, 512, 7, 7]]     [1, 2048, 7, 7]       1,048,576<br/>

BatchNorm2D-50 [[1, 2048, 7, 7]] [1, 2048, 7, 7] 8,192
BottleneckBlock-15 [[1, 2048, 7, 7]] [1, 2048, 7, 7] 0

 Conv2D-51       [[1, 2048, 7, 7]]      [1, 512, 7, 7]       1,048,576<br/>

BatchNorm2D-51 [[1, 512, 7, 7]] [1, 512, 7, 7] 2,048

  ReLU-17        [[1, 2048, 7, 7]]     [1, 2048, 7, 7]           0<br/>
 Conv2D-52        [[1, 512, 7, 7]]      [1, 512, 7, 7]       2,359,296<br/>

BatchNorm2D-52 [[1, 512, 7, 7]] [1, 512, 7, 7] 2,048

 Conv2D-53        [[1, 512, 7, 7]]     [1, 2048, 7, 7]       1,048,576<br/>

BatchNorm2D-53 [[1, 2048, 7, 7]] [1, 2048, 7, 7] 8,192
BottleneckBlock-16 [[1, 2048, 7, 7]] [1, 2048, 7, 7] 0
AdaptiveAvgPool2D-1 [[1, 2048, 7, 7]] [1, 2048, 1, 1] 0

 Linear-1           [[1, 2048]]            [1, 12]            24,588<br/>

===============================================================================
Total params: 23,585,740
Trainable params: 23,479,500
Non-trainable params: 106,240
——————————————————————————-
Input size (MB): 0.57
Forward/backward pass size (MB): 261.48
Params size (MB): 89.97

Estimated Total Size (MB): 352.02

{‘total_params’: 23585740, ‘trainableparams’: 23479500}

4.模型训练和优化

CosineAnnealingDecay

class paddle.optimizer.lr.CosineAnnealingDecay(learningrate, Tmax, etamin=0, lastepoch=- 1, verbose=False)[源代码]

该接口使用 cosine annealing 的策略来动态调整学习率。

η

t

=

η

min

+

1

2

(

η

max

η

min

)

(

1

+

cos

(

T

c

u

r

T

max

π

)

)

,

T

c

u

r

(

2

k

+

1

)

T

max

η

t

+

1

=

η

t

+

1

2

(

η

max

η

min

)

(

1

cos

(

1

T

max

π

)

)

,

T

c

u

r

=

(

2

k

+

1

)

T

max

\begin{aligned} \eta{t} &=\eta{\min }+\frac{1}{2}\left(\eta{\max }-\eta{\min }\right)\left(1+\cos \left(\frac{T{c u r}}{T{\max }} \pi\right)\right), & T{c u r} \neq(2 k+1) T{\max } \ \eta{t+1} &=\eta{t}+\frac{1}{2}\left(\eta{\max }-\eta{\min }\right)\left(1-\cos \left(\frac{1}{T{\max }} \pi\right)\right), & T{c u r}=(2 k+1) T{\max } \end{aligned}

ηt​ηt+1​​=ηmin​+21​(ηmax​−ηmin​)(1+cos(Tmax​Tcur​​π)),=ηt​+21​(ηmax​−ηmin​)(1−cos(Tmax​1​π)),​Tcur​​=(2k+1)Tmax​Tcur​=(2k+1)Tmax​​

ηmax 的初始值为 learning_rate, Tcur 是SGDR(重启训练SGD)训练过程中的当前训练轮数。SGDR的训练方法可以参考文档 SGDR: Stochastic Gradient Descent with Warm Restarts. 这里只是实现了 cosine annealing 动态学习率,热启训练部分没有实现。

参数:

  • learning_rate (float) - 初始学习率,也就是公式中的 ηmax ,数据类型为Python float。
  • T_max (float|int) - 训练的上限轮数,是余弦衰减周期的一半
  • eta_min (float|int, 可选) - 学习率的最小值,即公式中的 ηmin 。默认值为0。
  • last_epoch (int,可选) - 上一轮的轮数,重启训练时设置为上一轮的epoch数。默认值为 -1,则为初始学习率。
  • verbose (bool,可选) - 如果是 True ,则在每一轮更新时在标准输出 stdout 输出一条信息。默认值为 False

返回:用于调整学习率的 CosineAnnealingDecay 实例对象

Momentum

class paddle.optimizer.Momentum(learningrate=0.001, momentum=0.9, parameters=None, usenesterov=False, weightdecay=None, gradclip=None, name=None)[源代码]

该接口实现含有速度状态的Simple Momentum 优化器
该优化器含有牛顿动量标志,公式更新如下:
更新公式如下:

参数:

  • learning_rate (float|_LRScheduler, 可选) -
    学习率,用于参数更新的计算。可以是一个浮点型值或者一个_LRScheduler类,默认值为0.001
  • momentum (float, 可选) - 动量因子
  • parameters (list, 可选) -指定优化器需要优化的参数。在动态图模式下必须提供该参数;在静态图模式下默认值为None,这时所有的参数都将被优化。
  • use_nesterov (bool, 可选) - 赋能牛顿动量,默认值False。
  • weight_decay (float|Tensor, 可选) - 权重衰减系数,是一个float类型或者shape为[1]
    ,数据类型为float32的Tensor类型。默认值为0.01
  • grad_clip (GradientClipBase, 可选) – 梯度裁剪的策略,支持三种裁剪策略: cn_api_fluid_clip_GradientClipByGlobalNorm 、 cn_api_fluid_clip_GradientClipByNorm 、 cn_api_fluid_clip_GradientClipByValue 。 默认值为None,此时将不进行梯度裁剪。
  • name (str, 可选)- 该参数供开发人员打印调试信息时使用,具体用法请参见 Name ,默认值为None

API参考链接
https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/optimizer/momentum/Momentum_cn.html

EPOCHS = get(‘epochs’)
BATCH_SIZE = get(‘batch_size’) def create_optim(parameters):

step_each_epoch = get(&#39;total_images&#39;) // get(&#39;batch_size&#39;)<br/>
lr = paddle.optimizer.lr.CosineAnnealingDecay(learning_rate=get(&#39;LEARNING_RATE.params.lr&#39;),<br/>
                                              T_max=step_each_epoch * EPOCHS)

return paddle.optimizer.Momentum(learning_rate=lr,

                                 parameters=parameters,<br/>
                                 weight_decay=paddle.regularizer.L2Decay(get(&#39;OPTIMIZER.regularizer.factor&#39;))) #正则化来提升精度

模型训练配置

model.prepare(create_optim(network.parameters()), # 优化器

          paddle.nn.CrossEntropyLoss(),        # 损失函数<br/>
          paddle.metric.Accuracy(topk=(1, 5))) # 评估指标

训练可视化VisualDL工具的回调函数

visualdl = paddle.callbacks.VisualDL(log_dir=‘visualdl_log’)

启动模型全流程训练

model.fit(train_dataset, # 训练数据集

      valid_dataset,            # 评估数据集<br/>
      epochs=EPOCHS,            # 总的训练轮次<br/>
      batch_size=BATCH_SIZE,    # 批次计算的样本量大小<br/>
      shuffle=True,             # 是否打乱样本集<br/>
      verbose=1,                # 日志展示格式<br/>
      save_dir=&#39;./chk_points/&#39;, # 分阶段的训练模型存储路径<br/>
      callbacks=[visualdl])     # 回调函数使用<br/>

top1 表示预测的第一个答案就是正确答案的准确率

top5 表示预测里面前五个包含正确答案的准确率

预测可视化:

4.1模型存储

将我们训练得到的模型进行保存,以便后续评估和测试使用。

model.save(get(‘model_save_dir’))
5 模型评估和测试

5.1 批量预测测试

5.1.1 测试数据集

predict_dataset = ZodiacDataset(mode=‘test’)
print(‘测试数据集样本量:{}’.format(len(predict_dataset)))
from paddle.static import InputSpec

网络结构示例化

network = paddle.vision.models.resnet50(num_classes=get(‘num_classes’))

模型封装

model_2 = paddle.Model(network, inputs=[InputSpec(shape=[-1] + get(‘image_shape’), dtype=‘float32’, name=‘image’)])

训练好的模型加载

model_2.load(get(‘model_save_dir’))

模型配置

model_2.prepare()

执行预测

result = model_2.predict(predict_dataset)

import matplotlib.pyplot as plt

样本映射

LABEL_MAP = get(‘LABEL_MAP’) def show_img(img, predict):

plt.figure()<br/>
plt.title(&#39;predict: {}&#39;.format(LABEL_MAP[predict_label]))<br/>
image_file, label = predict_dataset.data[idx]<br/>
image = Image.open(image_file)<br/>
plt.imshow(image)<br/>
plt.show()

随机取样本展示

indexs = [50,150 , 250, 350, 450, 00] for idx in indexs:

predict_label = np.argmax(result[0][idx])<br/>
real_label = predict_dataset[idx][1]<br/>
show_img(real_label,predict_label )<br/>
print(&#39;样本ID:{}, 真实标签:{}, 预测值:{}&#39;.format(idx, LABEL_MAP[real_label], LABEL_MAP[predict_label]))

#或者不定义函数:
”“”
import matplotlib.pyplot as plt

样本映射

LABEL_MAP = get(‘LABEL_MAP’)

# 抽样展示

indexs = [50,150 , 250, 350, 450, 00] for idx in indexs:

predict_label = np.argmax(result[0][idx])<br/>
real_label = predict_dataset[idx][1]<br/>
print(&#39;样本ID:{}, 真实标签:{}, 预测值:{}&#39;.format(idx, LABEL_MAP[real_label], LABEL_MAP[predict_label]))<br/>
image_file, label = predict_dataset.data[idx]<br/>
image = Image.open(image_file)<br/>
plt.figure()<br/>
plt.title(&#39;predict: {}&#39;.format(LABEL_MAP[predict_label]))<br/>
plt.imshow(image)<br/>
plt.show()<br/>

“”“


样本ID:50, 真实标签:monkey, 预测值:monkey

样本ID:150, 真实标签:ratt, 预测值:ratt


样本ID:450, 真实标签:tiger, 预测值:tiger

6 模型部署
model_2.save(‘infer/zodiac’, training=False)
总结
  • 本次讲解了四种卷积神经网络的由来,以及采用resnet50实现了十二生肖分类项目

  • 本次项目重点在于数据集自定义、以及创建优化器。来使模型更加灵活可改动也提高准确率和有助于模型快速收敛。

  • 这里还是推荐模型封装不要采用高层api 自己用Sub Class写法或者用Sequential写法。尝试写写看虽然层数比较多!