第一步:准备数据
蔬菜数据集,英文为Vegetable。
train 目录下有15000 张图片。
共十五种植物的幼苗图片集,分别为classes = ['Bean', 'Bitter_Gourd', 'Bottle_Gourd', 'Brinjal', 'Broccoli', 'Cabbage', 'Capsicum', 'Carrot', 'Cauliflower', 'Cucumber', 'Papaya', 'Potato', 'Pumpkin', 'Radish', 'Tomato' ]
具体信息如下:
第二步:搭建模型
ShuffleNet_V2是由旷视科技的Ma, Ningning等人在《ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design【ECCV-2018】》【论文地址】一文中提出的改进模型,论文中提出了高效网络架构设计的两大原则:第一,使用直接指标(如速度)而非间接指标(如FLOPs);第二,提出了四条与跨平台的设计指南,并在该指南指导下设计了ShuffleNet_V2
ShuffleNetV2的一些关键特点:
-
优化的分组卷积:ShuffleNetV2使用了一种称为“channel split”的技术,将输入通道分成两半,分别进行不同的处理,然后合并结果以获得更好的性能1。
-
自适应分组卷积:ShuffleNetV2根据输入数据动态调整分组数量,以实现更高的效率1。
-
多尺度特征融合:引入了多尺度特征融合模块,以更好地捕捉不同尺度的特征1。
-
通道剪枝:应用通道剪枝策略来进一步减少计算复杂度,同时保持准确性1。
-
内存访问成本最小化:ShuffleNetV2试图最小化内存访问成本(MAC),通过精细调整组的数量和结构,找到了计算效率和模型性能之间的最佳平衡点2。
-
直接面向实际运行速度的优化:在设计过程中,除了理论上的计算量(FLOPs)外,还直接考虑了模型在实际硬件上的运行速度,包括CPU和GPU的特定性能特征2。
-
均衡通道宽度:保持每层网络的通道数相对均衡可以减少内存访问的开销,并且对模型性能影响不大2。
第三步:训练代码
1)损失函数为:交叉熵损失函数
2)ShuffleNet_V2代码:
from functools import partial
from typing import Any, Callable, List, Optional
import torch
import torch.nn as nn
from torch import Tensor
from ..transforms._presets import ImageClassification
from ..utils import _log_api_usage_once
from ._api import register_model, Weights, WeightsEnum
from ._meta import _IMAGENET_CATEGORIES
from ._utils import _ovewrite_named_param, handle_legacy_interface
__all__ = [
"ShuffleNetV2",
"ShuffleNet_V2_X0_5_Weights",
"ShuffleNet_V2_X1_0_Weights",
"ShuffleNet_V2_X1_5_Weights",
"ShuffleNet_V2_X2_0_Weights",
"shufflenet_v2_x0_5",
"shufflenet_v2_x1_0",
"shufflenet_v2_x1_5",
"shufflenet_v2_x2_0",
]
def channel_shuffle(x: Tensor, groups: int) -> Tensor:
batchsize, num_channels, height, width = x.size()
channels_per_group = num_channels // groups
# reshape
x = x.view(batchsize, groups, channels_per_group, height, width)
x = torch.transpose(x, 1, 2).contiguous()
# flatten
x = x.view(batchsize, num_channels, height, width)
return x
class InvertedResidual(nn.Module):
def __init__(self, inp: int, oup: int, stride: int) -> None:
super().__init__()
if not (1 <= stride <= 3):
raise ValueError("illegal stride value")
self.stride = stride
branch_features = oup // 2
if (self.stride == 1) and (inp != branch_features << 1):
raise ValueError(
f"Invalid combination of stride {stride}, inp {inp} and oup {oup} values. If stride == 1 then inp should be equal to oup // 2 << 1."
)
if self.stride > 1:
self.branch1 = nn.Sequential(
self.depthwise_conv(inp, inp, kernel_size=3, stride=self.stride, padding=1),
nn.BatchNorm2d(inp),
nn.Conv2