基于 PaddleClas的热轧钢带表面缺陷分类
- 任务目标:针对热轧钢带表面缺陷数据进行缺陷分类
- 背景介绍:
- 随着人工智能技术的兴起,我国的制造业也迎来了新的转型时机。融合使用新一代信息技术和人工智能技术已成为制造业重要发展趋势,对制造业未来的发展方向和模式产生了深远的影响。钢铁行业作为传统制造业一直发展到现在,正面临着越来越严峻的生存环境,安全、成本、质量等都是目前急需解决的难题和难点,而中国制造2025战略背景下智慧制造显然是转型升级新的机遇窗口。制造业与人工智能结合应运而生。
- 在热轧带钢的生产过程中受制造工艺、生产条件和原材料质量等相关问题的制约,生产出的工业产品往往存在缺陷。热轧带钢表面缺陷种类较多,其中常见的六种典型表面缺陷即轧制氧化皮,斑块,开裂,点蚀表面,内含物和划痕。这些缺陷会在带钢后续产品的使用过程中造成隐患,导致制造产品质量下降。因此准确、清楚且快速的了解缺陷的类型、位置和形状,对其做出检测和判断是非常有必要的。
- 在实际操作的过程中,我们往往不需要一步到位定位缺陷的位置,在初期质检中仅需要判断缺陷种类满足当下快速的质检工艺即可。因此考虑使用Paddlecla搭建热轧钢带表面缺陷分类模型。
- 最终效果:笔者尝试使用PaddleClas对东北大学热轧钢带数据集中的6类表面缺陷进行分类,经过调试最终在测试集上可达100%的准确率。
1. 数据集简介
收集了热轧钢带的六种典型表面缺陷即轧制氧化皮(RS),斑块(Pa),开裂(Cr),点蚀表面(PS),内含物(In)和划痕(Sc)。该数据库包括 1,800 个灰度图像:六种不同类型的典型表面缺陷中的每一个300 个样本,图像的原始分辨率为 200×200。
下表列出了六种典型表面缺陷的类型与外观特征:
缺陷类型 | 外观特征 |
---|---|
轧制氧化皮 | 通常呈小斑点、鱼鳞状、条状、块状不规则分布于带钢上、下表面的全部或局部,常伴有粗糙的麻点状表面。 |
斑块 | 在带钢的表面呈现片状或大面积的斑迹,个别的情况下在某个角度上有向外辐射的迹象。 |
点蚀表面 | 带钢表面有局部的或连续的粗糙面,严重时呈桔皮状。在上下表面都可能出现,而且在整个带钢长度方向上的密度不均。 |
开裂 | 轧件表面一种不连续的裂纹,它以某点为中心,以闪电状向外发散。 |
内含物 | 板带钢表面的薄层折叠,缺陷常呈灰白色,其大小、形状不一,不规则分布于板带钢表面。 |
划痕 | 轧件表面的机械损伤,其长度、宽度、深度各异。主要出现在沿轧制方向或垂直于轧制方向上。 |
- 如图展示了六种典型表面缺陷的样本图像。类内缺陷在外观上存在很大差异,例如,划痕(最后一列)可能是水平划痕,垂直划痕和倾斜划痕等。同样,类间缺陷也具有类似情况,例如,氧化皮,裂纹和表面凹痕。另外,由于照明和材料变化的影响,类内缺陷图像的灰度是变化的。
- 简而言之,NEU 表面缺陷数据库包括两个困难的挑战,即类内缺陷在外观上存在较大差异,类间缺陷具有相似问题。
2. PaddleClas
本项目中我们尝试使用 PaddleClas 图像分类套件对热轧钢带表面缺陷分类,最终在测试集图像上达到了 100% 的准确率。
PaddleClas图像分类开发套件,内置丰富的模型库,支持高阶优化(模型压缩与数据增广),特色拓展应用,工业级部署工具。
获取PaddleClas , 可以使用git clone https://github.com/paddlepaddle/paddleclas
下载PaddleClas,也可以使用本项目已下载好的PaddleClas
!unzip -q data/data47421/PaddleClas.zip
3. 数据处理
解压数据集,将数据集移动至指定位置,生成训练集与测试集
- 需要注意的是:
- 数据集存放的位置与生成的数据列表文件中的数据路径需要与配置文件对应,这也是初学者时常出现问题的地方。
- 数据列表文件中路径与标签之间的分割符号,行与行之间的换行符号
- 有些特定字符转义之后出现问题
!unzip -q data/data47260/NEU-CLS.zip
!mv NEU-CLS PaddleClas/dataset
import codecs
import os
import random
import shutil
from PIL import Image
train_ratio = 4.0 / 5
all_file_dir = 'PaddleClas/dataset/NEU-CLS'
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/NEU-CLS/train.txt
整体思路
- 选择使用的模型,根据自己的任务难度及数据的特点,选择适合自己数据的模型进行训练,本文选择ResNet_vd模型训练关于ResNet网络结构的代码分析可移步这里查看ResNet网络结构代码分析
- 根据选定的模型更改PaddleClas/configs下对应的配置文件参数。例如类别数,迭代次数,batch_size,数据集路径等等。为了查看方便本文将PaddleClas/configs/ResNet/ResNet50_vd.yaml移只根目录改名为neu.yaml,在训练时,修改配置文件的路径即可。
- 利用更改后的配置文件训练模型,使用训练技巧与不同的参数设置调优模型并选择最优模型进行推理预测(更多的训练技巧与参数设置,可以关注 https://paddleclas.readthedocs.io/zh_CN/latest/models/Tricks.html 其对相关参数进行了详细的解释说明)
- 下面逐步说明各部分:
4. 训练模型
训练模型分为一下几个步骤:
- 修改配置文件。查看使用模型对应的配置文件并修改配置文件中一些必要的参数,本项目已经根据数据集适配了配置文件neu.yaml
- 切换路径,设置环境变量
- 训练模型
配置文件说明
下面展示了配置文件中不同模块参数的含义
基础配置
mode: "train" 运行模式 2中选择["train"," valid"]
ARCHITECTURE: 模型结构名字
name: 'ResNet50_vd'
pretrained_model: "" 预训练模型路径
model_save_dir: "./output/" 保存模型路径
classes_num: 6 分类数
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/NEU-CLS/train.txt" train文件列表
data_dir: "./dataset/NEU-CLS" 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/NEU-CLS/eval.txt"
data_dir: "./dataset/NEU-CLS"
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"
关于预训练模型
- 如需使用预训练模型,则执行下面的脚本,并在训练脚本后增加
-o pretrained_model=./ResNet50_vd_pretrained
,不使用预训练模型则跳过下面的执行语句。
!python ../download_model.py ResNet50_vd_pretrained
!mv ../ResNet50_vd_pretrained ./
开始训练
- 执行训练脚本,使用已经配置好的neu.yaml文件
- 训练脚本指令如下:PaddleClas通过launch方式启动多卡多进程训练 通过设置FLAGS_selected_gpus 指定GPU运行卡号
python -m paddle.distributed.launch --selected_gpus="0" tools/train.py -c ./configs/ResNet/ResNet50_vd.yaml
!python -m paddle.distributed.launch --selected_gpus="0" tools/train.py -c ../neu.yaml
5. 模型评估
首先需要修改评估所需的配置文件,修改方式有两种,
- 直接修改configs/eval.yaml
- -o参数更新配置
这里建议直接修改configs/eval.yaml,使用-o设置需要评估的模型路径 较为方便
评估脚本代码如下:需要注意的是加载模型时,需要指定模型的前缀,如模型参数所在的文件夹为output/ResNet50_vd/19,模型参数的名称为output/ResNet50_vd/19/ppcls.pdparams,则pretrained_model参数需要指定为output/ResNet50_vd/19/ppcls,PaddleClas会自动补齐.pdparams的后缀。
!python -m paddle.distributed.launch --selected_gpus="0" tools/eval.py \
-c ./configs/eval.yaml \
-o pretrained_model=output/ResNet50_vd/best_model/ppcls
评估模型
!python -m paddle.distributed.launch --selected_gpus="0" tools/eval.py \
-c ../eval.yaml \
-o pretrained_model=output/ResNet50_vd/best_model/ppcls
评估结果展示
2020-08-03 23:58:36 INFO: eval step:0 loss: 0.4387 top1: 0.8906 top5: 1.0000 elapse: 0.690s
2020-08-03 23:58:37 INFO: END eval loss_avg: 0.6619 top1_avg: 0.9000 top5_avg: 0.9969 elapse_sum: 1.102ss
INFO 2020-08-03 15:58:39,735 launch.py:223] Local procs complete, POD info:rank:0 id:None addr:127.0.0.1 port:None visible_gpu:[] trainers:["gpu:['0'] endpoint:127.0.0.1:56541 rank:0"]
上面展示的结果是笔者训练50个epochs,在测试集上top1的准确率为0.9000,经过测试在进行400次迭代之后会达到100%的准确率。
关于PaddlePaddle模型的保存方式
- persistable 模型(fluid.save_persistabels保存的模型) 一般做为模型的 checkpoint,可以加载后重新训练。persistable 模型保存的是零散的权重文件,每个文件代表模型中的一个 Variable,这些零散的文件不包含结构信息,需要结合模型的结构一起使用。
- inference 模型(fluid.io.save_inference_model保存的模型) 一般是模型训练完成后保存的固化模型,用于预测部署。与 persistable 模型相比,inference 模型会额外保存模型的结构信息,用于配合权重文件构成完整的模型。如下所示,model 中保存的即为模型的结构信息。
6. 模型推理
由于模型的保存方式以及选择引擎的不同,PaddlePaddle衍生出三种方式进行预测推理:
- 预测引擎 + inference 模型
- 训练引擎 + persistable 模型
- 训练引擎 + inference 模型
本文选择使用 预测引擎 + inference模型 的方式进行推理,执行步骤如下:
- 对训练好的模型进行转换固化
- 通过预测引擎和inference模型进行推理
保存 inference 模型:
在训练过程中保存的persistable 模型中选择最优模型,本文设置在训练的同时进行模型评估并保存效果最好的persistable模型,命令如下:
python tools/export_model.py --m=模型名称 --p=persistable 模型路径 --o=model和params保存路径
预测引擎 + inference 模型推理预测:
命令如下:
python ./tools/infer/predict.py -i=./test.jpeg -m=./resnet50-vd/model -p=./resnet50-vd/params --use_gpu=1
需要注意的是对应的模型权重文件路径需要确认,其他的参数设置如下所示:
参数说明:
- image_file(简写 i):待预测的图片文件路径,如 ./test.jpeg
- model_file(简写 m):模型文件路径,如 ./resnet50-vd/model
- params_file(简写 p):权重文件路径,如 ./resnet50-vd/params
- batch_size(简写 b):批大小,如 1
- ir_optim:是否使用 IR 优化,默认值:True
- use_tensorrt:是否使用 TesorRT 预测引擎,默认值:True
- gpu_mem: 初始分配GPU显存,以M单位
- use_gpu:是否使用 GPU 预测,默认值:True
- enable_benchmark:是否启用benchmark,默认值:False
- model_name:模型名字
- 注意: 当启用benchmark时,默认开启tersorrt进行预测
!python tools/export_model.py \
--model='ResNet50_vd' \
--pretrained_model=output/ResNet50_vd/best_model/ppcls \
--output_path=./inference
!python tools/infer/predict.py \
-m inference/model \
-p inference/params \
-i "dataset/NEU-CLS/Rs/RS_5.bmp" \
--use_gpu=1
- 下面为Rs(氧化皮)瑕疵的图片:
预测结果展示:
2020-08-03 15:58:48,332-INFO: class: 3
2020-08-03 15:58:48,332-INFO: score: 0.8259640336036682
可以看到预测正确,Rs的标签为3
7. 总结
- 本文使用了PaddleClas在热轧钢带表面缺陷数据进行训练
- 使用训练好的ResNet_vd模型对热轧钢带表面图片数据进行分类预测
本文转自百度的aistudio上的文章,原作者的主页:zhou_lu的主页