计算机视觉——飞桨深度学习实战-图像分类算法原理与实战-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

基础理论

图像分类是深度学习在视觉领域第一个取得突破性成果的任务。本章首先介绍了图像分类任务的发展历程与评价指标。然后分为三个角度分别介绍了在图像分类领域具有重要地位的三种模型。第一种是基于残差网络的模型本章重点介绍了ResNet、DenseNet和DPN。第二种是基于Transformer思想的模型本章重点介绍了ViT和Swin-Transformer模型。第三种是用于移动端设备的轻量级模型本章重点介绍了MobileNet和PP-LCNet。最后本章使用飞桨框架完成了桃子分拣项目。学完本章希望读者能够掌握以下知识点:

  1. 了解图像分类的发展历程
  2. 掌握基于残差思想的模型特点
  3. 掌握基于Transformer思想的模型特点
  4. 掌握轻量级网络模型的特性。

图像分类任务是最早使用深度学习方法的计算机视觉任务很多经典的网络架构都是首先应用到图像分类任务上因此图像分类中的深度学习网络模型可以看做其他计算机视觉任务的基石。

早期的图像分类方法主要通过手工提取特征对整个图像进行描述然后使用分类器判别图像类别因此图像分类的核心在于对特征进行分类而如何提取图像的特征至关重要。底层特征中包含了大量冗余噪声为了提高特征表达的鲁棒性需要使用一种特征变换算法对底层特征进行编码称作特征编码。特征编码之后一般会经过空间特征约束也称作特征汇聚具体只在一个空间范围内对每一位特征取最大值或者平均值可以获得一定特征不变性的特征表达。


图像经过底层特征提取特征编码特征汇聚后可以表示为一个固定维度的向量描述将该特征向量经过分类器分类便可实现对图像的分类。
基于核方法的svm是传统方法中使用最广泛的分类器在传统图像分类任务上性能很好。传统的图像分类方法对于一些简单且具有明显特征的图像分类场景是有效的但由于实际情况非常复杂在面对复杂场景时传统的分类方法就无法达到满意的分类效果这是因为传统分类方法使用的手工提取特征方法无法全面准确的描述图像特征并且手工提取的特征无法应对多视角多角度不同光照遮挡同物多形态等问题。
 

CNN转变到Transformer类似从着眼于局部转变到专用于全局更加符合人类的视觉特点人类擅长快速捕获全局中的特征CNN擅长提取局部小而精的信息但存在提取能力不足的缺点Transformer依赖全局长距离的建模不关注局部信息也就丧失了CNN平移不变翻转不变等特性会产生捕捉信息冗余和对数据需求量更大的缺点。


综合来看,深度学习方法在图像分类问题上,经历了朴素MLP,CNN 、Transformer,复杂MLP等模型发展的过程。这些模型都具有各自的特点,卷积仅包含局部连接,因此计算高效;自注意力采用了动态权值,因此模型容量更大,它同时还具有全局感受野;MLP同样具有全局感受野,但没有使用动态权值。可以看出,卷积与自注意力具有互补特性,卷积具有最好的泛化能力,而Transformer在三种架构中具有最大的模型容量。卷积是设计轻量级模型的最佳选择,但设计大型模型应考虑 Transformer。因此,可以考虑使用卷积的局部建模帮助提升Transformer与MLP的性能。考虑到上述结构特憧人稀疏连接有助于提升泛化性能,而动态权值与全局感受野有助于提升模型容量。因此,在图像分类任务上,不断有革新性的方法出现,为计算机视觉提供了更广泛的应用空间。

实验名称桃子分类模型的搭建与训练

1.实验目标

本实验主要讲解用paddlepaddle深度学习框架搭建桃子分类模型并完成训练和测试的全过程。

完成此实验后可以掌握的能力有

  • 掌握paddlepaddle深度学习框架的使用方法
  • 掌握如何用paddlepaddle深度学习框架搭建 桃子分类模型
  • 掌握如何完成模型的训练、评估、保存、预测等深度学习工作过程

2.实验背景介绍

图像分类是计算机视觉的基础也是其他计算机复杂任务的基础。深度学习技术发展到现在诞生了许多优秀的图像分类算法。本次桃子分拣我们就使用其中的典型算法代表resnet

3. 使用paddlepaddle框架一般流程介绍

如今paddlepaddle已经推出了2.0版本在2.0版本中推出了高阶API使得代码更简洁变成更容易。

目前飞桨高层API由五个模块组成分别是数据加载、模型组建、模型训练、模型可视化和高阶用法。如下图所示

 

用paddlepaddle框架进行深度学习项目十分容易按照一般流程即可完成深度学习项目下图为项目实施一般流程。该流程主要包含五大步骤分别为数据处理、模型设计、训练配置、训练过程和模型保存。在数据处理阶段主要是为模型准可用的数据包括本地或者网络数据的收集与预处理。模型设计阶段就是深度学习项目中大家讨论最多的模型搭建在这阶段关键就是网络结构的设计与实现飞桨框架为开发者准备好了大量的经过工业验证的模型库和预训练模型方便开发者直接使用。在训练配置阶段开发者需要设定优化器类型和学习率衰减等参数同时还需要指定使用GPU还是CPU完成计算。在训练过程阶段就是框架真实运行计算过程的阶段。在该阶段飞桨框架不断的完成正向传播、反向传播和梯度下降的过程。最后是模型保存当模型达到预定指标或者达到预定的训练次数后开发者可以将“训练好”的模型保存起来用以下次训练或者用以部署。

用paddlepaddle框架进行深度学习项目流程

4. 实验内容

4.1 数据集介绍

本次实验我们使用的数据集是四个种类桃子这些桃子被分在四个文件夹中每一个文件夹的名字就对应着一类桃子。

桃子数据集

 

用我们自己的眼睛来观察好像这些桃子是按照 大小、颜色 来划分的四类究竟是不是这样呢等做完了这个实验深度学习模型自己就能判断出来是按照什么来划分了。

本次实验已经为大家提供好了数据集数据集存储在 “data/enhancement/” 文件夹下。图片分为2个文件夹一个是训练集一个是测试集。每个文件夹中有4个分类R0,B1,M2,S3。 桃子分拣原数据集包含两个文件夹“train”、“test”
每个文件夹下有“B1”、“M2”、“R0”、“S3”
训练集
    train_B1:1601张图片
    train_M2:1800张图片
    train_R0:1601张图片
    train_S3:1635张图片
测试集
    test_B1:16张图片
    test_M2:18张图片
    test_R0:18张图片
    test_S3:15张图片

# 数据解压。如果已经解压过一次了就将该段代码注释掉
# !unzip /home/aistudio/data/data103593/data.zip -d /home/aistudio/data/enhancement_data/
实验文件介绍

本次实验文件结构如下

本次实验的代码、数据集 都已经为大家准备好目录结构如下图所示

本次实验文件结构


 

4.2 导入实验需要的库

实验第一步需要导入相关的库最主要的是如下几个

  • os OS模块提供了非常丰富的方法用来处理文件和目录。
  • syssys模块提供了一系列有关Python运行环境的变量和函数。
  • shutil:用于文件拷贝的模块
  • numpynumpy 是 Python 语言的一个扩展程序库支持大量的维度数组与矩阵运算此外也针对数组运算提供大量的数学函数库。
  • randomPython中的random模块用于生成随机数。
  • paddle.vision.datasets该模块包含数据加载的相关函数比如可以用来加载常用的数据集等如mnist。
  • paddle.vision.transforms:该模块包含对图像进行转换的函数比如把HWC格式的图片转变成CHW模式的输入张量。也包含飞桨框架对于图像预处理的方式可以快速完成常见的图像预处理如调整色调、对比度图像大小等
  • paddle.io.Dataset:该模块包含了飞桨框架数据加载方式可以“一键”完成数据的批加载与异步加载。
#os  OS模块提供了非常丰富的方法用来处理文件和目录。
#syssys模块提供了一系列有关Python运行环境的变量和函数。
#shutil:用于文件拷贝的模块 
#numpynumpy 是 Python 语言的一个扩展程序库支持大量的维度数组与矩阵运算此外也针对数组运算提供大量的数学函数库。
#randomPython中的random模块用于生成随机数。
#paddle.vision.datasets该模块包含数据加载的相关函数比如可以用来加载常用的数据集等如mnist。
#paddle.vision.transforms:该模块包含对图像进行转换的函数比如把HWC格式的图片转变成CHW模式的输入张量。也包含飞桨框架对于图像预处理的方式可以快速完成常见的图像预处理如调整色调、对比度图像大小等
#paddle.io.Dataset:高模块包含了飞桨框架数据加载方式可以“一键”完成数据的批加载与异步加载。
import os
import sys
import shutil
import numpy as np
import paddle
import random
from paddle.io import Dataset, DataLoader
from paddle.vision.datasets import DatasetFolder, ImageFolder
from paddle.vision import transforms as T

4.3 数据集准备

本次实验已经为大家提供好了数据集数据集存储在 “data/enhancement/” 文件夹下。

本次实验的数据预处理包括

1.生成txt文件
2.拆分训练集、验证集

4.3.1 生成txt文件

为什么要生成txt文件呢我们看到在数据集中每一个文件夹对应一个类别但是并没有一个txt 文件来指定标签label于是我们首先要生成txt文件

为了代码的整齐和简洁我们把数据集路径等参数配置在一个全局变量train_parameters中。其解释如下

  • 'train_data_dir'是提供的经增强后的原始训练集
  • 'test_image_dir'是提供的原始测试集
  • 'train_image_dir'和'eval_image_dir'是由原始训练集经拆分后生成的实际训练集和验证集
  • 'train_list_dir'和'test_list_dir'是生成的txt文件路径
  • 'saved_model' 存放训练结果的文件夹
'''
参数配置
'train_data_dir'是提供的经增强后的原始训练集
'test_image_dir'是提供的原始测试集
'train_image_dir'和'eval_image_dir'是由原始训练集经拆分后生成的实际训练集和验证集
'train_list_dir'和'test_list_dir'是生成的txt文件路径
'saved_model' 存放训练结果的文件夹
'''
train_parameters = {          
    'train_image_dir''./data/splitted_training_data/train_images',
    'eval_image_dir''./data/splitted_training_data/eval_images',
    'test_image_dir''./data/enhancement_data/test',
    'train_data_dir':'./data/enhancement_data/train',
    'train_list_dir':'./data/enhancement_data/train.txt',
    'test_list_dir':'./data/enhancement_data/test.txt',  
    'saved_model':'./saved_model/'
}
#数据集的4个类别标签
labels = ['R0''B1''M2''S3']
labels.sort()
#准备生成训练集文件名、标签名的txt文件
write_file_name = train_parameters[ 'train_list_dir']
#以写方式打开write_file_name文件
with open(write_file_name, "w"as write_file:
    #针对不同的分类标签分别录入
    for label in labels:
        #建立空列表用于保存图片名
        file_list = [] 
        #用于找到该标签路径下的所有图片.
        train_txt_dir = train_parameters[ 'train_data_dir']+'/'+label+'/'     
        for file_name in os.listdir(train_txt_dir):
            dir_name = label        
            temp_line = dir_name + '/' + file_name + '\t' + label + '\n'    # 例如"B1/101.png B1"
            write_file.write(temp_line)
    
#准备生成测试集文件名、标签名的txt文件
write_file_name = train_parameters[ 'test_list_dir']
#以写方式打开write_file_name文件
with open(write_file_name, "w"as write_file:
    #针对不同的分类标签分别录入
    for label in labels:
        #建立空列表用于保存图片名
        file_list = [] 
        #用于找到该标签路径下的所有图片.
        train_txt_dir = train_parameters[ 'test_image_dir']+'/'+label+'/'     
        for file_name in os.listdir(train_txt_dir):
            dir_name = label        
            temp_line = dir_name + '/' + file_name + '\t' + label + '\n'    # 例如"B1/101.png B1"
            write_file.write(temp_line)

以上步骤操作完之后就会在 data/enhancement_data/目录下生成 train.txt test.txt两个文件。


 
4.3.2 划分训练集和验证集
  • 我们已经有了训练集、测试集最好还要把训练集再次拆分从训练集中拆分出来一个验证集。
  • 这样我们训练的时候就可以用验证集来验证我们的模型训练效果通过实时的观察训练效果便于我们及时的调参。
#判断splitted_training_data文件夹是否存在如果不存在就新建一个
if not os.path.exists('data/splitted_training_data'):
    os.makedirs('data/splitted_training_data')
#定义一个函数来拆分训练集、验证集
def create_train_eval():
    '''
    划分训练集和验证集
    '''
    train_dir = train_parameters['train_image_dir']
    eval_dir = train_parameters['eval_image_dir']
    train_list_path = train_parameters['train_list_dir']  
    train_data_dir = train_parameters[ 'train_data_dir'
    
    print('creating training and eval images')
    #如果文件夹不存在建立相应的文件夹
    if not os.path.exists(train_dir):
        os.mkdir(train_dir)
    if not os.path.exists(eval_dir):
        os.mkdir(eval_dir) 
    #打开txt文件分割数据
    file_name = train_list_path
    f = open(file_name, 'r'
    #按行读取数据
    lines = f.readlines()
    f.close()
        
    for i in range(len(lines)):
        #将每行数据按照空格分割成2部分并取第一部分的路径名和图像文件名例如:R0/1.png
        img_path = lines[i].split('\t')[0
        #取第二部分的标签例如:R0
        class_label = lines[i].split('\t')[1].strip('\n')
        # 每8张图片取一个做验证数据,其他用于训练
        if i % 8 == 0:
            #把目录和文件名合成一个路径
            eval_target_dir = os.path.join(eval_dir, class_label) 
            #将总的文件路径与当前图像的文件名合到一起实际就是得到训练集图像所在的文件夹下的图像名   
            eval_img_path = os.path.join(train_data_dir, img_path)
            if not os.path.exists(eval_target_dir):
                    os.mkdir(eval_target_dir)  
            #将图片复制到验证集指定标签的文件夹下      
            shutil.copy(eval_img_path, eval_target_dir) 
        else:           
            train_target_dir = os.path.join(train_dir, class_label)                                 
            train_img_path = os.path.join(train_data_dir, img_path)
            if not os.path.exists(train_target_dir):
                os.mkdir(train_target_dir)
            shutil.copy(train_img_path, train_target_dir) 
    print ('划分训练集和验证集完成')
# 制作数据集如果已经做好了就请将代码注释掉
create_train_eval()
creating training and eval images
划分训练集和验证集完成

运行完上面的代码就完成了训练集、验证集的拆分。拆分放在 ./data/splitted_training_data/ 目录下


 

4.5 自定义数据集类

飞桨框架将一些我们常用的数据集做成了API对用户开放对应API为paddle.vision.datasets与paddle.text.datasets。我们使用的时候可以直接调用这些API就可以完成数据集的下载和使用。这些集成好的数据集有

  • 视觉相关数据集 ['DatasetFolder', 'ImageFolder', 'MNIST', 'FashionMNIST', 'Flowers', 'Cifar10', 'Cifar100', 'VOC2012']
  • 自然语言相关数据集 ['Conll05st', 'Imdb', 'Imikolov', 'Movielens', 'UCIHousing', 'WMT14', 'WMT16']

但是在实际的使用场景中我们往往需要用到自己的数据集。比如本次实验我们就使用自己的桃子数据集。

飞桨为用户提供了paddle.io.Dataset基类让用户通过类的集成来快速实现数据集定义。

PaddlePaddle对数据集的加载方式是统一使用Dataset数据集定义 + DataLoader多进程数据集加载。

数据集定义-Dataset
  • 首先我们先进行数据集的定义
  • 数据集定义主要是实现一个新的Dataset类继承父类paddle.io.Dataset
  • 然后实现父类中以下两个抽象方法“__ getitem __ ”和 “__ len __”


 
class PeachDataset(Dataset):
    """
    步骤一继承paddle.io.Dataset类
    """
    def __init__(self, mode='train'):
        """
        步骤二实现构造函数定义数据读取方式划分训练、验证和测试数据集
        """
        super(PeachDataset, self).__init__()
        train_image_dir = train_parameters['train_image_dir']#训练集的路径
        eval_image_dir = train_parameters['eval_image_dir']
        test_image_dir = train_parameters['test_image_dir']        
        
        '''         ''' 
        #transform数据增强函数这里仅对图片的打开方式进行了转换            
        #这里用Transpose()将图片的打开方式(宽, 高, 通道数)更改为PaddlePaddle读取的方式是(通道数, 宽, 高)
        mean = [127.5127.5127.5# 归一化均值
        std = [127.5127.5127.5# 归一化标注差 
        transform_train = T.Compose([T.ColorJitter(0.40.40.40.4)
                                     ,T.Resize(size=(224,224)) 
                                     ,T.Transpose()
                                     ,T.Normalize(mean, std)
                                    ])
        transform_eval = T.Compose([T.Resize(size=(224,224)) 
                                    ,T.Transpose()
                                    ,T.Normalize(mean, std)
                                    ])
        transform_test = T.Compose([T.Resize(size=(224,224)) 
                                    ,T.Transpose()
                                    ,T.Normalize(mean, std)
                                    ])
        
        '''         
        # 参考APIhttps://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/Overview_cn.html#about-transforms
        #这里用Transpose()将图片的打开方式(宽, 高, 通道数)更改为PaddlePaddle读取的方式是(通道数, 宽, 高)
        # ColorJitter 随机调整图像的亮度对比度饱和度和色调。
        # hflip 对输入图像进行水平翻转。        
        # Normalize 归一化。mean = [127.5, 127.5, 127.5]std = [127.5, 127.5, 127.5]
        # RandomHorizontalFlip 基于概率来执行图片的水平翻转。
        # RandomVerticalFlip 基于概率来执行图片的垂直翻转。
        mean = [127.5, 127.5, 127.5] # 归一化均值
        std = [127.5, 127.5, 127.5] # 归一化标注差 
        transform_train = T.Compose([T.Resize(size=(224,224)), 
                                     T.Transpose(),                                
                                     T.ColorJitter(0.4, 0.4, 0.4, 0.4),
                                     T.RandomHorizontalFlip(prob=0.5,),
                                     T.RandomVerticalFlip(prob=0.5,),
                                     T.Normalize(mean, std)])
        transform_eval = T.Compose([T.Resize(size=(224,224)), T.Transpose()])
        transform_test = T.Compose([T.Resize(size=(224,224)), T.Transpose()])
        ''' 
        #飞桨推荐使用 paddle.io.DataLoader 完成数据的加载生成一个可以加载数据的迭代器
        参考API:https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/io/DataLoader_cn.html#cn-api-fluid-io-dataloader
        #加载训练集train_data_folder 是一个迭代器
        参考APIhttps://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/datasets/DatasetFolder_cn.html#datasetfolder
        train_data_folder = DatasetFolder(train_image_dir, transform=transform_train)
        #加载验证集eval_data_folder 是一个迭代器
        eval_data_folder = DatasetFolder(eval_image_dir, transform=transform_eval)
        #加载测试集test_data_folder 是一个迭代器
        test_data_folder = DatasetFolder(test_image_dir, transform=transform_test)
        self.mode = mode
        if self.mode  == 'train':
            self.data = train_data_folder
        elif self.mode  == 'eval':
            self.data = eval_data_folder
        elif self.mode  == 'test':
            self.data = test_data_folder
    # 每次迭代时返回数据和对应的标签
    def __getitem__(self, index):
        """
        步骤三实现__getitem__方法定义指定index时如何获取数据并返回单条数据训练数据对应的标签
        """
        data = np.array(self.data[index][0]).astype('float32')
        label = np.array([self.data[index][1]]).astype('int64')
        return data, label
    # 返回整个数据集的总数
    def __len__(self):
        """
        步骤四实现__len__方法返回数据集总数目
        """
        return len(self.data)
#用自定义的PeachDataset类加载自己的数据集
train_dataset = PeachDataset(mode='train')
val_dataset = PeachDataset(mode='eval')
test_dataset = PeachDataset(mode='test')
数据集加载-DataLoader

DataLoader 返回一个迭代器迭代器返回的数据中的每个元素都是一个Tensor其调用方法如下

class paddle.io.DataLoader(dataset, feed_list=None, places=None, return_list=False, batch_sampler=None, batch_size=1, shuffle=False, drop_last=False, collate_fn=None, num_workers=0, use_buffer_reader=True, use_shared_memory=True, timeout=0, worker_init_fn=None) 

DataLoader 迭代一次给定的 dataset顺序由 batch_sampler 给定
DataLoader支持单进程多进程的数据加载方式当 num_workers 大于0时将使用多进程方式异步加载数据。

详细介绍 https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/io/DataLoader_cn.html

下面的代码用来展示如何使用 DataLoader

# DataLoader 示例代码
# 加载库
import cv2 as cv #使用 OpenCV
print("opencv 版本号为" + cv.__version__) #查看版本号
# 事实上在使用 OpenCV之前应该安装该类库但是由于使用了 AI-Studio所以系统已经替开发者预先安装好了 opencv-python 4.1.1.26       
from matplotlib import pyplot as plt #在该页面画图
%matplotlib inline 
# 构造一个 DataLoader
test_loader = DataLoader(test_dataset,
                    batch_size=2,
                    shuffle=True,
                    drop_last=True,
                    num_workers=2)
opencv 版本号为4.1.1
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/__init__.py:107: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import MutableMapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/rcsetup.py:20: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Iterable, Mapping
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/colors.py:53: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  from collections import Sized
# 使用 DataLoader 来遍历数据集
for mini_batch in test_loader(): # 从 DataLoader 中获取 mini_batch 
    print("mini_batch 的类型为" + str(type(mini_batch)))
    pic_list = mini_batch[0#图片数据
    label_list = mini_batch[1#标记
    print("mini_batch 的大小为" + str(len(pic_list)))
    # 将图片显示转化为 numpy 格式并且将内部的数字设置为 整数类型
    pic_1 = pic_list[0]
    pic_2 = pic_list[1]
    arr1 = np.asarray(pic_1, dtype=np.float64) 
    print(arr1.shape)
    arr2 = np.asarray(pic_2, dtype=np.float64)    
    print(arr2.shape)
    break #由于是示例所以仅拿出第一个 mini_batch
    
mini_batch 的类型为<class 'list'>
mini_batch 的大小为2
(3, 224, 224)
(3, 224, 224)
# 把获取到的图片数据展示出来
r = arr1[0]
g = arr1[1]
b = arr1[2]
img = cv.merge([r,g,b])
plt.imshow(img)
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/cbook/__init__.py:2349: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  if isinstance(obj, collections.Iterator):
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/cbook/__init__.py:2366: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  return list(data) if isinstance(data, collections.MappingView) else data
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
<matplotlib.image.AxesImage at 0x7fb4678d8350>

如上图所示这时候取得的图片并不是原始图片的样子。那是因为在 PeachDataset 类中对图片数据使用了 transform 方法也就是对图片做了一些变化。为了说明这一点可以使用下面的方法作对比。

下图为已经发生变化图片

为了对比说明请修改 PeachDataset 类中的代码加入一个注释符号代码如下
这样做的目的是既可以改变图片的大小而且符合paddle的要求又可以避免产生归一化引起的数据变动。

transform_test = T.Compose([ T.Resize(size=(224,224)) ,T.Transpose() #,T.Normalize(mean, std) ]) 

修改完该代码后请打开下面的代码的注释重启执行器点击右上角的 “运行->运行当前选中及之前的所有cell”

就可以看到原始数据集中的图片如下图所示

查看完数据后请不要忘记打开 PeachDataset 类中的注释。和加入下方代码的注释。

'''
# 把获取到的图片数据展示出来
arr1 = arr1 / 255 # 把每一个像素都变到 0-1 之间
r = arr1[0]
g = arr1[1]
b = arr1[2]
img = cv.merge([r,g,b])
plt.imshow(img)
'''

4.6 搭建分类模型

接下来我们就要搭建一个图像分类模型用这个模型可以实现桃子数据集的分类。

怎么搭建分类模型呢

  • 我们可以按照自己的想法搭建DNN网络模型或者CNN网络模型或者其他网络模型但是这对我们的算法研究能力要求很高
  • 我们可以使用已经成熟的、经典的网络模型比如VGG、ResNet等用paddle框架来搭建这些模型来为我们所用。

本次实验我们就采用50层的残差网络ResNet作为我们的分类模型。

并且本次实验为了增加我们的模型效果我们还是使用了迁移学习方法。那么为什么要用迁移学习呢怎么使用迁移学习呢

4.6.1 迁移学习

现实的工程开发中很少有人从零开始训练一个完整的神经网络。

为什么

因为一般我们的数据集都不是很大所以训练出的模型泛化能力往往不强。且训练非常耗时。

怎么做

常用的方法是找到一个很大的公有数据集比如ImageNet包含了120万张图片和1000个类别在这个数据集上先训练好一个神经网络模型A这个A一般别人已经训练好了然后将这个A作为一个起始点经过微调再训练我们自己的数据集。这个A也叫做 “预训练模型”

这就是 迁移学习 的一种方法也叫做 fine tune

那么fine tune的理论依据是什么也即是为什么我们可以在别人训练好的模型的基础上进行微调这就要从卷积神经网络的结构原理上进行分析。

  • 对于卷积网络来说前面几层都学习到的是通用的特征generalfeature比如图像的边缘随着网络层次的加深后面的网络更偏重于学习特定的特征specific feature例如身体部位、面部和其他组合性特征。
  • 最后的全连接层通常被认为是捕获了与解决相应任务相关的信息例如 AlexNet 的全连接层可以指出提取的这些特征属于1000 类物体中的哪一类。
  • 比如在人脸识别过程中初级的若干层卷积会提取到直线、曲线等通用特征中间若干层卷积会进一步学习到眼睛、鼻子等特定部位高层卷积则可以学习到组合特征从而判断出这是一张人脸图像。 -卷积神经网络的这种特性就是我们的fine tune理论依据。

可能有同学会问那为什么我们不直接用别人在大数据集比如ImageNet上训练好的模型而是还要微调呢

  • 因为别人训练好的模型可能并不是完全适用于我们自己的任务。可能别人的网络能做比我们的任务更多的事情可能别人的网络比较复杂我们的任务比较简单。 -举一个例子假如我们想训练一个猫狗图像二分类的网络我们首先会想到直接使用别人在 ImageNet上训练好的网络模型。但是 ImageNet 有 1000 个类别而我们只需要2 个类别。此时就需要针对我们自己的任务来进行微调了比如可以固定原始网络的相关层修改网络的输出层以使结果更符合我们的需要。

在PaddlePaddle2.0中使用预训练模型只需要设定模型参数pretained=True。

4.6.2 搭建模型

使用飞桨很便利的一点是飞桨框架内置了许多模型真正的一行代码实现深度学习模型。

目前飞桨框架内置的模型都是CV领域的模型在paddle.vision.models目录下具体包含如下的模型

飞桨框架内置模型 ['ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', 'resnet152', 'VGG', 'vgg11', 'vgg13', 'vgg16', 'vgg19', 'MobileNetV1', 'mobilenet_v1', 'MobileNetV2', 'mobilenet_v2', 'LeNet']

比如我们本次使用的resnet50就已经有内置模型了。


 
# 使用内置的模型,这边可以选择多种不同网络这里选了resnet50网络
#pretrained (bool可选) - 是否加载在imagenet数据集上的预训练权重
model = paddle.vision.models.resnet18(pretrained=True, num_classes=4)    
#尝试不同的网络结构MobileNetV2
MobileNetV2参考文档https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/models/MobileNetV2_cn.html
# model = paddle.vision.models.mobilenet_v2(pretrained=True, num_classes=4)    
#使用paddle.Model完成模型的封装将网络结构组合成一个可快速使用高层API进行训练和预测的类。
model = paddle.Model(model)
100%|██████████| 69183/69183 [00:01<00:00, 47292.60it/s]
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dygraph/layers.py:1301: UserWarning: Skip loading for fc.weight. fc.weight receives a shape [512, 1000], but the expected shape is [512, 4].
  warnings.warn(("Skip loading for {}. ".format(key) + str(err)))
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dygraph/layers.py:1301: UserWarning: Skip loading for fc.bias. fc.bias receives a shape [1000], but the expected shape is [4].
  warnings.warn(("Skip loading for {}. ".format(key) + str(err)))

使用 model.summary 观察网络情况

参考文档 https://github.com/PaddlePaddle/Paddle/blob/release/2.1/python/paddle/hapi/model.py#L883

API 文档写的和真实代码之间稍有不同

# 以下为源代码 def summary(self, input_size=None, dtype=None): """Prints a string summary of the network. Args: input_size (tuple|InputSpec|list[tuple|InputSpec], optional): size of input tensor. if not set, input_size will get from ``self._inputs`` if network only have one input, input_size can be tuple or InputSpec. if model have multiple input, input_size must be a list which contain every input's shape. Default: None. dtypes (str, optional): if dtypes is None, 'float32' will be used, Default: None. Returns: Dict: a summary of the network including total params and total trainable params. Examples: .. code-block:: python import paddle from paddle.static import InputSpec input = InputSpec([None, 1, 28, 28], 'float32', 'image') label = InputSpec([None, 1], 'int64', 'label') model = paddle.Model(paddle.vision.models.LeNet(), input, label) optim = paddle.optimizer.Adam( learning_rate=0.001, parameters=model.parameters()) model.prepare( optim, paddle.nn.CrossEntropyLoss()) params_info = model.summary() print(params_info) """ assert (input_size is not None or self._inputs is not None ), "'input_size' or 'self._input' must be set" if input_size is not None: _input_size = input_size else: _input_size = self._inputs return summary(self.network, _input_size, dtype) 

参数

  • input_size (tuple|InputSpec|list) - 输入张量的大小。如果网络只有一个输入那么该值需要设定为tuple或InputSpec。如果模型有多个输入。那么该值需要设定为list[tuple|InputSpec]包含每个输入的shape。如果该值没有设置会将 self._inputs 作为输入。默认值None。
  • dtypes (str可选) - 输入张量的数据类型如果没有给定默认使用 float32 类型。默认值None。

返回字典。包含网络全部参数的大小和全部可训练参数的大小。

# 使用 summary 观察网络信息
model.summary(input_size=(13224224), dtype='float32'
-------------------------------------------------------------------------------
   Layer (type)         Input Shape          Output Shape         Param #    
===============================================================================
     Conv2D-1        [[1, 3, 224, 224]]   [1, 64, 112, 112]        9,408     
   BatchNorm2D-1    [[1, 64, 112, 112]]   [1, 64, 112, 112]         256      
      ReLU-1        [[1, 64, 112, 112]]   [1, 64, 112, 112]          0       
    MaxPool2D-1     [[1, 64, 112, 112]]    [1, 64, 56, 56]           0       
     Conv2D-2        [[1, 64, 56, 56]]     [1, 64, 56, 56]        36,864     
   BatchNorm2D-2     [[1, 64, 56, 56]]     [1, 64, 56, 56]          256      
      ReLU-2         [[1, 64, 56, 56]]     [1, 64, 56, 56]           0       
     Conv2D-3        [[1, 64, 56, 56]]     [1, 64, 56, 56]        36,864     
   BatchNorm2D-3     [[1, 64, 56, 56]]     [1, 64, 56, 56]          256      
   BasicBlock-1      [[1, 64, 56, 56]]     [1, 64, 56, 56]           0       
     Conv2D-4        [[1, 64, 56, 56]]     [1, 64, 56, 56]        36,864     
   BatchNorm2D-4     [[1, 64, 56, 56]]     [1, 64, 56, 56]          256      
      ReLU-3         [[1, 64, 56, 56]]     [1, 64, 56, 56]           0       
     Conv2D-5        [[1, 64, 56, 56]]     [1, 64, 56, 56]        36,864     
   BatchNorm2D-5     [[1, 64, 56, 56]]     [1, 64, 56, 56]          256      
   BasicBlock-2      [[1, 64, 56, 56]]     [1, 64, 56, 56]           0       
     Conv2D-7        [[1, 64, 56, 56]]     [1, 128, 28, 28]       73,728     
   BatchNorm2D-7     [[1, 128, 28, 28]]    [1, 128, 28, 28]         512      
      ReLU-4         [[1, 128, 28, 28]]    [1, 128, 28, 28]          0       
     Conv2D-8        [[1, 128, 28, 28]]    [1, 128, 28, 28]       147,456    
   BatchNorm2D-8     [[1, 128, 28, 28]]    [1, 128, 28, 28]         512      
     Conv2D-6        [[1, 64, 56, 56]]     [1, 128, 28, 28]        8,192     
   BatchNorm2D-6     [[1, 128, 28, 28]]    [1, 128, 28, 28]         512      
   BasicBlock-3      [[1, 64, 56, 56]]     [1, 128, 28, 28]          0       
     Conv2D-9        [[1, 128, 28, 28]]    [1, 128, 28, 28]       147,456    
   BatchNorm2D-9     [[1, 128, 28, 28]]    [1, 128, 28, 28]         512      
      ReLU-5         [[1, 128, 28, 28]]    [1, 128, 28, 28]          0       
     Conv2D-10       [[1, 128, 28, 28]]    [1, 128, 28, 28]       147,456    
  BatchNorm2D-10     [[1, 128, 28, 28]]    [1, 128, 28, 28]         512      
   BasicBlock-4      [[1, 128, 28, 28]]    [1, 128, 28, 28]          0       
     Conv2D-12       [[1, 128, 28, 28]]    [1, 256, 14, 14]       294,912    
  BatchNorm2D-12     [[1, 256, 14, 14]]    [1, 256, 14, 14]        1,024     
      ReLU-6         [[1, 256, 14, 14]]    [1, 256, 14, 14]          0       
     Conv2D-13       [[1, 256, 14, 14]]    [1, 256, 14, 14]       589,824    
  BatchNorm2D-13     [[1, 256, 14, 14]]    [1, 256, 14, 14]        1,024     
     Conv2D-11       [[1, 128, 28, 28]]    [1, 256, 14, 14]       32,768     
  BatchNorm2D-11     [[1, 256, 14, 14]]    [1, 256, 14, 14]        1,024     
   BasicBlock-5      [[1, 128, 28, 28]]    [1, 256, 14, 14]          0       
     Conv2D-14       [[1, 256, 14, 14]]    [1, 256, 14, 14]       589,824    
  BatchNorm2D-14     [[1, 256, 14, 14]]    [1, 256, 14, 14]        1,024     
      ReLU-7         [[1, 256, 14, 14]]    [1, 256, 14, 14]          0       
     Conv2D-15       [[1, 256, 14, 14]]    [1, 256, 14, 14]       589,824    
  BatchNorm2D-15     [[1, 256, 14, 14]]    [1, 256, 14, 14]        1,024     
   BasicBlock-6      [[1, 256, 14, 14]]    [1, 256, 14, 14]          0       
     Conv2D-17       [[1, 256, 14, 14]]     [1, 512, 7, 7]       1,179,648   
  BatchNorm2D-17      [[1, 512, 7, 7]]      [1, 512, 7, 7]         2,048     
      ReLU-8          [[1, 512, 7, 7]]      [1, 512, 7, 7]           0       
     Conv2D-18        [[1, 512, 7, 7]]      [1, 512, 7, 7]       2,359,296   
  BatchNorm2D-18      [[1, 512, 7, 7]]      [1, 512, 7, 7]         2,048     
     Conv2D-16       [[1, 256, 14, 14]]     [1, 512, 7, 7]        131,072    
  BatchNorm2D-16      [[1, 512, 7, 7]]      [1, 512, 7, 7]         2,048     
   BasicBlock-7      [[1, 256, 14, 14]]     [1, 512, 7, 7]           0       
     Conv2D-19        [[1, 512, 7, 7]]      [1, 512, 7, 7]       2,359,296   
  BatchNorm2D-19      [[1, 512, 7, 7]]      [1, 512, 7, 7]         2,048     
      ReLU-9          [[1, 512, 7, 7]]      [1, 512, 7, 7]           0       
     Conv2D-20        [[1, 512, 7, 7]]      [1, 512, 7, 7]       2,359,296   
  BatchNorm2D-20      [[1, 512, 7, 7]]      [1, 512, 7, 7]         2,048     
   BasicBlock-8       [[1, 512, 7, 7]]      [1, 512, 7, 7]           0       
AdaptiveAvgPool2D-1   [[1, 512, 7, 7]]      [1, 512, 1, 1]           0       
     Linear-1            [[1, 512]]             [1, 4]             2,052     
===============================================================================
Total params: 11,188,164
Trainable params: 11,168,964
Non-trainable params: 19,200
-------------------------------------------------------------------------------
Input size (MB): 0.57
Forward/backward pass size (MB): 57.04
Params size (MB): 42.68
Estimated Total Size (MB): 100.30
-------------------------------------------------------------------------------

{'total_params': 11188164, 'trainable_params': 11168964}
# 调用Paddle的VisualDL模块保存信息到目录中。
#log_dir (str) - 输出日志保存的路径。
callback = paddle.callbacks.VisualDL(log_dir='visualdl_log_dir')

4.6.3 训练配置

优化器配置

用paddle.Model完成模型的封装后在训练前需要对模型进行配置通过Model.prepare接口来对训练进行提前的配置准备工作包括设置模型优化器Loss计算方法精度计算方法等。

 
  • 学习率(learning_rate)参数很重要。
  • 如果训练过程中的准确率呈震荡状态忽大忽小可以试试把学习率调低
#通过Model.prepare接口来对训练进行提前的配置准备工作包括设置模型优化器Loss计算方法精度计算方法等
# 优化器API文档 https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/optimizer/Overview_cn.html#paddle-optimizer
# 学习率衰减策略
# 学习率衰减策略 API 文档https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/optimizer/Overview_cn.html#about-lr
scheduler_StepDecay = paddle.optimizer.lr.StepDecay(learning_rate=0.1, step_size=50, gamma=0.9, verbose=False)
scheduler_PiecewiseDecay = paddle.optimizer.lr.PiecewiseDecay(boundaries=[1001000400050006000], values=[0.10.50.010.005], verbose=False)
# 尝试使用 SGD、Momentum 方法
sgd = paddle.optimizer.SGD(
                learning_rate=scheduler_StepDecay, 
                parameters=model.parameters())
adam = paddle.optimizer.Adam( 
                learning_rate=0.01#调参
                parameters=model.parameters())
model.prepare(optimizer= adam, # adam
              loss=paddle.nn.CrossEntropyLoss(),
              metrics=paddle.metric.Accuracy())
计算资源配置

设置该次计算使用的具体计算资源。
首先可以查看当前使用的计算设备。此步骤不是必须的
然后设置本次训练使用的计算设备。

# 查看当前计算设备
device = paddle.device.get_device()
print(device)
# 使用GPU训练
device = paddle.set_device('gpu'# or 'cpu'
print(device)

4.6.4 训练模型

做好模型训练的前期准备工作后我们正式调用fit()接口来启动训练过程需要指定以下至少3个关键参数训练数据集训练轮次和单次训练数据批次大小。


 

训练时间说明

  • 在CPU上运行10个epoch需要1.5小时左右
  • 在GPU上运行10个epoch需要30分钟左右
# fit API文档 https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/Model_cn.html#fit-train-data-none-eval-data-none-batch-size-1-epochs-1-eval-freq-1-log-freq-10-save-dir-none-save-freq-1-verbose-2-drop-last-false-shuffle-true-num-workers-0-callbacks-none
# 启动模型训练指定训练数据集设置训练轮次设置每次数据集计算的批次大小设置日志格式
#epochs总共训练的轮数
#batch_size一个批次的样本数量
#如果提示内存不足可以尝试将batch_size调低
#verbose日志显示0为不在标准输出流输出日志信息,1为输出进度条记录2为每个epoch输出一行记录;1为输出进度条记录2为每个epoch输出一行记录
model.fit(train_dataset,
          val_dataset,
          epochs=1,
          batch_size=2,
          callbacks=callback,
          verbose=1)
The loss value printed in the log is the current step, and the metric is the average value of previous steps.
Epoch 1/1
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/layers/utils.py:77: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  return (isinstance(seq, collections.Sequence) and
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/nn/layer/norm.py:641: UserWarning: When training, we now always track global mean and variance.
  "When training, we now always track global mean and variance.")
step 2904/2904 [==============================] - loss: 0.0549 - acc: 0.5786 - 62ms/step         
Eval begin...
step 415/415 [==============================] - loss: 0.0867 - acc: 0.7819 - 23ms/step        
Eval samples: 830

4.6.5 模型评估和保存

模型训练结束后我们得到了一个训练好的模型但是这个模型效果怎么样还需要我们去具体做下评估。

什么是模型评估呢

  • 模型评估其实就是使用我们预留的测试数据放到所得到的模型中进行实际的预测并基于标签进行校验来看模型在测试集上的表现。
  • 模型评估的代码实现在高层API中也非常地简单我们事先定义好用于评估使用的数据集后对于训练好的模型进行评估操作可以使用model.evaluate接口操作结束后会根据prepare接口配置的loss和metric来进行相关指标计算返回。

本实验评价指标

本次实验我们采用的评价指标是 准确率accuracy简称acc

同学们相互之间比较一下你的模型评估结果怎么样你的acc值达到多少了

该实验如果进行了合理的 数据增强准确率 accuracy是可以达到很高的请大家努力把acc值提升到90%以上。

#模型评估
#对于训练好的模型进行评估操作可以使用 model.evaluate 接口操作结束后会根据 prepare 接口配置的 loss 和 metric 来进行相关指标计算返回。
评价指标参考文档https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/Model_cn.html#evaluate-eval-data-batch-size-1-log-freq-10-verbose-2-num-workers-0-callbacks-none
model.evaluate(test_dataset, verbose=1)
Eval begin...
step 67/67 [==============================] - loss: 0.0052 - acc: 0.7910 - 13ms/step        
Eval samples: 67
{'loss': [0.0051732725], 'acc': 0.7910447761194029}
#模型保存
model.save('./saved_model/saved_model')  # save for training

4.6.6 模型预测

以上步骤我们完成了模型的训练、模型的评估、模型保存如果这个模型经过评估之后效果不错那么就可以使用了。我们就可以使用这个保存的模型来进行预测。

如何进行模型预测呢

  • 飞桨高层API中提供了model.predict接口来方便用户对训练好的模型进行预测
  • 我们只需要将“预测数据+保存的模型”放到model.predict接口进行计算即可接口会把模型计算得到的预测结果返回从而完成我们的任务。
#预测模型
results = model.predict(test_dataset)
Predict begin...
step 67/67 [==============================] - 12ms/step         
Predict samples: 67
# 观察 result
print(type(results)) #list
print(len(results)) #len == 1
# 一行一行打印结果
for i in results[0]:
    print(i)
<class 'list'>
1
[[ 0.4494054   1.8589294  -2.709025   -0.98785317]]
[[ 0.80108535  2.0312922  -2.3985271  -1.667168  ]]
[[-0.487098    2.5169828  -3.8384209   0.09941977]]
[[ 1.1755923  1.9356494 -2.7956083 -1.824508 ]]
[[ 0.6587918  1.5227697 -1.9370861 -1.2466118]]
[[ 1.9423198  1.8514836 -2.0579038 -3.0512297]]
[[-0.12070499  2.1658874  -3.2705145  -0.2214822 ]]
[[ 2.30185    1.9300838 -2.6378424 -3.3231502]]
[[ 1.7931688  1.7564571 -2.713827  -2.3772974]]
[[ 1.018136   1.9348547 -2.1037087 -2.093875 ]]
[[ 1.2455556  1.7356219 -2.3573794 -1.9229555]]
[[ 1.3166553  2.0454793 -2.1393437 -2.5154655]]
[[ 2.2485528  2.5826378 -2.3228188 -4.113832 ]]
[[ 0.6856951  1.9657588 -2.340539  -1.5627216]]
[[ 0.34038985  2.5555618  -3.4037375  -1.1876322 ]]
[[ 1.7155951  2.2181606 -2.2069125 -3.0874062]]
[[-0.9589406  2.3568041 -3.914858   0.8861027]]
[[-2.2687616  3.561953  -6.1434994  2.204158 ]]
[[-0.8965972   2.812673   -4.498936    0.67248255]]
[[-1.7266133  3.0567627 -5.3219457  1.823607 ]]
[[-1.2236824  2.9153998 -5.2624416  1.1972692]]
[[-1.6313993  2.393093  -4.390437   1.8520648]]
[[-2.261466   3.1709478 -5.7391357  2.475055 ]]
[[-2.0998657  2.7529852 -5.1272326  2.396462 ]]
[[-1.6497151  2.9010382 -5.0573497  1.7648369]]
[[-2.6754675  2.9362612 -5.56551    2.9678605]]
[[-1.073315   2.3352654 -4.07773    1.1857122]]
[[-0.88414484  2.4533503  -4.0443926   0.775055  ]]
[[-1.7560171  3.3508494 -5.375548   1.4013046]]
[[-2.615417   4.013784  -6.8865647  2.4297483]]
[[-1.829337   3.1974657 -5.3266735  1.5116838]]
[[-1.1488906  2.4435222 -4.151718   1.1106087]]
[[-2.672726   3.7604275 -6.60363    2.6530373]]
[[-1.3436769  2.810868  -4.783174   1.3363845]]
[[-7.1727552 -4.178957   6.645717   1.3258969]]
[[-10.802859   -8.898961   13.038587    0.8829916]]
[[-6.100724  -3.6756551  5.3887143  2.429795 ]]
[[-6.956199  -4.8285522  7.192293   1.4987972]]
[[-6.806343  -4.737133   7.0949545  1.9803424]]
[[-10.631139   -8.797351   12.851841    0.9559243]]
[[-9.890509  -7.7998743 11.965744   1.0906614]]
[[-6.637445  -4.125729   6.246958   2.3932679]]
[[-4.850948   -3.7300088   5.50579    -0.28020984]]
[[-5.89312   -3.9382315  5.5570445  1.115171 ]]
[[-9.489717  -7.5113807 11.062157   1.4899993]]
[[-4.060526  -4.7304277  7.44195   -1.7170902]]
[[-6.123046  -5.145837   7.891695  -0.3783728]]
[[-6.7471647  -5.1568007   7.3376994  -0.14631017]]
[[-5.768033  -6.0288777  9.360904  -1.9037125]]
[[-7.037687  -5.0647235  7.345336   1.0650041]]
[[-6.3333025 -4.003666   6.096233   2.0686429]]
[[-8.165305  -4.0971665  5.59594    4.208836 ]]
[[-6.3591156 -0.0809775 -2.1494312  5.8446784]]
[[-5.998541  -0.3071279 -1.633659   5.444659 ]]
[[-5.982375   -0.13737446 -2.0219755   5.588227  ]]
[[-6.2784123  -0.28474385 -1.8074901   5.720227  ]]
[[-5.9097333   0.21499354 -2.4844441   5.4800773 ]]
[[-5.815046    0.34615326 -2.749436    5.516311  ]]
[[-6.144201    0.20839332 -2.5092714   5.6507225 ]]
[[-6.217258   -0.11974069 -2.2099724   5.8341565 ]]
[[-6.0395765   0.08458082 -2.2998967   5.641852  ]]
[[-6.292765   -0.22815469 -1.8958219   5.7871137 ]]
[[-5.9349203   0.03097157 -2.209548    5.578063  ]]
[[-4.8454432  0.6837326 -2.8405902  4.569208 ]]
[[-5.5436296 -0.4322207 -1.2610528  5.0055714]]
[[-5.8578863  -0.32924837 -1.6607574   5.3581743 ]]
[[-5.7073674   0.08094054 -2.3335297   5.431057  ]]
# 将结果用 softmax 处理后变成概率值
x = paddle.to_tensor(results[0])
m = paddle.nn.Softmax()
out = m(x)
print(out)
Tensor(shape=[67, 1, 4], dtype=float32, place=CUDAPlace(0), stop_gradient=True,
       [[[0.18607847, 0.76180643, 0.00790692, 0.04420818]],

        [[0.21990354, 0.75249618, 0.00896723, 0.01863303]],

        [[0.04347746, 0.87683898, 0.00152336, 0.07816018]],

        [[0.31181487, 0.66678441, 0.00587796, 0.01552279]],

        [[0.27809274, 0.65979725, 0.02074026, 0.04136980]],

        [[0.51592660, 0.47112727, 0.00944741, 0.00349878]],

        [[0.08482961, 0.83483732, 0.00363582, 0.07669736]],

        [[0.58813888, 0.40553078, 0.00420919, 0.00212116]],

        [[0.50240386, 0.48429418, 0.00554229, 0.00775965]],

        [[0.27857813, 0.69674349, 0.01227855, 0.01239989]],

        [[0.37013263, 0.60421354, 0.01008376, 0.01557007]],

        [[0.31991184, 0.66306269, 0.01009506, 0.00693045]],

        [[0.41515639, 0.57983309, 0.00429428, 0.00071625]],

        [[0.21048497, 0.75708681, 0.01020809, 0.02222010]],

        [[0.09612054, 0.88075089, 0.00227385, 0.02085473]],

        [[0.37300166, 0.61655551, 0.00738223, 0.00306051]],

        [[0.02863417, 0.78866822, 0.00148986, 0.18120776]],

        [[0.00232973, 0.79350960, 0.00004836, 0.20411235]],

        [[0.02143462, 0.87504727, 0.00058431, 0.10293392]],

        [[0.00643685, 0.76924914, 0.00017670, 0.22413737]],

        [[0.01332989, 0.83638644, 0.00023486, 0.15004875]],

        [[0.01116226, 0.62454951, 0.00070716, 0.36358106]],

        [[0.00290894, 0.66527551, 0.00008983, 0.33172569]],

        [[0.00456953, 0.58538061, 0.00022136, 0.40982854]],

        [[0.00792769, 0.75078166, 0.00026256, 0.24102813]],

        [[0.00179510, 0.49116838, 0.00009976, 0.50693673]],

        [[0.02448242, 0.73991507, 0.00121354, 0.23438902]],

        [[0.02903091, 0.81717736, 0.00123135, 0.15256041]],

        [[0.00527186, 0.87065840, 0.00014126, 0.12392850]],

        [[0.00109510, 0.82885396, 0.00001529, 0.17003568]],

        [[0.00550288, 0.83888549, 0.00016662, 0.15544505]],

        [[0.02129946, 0.77363062, 0.00105744, 0.20401244]],

        [[0.00120668, 0.75071740, 0.00002368, 0.24805219]],

        [[0.01260382, 0.80315262, 0.00040434, 0.18383917]],

        [[0.00000099, 0.00001980, 0.99510950, 0.00486970]],

        [[0.00000000, 0.00000000, 0.99999475, 0.00000526]],

        [[0.00000973, 0.00011000, 0.95056945, 0.04931074]],

        [[0.00000071, 0.00000600, 0.99663687, 0.00335647]],

        [[0.00000091, 0.00000722, 0.99401951, 0.00597238]],

        [[0.00000000, 0.00000000, 0.99999321, 0.00000682]],

        [[0.00000000, 0.00000000, 0.99998105, 0.00001892]],

        [[0.00000248, 0.00003062, 0.97920632, 0.02076050]],

        [[0.00003168, 0.00009718, 0.99681073, 0.00306045]],

        [[0.00001052, 0.00007432, 0.98827934, 0.01163586]],

        [[0.00000000, 0.00000001, 0.99993038, 0.00006964]],

        [[0.00001010, 0.00000517, 0.99987948, 0.00010525]],

        [[0.00000082, 0.00000218, 0.99974102, 0.00025600]],

        [[0.00000076, 0.00000375, 0.99943382, 0.00056168]],

        [[0.00000027, 0.00000021, 0.99998665, 0.00001282]],

        [[0.00000057, 0.00000407, 0.99812609, 0.00186927]],

        [[0.00000393, 0.00004036, 0.98245114, 0.01750455]],

        [[0.00000084, 0.00004937, 0.80008936, 0.19986045]],

        [[0.00000500, 0.00266204, 0.00033643, 0.99699652]],

        [[0.00001068, 0.00316434, 0.00083980, 0.99598515]],

        [[0.00000940, 0.00324916, 0.00049351, 0.99624795]],

        [[0.00000613, 0.00245906, 0.00053635, 0.99699843]],

        [[0.00001125, 0.00514054, 0.00034567, 0.99450254]],

        [[0.00001192, 0.00565004, 0.00025565, 0.99408239]],

        [[0.00000751, 0.00430946, 0.00028455, 0.99539846]],

        [[0.00000582, 0.00258814, 0.00032005, 0.99708599]],

        [[0.00000841, 0.00384306, 0.00035409, 0.99579442]],

        [[0.00000566, 0.00243412, 0.00045929, 0.99710089]],

        [[0.00000996, 0.00388200, 0.00041306, 0.99569499]],

        [[0.00007983, 0.02011120, 0.00059271, 0.97921628]],

        [[0.00002605, 0.00432196, 0.00188679, 0.99376523]],

        [[0.00001340, 0.00337382, 0.00089095, 0.99572182]],

        [[0.00001447, 0.00472310, 0.00042231, 0.99484009]]])
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/tensor/creation.py:125: DeprecationWarning: `np.object` is a deprecated alias for the builtin `object`. To silence this warning, use `object` by itself. Doing this will not modify any behavior and is safe. 
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
  if data.dtype == np.object:

为了观察预测结果我们还需要把标签转换一下


 
#用一个字典指名标签对应的数值
label_dic = {}
for i, label in enumerate(labels):
    label_dic[i] = label
#预测标签结果写入predict_labels
predict_labels = []
#依次取results[0]中的每个图片的预测数组
for result in results[0]: 
    #np.argmax:返回一个numpy数组中的最大值的索引
    #注意索引是标签不是返回数据的最大值
    lab_index = np.argmax(result)
    lab = label_dic[lab_index]
    predict_labels.append(lab)
#看一下预测结果
print(predict_labels)
['M2', 'M2', 'M2', 'M2', 'M2', 'B1', 'M2', 'B1', 'B1', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'S3', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'M2', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'R0', 'S3', 'S3', 'S3', 'S3', 'S3', 'S3', 'S3', 'S3', 'S3', 'S3', 'S3', 'S3', 'S3', 'S3', 'S3']

为了更直观的观察预测效果我们生成一个result.csv文件把预测的结果列在这个csv文件里运行下面这段代码就会在当前目录下生成一个result.csv文件。

打开result.csv文件我们可以看到结果


 
final_result = [ ]
file_name_test = train_parameters['test_list_dir'
f = open(file_name_test, 'r'
#按行读取数据
data = f.readlines()
for i in range(len(data)):
    #将每行数据按照空格分割成2部分并取第一部分的路径名和图像文件名例如:R0/1.png
    img_path = data[i].split('\t')[0]
    final_result.append(img_path + ',' + str(predict_labels[i]) + '\n')
f.close( )
with open('result.csv',"w"as f: 
    f.writelines(final_result)

5.总结

本次实验我们用paddlepaddle飞桨深度学习框架 搭建了一个 图像分类模型完成了桃子的分类任务。

通过本次实验我们学习了

  • paddlepaddle深度学习框架的使用方法
  • 如何用paddlepaddle深度学习框架搭建 桃子分类模型
  • 如何完成模型的训练、评估、保存、预测等深度学习工作过程

图像分类任务是计算机视觉CV领域的基础性任务虽然难度不大但是却很重要是其他计算机视觉任务的基石。我们一定要多动手多调试代码增加熟练程度为更复杂的深度学习项目打下基础。

文章部分转载于 图像分类-桃子分拣 - 飞桨AI Studio星河社区 (baidu.com)其他部分均由本来结合整理而来。各位喜欢本文的请 点赞收藏加关注哦

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

“计算机视觉——飞桨深度学习实战-图像分类算法原理与实战-CSDN博客” 的相关文章