VanillaNet实战:使用VanillaNet实现图像分类(一)

2023-10-27

摘要

论文翻译:https://blog.csdn.net/m0_47867638/article/details/131057152
官方源码:https://github.com/huawei-noah/VanillaNet

VanillaNet是华为在2023年发布的一种极简风格的CNN网络,采用了最普通的CNN网络,却达到了很好的效果。
在这里插入图片描述

这篇文章使用VanillaNet完成植物分类任务,模型采用VanillaNet10向大家展示如何使用VanillaNet。由于没有预训练模型,VanillaNet10在这个数据集上实现了87%的ACC,如下图:

请添加图片描述
请添加图片描述

通过这篇文章能让你学到:

  1. 如何使用数据增强,包括transforms的增强、CutOut、MixUp、CutMix等增强手段?
  2. 如何实现VanillaNet模型实现训练?
  3. 如何使用pytorch自带混合精度?
  4. 如何使用梯度裁剪防止梯度爆炸?
  5. 如何使用DP多显卡训练?
  6. 如何绘制loss和acc曲线?
  7. 如何生成val的测评报告?
  8. 如何编写测试脚本测试测试集?
  9. 如何使用余弦退火策略调整学习率?
  10. 如何使用AverageMeter类统计ACC和loss等自定义变量?
  11. 如何理解和统计ACC1和ACC5?
  12. 如何使用EMA?
  13. 如果使用Grad-CAM 实现热力图可视化?

如果基础薄弱,对上面的这些功能难以理解可以看我的专栏:经典主干网络精讲与实战
这个专栏,从零开始时,一步一步的讲解这些,让大家更容易接受。

安装包

安装timm

使用pip就行,命令:

pip install timm

mixup增强和EMA用到了timm

安装 grad-cam

pip install grad-cam

数据增强Cutout和Mixup

为了提高成绩我在代码中加入Cutout和Mixup这两种增强方式。实现这两种增强需要安装torchtoolbox。安装命令:

pip install torchtoolbox

Cutout实现,在transforms中。

from torchtoolbox.transform import Cutout
# 数据预处理
transform = transforms.Compose([
    transforms.Resize((224, 224)),
    Cutout(),
    transforms.ToTensor(),
    transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])

])

需要导入包:from timm.data.mixup import Mixup,

定义Mixup,和SoftTargetCrossEntropy

  mixup_fn = Mixup(
    mixup_alpha=0.8, cutmix_alpha=1.0, cutmix_minmax=None,
    prob=0.1, switch_prob=0.5, mode='batch',
    label_smoothing=0.1, num_classes=12)
 criterion_train = SoftTargetCrossEntropy()

参数详解:

mixup_alpha (float): mixup alpha 值,如果 > 0,则 mixup 处于活动状态。

cutmix_alpha (float):cutmix alpha 值,如果 > 0,cutmix 处于活动状态。

cutmix_minmax (List[float]):cutmix 最小/最大图像比率,cutmix 处于活动状态,如果不是 None,则使用这个 vs alpha。

如果设置了 cutmix_minmax 则cutmix_alpha 默认为1.0

prob (float): 每批次或元素应用 mixup 或 cutmix 的概率。

switch_prob (float): 当两者都处于活动状态时切换cutmix 和mixup 的概率 。

mode (str): 如何应用 mixup/cutmix 参数(每个’batch’,‘pair’(元素对),‘elem’(元素)。

correct_lam (bool): 当 cutmix bbox 被图像边框剪裁时应用。 lambda 校正

label_smoothing (float):将标签平滑应用于混合目标张量。

num_classes (int): 目标的类数。

EMA

EMA(Exponential Moving Average)是指数移动平均值。在深度学习中的做法是保存历史的一份参数,在一定训练阶段后,拿历史的参数给目前学习的参数做一次平滑。具体实现如下:


import logging
from collections import OrderedDict
from copy import deepcopy
import torch
import torch.nn as nn

_logger = logging.getLogger(__name__)

class ModelEma:
    def __init__(self, model, decay=0.9999, device='', resume=''):
        # make a copy of the model for accumulating moving average of weights
        self.ema = deepcopy(model)
        self.ema.eval()
        self.decay = decay
        self.device = device  # perform ema on different device from model if set
        if device:
            self.ema.to(device=device)
        self.ema_has_module = hasattr(self.ema, 'module')
        if resume:
            self._load_checkpoint(resume)
        for p in self.ema.parameters():
            p.requires_grad_(False)

    def _load_checkpoint(self, checkpoint_path):
        checkpoint = torch.load(checkpoint_path, map_location='cpu')
        assert isinstance(checkpoint, dict)
        if 'state_dict_ema' in checkpoint:
            new_state_dict = OrderedDict()
            for k, v in checkpoint['state_dict_ema'].items():
                # ema model may have been wrapped by DataParallel, and need module prefix
                if self.ema_has_module:
                    name = 'module.' + k if not k.startswith('module') else k
                else:
                    name = k
                new_state_dict[name] = v
            self.ema.load_state_dict(new_state_dict)
            _logger.info("Loaded state_dict_ema")
        else:
            _logger.warning("Failed to find state_dict_ema, starting from loaded model weights")

    def update(self, model):
        # correct a mismatch in state dict keys
        needs_module = hasattr(model, 'module') and not self.ema_has_module
        with torch.no_grad():
            msd = model.state_dict()
            for k, ema_v in self.ema.state_dict().items():
                if needs_module:
                    k = 'module.' + k
                model_v = msd[k].detach()
                if self.device:
                    model_v = model_v.to(device=self.device)
                ema_v.copy_(ema_v * self.decay + (1. - self.decay) * model_v)

加入到模型中。

#初始化
if use_ema:
     model_ema = ModelEma(
            model_ft,
            decay=model_ema_decay,
            device='cpu',
            resume=resume)

# 训练过程中,更新完参数后,同步update shadow weights
def train():
    optimizer.step()
    if model_ema is not None:
        model_ema.update(model)


# 将model_ema传入验证函数中
val(model_ema.ema, DEVICE, test_loader)

针对没有预训练的模型,容易出现EMA不上分的情况,这点大家要注意啊!

项目结构

VanillaNet_Demo
├─data1
│  ├─Black-grass
│  ├─Charlock
│  ├─Cleavers
│  ├─Common Chickweed
│  ├─Common wheat
│  ├─Fat Hen
│  ├─Loose Silky-bent
│  ├─Maize
│  ├─Scentless Mayweed
│  ├─Shepherds Purse
│  ├─Small-flowered Cranesbill
│  └─Sugar beet
├─models
│  └─vanillanet.py
├─mean_std.py
├─makedata.py
├─train.py
├─cam_image.py
└─test.py

models:来源官方代码,对面的代码做了一些适应性修改。增加了一些加载预训练,调用模型的逻辑。
mean_std.py:计算mean和std的值。
makedata.py:生成数据集。
ema.py:EMA脚本
train.py:训练SeaFormer模型
cam_image.py:热力图可视化

计算mean和std

为了使模型更加快速的收敛,我们需要计算出mean和std的值,新建mean_std.py,插入代码:

from torchvision.datasets import ImageFolder
import torch
from torchvision import transforms

def get_mean_and_std(train_data):
    train_loader = torch.utils.data.DataLoader(
        train_data, batch_size=1, shuffle=False, num_workers=0,
        pin_memory=True)
    mean = torch.zeros(3)
    std = torch.zeros(3)
    for X, _ in train_loader:
        for d in range(3):
            mean[d] += X[:, d, :, :].mean()
            std[d] += X[:, d, :, :].std()
    mean.div_(len(train_data))
    std.div_(len(train_data))
    return list(mean.numpy()), list(std.numpy())

if __name__ == '__main__':
    train_dataset = ImageFolder(root=r'data1', transform=transforms.ToTensor())
    print(get_mean_and_std(train_dataset))

数据集结构:

image-20220221153058619

运行结果:

([0.3281186, 0.28937867, 0.20702125], [0.09407319, 0.09732835, 0.106712654])

把这个结果记录下来,后面要用!

生成数据集

我们整理还的图像分类的数据集结构是这样的

data
├─Black-grass
├─Charlock
├─Cleavers
├─Common Chickweed
├─Common wheat
├─Fat Hen
├─Loose Silky-bent
├─Maize
├─Scentless Mayweed
├─Shepherds Purse
├─Small-flowered Cranesbill
└─Sugar beet

pytorch和keras默认加载方式是ImageNet数据集格式,格式是

├─data
│  ├─val
│  │   ├─Black-grass
│  │   ├─Charlock
│  │   ├─Cleavers
│  │   ├─Common Chickweed
│  │   ├─Common wheat
│  │   ├─Fat Hen
│  │   ├─Loose Silky-bent
│  │   ├─Maize
│  │   ├─Scentless Mayweed
│  │   ├─Shepherds Purse
│  │   ├─Small-flowered Cranesbill
│  │   └─Sugar beet
│  └─train
│      ├─Black-grass
│      ├─Charlock
│      ├─Cleavers
│      ├─Common Chickweed
│      ├─Common wheat
│      ├─Fat Hen
│      ├─Loose Silky-bent
│      ├─Maize
│      ├─Scentless Mayweed
│      ├─Shepherds Purse
│      ├─Small-flowered Cranesbill
│      └─Sugar beet

新增格式转化脚本makedata.py,插入代码:

import glob
import os
import shutil

image_list=glob.glob('data1/*/*.png')
print(image_list)
file_dir='data'
if os.path.exists(file_dir):
    print('true')
    #os.rmdir(file_dir)
    shutil.rmtree(file_dir)#删除再建立
    os.makedirs(file_dir)
else:
    os.makedirs(file_dir)

from sklearn.model_selection import train_test_split
trainval_files, val_files = train_test_split(image_list, test_size=0.3, random_state=42)
train_dir='train'
val_dir='val'
train_root=os.path.join(file_dir,train_dir)
val_root=os.path.join(file_dir,val_dir)
for file in trainval_files:
    file_class=file.replace("\\","/").split('/')[-2]
    file_name=file.replace("\\","/").split('/')[-1]
    file_class=os.path.join(train_root,file_class)
    if not os.path.isdir(file_class):
        os.makedirs(file_class)
    shutil.copy(file, file_class + '/' + file_name)

for file in val_files:
    file_class=file.replace("\\","/").split('/')[-2]
    file_name=file.replace("\\","/").split('/')[-1]
    file_class=os.path.join(val_root,file_class)
    if not os.path.isdir(file_class):
        os.makedirs(file_class)
    shutil.copy(file, file_class + '/' + file_name)

完成上面的内容就可以开启训练和测试了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

VanillaNet实战:使用VanillaNet实现图像分类(一) 的相关文章

  • Android小项目集合100多个

    是学习和联系Android小项目的使用资料 排名完全是根据 GitHub 搜索 Java 语言选择 Best Match 得到的结果 然后过滤了跟 Android 不相关的项目 所以排名并不具备任何官方效力 仅供参考学习 方便初学者快速了解
  • C++11 promise

    目录 一 promise 二 定义 三 成员函数 四 get future 五 设置结果 1 set value 2 set value at thread exit 3 set exception 4 set exception at t
  • java消费者接口应用_几个常用的Java接口之Consumer、Supplier、Predicate和Function

    Java8之Consumer Supplier Predicate和Function攻略 来聊聊Consumer Supplier Predicate Function这几个接口的用法 在 Java8 的用法当中 这几个接口虽然没有明目张胆

随机推荐

  • 嵌入式(文件属性、目录操作)

    打开目录 include
  • 【目标检测】基于yolov5的火灾烟雾检测和识别(附代码和数据集)

    写在前面 首先感谢兄弟们的订阅 让我有创作的动力 在创作过程我会尽最大能力 保证作品的质量 如果有问题 可以私信我 让我们携手共进 共创辉煌 文末附项目代码和数据集 请看检测效果 1 介绍 火灾烟雾检测和识别是指通过计算机视觉技术 对火灾现
  • 设计模式(UML类图)

    UML 基本介绍 UML Unified modeling language UML 统一建模语言 是一种用于软件系统分析和设计的语言工具 它用于帮助软件开发人员进行思考和记录思路的结果 UML 本身是一套符号的规定 就像数学符号和化学符号
  • Java高阶面试问答-分布式

    理论 CAP原则又称CAP定理 指的是在一个分布式系统中 Consistency 一致性 Availability 可用性 Partition tolerance 分区容错性 三者不可兼得 一致性 C 对某个指定的客户端来说 读操作能返回最
  • git:git的使用资料(一)

    1 什么是git git是一个分布式的版本控制软件 为什么要做版本控制 要保留之前所有的版本 以便回滚和修改 中心有很多例如github 码云 csdn等 2 安装git 我以win10为例 官网下载应用程序点击此处下载 双击运行 一直点击
  • char显示为4个字节

    char显示为4个字节 问题描述 问题解决 问题分析 问题描述 读取bmp图像长宽 用char型数组存储 但是读出来的却不是预期的1个字节的值 而是4个字节的 数组是有初始化的 问题解决 强转为unsinged char处理 正常 问题分析
  • 0欧姆电阻能流过无穷大电流吗

    电阻有插件电阻和贴片电阻 电阻的功率P II R 那么有的同学就要问了 我们0R的电阻是不是可以流过无穷打的电流呢 答案是否定的 其实我们可以在电阻的规格书上找到答案 我以普通贴片电阻为例 大家可以看下贴片电阻数据手册中标有jumper这个
  • JetBrains CLion/IDEA/PyCharm字体、Tab退四格、编译器和解释器设置

    文章目录 CLion设置代码字体大小 设置Tab键退四格 安装cygwin编译器 设置project编译器 IDEA设置代码字体大小 设置Tab键退四格 设置project解释器 project添加第三方jar包 PyCharm设置代码字体
  • Apache

    看到这个有没有想到阿帕奇 武装直升机 显然他不是呀 下面让我们一起了解一下Apache吧 一 概述 Apache是一个开源的 多平台 可扩展的Web服务器软件 它由Apache软件基金会开发和维护 目前是互联网上使用最广泛的Web服务器软件
  • 多台群晖实现按计划WOL网络自动唤醒数据冷备份

    几年前买了2盘位的DS218 但是随着照片的增加已经不够用 年中购入了4盘位的群晖DS923 2块16T西数数企业级硬盘 1块2T intel企业级 SSD 1 什么是冷备份 冷备是离线备份 备份好的数据可以单独存取 定期冷备可以保证数据安
  • 浅谈初次做外包项目及背后的思考

    谈起外包经历 我的第一次外包源自前两年某天陪着女友逛商场时 接到一个朋友的电话 朋友兴高采烈地跟我介绍一个大项目 需求不多 钱不少 难度不大 口气不小 我一听心动了 原以为要赚一笔 easy money 后面再看看 这次外包踩了大大小小不少
  • 手撕哈希表(HashTable)——C++高阶数据结构详解

    目录 传统艺能 概念 哈希碰撞 哈希函数 解决哈希冲突 闭散列 开散列 闭散列实现 数据插入 数据查找 数据删除 开散列实现 插入数据 查找数据 数据删除 利用素数来规定哈希表大小 实现方案 传统艺能 小编是双非本科大一菜鸟不赘述 欢迎米娜
  • 镜头选型——景深计算

    正在上传 重新上传取消 1 概述 先看两个例子 拍摄花 昆虫等照片时 背景拍的比较模糊 突出被拍物 但当拍摄纪念照 风景等照片时 却会把背景拍摄得和被拍对象一样清晰 这两者就是不同景深 前者为浅景深 拍摄聚焦到被拍物上 只能拍清一小段距离
  • JavaScript let 和 const

    在JavaScript中 let 和 const 是用于声明变量的关键字 let 关键字用于声明一个块级作用域的变量 块级作用域是指在一个代码块 通常是在花括号 内部 中声明的变量只在该代码块内部有效 例如 javascript funct
  • MATLAB使用Simulink 进行建模与仿真方法 - Simulink基本操作与入门教程

    Simulink 是 MATLAB 很强大的功能组件 广泛用于系统建模 仿真和分析 下面分享给大家MATLAB使用Simulink 进行建模与仿真方法 步骤 希望能够帮助大家 1 工具 原料 电脑 MATLAB及Simulink 组件 MA
  • 对于产业互联网参与者来讲,只需要重构穿传统意义上的生产关系即可

    消费互联网模式的固定思维 让玩家们想当然地认为 所谓的产业互联网 仅仅只是一种重构生产关系的过程 对于产业互联网的玩家们来讲 他们只需要重构穿传统意义上的生产关系即可 正是在这样一种思维的影响之下 我们才看到了以新零售为代表的诸多看似新物种
  • 2023最新版本Activiti7系列-网关服务

    网关篇 网关可控制流程的执行流向 常用于拆分或合并复杂的流程场景 在Activiti7中 有以下几种类型的网关 排他网关 Exclusive Gateway 用于在流程中进行条件判断 根据不同的条件选择不同的分支路径 只有满足条件的分支会被
  • 使用python读取gif,合并gif,视频转换为gif

    一 将视频转换为gif 采用opencv读取gif图并使用imageio转换 import cv2 import imageio def read video video path video cap cv2 VideoCapture vi
  • centos7 从python 2.7升级到python 3.6

    1 检查之前系统的python版本 root localhost python Python 2 7 5 default Apr 2 2020 13 16 51 GCC 4 8 5 20150623 Red Hat 4 8 5 39 on
  • VanillaNet实战:使用VanillaNet实现图像分类(一)

    文章目录 摘要 安装包 安装timm 安装 grad cam 数据增强Cutout和Mixup EMA 项目结构 计算mean和std 生成数据集 摘要 论文翻译 https blog csdn net m0 47867638 articl