重走长征路-PaddleClas训练ImageNet 1K数据集实践

让我们:复现PaddleClas工程师的工作
友情提醒,本项目默认配置后台任务运行时间为单卡100小时,四卡25小时!非土豪请修改Epoch数值,土豪和训练发烧友随意!

缘起

尽管我使用PaddleClas很长一段时间了,但是一直没有涉足ImageNet 1K训练。所以一直以来PaddleClas那些模型库的精度我是耳熟能详,但怎么训练出来,从来只闻其声,未亲自实践。
本项目就是“重走长征路”,在AIStudio平台从头训模型,查看能否复现论文和PaddleClas文档中标明的精度。

对我这个新手来说,拦路虎主要是两个:

  1. 数据集,也就是ImageNet 1K数据集太大
    这个数据集无法在AIStudio的notebook环境下打开,因为超过了100G硬盘空间限制。
  2. 训练需要耗费大量算力。
    官方已经提供训好的模型,一般人谁也不会无缘无故去重新训一遍啊。

但是新手终归要变成老手,将一个主流模型从头训一遍,是每个AI届的朋友必然要经历的过程。所以不要怕拦路虎,明知山有虎,偏向虎山行!

解决方法:

  1. 针对数据集太大超限的问题,可以使用后台训练或者脚本训练,这两种训练方式允许大数据集的使用。

  2. 训练使用后台训练或脚本模式,尽管会耗费大量算力,但是在很多时候,比如论文复现等场景下,这是必然且无法回避的。

实践步骤分解

1 处理ImageNet数据集
还是那句话,懂这个数据集和不懂,只隔一层窗户纸。
经历了迷茫的第一次的训练之后,后面再用这个数据集,就会信手拈来,拿来用即可。
如果还没有捅破这层窗户纸,想具体了解ImageNet 1K数据集,可以移步这里:万里长征第一步–玩转ImageNet 1K数据集

2 模型训练
使用PaddleClas套件训练,套件已经把所有的代码全部写好,只要把数据集放在指定目录,然后执行命令即可。

但是针对后台和脚本任务,要稍微改变一点,主要原因是后台和脚本任务对存盘文件的大小和文件数量有限制。比如后台任务,如果我们把数据集放在默认的PaddleClas/dataset目录下,那么除非我们最后把数据集删除,否则会因为空间超限而报错“任务未能成功运行,处理结果失败,请保证输出结果少于1万个文件且小于20GB”, 并最终导致我们拿不到耗费大量算力训练的结果。

下面,请开始我们的实践!

一、玩转ImageNet数据集


ImageNet数据集包括14197122张图片,21841类,也就是传说中的1500万张图片,20000类!大约1TB数据。 下载网址:https://image-net.org/download.php

也可参考这个项目万里长征第一步–玩转ImageNet 1K数据集

ImageNet 1K数据集:ImageNet 数据集中使用率最高的子集是ImageNet 1K数据集,它是大规模视觉识别挑战赛 (ILSVRC) 2012-2017 图像分类和定位数据集,因此也被称为ImageNet ILSVRC数据集。该数据集有1000 个分类,包含 1,281,167 个训练图像、50,000 个验证图像和 100,000 个测试图像。我们经常讲的某个模型的精度,一般是指在这个数据集下训练和验证的精度。当然更权威和可信的是用测试集来得出的精度,那个在网上有排名。

对于我们来说,ImageNet-1k 数据集拿来就用即可。本项目已经载入两份数据集(/home/aistudio/data/data114241/Light_ILSVRC2012_part_0.tar 和 /home/aistudio/data/data114746/Light_ILSVRC2012_part_1.tar),之所以是两份,是因为太大,一份放不下。

因为太大,也不能在Notebook里面展开,会因为硬盘超额而被终止项目。 因此最终我们要在后台任务或脚本任务中进行训练。本项目为使用后台任务训练。

二、 万里挑一选模型

初期选型Swin Transformer

初期选了Swin Transformer模型,2021.06.29 新添加Swin-transformer 系列模型,ImageNet1k 数据集上 Top1 acc 最高精度可达 87.2%;支持训练预测评估与 whl 包部署,预训练模型可以从这里下载。

https://gitee.com/paddlepaddle/PaddleClas/blob/release/2.4/docs/zh_CN/algorithm_introduction/ImageNet_models.md

但是经过实践测试,发现训练速度特别慢,我们无法在4卡模式下用100个以内的点卡训练Swin的最高精度模型:SwinTransformer_large_patch4_window12_384。
经测试,该模型1个Epoch需要6小时(16G V100版本),这样32G V100 跑300个Epoch大约需要6/2*300 = 900小时。即使使用tiny模型:SwinTransformer_tiny_patch4_window7_224,也要100多个小时,也就是100多点GPU训练点卡。

它的训练速度为:SwinTransformer_large_patch4_window12_384的ips为13

SwinTransformer_tiny_patch4_window7_224的ips为97

因此我们开始寻找其它模型。

最终选型PP-LCNet模型

PP-LCNet是飞桨轻量明星模型,其最大的特点是推理速度快,尤其是在CPU环境下推理速度远超竟品!

它的训练速度大约是500ips,速度比Swin快很多!
最终就选它了!

下面首先下载PaddleClas库文件。
其次准备好数据集。
做好训练的准备!

# 下载PaddleClas
!git clone https://gitee.com/paddlepaddle/PaddleClas
# 安装相关库。在AIStudio项目中该步骤可以省略
# !pip install -qr ~/PaddleClas/requirements.txt -i https://mirror.baidu.com/pypi/simple 
# 将需要训练的配置文件放入~/work目录,以便修改
# !cp PaddleClas/ppcls/configs/ImageNet/SwinTransformer/SwinTransformer_large_patch4_window12_384.yaml ~/work

将数据集解压在~/data目录, 包目录为~/data/Light_ILSVRC2012/

这个需要在后台任务解压缩,否则会因为硬盘空间超限而被关闭项目。

另外因为数据集被解压缩到~/data目录,而没有按照PaddleClas的惯例放在~/PaddleClas/dataset 默认目录,因此需要修改模型配置文件,比如work/PPLCNet_x1_0.yaml文件中数据集设置部分:

    # 原配置
          image_root: ./dataset/ILSVRC2012/
          cls_label_path: ./dataset/ILSVRC2012/train_list.txt
    # 修改后配置
          image_root: /home/aistudio/data/Light_ILSVRC2012/
          cls_label_path: /home/aistudio/data/Light_ILSVRC2012/train_list.txt

解压缩大约耗时20分钟。

!cd ~/data && tar -xf /home/aistudio/data/data114241/Light_ILSVRC2012_part_0.tar
!cd ~/data && tar -xf /home/aistudio/data/data114746/Light_ILSVRC2012_part_1.tar
!cd ~/data && ls

三、 万里长征:训练

因为Notebook下无法使用全量的ImageNet 1K数据,因此这里我们用了后台任务来进行训练(未来会再发一个脚本任务)。
PaddleClas训练技巧

在这里插入图片描述

后台训练设置

官方配置文件的训练Epoch数为360,这里我们设置为160,这样总耗费GPU点卡大约100点!
相信我,这100点卡花的非常值! 经历过长征的红军,比以前战斗力更强,思想觉悟更高! 而训练过100点卡的朋友们,也会感觉自己比以前更懂PaddleClas训练,训练能力更强,进而更懂AI人工智能!

没有人比我更懂人工智能! (唐纳德.特朗普与诸君共勉)

如果点卡不太富裕,可以设为12Epoch,运行耗时大约8小时。修改~/work/PPLCNet_train_ImageNet.yaml文件中的epochs参数,设置为epochs: 12即可。

开始后台训练

  • 首先fork 本项目
  • 启动项目后,点左侧“版本”生成新的版本
  • 点“任务”,创建新的后台任务

后面就是漫长的等待了。总运行时间,单卡大约100小时,四卡大约25小时。

可以通过“查看日志”观看训练log输出。会类似这样:

[2022/08/05 08:56:08] ppcls INFO: [Train][Epoch 1/360][Iter: 160/2503]lr(LinearWarmup): 0.01029165, top1: 0.00121, top5: 0.00543, CELoss: 6.92706, loss: 6.92706, batch_cost: 0.96780s, reader_cost: 0.68862, ips: 529.03591 samples/s, eta: 10 days, 2:11:48



!echo "PPLCNet训练160Epochs"
!cd ~/PaddleClas/ && python3 -m paddle.distributed.launch \
    tools/train.py \
        -c ~/work/PPLCNet_train_ImageNet.yaml

4卡后台任务中,每个epoch平均用时8.5分钟,也就是单卡34分钟。

[2022/08/11 17:35:13] ppcls INFO: [Train][Epoch 2/12][Iter: 0/626]lr(LinearWarmup): 0.16025559, top1: 0.02148, top5: 0.07422, CELoss: 6.28872, loss: 6.28872, batch_cost: 0.89291s, reader_cost: 0.43947, ips: 573.40513 samples/s, eta: 1:42:28
[2022/08/11 17:41:39] ppcls INFO: [Train][Epoch 2/12][Iter: 500/626]lr(LinearWarmup): 0.28805112, top1: 0.04258, top5: 0.12995, CELoss: 5.90013, loss: 5.90013, batch_cost: 0.77378s, reader_cost: 0.05757, ips: 661.68411 samples/s, eta: 1:22:21
[2022/08/11 17:43:11] ppcls INFO: [Train][Epoch 2/12][Avg]top1: 0.05146, top5: 0.14980, CELoss: 5.79814, loss: 5.79814
[2022/08/11 17:43:13] ppcls INFO: [Eval][Epoch 2][Iter: 0/196]CELoss: 4.68889, loss: 4.68889, top1: 0.09766, top5: 0.29688, batch_cost: 1.56802s, reader_cost: 1.28539, ips: 40.81592 images/sec
[2022/08/11 17:43:36] ppcls INFO: [Eval][Epoch 2][Avg]CELoss: 4.85905, loss: 4.85905, top1: 0.10064, top5: 0.26262
[2022/08/11 17:43:36] ppcls INFO: Already save model in ./output/PPLCNet_x1_0/best_model
[2022/08/11 17:43:36] ppcls INFO: [Eval][Epoch 2][best metric: 0.10063999891281128]
[2022/08/11 17:43:36] ppcls INFO: Already save model in ./output/PPLCNet_x1_0/epoch_2
[2022/08/11 17:43:36] ppcls INFO: Already save model in ./output/PPLCNet_x1_0/latest
[2022/08/11 17:43:43] ppcls INFO: [Train][Epoch 3/12][Iter: 0/626]lr(LinearWarmup): 0.32025559, top1: 0.08398, top5: 0.24609, CELoss: 5.41669, loss: 5.41669, batch_cost: 0.77636s, reader_cost: 0.07374, ips: 659.48866 samples/s, eta: 1:21:00

四、 胜利会师:评估、预测和推理

前面训练完成后,后面就是如何使用训练的模型了。

模型评估

训练好模型之后,可以通过以下命令实现对模型指标的评估。

python3 tools/eval.py
-c ppcls/configs/ImageNet/ResNet/ResNet50.yaml
-o Global.pretrained_model=output/ResNet50/best_model

其中 -o Global.pretrained_model=“output/ResNet50/best_model” 指定了当前最佳权重所在的路径,如果指定其他权重,只需替换对应的路径即可。

!echo "评估"
!cd ~/PaddleClas/ && python tools/eval.py \
    -c ~/work/PPLCNet_train_ImageNet.yaml \
    -o Global.pretrained_model=output/PPLCNet_x1_0/best_model

评估结果[2022/08/11 19:07:46] ppcls INFO: [Eval][Epoch 0][Avg]CELoss: 1.91064, loss: 1.91064, top1: 0.57338, top5: 0.80684
可以看到12个Epoch可以达到57%的top1精度,还是挺不错的。

模型预测

模型训练完成之后,可以加载训练得到的预训练模型,进行模型预测。在模型库的 tools/infer.py 中提供了完整的示例,只需执行下述命令即可完成模型预测:

python3 tools/infer.py \
    -c ppcls/configs/ImageNet/ResNet/ResNet50.yaml \
    -o Global.pretrained_model=output/ResNet50/best_model 

预测图片为~/PaddleClas/docs/images/inference_deployment/whl_demo.jpg 我们可以先看下这个图片的样子

from PIL import Image
img = Image.open("/home/aistudio/PaddleClas/docs/images/inference_deployment/whl_demo.jpg")
img

在这里插入图片描述

!echo "模型预测"
!cd PaddleClas && python tools/infer.py \
    -c ~/work/PPLCNet_train_ImageNet.yaml \
    -o Global.pretrained_model=output/PPLCNet_x1_0/best_model 

预测结果:[{'class_ids': [8, 7, 12, 88, 86], 'scores': [0.71487, 0.22119, 0.00517, 0.00416, 0.00282], 'file_name': 'docs/images/inference_deployment/whl_demo.jpg', 'label_names': ['hen', 'cock', 'house finch, linnet, Carpodacus mexicanus', 'macaw', 'partridge']}]
可以通过ImageNet数据集标签对应表来查看结果

7 cock
8 hen
9 ostrich, Struthio camelus
10 brambling, Fringilla montifringilla
11 goldfinch, Carduelis carduelis
12 house finch, linnet, Carpodacus mexicanus

可见识别效果还是不错的,母鸡概率71%,公鸡概率22%。

基于训练得到的权重导出 inference 模型

此处,我们提供了将权重和模型转换的脚本,执行该脚本可以得到对应的 inference 模型:

python3 tools/export_model.py
-c ppcls/configs/ImageNet/ResNet/ResNet50.yaml
-o Global.pretrained_model=output/ResNet50/best_model
-o Global.save_inference_dir=deploy/models/ResNet50_infer
执行完该脚本后会在 deploy/models/ 下生成 ResNet50_infer 文件夹,models 文件夹下应有如下文件结构:

├── ResNet50_infer
│ ├── inference.pdiparams
│ ├── inference.pdiparams.info
│ └── inference.pdmodel

!echo "导出inference 模型"
!cd PaddleClas && python tools/export_model.py \
    -c ~/work/PPLCNet_train_ImageNet.yaml \
    -o Global.pretrained_model=output/PPLCNet_x1_0/best_model \
    -o Global.save_inference_dir=deploy/models/PPLCNet_x1_0_infer

基于 Python 预测引擎推理

预测单张图像

进入deploy 目录:
运行下面的命令,对图像 ./images/ImageNet/ILSVRC2012_val_00000010.jpeg 进行分类。预测图像在configs/inference_cls.yaml文件中设置:infer_imgs: "./images/ImageNet/ILSVRC2012_val_00000010.jpeg"

使用下面的命令使用 GPU 进行预测
python3 python/predict_cls.py -c configs/inference_cls.yaml -o Global.inference_model_dir=models/ResNet50_infer

使用下面的命令使用 CPU 进行预测
python3 python/predict_cls.py -c configs/inference_cls.yaml -o Global.inference_model_dir=models/ResNet50_infer -o Global.use_gpu=False

我们先来看看图片的样子:

from PIL import Image
img = Image.open("/home/aistudio/PaddleClas/deploy/images/ImageNet/ILSVRC2012_val_00000010.jpeg")
img

在这里插入图片描述

!echo "基于 Python 预测引擎推理"
!cd ~/PaddleClas/deploy && python python/predict_cls.py \
    -c configs/inference_cls.yaml \
    -o Global.inference_model_dir=models/PPLCNet_x1_0_infer

输出结果

ILSVRC2012_val_00000010.jpeg:	class id(s): [153, 333, 204, 332, 283], score(s): [0.27, 0.12, 0.10, 0.08, 0.03], label_name(s): ['Maltese dog, Maltese terrier, Maltese', 'hamster', 'Lhasa, Lhasa apso', 'Angora, Angora rabbit', 'Persian cat']

对应标签分类为:

153 Maltese dog, Maltese terrier, Maltese 马耳他狗
333 hamster 仓鼠
204 Lhasa, Lhasa apso
332 Angora, Angora rabbit 安哥拉兔

说实话,我也不知道该是啥。

通过查看val_list.txt文件,可以知道其标签为:ILSVRC2012_val_00000010.JPEG 332
也就是这张图片是安哥拉兔.

在训练更多轮数后,比如160Epoch,模型的识别率会更高,预测结果为

ILSVRC2012_val_00000010.jpeg: class id(s): [153, 332, 229, 204, 265], score(s): [0.41, 0.39, 0.05, 0.04, 0.04], label_name(s): ['Maltese dog, Maltese terrier, Maltese', 'Angora, Angora rabbit', 'Old English sheepdog, bobtail', 'Lhasa, Lhasa apso', 'toy poodle']

可见精度已经达到它认为39%几率为安哥拉兔,41%几率为马耳他狗,表现不错。当然这幅图即使对人也较难分辨。

基于文件夹的批量预测

如果希望预测文件夹内的图像,可以直接修改配置文件中的 Global.infer_imgs 字段,infer_imgs: "./images/ImageNet/",也可以通过 -o 参数修改对应的配置。

!echo "基于Python预测引擎推理:文件夹批量预测"
!cd ~/PaddleClas/deploy && python python/predict_cls.py \
    -c configs/inference_cls.yaml \
    -o Global.inference_model_dir=models/PPLCNet_x1_0_infer \
    -o Global.infer_imgs=./images/ImageNet/

输出结果为

ILSVRC2012_val_00000010.jpeg:	class id(s): [153, 333, 204, 332, 283], score(s): [0.27, 0.12, 0.10, 0.08, 0.03], label_name(s): ['Maltese dog, Maltese terrier, Maltese', 'hamster', 'Lhasa, Lhasa apso', 'Angora, Angora rabbit', 'Persian cat']
ILSVRC2012_val_00010010.jpeg:	class id(s): [732, 662, 622, 633, 710], score(s): [0.11, 0.10, 0.07, 0.06, 0.04], label_name(s): ['Polaroid camera, Polaroid Land camera', 'modem', 'lens cap, lens cover', "loupe, jeweler's loupe", 'pencil sharpener']
ILSVRC2012_val_00020010.jpeg:	class id(s): [178, 209, 211, 208, 180], score(s): [0.99, 0.00, 0.00, 0.00, 0.00], label_name(s): ['Weimaraner', 'Chesapeake Bay retriever', 'vizsla, Hungarian pointer', 'Labrador retriever', 'American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier']
ILSVRC2012_val_00030010.jpeg:	class id(s): [80, 134, 7, 23, 8], score(s): [0.95, 0.01, 0.01, 0.01, 0.00], label_name(s): ['black grouse', 'crane', 'cock', 'vulture', 'hen']

可以发现后面两张图的识别更精准。

五、总结

世上无难事,只怕有心人!
经过漫长的训练过程,我们终于完成了100小时的训练,大家心里有点小激动吧!

后面就是灵活运用了。比如自己写了一个模型或者复现了一个模型,就可以用这个后台任务进行训练测试。其实本项目设立之初,就是为了参加论文复现,为使用PaddleClas训练应运而生的。

六、再一次重走长征路,在Notebook环境重温训练

根据文档的使用有人/无人场景数据集进行PP-LCNet训练文档,将模型改成Swin Transformer,在Notebook模式下重温一下,加深印象!

将下面各部分的注释去掉(全选,然后按快捷键ctrl+/,即可全部去掉注释),然后直接执行即可。当然主要是为了熟悉操作,真的要训练,还是要用后台任务,上ImageNet 1K数据训练才行。

注意:下面代码可以在notebook下执行

训练前准备

首先下载PaddleClas库文件。
然后进入 PaddleClas/dataset/ 目录,下载并解压有人/无人场景的数据。

# !git clone https://gitee.com/paddlepaddle/PaddleClas
# !cd ~/PaddleClas/dataset && wget https://paddleclas.bj.bcebos.com/data/PULC/person_exists.tar && tar -xf person_exists.tar

训练

# !echo "测试swin tiny 300Epochs 单卡3天4小时, 4Epochs 约1小时。"
# !cd ~/PaddleClas/ && python3 tools/train.py \
#         -c ~/work/swin_tiny.yaml

评估

# !cd ~/PaddleClas/ && python tools/eval.py \
#     -c ~/work/swin_tiny.yaml \
#     -o Global.pretrained_model=output/SwinTransformer_tiny_patch4_window7_224/best_model

预测

# !cd PaddleClas && python tools/infer.py \
#     -c ~/work/swin_tiny.yaml \
#     -o Global.pretrained_model=output/SwinTransformer_tiny_patch4_window7_224/best_model 

基于训练得到的权重导出 inference 模型

# !cd PaddleClas && python tools/export_model.py \
#     -c ~/work/swin_tiny.yaml \
#     -o Global.pretrained_model=output/SwinTransformer_tiny_patch4_window7_224/best_model \
#     -o Global.save_inference_dir=deploy/models/SwinTransformer_tiny_patch4_window7_224_infer

使用python进行推理

推理图片为"./images/ImageNet/ILSVRC2012_val_00000010.jpeg",让我们看看推理图片的样子:

from PIL import Image
img = Image.open("/home/aistudio/PaddleClas/deploy/images/ImageNet/ILSVRC2012_val_00000010.jpeg")
img

在这里插入图片描述

# !cd ~/PaddleClas/deploy && python python/predict_cls.py \
#     -c configs/inference_cls.yaml \
#     -o Global.inference_model_dir=models/SwinTransformer_tiny_patch4_window7_224_infer

调试

报错’_DataLoaderIterMultiProcess’ object has no attribute ‘_shutdown’

在训练的时候既偶尔又经常有下面的报错:

    for iter_id, batch in enumerate(engine.train_dataloader):
  File "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/reader.py", line 566, in __iter__
    return _DataLoaderIterMultiProcess(self)
  File "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dataloader/dataloader_iter.py", line 379, in __init__
    self._init_workers()
  File "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dataloader/dataloader_iter.py", line 391, in _init_workers
    self._workers_idx_cycle = itertools.cycle(range(self._num_workers))
TypeError: 'float' object cannot be interpreted as an integer
Exception ignored in: <function _DataLoaderIterMultiProcess.__del__ at 0x7fd7a7a15680>
Traceback (most recent call last):
  File "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dataloader/dataloader_iter.py", line 712, in __del__
    self._try_shutdown_all()
  File "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dataloader/dataloader_iter.py", line 503, in _try_shutdown_all
    if not self._shutdown:
AttributeError: '_DataLoaderIterMultiProcess' object has no attribute '_shutdown'
LAUNCH INFO 2022-08-04 23:27:41,651 Exit code 1

这个报错大约在16G显存环境下比较容易出。在后台和脚本任务中,不太容易出此问题。

经百度搜索,问题是:ubuntu交换内存过小的原因导致不能创建新线程。出现这个问题可以尝试调大交换内存。

可以用free命令查看,发现系统的swap为0:

              total        used        free      shared  buff/cache   available
Mem:      528380564    13541812   173683904     1090716   341154848   509119516
Swap:             0           0           0

增大swap交换区的方法

增加swap文件
mkdir swap
cd swap
sudo dd if=/dev/zero of=sfile bs=1024 count=1000000

转化为swap文件
sudo mkswap sfile

4.激活swap文件
sudo swapon sfile

5.查看效果
再次输入:free -m

解决方法一

但是因为在AIStudio项目里,无法进行sudo操作,所以最后是通过加大num_workers来解决,从4和2改为8 。

解决方法二

另外还有一种解决方法,就是把use_shared_memory设为False:‘use_shared_memory: False’

结束语

用飞桨,划时代!让我们荡起双桨,在AI的海洋乘风破浪!

飞桨官网:https://www.paddlepaddle.org.cn

因为水平有限,难免有不足之处,还请大家多多帮助。

作者:段春华, 网名skywalk 或 天马行空,济宁市极快软件科技有限公司的AI架构师,百度飞桨PPDE。

我在AI Studio上获得至尊等级,点亮11个徽章,来关注啊~ https://aistudio.baidu.com/aistudio/personalcenter/thirdview/141218

声明

此项目为搬运
原项目链接

Logo

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

更多推荐