医生省力系列:基于飞桨完成癌症速检(100%精度)

一、项目简介

  在医学上,癌(cancer)是指起源于上皮组织的恶性肿瘤,是恶性肿瘤中最常见的一类。相对应的,起源于间叶组织的恶性肿瘤统称为肉瘤。有少数恶性肿瘤不按上述原则命名,如肾母细胞瘤、恶性畸胎瘤等。一般人们所说的“癌症”习惯上泛指所有恶性肿瘤。癌症具有细胞分化和增殖异常、生长失去控制、浸润性和转移性等生物学特征,其发生是一个多因子、多步骤的复杂过程,分为致癌、促癌、演进三个过程,与吸烟、感染、职业暴露、环境污染、不合理膳食、遗传因素密切相关。数据统计,几乎全球每两个人中,有一人就是癌症患者,每天医院接受的癌症患者也很多,给医生增加了很大的负担,于是我们想到,是否可以设计出一个小助手,在病人较多、工作量较大时,将癌症患者和正常人进行区分,为医生减少工作时间

二、PaddleClas介绍

  众所周知,PaddleClas是飞桨研发专门针对实现图像分类的套件,PaddleClas中包括了多达23个系列的模型结构、ImageNet1k验证集精度82.4%的ResNet50_vd模型、10W类的图像分类预训练模型,同时提供了知识蒸馏以及丰富的数据增广方案,帮助图像分类模型进一步获得性能提升。

图像分类任务简介

  图像分类是根据图像的语义信息将不同类别图像区分开来,是计算机视觉中重要的基础任务。
  图像分类在很多领域有广泛应用,包括零售商品分类、农作物品质分级、医学领域的图像识别、交通标志分类等。作为计算机视觉任务的基础,还可辅助目标检测、图像分割、物体跟踪、行为分析等其他高层视觉任务组网和效果提升,比如经典的目标检测模型FasterRCNN的结构,骨干网络即使用分类任务中的网络结构,如ResNet。作为骨干网络,分类任务的预训练模型也可以帮助其他视觉任务在训练时更快地收敛。

如果有想更详细了解的小伙伴可以戳下方链接慢慢了解:

PaddleClas github地址:

https://github.com/PaddlePaddle/PaddleClas/

PaddleClas教程文档地址:

https://paddleclas.readthedocs.io/zh_CN/latest/index.html

解压PaddleClas

PaddleClas需要大家从github上clone下载,速度有时会比较慢,这里已经为大家准备好了,放在data目录下,大家自己解压使用就好,如果有想克隆的小伙伴可以自己克隆,代码如下:git clone https://github.com/PaddlePaddle/PaddleClas.git
!unzip -oq /home/aistudio/data/data118240/PaddleClas.zip

三、数据集介绍

此数据集包含 25,000个组织病理学图像,共 5 个类。所有图像的大小均为 768 x 768 像素,并且采用 jpeg 文件格式。图像来自符合 HIPAA 并经验证的源的原始样本, 包括750张肺组织(250个良性肺组织、250个肺腺癌和250个肺鳞状细胞癌)和500张结肠组织(250个良性结肠组织和250个结肠腺癌)的总图像,并使用包装增加到25,000张。

数据集中有五个类,每个类别有 5,000 个图像,分别是:肺良性组织(Lung_n)、肺腺癌(Lung_aca)、肺鳞状细胞癌(Lung_scc)、结肠腺癌(Colon_adenocarcinoma)、结肠良性组织(Colon_n)

  • 需要注意的是:
    • 数据集存放路径要与配置文件中一直,不要忘记修改
    • 数据列表文件中路径与标签之间的空格要划分准确
    • 数据列表文件中不要包含其他文件

解压数据集

下面我们开始解压数据集,并且将数据集移动到PaddleClas/dataset下,这样修改的配置文件较少
!unzip -oq /home/aistudio/data/data117240/archive.zip
!mv archive PaddleClas/dataset
from PIL import Image

# 读取图片
png_img = Image.open('PaddleClas/dataset/archive/colon_aca/colonca1005.jpeg')
png_img  # 展示图片

四、 数据处理

大家不难发现,数据集的格式是每类图片集的文件名就是图片的lable,而且为了模拟真实的情况,所有的文件都没有标注,需要我们进行数据集和验证集的划分,同时PaddleClas里面也有大量的类如图像增广的处理方法,所以不需要对数据集做太多修改也能达到很好的效果
  • 需要注意的是:
    • 数据集存放的位置与生成的数据列表文件中的数据路径需要与配置文件对应,这也是初学者时常出现问题的地方。
    • 数据列表文件中路径与标签之间的分割符号,行与行之间的换行符号
    • 有些特定字符转义之后出现问题
#划分数据集
import codecs
import os
import random
import shutil
from PIL import Image

train_ratio = 4.0 / 5
all_file_dir = 'PaddleClas/dataset/archive'
class_list = [c for c in os.listdir(all_file_dir) if os.path.isdir(os.path.join(all_file_dir, c)) and not c.endswith('Set') and not c.startswith('.')]
class_list.sort()
print(class_list)
train_image_dir = os.path.join(all_file_dir, "trainImageSet")
if not os.path.exists(train_image_dir):
    os.makedirs(train_image_dir)
    
eval_image_dir = os.path.join(all_file_dir, "evalImageSet")
if not os.path.exists(eval_image_dir):
    os.makedirs(eval_image_dir)

train_file = codecs.open(os.path.join(all_file_dir, "train.txt"), 'w')
eval_file = codecs.open(os.path.join(all_file_dir, "eval.txt"), 'w')

with codecs.open(os.path.join(all_file_dir, "label_list.txt"), "w") as label_list:
    label_id = 0
    for class_dir in class_list:
        label_list.write("{0}\t{1}\n".format(label_id, class_dir))
        image_path_pre = os.path.join(all_file_dir, class_dir)
        for file in os.listdir(image_path_pre):
            try:
                img = Image.open(os.path.join(image_path_pre, file))
                if random.uniform(0, 1) <= train_ratio:
                    shutil.copyfile(os.path.join(image_path_pre, file), os.path.join(train_image_dir, file))
                    train_file.write("{0} {1}\n".format(os.path.join("trainImageSet", file), label_id))
                else:
                    shutil.copyfile(os.path.join(image_path_pre, file), os.path.join(eval_image_dir, file))
                    eval_file.write("{0} {1}\n".format(os.path.join("evalImageSet", file), label_id))
            except Exception as e:
                pass
                # 存在一些文件打不开,此处需要稍作清洗
        label_id += 1
            
train_file.close()
eval_file.close()

查看划分的情况

这里看到我们已经划分完整
! head PaddleClas/dataset/archive/train.txt

五、模型选择

整体思路

  1. 选择使用的模型,根据自己的任务难度及数据的特点,选择适合自己数据的模型进行训练,本文选择ResNet_vd模型训练关于ResNet网络结构的代码分析可移步这里查看ResNet网络结构代码分析
  2. 根据选定的模型更改PaddleClas/configs下对应的配置文件参数。例如类别数,迭代次数,batch_size,数据集路径等等。为了查看方便本文将PaddleClas/configs/ResNet/ResNet50_vd.yaml移只根目录改名为cancer.yaml,在训练时,修改配置文件的路径即可。
  3. 利用更改后的配置文件训练模型,使用训练技巧与不同的参数设置调优模型并选择最优模型进行推理预测(更多的训练技巧与参数设置,可以关注 https://paddleclas.readthedocs.io/zh_CN/latest/models/Tricks.html 其对相关参数进行了详细的解释说明)

骨干网络介绍

上次在表情识别时,给大家介绍了PaddleClas中各个参数的含义及使用方法,这里不再介绍,请大家去项目里查看

https://aistudio.baidu.com/aistudio/projectdetail/2544424?contributionType=1&shared=1

今天主要给大家详细介绍一下ResNet50_vd_ssld的网络结构,首先请看整体结构

ResNet各个Stage具体结构

  如本图所示,ResNet分为5个stage(阶段),其中Stage 0的结构比较简单,可以视其为对INPUT的预处理,后4个Stage都由Bottleneck组成,结构较为相似。Stage 1包含3个Bottleneck,剩下的3个stage分别包括4、6、3个Bottleneck。

现在对Stage 0和Stage 1进行详细描述,同理就可以理解后3个Stage。

Stage 0

  • (3,224,224)指输入INPUT的通道数(channel)、高(height)和宽(width),即(C,H,W)。现假设输入的高度和宽度相等,所以用(C,W,W)表示。
  • 该stage中第1层包括3个先后操作

CONV

CONV是卷积(Convolution)的缩写,7×7指卷积核大小,64指卷积核的数量(即该卷积层输出的通道数),/2指卷积核的步长为2。

BN

BN是Batch Normalization的缩写,即常说的BN层。

RELU

RELU指ReLU激活函数。

  • 该stage中第2层为MAXPOOL,即最大池化层,其kernel大小为3×3、步长为2。
  • (64,56,56)是该stage输出的通道数(channel)、高(height)和宽(width),其中64等于该stage第1层卷积层中卷积核的数量,56等于224/2/2(步长为2会使输入尺寸减半)。
    总体来讲,在Stage 0中,形状为(3,224,224)的输入先后经过卷积层、BN层、ReLU激活函数、MaxPooling层得到了形状为(64,56,56)的输出。

Stage 1

在理解了Stage 0以及熟悉图中各种符号的含义之后,可以很容易地理解Stage 1。理解了Stage 1之后,剩下的3个stage就不用我讲啦,你自己就能看懂。

Stage 1的输入的形状为(64,56,56),输出的形状为(64,56,56)。

下面介绍Bottleneck的具体结构(难点),把Bottleneck搞懂后,你就懂Stage 1了。

Bottleneck具体结构

现在让我们把目光放在本图最右侧,最右侧介绍了2种Bottleneck的结构。

“BTNK”是BottleNeck的缩写。

2种Bottleneck分别对应了2种情况:输入与输出通道数相同(BTNK2)、输入与输出通道数不同(BTNK1),这一点可以结合ResNet原文去看喔。

BTNK2

我们首先来讲BTNK2。

BTNK2有2个可变的参数C和W,即输入的形状(C,W,W)中的c和W。

令形状为(C,W,W)的输入为 x x x,令BTNK2左侧的3个卷积块(以及相关BN和RELU)为函数 F ( x ) F(x) F(x),两者相加( F ( x ) + x F(x)+x F(x)+x)后再经过1个ReLU激活函数,就得到了BTNK2的输出,该输出的形状仍为(C,W,W),即上文所说的BTNK2对应输入 x x x与输出 F ( x ) F(x) F(x)通道数相同的情况。

BTNK1

BTNK1有4个可变的参数C、W、C1和S。

与BTNK2相比,BTNK1多了1个右侧的卷积层,令其为函数 G ( x ) G(x) G(x)。BTNK1对应了输入 x x x与输出 F ( x ) F(x) F(x)通道数不同的情况,也正是这个添加的卷积层将 x x x变为 G ( x ) G(x) G(x),起到匹配输入与输出维度差异的作用( G ( x ) G(x) G(x) F ( x ) F(x) F(x)通道数相同),进而可以进行求和 G ( x ) + F ( x ) G(x)+F(x) G(x)+F(x)

参考链接: https://zhuanlan.zhihu.com/p/353235794

六、 训练模型

训练模型分为一下几个步骤:

  1. 修改配置文件。查看使用模型对应的配置文件并修改配置文件中一些必要的参数,本项目已经根据数据集适配了配置文件fer.yaml
  2. 切换路径,设置环境变量
  3. 训练模型

配置文件说明

想必经过前面的介绍,大家已经能明白配置文件中各个参数的意思了,现在将训练和评估的配置文件合并成一个文件一起使用

基础配置

mode: "train"  运行模式 2中选择["train"," valid"]
ARCHITECTURE: 模型结构名字
    name: 'ResNet50_vd'
pretrained_model: "" 预训练模型路径
model_save_dir: "./output/" 保存模型路径
classes_num: 5 分类数
total_images: 1800 总图片数
save_interval: 10 每隔多少个epoch保存模型
validate: True 是否在训练时进行评估
valid_interval: 10 每隔多少个epoch进行模型评估
epochs: 400 训练总epoch数
topk: 5 评估指标K值大小
image_shape: [3, 224, 224] 图片大小
use_mix: True 是否启用mixup
ls_epsilon: 0.1 label_smoothing epsilon值

学习率与优化配置

LEARNING_RATE:
    function: 'Cosine' decay方法名  ["Linear", "Cosine","Piecewise", "CosineWarmup"]         
    params: 初始学习率     大部分的神经网络选择的初始学习率为0.1,batch_size是256,所以根据实际的模型大小和显存情况,可以将学习率设置为0.1*k,batch_size设置为256*k              
        lr: 0.1   
*还可设置的参数
params:
	decayepochs	 piecewisedecay中衰减学习率的milestone
params:
	gamma	    piecewisedecay中gamma值	
params:
	warmupepoch	 warmup轮数	
parmas:
	steps	    lineardecay衰减steps数	
params:
	endlr	    lineardecayendlr值	

OPTIMIZER:
    function: 'Momentum' 优化器方法名 ["Momentum", "RmsProp"]
    params:
        momentum: 0.9 momentum值
    regularizer:
        function: 'L2' 正则化方法名	
        factor: 0.000070 正则化系数

训练配置

TRAIN:
    batch_size: 32 批大小
    num_workers: 4 数据读取器worker数量
    file_list: "./dataset/archive/train.txt" train文件列表
    data_dir: "./dataset/archive" train文件路径
    shuffle_seed: 0 用来进行shuffle的seed值
    transforms: 数据处理
        - DecodeImage:
            to_rgb: True 数据转RGB
            to_np: False 数据转numpy
            channel_first: False 按CHW排列的图片数据
        - RandCropImage: 随机裁剪
            size: 224
        - RandFlipImage: 随机翻转
            flip_code: 1
        - NormalizeImage:
            scale: 1./255. 归一化scale值
            mean: [0.485, 0.456, 0.406] 归一化均值
            std: [0.229, 0.224, 0.225] 归一化方差
            order: '' 归一化顺序
        - ToCHWImage: 调整为CHW
    mix:                       
        - MixupOperator:    
            alpha: 0.2      
*还可设置的参数
-CropImage	
	size:	裁剪大小
-ResizeImage	
	resize_short:	按短边调整大小

测试配置

VALID:
    batch_size: 64
    num_workers: 4
    file_list: "./dataset/archive/eval.txt"
    data_dir: "./dataset/archive"
    shuffle_seed: 0
    transforms:
        - DecodeImage:
            to_rgb: True
            to_np: False
            channel_first: False
        - ResizeImage:
            resize_short: 256
        - CropImage:
            size: 224
        - NormalizeImage:
            scale: 1.0/255.0
            mean: [0.485, 0.456, 0.406]
            std: [0.229, 0.224, 0.225]
            order: ''
        - ToCHWImage:

设置环境变量

%cd PaddleClas
import os 
os.environ['PYTHONPATH']="/home/aistudio/PaddleClas"
%%writefile ppcls/configs/quick_start/ResNet50_vd.yaml
# global configs
Global:
  checkpoints: null
  pretrained_model: null
  output_dir: ./output/
  device: gpu
  save_interval: 1
  eval_during_train: True
  eval_interval: 1
  epochs: 5
  print_batch_step: 50
  use_visualdl: False
  # used for static mode and model export
  image_shape: [3, 224, 224]
  save_inference_dir: ./inference

# model architecture
Arch:
  name: ResNet50_vd 
  class_num: 102

# loss function config for traing/eval process
Loss:
  Train:
    - CELoss:
        weight: 1.0
  Eval:
    - CELoss:
        weight: 1.0


Optimizer:
  name: Momentum
  momentum: 0.9
  lr:
    name: Cosine
    learning_rate: 0.0125
    warmup_epoch: 5
  regularizer:
    name: 'L2'
    coeff: 0.00001


# data loader for train and eval
DataLoader:
  Train:
    dataset:
      name: ImageNetDataset
      image_root: ./dataset/archive
      cls_label_path: ./dataset/archive/train.txt
      transform_ops:
        - DecodeImage:
            to_rgb: True
            channel_first: False
        - RandCropImage:
            size: 224
        - RandFlipImage:
            flip_code: 1
        - NormalizeImage:
            scale: 1.0/255.0
            mean: [0.485, 0.456, 0.406]
            std: [0.229, 0.224, 0.225]
            order: ''

    sampler:
      name: DistributedBatchSampler
      batch_size: 64
      drop_last: False
      shuffle: True
    loader:
      num_workers: 4
      use_shared_memory: True

  Eval:
    dataset: 
      name: ImageNetDataset
      image_root: ./dataset/archive/
      cls_label_path: ./dataset/archive/eval.txt
      transform_ops:
        - DecodeImage:
            to_rgb: True
            channel_first: False
        - ResizeImage:
            resize_short: 256
        - CropImage:
            size: 224
        - NormalizeImage:
            scale: 1.0/255.0
            mean: [0.485, 0.456, 0.406]
            std: [0.229, 0.224, 0.225]
            order: ''
    sampler:
      name: DistributedBatchSampler
      batch_size: 64
      drop_last: False
      shuffle: False
    loader:
      num_workers: 4
      use_shared_memory: True

Infer:
  infer_imgs: ./dataset/archive/colon_aca/colonca10.jpeg
  batch_size: 10
  transforms:
    - DecodeImage:
        to_rgb: True
        channel_first: False
    - ResizeImage:
        resize_short: 256
    - CropImage:
        size: 224
    - NormalizeImage:
        scale: 1.0/255.0
        mean: [0.485, 0.456, 0.406]
        std: [0.229, 0.224, 0.225]
        order: ''
    - ToCHWImage:
  PostProcess:
    name: Topk
    topk: 5
    class_id_map_file: ./dataset/archive/label_list.txt

Metric:
  Train:
    - TopkAcc:
        topk: [1, 5]
  Eval:
    - TopkAcc:
        topk: [1, 5]

开始训练

  • 执行训练脚本,使用已经配置好的ResNet50_vd.yaml文件
  • 训练脚本指令如下:PaddleClas通过launch方式启动多卡多进程训练 通过设置FLAGS_selected_gpus 指定GPU运行卡号
!python3 tools/train.py \
    -c ./ppcls/configs/quick_start/ResNet50_vd.yaml \
    -o Global.device=gpu

我们可以看到如下的训练日志

[2021/11/24 23:34:59] root INFO: [Train][Epoch 5/5][Iter: 200/312]lr: 0.01161, top1: 0.94178, top5: 1.00000, CELoss: 0.15635, loss: 0.15635, batch_cost: 0.23105s, reader_cost: 0.00837, ips: 277.00027 images/sec, eta: 0:00:25
[2021/11/24 23:35:16] root INFO: [Train][Epoch 5/5][Iter: 250/312]lr: 0.01201, top1: 0.94211, top5: 1.00000, CELoss: 0.15497, loss: 0.15497, batch_cost: 0.25122s, reader_cost: 0.02926, ips: 254.75556 images/sec, eta: 0:00:15
[2021/11/24 23:35:35] root INFO: [Train][Epoch 5/5][Iter: 300/312]lr: 0.01241, top1: 0.94150, top5: 1.00000, CELoss: 0.15539, loss: 0.15539, batch_cost: 0.27213s, reader_cost: 0.05275, ips: 235.18103 images/sec, eta: 0:00:03
[2021/11/24 23:35:37] root INFO: [Train][Epoch 5/5][Avg]top1: 0.94151, top5: 1.00000, CELoss: 0.15497, loss: 0.15497
[2021/11/24 23:35:39] root INFO: [Eval][Epoch 5][Iter: 0/80]CELoss: 0.06137, loss: 0.06137, top1: 0.96875, top5: 1.00000, batch_cost: 1.22453s, reader_cost: 1.15587, ips: 52.26513 images/sec
[2021/11/24 23:35:49] root INFO: [Eval][Epoch 5][Iter: 50/80]CELoss: 0.00422, loss: 0.00422, top1: 1.00000, top5: 1.00000, batch_cost: 0.21143s, reader_cost: 0.13900, ips: 302.70455 images/sec
[2021/11/24 23:35:55] root INFO: [Eval][Epoch 5][Avg]CELoss: 0.08479, loss: 0.08479, top1: 0.97176, top5: 1.00000
[2021/11/24 23:35:55] root INFO: [Eval][Epoch 5][best metric: 0.9723538704581358]
[2021/11/24 23:35:56] root INFO: Already save model in ./output/ResNet50_vd/epoch_5
[2021/11/24 23:35:56] root INFO: Already save model in ./output/ResNet50_vd/latest
!python3 tools/train.py \
    -c ./ppcls/configs/quick_start/ResNet50_vd.yaml \
    -o Global.device=gpu

API 说明

  • -c用于指定配置文件的路径
  • -o用于指定需要修改或者添加的参数,其中-o Arch.pretrained=False表示不使用预训练模型
  • -o Global.device=gpu表示使用GPU进行训练。如果希望使用CPU进行训练,则需要将Global.device设置为cpu。

PS

  • 如果在训练中使用了mixup或者cutmix的数据增广方式,那么日志中将不会打印top-1与top-k(默认为5)信息:
  • 如果训练过程中没有使用mixup或者cutmix的数据增广,那么除了上述信息外,日志中也会打印出top-1与top-k(默认为5)的信息:

更换模型

   大家可以看到,这样的精度虽然比较高,但是我们对它的精度和收敛速度并不满意,因为有更好的模型可以更换,如图所示
我们可以考虑将模型文件更换成ResNet50_vd_ssld_random_erasing_finetune

PS

由于新版PaddleClas删除了ResNet50_vd_ssld_random_erasing_finetune.yaml,于是本人自己写了一个,并且下载了对应的预训练模型放在里面

%%writefile ppcls/configs/quick_start/ResNet50_vd_ssld_random_erasing_finetune.yaml
# global configs
Global:
  checkpoints: null
  pretrained_model: null
  output_dir: ./output/
  device: gpu
  save_interval: 1
  eval_during_train: True
  eval_interval: 1
  epochs: 5
  print_batch_step: 50
  use_visualdl: False
  # used for static mode and model export
  image_shape: [3, 224, 224]
  save_inference_dir: ./inference

# model architecture
Arch:
  name: ResNet50_vd 
  class_num: 1000

# loss function config for traing/eval process
Loss:
  Train:
    - CELoss:
        weight: 1.0
  Eval:
    - CELoss:
        weight: 1.0


Optimizer:
  name: Momentum
  momentum: 0.9
  lr:
    name: Cosine
    learning_rate: 0.0125
    warmup_epoch: 5
  regularizer:
    name: 'L2'
    coeff: 0.00001


# data loader for train and eval
DataLoader:
  Train:
    dataset:
      name: ImageNetDataset
      image_root: ./dataset/archive
      cls_label_path: ./dataset/archive/train.txt
      transform_ops:
        - DecodeImage:
            to_rgb: True
            channel_first: False
        - RandCropImage:
            size: 224
        - RandFlipImage:
            flip_code: 1
        - NormalizeImage:
            scale: 1.0/255.0
            mean: [0.485, 0.456, 0.406]
            std: [0.229, 0.224, 0.225]
            order: ''
        

    sampler:
      name: DistributedBatchSampler
      batch_size: 64
      drop_last: False
      shuffle: True
    loader:
      num_workers: 4
      use_shared_memory: True

  Eval:
    dataset: 
      name: ImageNetDataset
      image_root: ./dataset/archive/
      cls_label_path: ./dataset/archive/eval.txt
      transform_ops:
        - DecodeImage:
            to_rgb: True
            channel_first: False
        - ResizeImage:
            resize_short: 256
        - CropImage:
            size: 224
        - NormalizeImage:
            scale: 1.0/255.0
            mean: [0.485, 0.456, 0.406]
            std: [0.229, 0.224, 0.225]
            order: ''
    sampler:
      name: DistributedBatchSampler
      batch_size: 64
      drop_last: False
      shuffle: False
    loader:
      num_workers: 4
      use_shared_memory: True

Infer:
  infer_imgs: ./dataset/archive/colon_aca/colonca10.jpeg
  batch_size: 10
  transforms:
    - DecodeImage:
        to_rgb: True
        channel_first: False
    - ResizeImage:
        resize_short: 256
    - CropImage:
        size: 224
    - NormalizeImage:
        scale: 1.0/255.0
        mean: [0.485, 0.456, 0.406]
        std: [0.229, 0.224, 0.225]
        order: ''
    - RandomErasing:
        EPSILON: 0.5
    - ToCHWImage:
  PostProcess:
    name: Topk
    topk: 5
    class_id_map_file: ./dataset/archive/label_list.txt

Metric:
  Train:
    - TopkAcc:
        topk: [1, 5]
  Eval:
    - TopkAcc:
        topk: [1, 5]
!python3 tools/train.py \
    -c ./ppcls/configs/quick_start/ResNet50_vd_ssld_random_erasing_finetune.yaml \
    -o Arch.pretrained=True\
    -o Global.device=gpu

API 说明

  • Arch.pretrained设置为True表示加载ImageNet的预训练模型,此外,Arch.pretrained也可以指定具体的模型权重文件的地址,使用时需要换成自己的预训练模型权重文件的路径。

我们也提供了大量基于ImageNet-1k数据集的预训练模型,模型列表及下载地址详见
模型库预览
https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/docs/zh_CN/algorithm_introduction/ImageNet_models.md

七、 模型评估

大家看上面的训练日志是不是特别长?找不到模型最优的准确度,PaddleClas通过launch方式也可以进行评估。

下方命令将使用./ppcls/configs/quick_start/ResNet50_vd.yaml作为配置文件,对上述训练得到的模型./output/ResNet50_vd/best_model进行评估。你也可以通过更改配置文件中的参数来设置评估,也可以通过-o参数更新配置,如下所示。

可配置的部分评估参数说明如下:

  • Arch.name:模型名称

  • Global.pretrained_model:待评估的模型预训练模型文件路径

  • 评估脚本代码如下:需要注意的是加载模型时,需要指定模型的前缀,如模型参数所在的文件夹为output/ResNet50_vd/19,模型参数的名称为output/ResNet50_vd/19/ppcls.pdparams,则pretrained_model参数需要指定为output/ResNet50_vd/19/ppcls,PaddleClas会自动补齐.pdparams的后缀。

!python3 tools/eval.py \
    -c ./ppcls/configs/quick_start/ResNet50_vd.yaml \
    -o Global.pretrained_model=./output/ResNet50_vd/best_model
!python3 tools/eval.py \
    -c ./ppcls/configs/quick_start/ResNet50_vd_ssld_random_erasing_finetune.yaml \
    -o Global.pretrained_model=./output/ResNet50_vd/best_model
我们可以看到如下的日志
W1125 00:42:04.256417 15788 device_context.cc:465] device: 0, cuDNN Version: 7.6.
[2021/11/25 00:42:10] root INFO: [Eval][Epoch 0][Iter: 0/80]CELoss: 0.00002, loss: 0.00002, top1: 1.00000, top5: 1.00000, batch_cost: 1.42733s, reader_cost: 1.34530, ips: 44.83886 images/sec
[2021/11/25 00:42:20] root INFO: [Eval][Epoch 0][Iter: 50/80]CELoss: 0.00003, loss: 0.00003, top1: 1.00000, top5: 1.00000, batch_cost: 0.21186s, reader_cost: 0.14290, ips: 302.08290 images/sec
[2021/11/25 00:42:26] root INFO: [Eval][Epoch 0][Avg]CELoss: 0.00558, loss: 0.00558, top1: 0.99763, top5: 1.00000

大家可以惊人的发现,ResNet50_vd_ssld_random_erasing_finetune模型,只需要5个epoch评估时概率就达到了100%,效果惊人!

关于PaddlePaddle模型的保存方式

  1. persistable 模型(fluid.save_persistabels保存的模型) 一般做为模型的 checkpoint,可以加载后重新训练。persistable 模型保存的是零散的权重文件,每个文件代表模型中的一个 Variable,这些零散的文件不包含结构信息,需要结合模型的结构一起使用。
  2. inference 模型(fluid.io.save_inference_model保存的模型) 一般是模型训练完成后保存的固化模型,用于预测部署。与 persistable 模型相比,inference 模型会额外保存模型的结构信息,用于配合权重文件构成完整的模型。如下所示,model 中保存的即为模型的结构信息。

八、 模型推理

由于模型的保存方式以及选择引擎的不同,PaddlePaddle衍生出三种方式进行预测推理:

  • 预测引擎 + inference 模型
  • 训练引擎 + persistable 模型
  • 训练引擎 + inference 模型

本文选择使用 预测引擎 + inference模型 的方式进行推理,执行步骤如下:

  1. 对训练好的模型进行转换固化
  2. 通过预测引擎和inference模型进行推理

保存 inference 模型

在训练过程中保存的persistable 模型中选择最优模型,本文设置在训练的同时进行模型评估并保存效果最好的persistable模型,命令如下:

python tools/export_model.py --m=模型名称 --p=persistable 模型路径 --o=model和params保存路径

在新版PaddleClas中,模型名称已经保存在配置文件当中,只需要声明模型保存路径就好

预测引擎 + inference 模型推理预测:

命令如下:

!python3 python/predict_cls.py \
-c configs/inference_cls.yaml \
-o Global.infer_imgs=../dataset/archive/colon_n/colonn1042.jpeg \
-o Global.inference_model_dir=../inference/ \
-o PostProcess.Topk.class_id_map_file=None

需要注意的是对应的模型权重文件路径需要确认,其他的参数设置如下所示:

参数说明:

  • Global.infer_imgs:待预测的图片文件路径。
  • Global.inference_model_dir:inference模型结构文件路径,如 …/inference/inference.pdmodel
  • Global.use_tensorrt:是否使用 TesorRT 预测引擎,默认值:False
  • Global.use_gpu:是否使用 GPU 预测,默认值:True
  • Global.enable_mkldnn:是否启用MKL-DNN加速,默认为False。注意enable_mkldnn与use_gpu同时为True时,将忽略enable_mkldnn,而使用GPU运行。
  • Global.use_fp16:是否启用FP16,默认为False。

注意:

如果使用Transformer系列模型,如DeiT_***384, ViT***_384等,请注意模型的输入数据尺寸,需要设置参数resize_short=384, resize=384。

如果你希望提升评测模型速度,使用gpu评测时,建议开启TensorRT加速预测,使用cpu评测时,建议开启MKLDNN加速预测。

!python3 tools/export_model.py \
    -c ./ppcls/configs/quick_start/ResNet50_vd_ssld_random_erasing_finetune.yaml \
    -o Global.pretrained_model=output/ResNet50_vd/best_model
%cd deploy
!python3 python/predict_cls.py \
    -c configs/inference_cls.yaml \
    -o Global.infer_imgs=../dataset/archive/colon_n/colonn1042.jpeg \
    -o Global.inference_model_dir=../inference/ \
/inference_cls.yaml \
    -o Global.infer_imgs=../dataset/archive/colon_n/colonn1042.jpeg \
    -o Global.inference_model_dir=../inference/ \
    -o PostProcess.Topk.class_id_map_file=None

预测结果展示

预测结果展示:

class id(s): [4, 0, 1, 2, 3], score(s): [1.00, 0.00, 0.00, 0.00, 0.00]
0	colon_aca
1	colon_n
2	lung_aca
3	lung_n
4	lung_scc


这里面class id与score一一对应,可以看到预测正确,同时置信度也特别高,图片的标签为4,与label.txt对应lung_scc

九、 亮点与总结

  1. 本文使用了PaddleClas的ResNet50_vd_ssld_random_erasing_finetune模型,官方认定效果最好,可达100%
  2. 魔改思路:还有另一种方法就是可以进行模型蒸馏,使模型轻量化并效果更好,因为本项目精度已经最好,所以请读者自己尝试
  3. 蒸馏过程中,教师模型使用的预训练模型为archive数据集上的训练结果,学生模型使用的是ResNet50_vd_ssld预训练模型。
  4. 本文还体现出两种模型的精度对比,凸显ResNet50_vd_ssld_random_erasing_finetune的优势
  5. 本文同时包含骨干网络讲解的理论板块和实际应用板块,让我们拒绝成为掉包侠

个人简介

本人来自江苏科技大学本科三年级,刚刚接触深度学习不久希望大家多多关注

感兴趣的方向:目标检测,强化学习,自然语言处理、

个人链接:
马骏骁

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

请点击此处查看本环境基本用法.

Please click here for more detailed instructions.

Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐