图像分类MMClassification
一、前提环境
-
我连接的GPU环境,下面几个环境是需要先下载whl然后进行安装
GPU选择的AtuoDL,够个人学习使用,价格亲民,不用配置cuda环境,已经配置好
-
CPU环境的话直接用
pip install 环境包==版本号
安装即可
1.1、安装版本
库 | 版本号 |
---|---|
pytorch | 1.11.0+cu113 |
torchvision | 0.12.0+cu113 |
mmcv-full | 1.5.0 |
其余包直接pip install
安装即可
1.2、安装库
二、安装下载项目
2.1、github项目 mmclassification
2.2、mmclassification 安装文档
三、项目解析
3.1、项目结构
3.2、分类模型构成
1000种
3.3、分类模型构建
说明
代码目录
对应代码(以resnet18_8xb32_in1k
模型为例)
configs->resne->resnet18_8xb32_in1k.py
中可以看到restnet18.py
文件目录
- 找到
restnet18.py
目录可以看到对应代码
- 对应的模型可以在
mmcls->models
里面相应的目录里面找到相应的类
举例
- 对应的模型可以在
3.4、数据集构建
说明
代码目录
对应代码(以resnet18_8xb32_in1k
模型为例)
configs->resne->resnet18_8xb32_in1k.py
中可以看到restnet18.py
文件目录
- 找到
imagenet_bs32.py
目录可以看到对应代码
- 对应的模型可以在
mmcls->datasets
里面相应的目录里面找到相应的类
图中蓝紫色:data中存放数据,之后自己定义的数据集也放到这里
图中红色代表模型的位置
3.5、数据加载流水线
说明
代码目录
对应代码(以resnet18_8xb32_in1k
模型为例)
- 和上面的数据集构建在同一个文件
imagenet_bs32.py
中
- 对应的模型可以在
mmcls->datasets->pipelines
里面相应的目录里面找到相应的类
3.6、配置学习策略
说明
代码目录
对应代码(以resnet18_8xb32_in1k
模型为例)
configs->resne->resnet18_8xb32_in1k.py
中可以看到imagenet_bs256.py
文件目录
- 找到
imagenet_bs256.py
目录可以看到对应代码
- 对应的模型可以在
mmcls->core
里面相应的目录里面找到相应的类
四、训练自己的数据集模型
4.1、执行train.py
执行tools->train.py
-
配置执行环境
Parameters中可以直接写路径,也可以--config 路径
(以resnet18_8xb32_in1k.py
模型为例)
-
执行结果报错不用管(相应的目录文件找不到)
4.2、修改生成文件
4.2.1、找到生成文件
在tools->work_dirs
中会生成一个文件夹,点开会看到生成的resnet18_8xb32_in1k.py
文件
生成的代码将上面几个模型混到一个文件中,如果想分离可以按照上述目录分离,这里就不在分离
# 模型
model = dict(
type='ImageClassifier',
backbone=dict(
type='ResNet',
depth=18,
num_stages=4,
out_indices=(3, ),
style='pytorch'),
neck=dict(type='GlobalAveragePooling'),
head=dict(
type='LinearClsHead',
num_classes=1000,
in_channels=512,
loss=dict(type='CrossEntropyLoss', loss_weight=1.0),
topk=(1, 5)))
img_norm_cfg = dict(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
# 数据加载流水线
train_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='RandomResizedCrop', size=224),
dict(type='RandomFlip', flip_prob=0.5, direction='horizontal'),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='ImageToTensor', keys=['img']),
dict(type='ToTensor', keys=['gt_label']),
dict(type='Collect', keys=['img', 'gt_label'])
]
test_pipeline = [
dict(type='LoadImageFromFile'),
dict(type='Resize', size=(256, -1)),
dict(type='CenterCrop', crop_size=224),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img'])
]
# 数据
dataset_type = 'ImageNet'
data = dict(
samples_per_gpu=32,
workers_per_gpu=2,
train=dict(
type='ImageNet',
data_prefix='data/imagenet/train',
pipeline=[
dict(type='LoadImageFromFile'),
dict(type='RandomResizedCrop', size=224),
dict(type='RandomFlip', flip_prob=0.5, direction='horizontal'),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='ImageToTensor', keys=['img']),
dict(type='ToTensor', keys=['gt_label']),
dict(type='Collect', keys=['img', 'gt_label'])
]),
val=dict(
type='ImageNet',
data_prefix='data/imagenet/val',
ann_file='data/imagenet/meta/val.txt',
pipeline=[
dict(type='LoadImageFromFile'),
dict(type='Resize', size=(256, -1)),
dict(type='CenterCrop', crop_size=224),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img'])
]),
test=dict(
type='ImageNet',
data_prefix='data/imagenet/val',
ann_file='data/imagenet/meta/val.txt',
pipeline=[
dict(type='LoadImageFromFile'),
dict(type='Resize', size=(256, -1)),
dict(type='CenterCrop', crop_size=224),
dict(
type='Normalize',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
to_rgb=True),
dict(type='ImageToTensor', keys=['img']),
dict(type='Collect', keys=['img'])
]))
# 策略
evaluation = dict(interval=1, metric='accuracy')
optimizer = dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=None)
lr_config = dict(policy='step', step=[30, 60, 90])
runner = dict(type='EpochBasedRunner', max_epochs=100)
checkpoint_config = dict(interval=1)
log_config = dict(interval=100, hooks=[dict(type='TextLoggerHook')])
dist_params = dict(backend='nccl')
log_level = 'INFO'
load_from = None
resume_from = None
workflow = [('train', 1)]
work_dir = './work_dirs/resnet18_8xb32_in1k'
gpu_ids = [0]
将生成文件resnet18_8xb32_in1k.py
放入configs->resnet
中并修改名称(today20221029_resnet18_8xb32_in1k.py
)
4.2.2、修改生成文件
-
在目录
mmcls->data
中存放自己的数据集(flower_data数据集没有标签,以文件夹名称为标签)
- 修改文件
today20221029_resnet18_8xb32_in1k.py
中的数据集路径
- 修改文件
-
修改标签总类别数目
-
修改模型
mmcls->datasets->imagenet
为imagenet2
,改变里面的总类别1000->102复制一份,然后修改文件名字和类的名字
修改里面的内容CLASSE列表为102类型
注册模型:在同目录下面的_init_.py
文件中完成注册,具体操作如下
注意:将目录文件设置为根目录文件,否则会报错
KeyError: ‘ImageNet2 is not in the dataset registry’
4.2.3、数据集修改
将当前数据集flower_data修改成有标签的格式,这一步可以忽略
执行filelist.py
,生成相应的目录结构
将文件today20221029_resnet18_8xb32_in1k.py
复制一份文件改名为today_resnet18_8xb32_in1k.py
,数据集路径替换,并且添加数据集标签路径
filelist.py
的代码
import numpy as np
import os
import shutil
train_path = './train'
train_out = './train.txt'
val_path = './valid'
val_out = './val.txt'
data_train_out = './train_filelist'
data_val_out = './val_filelist'
def get_filelist(input_path,output_path):
with open(output_path,'w') as f:
for dir_path,dir_names,file_names in os.walk(input_path):
if dir_path != input_path:
label = int(dir_path.split('/')[-1]) -1
#print(label)
for filename in file_names:
f.write(filename +' '+str(label)+"\n")
def move_imgs(input_path,output_path):
for dir_path, dir_names, file_names in os.walk(input_path):
for filename in file_names:
#print(os.path.join(dir_path,filename))
source_path = os.path.join(dir_path,filename)
shutil.copyfile(source_path, os.path.join(output_path,filename))
get_filelist(train_path,train_out)
get_filelist(val_path,val_out)
move_imgs(train_path,data_train_out)
move_imgs(val_path,data_val_out)
4.3、再次执行train.py文件
-
将Parameters中的路径文件更改为上面更改的名字
today20221029_resnet18_8xb32_in1k.py
运行结果- 损失 loss 在下降
- 准确率 accuracy 在提升
-
将Parameters中的路径文件更改为上面更改的名字
today_resnet18_8xb32_in1k.py
运行结果(截图没有跑完)
在路径tools->work_dirs->resnet18_8xb32_in1k
下回生成 epoch_50.pth,epoch_100.pth,latest.pth模型
五、训练结果测试与验证
5.1测试demo效果
- 找到demo下面的
image_demo.py
文件
- 相关参数配置
- 运行结果
预测结果正确 1 分类 (下标从0开始的)
5.2 测试评估模型效果
- 找到
tools->test.py
文件,并新建存放测试结果的目录val_result
- 相关参数配置
- 训练结果展示
六、训练模型参数替换及改进
预训练模型
-
下载
从github项目中选取对应的模型
找到自己训练的模型,下载
-
下载完之后放到相应的目录,在代码中写入相关的加载路径
七、数据增强流程可视化
数据处理可视化
不需要训练好的模型,处理过程的可视化
数据处理流程(旋转平移等操作)可视化(vis_pipeline.py
)
- 配置参数
../../configs/resnet/today_resnet18_8xb32_in1k.py --output-dir ../work_dirs/resnet18_8xb32_in1k/vis/vis_pipeline --phase train --number 10 --mode pipeline
- 输出结果
- 切换参数,将上面的pipeline切换成concat
- 输出结果
数据核心区域可视化
整体流程
-
需要训练好的模型
-
使用命令
pip install "grad-cam>=1.3.6"
实现,注意版本要求 -
配置文件(如果linux是系统运行会报错,不能展示图片,请点击链接)
image_05094.jpg ../../configs/resnet/today_resnet18_8xb32_in1k.py ../work_dirs/resnet18_8xb32_in1k/epoch_101.pth
-
输出结果
参数调节
- 输出结果为最后一层
- 调节层数
281类别为猫
238类别为狗 - 输出结果
281猫的输出
238狗的输出
模型输出
输入参数 --preview-model
可以打印模型参考
打印出模型的原因
输出结果
ImageClassifier(
(backbone): ResNet(
(conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
(layer1): ResLayer(
(0): BasicBlock(
(conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(drop_path): Identity()
)
(1): BasicBlock(
(conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(drop_path): Identity()
)
)
(layer2): ResLayer(
(0): BasicBlock(
(conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(downsample): Sequential(
(0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(drop_path): Identity()
)
(1): BasicBlock(
(conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(drop_path): Identity()
)
)
(layer3): ResLayer(
(0): BasicBlock(
(conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(downsample): Sequential(
(0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(drop_path): Identity()
)
(1): BasicBlock(
(conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(drop_path): Identity()
)
)
(layer4): ResLayer(
(0): BasicBlock(
(conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(downsample): Sequential(
(0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(drop_path): Identity()
)
(1): BasicBlock(
(conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(drop_path): Identity()
)
)
)
init_cfg=[{'type': 'Kaiming', 'layer': ['Conv2d']}, {'type': 'Constant', 'val': 1, 'layer': ['_BatchNorm', 'GroupNorm']}]
(neck): GlobalAveragePooling(
(gap): AdaptiveAvgPool2d(output_size=(1, 1))
)
(head): LinearClsHead(
(compute_loss): CrossEntropyLoss()
(compute_accuracy): Accuracy()
(fc): Linear(in_features=512, out_features=1000, bias=True)
)
init_cfg={'type': 'Normal', 'layer': 'Linear', 'std': 0.01}
)
Please remove `--preview-model` to get the CAM.
Process finished with exit code 0
重新选择层数配置参数
输出结果
数据集分享
flower_data数据集
链接:https://pan.baidu.com/s/10NdA6nJZC5xK5ozq5ADaPA
提取码:wuts
相关文章
- Word控件Spire.Doc 转换教程(十一):如何将 HTML 转换为图像
- Google Earth Engine(GEE)—Julia如何使用 EE Julia API 对数据进行采样和对图像进行分类
- Paddle 环境中 使用LeNet在MNIST数据集实现图像分类
- 图像特征提取之LBP特征
- 基于2D图像的人脸三维模型重建
- 计算机视觉+人工智能面试笔试总结——使用CNN进行图像分类
- k-mean图像分类
- 图像的形态学梯度运算(基本梯度、外部梯度、内部梯度、X方向梯度、Y方向梯度)的概念、作用以及相关的OpenCV示例代码
- Linux有问必答:如何在命令行下压缩JPEG图像
- Visionpro实现多图像拼接
- 关于 h5 获取摄像头图像
- 快速构建ML Kit自定义模型,实现特定领域图像/文本分类
- 《Arduino家居安全系统构建实战》——1.2 经典的机器学习问题:图像分类
- 《HTML5游戏编程核心技术与实战》一2.3 图像API
- Swift - 图像控件(UIImageView)的用法
- 【转载】 优必选悉尼 AI 研究院何诗怡:基于课程学习的强化多标签图像分类算法 | 分享总结
- 解决Cacti监控图像断断续续问题
- IEEE院士华先胜加入阿里云 主攻视频图像领域AI技术研发
- 如何给图像分类
- 【Computer Vision实战】一、使用OpenCV进行图像全景拼接(基于Python3)
- 【openVINO+paddle】CPU部署新冠肺炎CT图像分类识别与病害分割
- 图像切割之(五)活动轮廓模型之Snake模型简单介绍
- Tensorflow最简单实现ResNet50残差神经网络,进行图像分类,速度超快
- OpenCV:图像的合并和切分