Stage1为Conv, Stage2~8为MBConv,Stage9为Conv + Pooling + FC

第三列Resolution(分辨率)为输入每个Stage时的分辨率(高度和宽度)

第四列Channels为每个Stage输出特征矩阵的通道数

第五列Layers为将对应的Stage重复多少次

第六列stride(步距)为对应每一个Stage中的第一个Operator的步距,其余Operator的步距都为1

MBConv1,n=1; MBConv6,n=6;MBConv模块后面跟的是几,n就等于几

对于DW卷积,输入特征矩阵和输出特征矩阵的channel是一样的

调整网络的宽度就是调整所使用卷积核的个数,即channel*width_coefficient

调整网络的深度就是layers*depth_coefficient

drop_connect_rate对应MBConv模块中dropout的随机失活比例

dropout_rate对应Stage9全连接层之前的dropout随机失活的比例

查看图片是 RGB图还是灰度图

右击图片属性,摘要,点击详细属性,里面有位深度一项,如果是RGB图,位深度是24,如果是灰度图,位深度是8 。

model.py

import math
import copy
from functools import partial
from collections import OrderedDict
from typing import Optional, Callableimport torch
import torch.nn as nn
from torch import Tensor
from torch.nn import functional as Fdef _make_divisible(ch, divisor=8, min_ch=None): # 将传入channl的个数调整到离他最近的8的整数倍,对硬件更加友好"""This function is taken from the original tf repo.It ensures that all layers have a channel number that is divisible by 8It can be seen here:https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py"""if min_ch is None:min_ch = divisornew_ch = max(min_ch, int(ch + divisor / 2) // divisor * divisor)# Make sure that round down does not go down by more than 10%.if new_ch < 0.9 * ch:new_ch += divisorreturn new_chdef drop_path(x, drop_prob: float = 0., training: bool = False):"""Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks)."Deep Networks with Stochastic Depth", https://arxiv.org/pdf/1603.09382.pdfThis function is taken from the rwightman.It can be seen here:https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/layers/drop.py#L140"""if drop_prob == 0. or not training:return xkeep_prob = 1 - drop_probshape = (x.shape[0],) + (1,) * (x.ndim - 1)  # work with diff dim tensors, not just 2D ConvNetsrandom_tensor = keep_prob + torch.rand(shape, dtype=x.dtype, device=x.device)random_tensor.floor_()  # binarizeoutput = x.div(keep_prob) * random_tensorreturn outputclass DropPath(nn.Module):"""Drop paths (Stochastic Depth) per sample  (when applied in main path of residual blocks)."Deep Networks with Stochastic Depth", https://arxiv.org/pdf/1603.09382.pdf"""def __init__(self, drop_prob=None):super(DropPath, self).__init__()self.drop_prob = drop_probdef forward(self, x):return drop_path(x, self.drop_prob, self.training)class ConvBNActivation(nn.Sequential): # 定义卷积、BN、激活函数def __init__(self,in_planes: int,out_planes: int,kernel_size: int = 3,stride: int = 1,groups: int = 1, # 控制当前卷积是普通卷积还是DW卷积norm_layer: Optional[Callable[..., nn.Module]] = None, # BN结构activation_layer: Optional[Callable[..., nn.Module]] = None): # 激活函数padding = (kernel_size - 1) // 2if norm_layer is None:norm_layer = nn.BatchNorm2dif activation_layer is None:activation_layer = nn.SiLU  # alias Swish  (torch>=1.7) SiLU激活函数和Swish激活函数是一样的super(ConvBNActivation, self).__init__(nn.Conv2d(in_channels=in_planes,out_channels=out_planes,kernel_size=kernel_size,stride=stride,padding=padding,groups=groups,bias=False),norm_layer(out_planes),activation_layer())class SqueezeExcitation(nn.Module): # 定义SE模块def __init__(self,input_c: int,   # MBConv模块输入特征矩阵的channelexpand_c: int,  # MBConv模块中第一个1*1Conv输出的channel,也就是输入SE模块特征层的channelsqueeze_factor: int = 4):super(SqueezeExcitation, self).__init__()squeeze_c = input_c // squeeze_factor # 第一个全连接层对应的节点个数self.fc1 = nn.Conv2d(expand_c, squeeze_c, kernel_size=1) # 第一个全连接层,利用卷积层实现全连接层self.ac1 = nn.SiLU()  # alias Swishself.fc2 = nn.Conv2d(squeeze_c, expand_c, kernel_size=1) # 第二个全连接层self.ac2 = nn.Sigmoid()def forward(self, x: Tensor) -> Tensor: # 定义正向传播过程scale = F.adaptive_avg_pool2d(x, output_size=(1, 1)) # 全局平均池化层scale = self.fc1(scale)scale = self.ac1(scale)scale = self.fc2(scale)scale = self.ac2(scale)return scale * x # SE模块输出class InvertedResidualConfig: # 每一个MBConv模块的配置参数# kernel_size, in_channel, out_channel, exp_ratio, strides, use_SE, drop_connect_ratedef __init__(self,kernel: int,          # 3 or 5input_c: int,         # MBConv模块的输入out_c: int,           # MBConv模块的输出expanded_ratio: int,  # 1 or 6stride: int,          # 1 or 2use_se: bool,         # Truedrop_rate: float,  # 对应Dropout的随机失活比例index: str,           # 用于记录MBConv模块的名称,类似于1a, 2a, 2b, ...width_coefficient: float): # 网络宽度方向的倍率因子self.input_c = self.adjust_channels(input_c, width_coefficient)self.kernel = kernelself.expanded_c = self.input_c * expanded_ratioself.out_c = self.adjust_channels(out_c, width_coefficient)self.use_se = use_seself.stride = strideself.drop_rate = drop_rateself.index = index@staticmethoddef adjust_channels(channels: int, width_coefficient: float):return _make_divisible(channels * width_coefficient, 8) # 将channels * width_coefficient(宽度方向的倍率因子)在调整到离他最近的8的整数倍# ----------------搭建MBConv模块-------------------
class InvertedResidual(nn.Module): # 定义MBConv模块def __init__(self,cnf: InvertedResidualConfig,norm_layer: Callable[..., nn.Module]):super(InvertedResidual, self).__init__()if cnf.stride not in [1, 2]:raise ValueError("illegal stride value.")self.use_res_connect = (cnf.stride == 1 and cnf.input_c == cnf.out_c) # 是否使用shortcut连接layers = OrderedDict()activation_layer = nn.SiLU  # alias Swish# 搭建MBConv结构# expandif cnf.expanded_c != cnf.input_c: # 判断是否需要搭建MBConv模块中的第一个1*1Convlayers.update({"expand_conv": ConvBNActivation(cnf.input_c,cnf.expanded_c,kernel_size=1,norm_layer=norm_layer,activation_layer=activation_layer)})# depthwise 搭建DW卷积layers.update({"dwconv": ConvBNActivation(cnf.expanded_c,cnf.expanded_c,kernel_size=cnf.kernel, # 传入配置文件中的kernel_sizestride=cnf.stride,  # 传入配置文件中的stridegroups=cnf.expanded_c,norm_layer=norm_layer,activation_layer=activation_layer)})# 搭建SE模块if cnf.use_se:layers.update({"se": SqueezeExcitation(cnf.input_c,cnf.expanded_c)})# project 搭建MBConv模块中的第二个1*1Convlayers.update({"project_conv": ConvBNActivation(cnf.expanded_c,cnf.out_c,kernel_size=1,norm_layer=norm_layer,activation_layer=nn.Identity)}) # 第二个1*1Conv后面没有激活函数,Identity就是不做任何处理的意思self.block = nn.Sequential(layers) # 将有序字典layers传入Sequential类,搭建出MBConv模块中的主分支self.out_channels = cnf.out_cself.is_strided = cnf.stride > 1# 只有在使用shortcut连接时才使用dropout层if self.use_res_connect and cnf.drop_rate > 0: # 有shortcut连接并且drop_rate>0,则使用dropoutself.dropout = DropPath(cnf.drop_rate)else:self.dropout = nn.Identity()def forward(self, x: Tensor) -> Tensor: # 正向传播过程result = self.block(x) # 先通过主分支blockresult = self.dropout(result) # 再通过dropoutif self.use_res_connect: # 判断是否使用shortcutresult += xreturn result
# ----------------------end----------------------------class EfficientNet(nn.Module):def __init__(self,width_coefficient: float,depth_coefficient: float,num_classes: int = 1000,dropout_rate: float = 0.2,drop_connect_rate: float = 0.2,block: Optional[Callable[..., nn.Module]] = None,norm_layer: Optional[Callable[..., nn.Module]] = None):super(EfficientNet, self).__init__()# kernel_size, in_channel, out_channel, exp_ratio, strides, use_SE, drop_connect_rate, repeats(重复几次)default_cnf = [[3, 32, 16, 1, 1, True, drop_connect_rate, 1], # Stage2[3, 16, 24, 6, 2, True, drop_connect_rate, 2], # Stage3[5, 24, 40, 6, 2, True, drop_connect_rate, 2], # Stage4[3, 40, 80, 6, 2, True, drop_connect_rate, 3], # Stage5[5, 80, 112, 6, 1, True, drop_connect_rate, 3], # Stage6[5, 112, 192, 6, 2, True, drop_connect_rate, 4], # Stage7[3, 192, 320, 6, 1, True, drop_connect_rate, 1]] # Stage8def round_repeats(repeats):"""Round number of repeats based on depth multiplier."""return int(math.ceil(depth_coefficient * repeats)) # 乘得结果向上取整if block is None:block = InvertedResidualif norm_layer is None:norm_layer = partial(nn.BatchNorm2d, eps=1e-3, momentum=0.1)adjust_channels = partial(InvertedResidualConfig.adjust_channels,width_coefficient=width_coefficient)# build inverted_residual_settingbneck_conf = partial(InvertedResidualConfig,width_coefficient=width_coefficient)b = 0num_blocks = float(sum(round_repeats(i[-1]) for i in default_cnf))inverted_residual_setting = []for stage, args in enumerate(default_cnf):cnf = copy.copy(args)for i in range(round_repeats(cnf.pop(-1))):if i > 0:# strides equal 1 except first cnfcnf[-3] = 1  # stridescnf[1] = cnf[2]  # input_channel equal output_channelcnf[-1] = args[-2] * b / num_blocks  # update dropout ratioindex = str(stage + 1) + chr(i + 97)  # 1a, 2a, 2b, ...inverted_residual_setting.append(bneck_conf(*cnf, index))b += 1# create layerslayers = OrderedDict()# Stage1  first convlayers.update({"stem_conv": ConvBNActivation(in_planes=3,out_planes=adjust_channels(32),kernel_size=3,stride=2,norm_layer=norm_layer)})# Stage2~8  building inverted residual blocksfor cnf in inverted_residual_setting:layers.update({cnf.index: block(cnf, norm_layer)})# Stage9  build toplast_conv_input_c = inverted_residual_setting[-1].out_clast_conv_output_c = adjust_channels(1280)# Stage9中的1*1Convlayers.update({"top": ConvBNActivation(in_planes=last_conv_input_c,out_planes=last_conv_output_c,kernel_size=1,norm_layer=norm_layer)})self.features = nn.Sequential(layers) # 实例化得到featuresself.avgpool = nn.AdaptiveAvgPool2d(1) # 池化层classifier = []if dropout_rate > 0:classifier.append(nn.Dropout(p=dropout_rate, inplace=True))classifier.append(nn.Linear(last_conv_output_c, num_classes)) # 全连接层self.classifier = nn.Sequential(*classifier)# initial weights 权重初始化for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode="fan_out")if m.bias is not None:nn.init.zeros_(m.bias)elif isinstance(m, nn.BatchNorm2d):nn.init.ones_(m.weight)nn.init.zeros_(m.bias)elif isinstance(m, nn.Linear):nn.init.normal_(m.weight, 0, 0.01)nn.init.zeros_(m.bias)def _forward_impl(self, x: Tensor) -> Tensor:x = self.features(x) # 特征提取x = self.avgpool(x) # 平均池化x = torch.flatten(x, 1) # 展平x = self.classifier(x) # dropout和全连接层得到输出return xdef forward(self, x: Tensor) -> Tensor: # 正向传播过程return self._forward_impl(x)def efficientnet_b0(num_classes=1000):# input image size 224x224return EfficientNet(width_coefficient=1.0,depth_coefficient=1.0,dropout_rate=0.2,num_classes=num_classes)def efficientnet_b1(num_classes=1000):# input image size 240x240return EfficientNet(width_coefficient=1.0,depth_coefficient=1.1,dropout_rate=0.2,num_classes=num_classes)def efficientnet_b2(num_classes=1000):# input image size 260x260return EfficientNet(width_coefficient=1.1,depth_coefficient=1.2,dropout_rate=0.3,num_classes=num_classes)def efficientnet_b3(num_classes=1000):# input image size 300x300return EfficientNet(width_coefficient=1.2,depth_coefficient=1.4,dropout_rate=0.3,num_classes=num_classes)def efficientnet_b4(num_classes=1000):# input image size 380x380return EfficientNet(width_coefficient=1.4,depth_coefficient=1.8,dropout_rate=0.4,num_classes=num_classes)def efficientnet_b5(num_classes=1000):# input image size 456x456return EfficientNet(width_coefficient=1.6,depth_coefficient=2.2,dropout_rate=0.4,num_classes=num_classes)def efficientnet_b6(num_classes=1000):# input image size 528x528return EfficientNet(width_coefficient=1.8,depth_coefficient=2.6,dropout_rate=0.5,num_classes=num_classes)def efficientnet_b7(num_classes=1000):# input image size 600x600return EfficientNet(width_coefficient=2.0,depth_coefficient=3.1,dropout_rate=0.5,num_classes=num_classes)

train.py

import os
import math
import argparseimport torch
import torch.optim as optim
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
import torch.optim.lr_scheduler as lr_schedulerfrom model import efficientnet_b0 as create_model
from my_dataset import MyDataSet
from utils import read_split_data, train_one_epoch, evaluatedef main(args):device = torch.device(args.device if torch.cuda.is_available() else "cpu")print(args)print('Start Tensorboard with "tensorboard --logdir=runs", view at http://localhost:6006/')tb_writer = SummaryWriter()if os.path.exists("./weights") is False:os.makedirs("./weights")train_images_path, train_images_label, val_images_path, val_images_label = read_split_data(args.data_path)img_size = {"B0": 224, # B0到B7输入图片的尺寸"B1": 240,"B2": 260,"B3": 300,"B4": 380,"B5": 456,"B6": 528,"B7": 600}num_model = "B0"            # 对应训练的模型data_transform = {"train": transforms.Compose([transforms.RandomResizedCrop(img_size[num_model]),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),"val": transforms.Compose([transforms.Resize(img_size[num_model]),transforms.CenterCrop(img_size[num_model]),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])}# 实例化训练数据集train_dataset = MyDataSet(images_path=train_images_path,images_class=train_images_label,transform=data_transform["train"])# 实例化验证数据集val_dataset = MyDataSet(images_path=val_images_path,images_class=val_images_label,transform=data_transform["val"])batch_size = args.batch_sizenw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workersprint('Using {} dataloader workers every process'.format(nw))train_loader = torch.utils.data.DataLoader(train_dataset,batch_size=batch_size,shuffle=True,pin_memory=True,num_workers=nw,collate_fn=train_dataset.collate_fn)val_loader = torch.utils.data.DataLoader(val_dataset,batch_size=batch_size,shuffle=False,pin_memory=True,num_workers=nw,collate_fn=val_dataset.collate_fn)# 如果存在预训练权重则载入model = create_model(num_classes=args.num_classes).to(device)if args.weights != "":if os.path.exists(args.weights):weights_dict = torch.load(args.weights, map_location=device)load_weights_dict = {k: v for k, v in weights_dict.items()if model.state_dict()[k].numel() == v.numel()}print(model.load_state_dict(load_weights_dict, strict=False))else:raise FileNotFoundError("not found weights file: {}".format(args.weights))# 是否冻结权重if args.freeze_layers:for name, para in model.named_parameters():# 除最后一个卷积层和全连接层外,其他权重全部冻结if ("features.top" not in name) and ("classifier" not in name):para.requires_grad_(False)else:print("training {}".format(name))pg = [p for p in model.parameters() if p.requires_grad]optimizer = optim.SGD(pg, lr=args.lr, momentum=0.9, weight_decay=1E-4)# Scheduler https://arxiv.org/pdf/1812.01187.pdflf = lambda x: ((1 + math.cos(x * math.pi / args.epochs)) / 2) * (1 - args.lrf) + args.lrf  # cosinescheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)for epoch in range(args.epochs):# trainmean_loss = train_one_epoch(model=model,optimizer=optimizer,data_loader=train_loader,device=device,epoch=epoch)scheduler.step()# validateacc = evaluate(model=model,data_loader=val_loader,device=device)print("[epoch {}] accuracy: {}".format(epoch, round(acc, 3)))tags = ["loss", "accuracy", "learning_rate"]tb_writer.add_scalar(tags[0], mean_loss, epoch)tb_writer.add_scalar(tags[1], acc, epoch)tb_writer.add_scalar(tags[2], optimizer.param_groups[0]["lr"], epoch)torch.save(model.state_dict(), "./weights/model-{}.pth".format(epoch))if __name__ == '__main__':parser = argparse.ArgumentParser()parser.add_argument('--num_classes', type=int, default=2)parser.add_argument('--epochs', type=int, default=10)parser.add_argument('--batch-size', type=int, default=4)parser.add_argument('--lr', type=float, default=0.01)parser.add_argument('--lrf', type=float, default=0.01)# 数据集所在根目录# https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgzparser.add_argument('--data-path', type=str, default="./val")# 预训练权重 download model weights# 链接: https://pan.baidu.com/s/1ouX0UmjCsmSx3ZrqXbowjw  密码: 090iparser.add_argument('--weights', type=str, default='./efficientnetb0.pth',help='initial weights path')parser.add_argument('--freeze-layers', type=bool, default=False)parser.add_argument('--device', default='cuda:0', help='device id (i.e. 0 or 0,1 or cpu)')opt = parser.parse_args()main(opt)

reference

9.1 EfficientNet网络详解_哔哩哔哩_bilibili

9.2使用Pytorch搭建EfficientNet网络_哔哩哔哩_bilibili

EfficientNet网络结构详解相关推荐

  1. AlexNet网络结构详解与代码复现

    参考内容来自up:3.1 AlexNet网络结构详解与花分类数据集下载_哔哩哔哩_bilibili up主的CSDN博客:太阳花的小绿豆的博客_CSDN博客-深度学习,软件安装,Tensorflow领 ...

  2. U-Net网络结构详解

    U-Net网络结构详解 U-Net网络结构是对称的,由于网络结构像U型,所以被命名为U-Net.整体而言,U-Net是一个Encoder-Decoder(编码器-解码器)的结构,这一点是与FCN的结构 ...

  3. pytorch图像分类篇:6. ResNet网络结构详解与迁移学习简介

    前言 最近在b站发现了一个非常好的 计算机视觉 + pytorch 的教程,相见恨晚,能让初学者少走很多弯路. 因此决定按着up给的教程路线:图像分类→目标检测→-一步步学习用pytorch实现深度学 ...

  4. 深度学习之目标检测(五)-- RetinaNet网络结构详解

    深度学习之目标检测(五)-- RetinaNet网络结构详解 深度学习之目标检测(五)RetinaNet网络结构详解 1. RetinaNet 1.1 backbone 部分 1.2 预测器部分 1. ...

  5. AlexNet网络结构详解(含各层维度大小计算过程)与PyTorch实现

    AlexNet网络结构详解(含各层维度大小计算过程)与PyTorch实现 1.AlexNet之前的思考 2.AlexNet网络结构 3.AlexNet网络结构的主要贡献 4.PyTorch实现     ...

  6. 基于CIFAR100的VGG网络结构详解

    基于CIFAR100的VGG网络详解 码字不易,点赞收藏 1 数据集概况 1.1 CIFAR100 cifar100包含20个大类,共100类,train集50000张图片,test集10000张图片 ...

  7. ResNet网络结构详解,网络搭建,迁移学习

    前言: 参考内容来自up:6.1 ResNet网络结构,BN以及迁移学习详解_哔哩哔哩_bilibili up的代码和ppt:https://github.com/WZMIAOMIAO/deep-le ...

  8. OSI七层网络结构详解

    OSI模型的分层结构 OSI(Open System Interconnection),开放式系统互联参考模型 ,它把网络协议从逻辑上分为了7层.这7层分别为:物理层.数据链路层.网络层.传输层.会话 ...

  9. Network in Network(NIN)网络结构详解,网络搭建

    一.简介 Network in Network,描述了一种新型卷积神经网络结构. LeNet,AlexNet,VGG都秉承一种设计思路:先用卷积层构成的模块提取空间特征,再用全连接层模块来输出分类结果 ...

  10. Alex网络结构详解

    网络结构及算法详解: 参考: http://www.cnblogs.com/gongxijun/p/6027747.html http://blog.sina.com.cn/s/blog_eb3aea ...

最新文章

  1. Gartner:人工智能将改变个人设备领域的游戏规则
  2. SqlServer2005高效分页sql查询语句汇总
  3. mac安装mysql mysql命令找不到_MacBook通过Homebrew安装mysql
  4. 通过数据库动态视图'v$',查看数据库信息
  5. vscode Python 运行环境配置
  6. python代码实例sicket_Python socket聊天脚本代码实例
  7. java求平均值Scanner_Scanner的一些问题
  8. Linux学习笔记-对父子进程直接通信基础与实例
  9. (07)VHDL实现闪灯
  10. Java中逗号运算符的使用
  11. windows技巧——notepad2 取代自带 notepad ,功能强大!
  12. SPSS问卷信度分析
  13. 全国计算机城市排名,这五大城市教育资源全国领先,各城市优质高校排行榜一定要收藏!...
  14. 光立方原理讲解_3D打印设备SLA工艺原理是什么?
  15. 多个jdk共存与切换
  16. matlab手动抠图,MATLAB可视化手动抠图
  17. 麻雀优化算法SSA及其改进策略
  18. excel查找指定表计算机,两个excel表格找文本相同数据库-excel如何查找并自动填写对应数据...
  19. Excel 2010中上下标的输入方法
  20. 用 ListBox 和 DataBinding 显示列表数据 (木野狐译) 1

热门文章

  1. Oracle Spatial 安装和使用
  2. 立下flag,一周一篇博客
  3. Roaring BitMap(高效压缩位图)
  4. 无盘服务器内存回写速度,网吧文化监管平台异常频繁回写数据导致无盘客户机游戏秒卡,打字卡...
  5. QT之如何添加现有文件
  6. 12_传智播客iOS视频教程_注释和函数的定义和调用
  7. 实现金钱数字格式化:一行代码解决(三位分隔)
  8. 解决打开word很慢
  9. 动态规划算法典型应用之背包问题
  10. vue 页面刷新404