致谢:霹雳吧啦Wz:霹雳吧啦Wz的个人空间_哔哩哔哩_Bilibili

目录

致谢:霹雳吧啦Wz:霹雳吧啦Wz的个人空间_哔哩哔哩_Bilibili

1 本次要点

1.1 pytorch框架语法

2 网络简介

2.1 历史意义

2.2 网络亮点

V1版亮点

V2版亮点

V3版亮点

2.3 V1网络

DW卷积和PW卷积介绍

计算量

网络结构(和VGG差不多,就是卷积层的串联)

效果

2.4 V2网络

倒残差结构

ReLU6激励函数

V2网络结构

效果

2.5 V3网络

V3算法的 block结构

SE模块(注意力机制)

重新设计耗时层结构

重新设计激活函数

V3网络结构

3 代码结构(V2版)

3.1 model.py

3.2 train.py

3.3 predict.py


1 本次要点

1.1 pytorch框架语法

  • 要使用DW卷积功能,只要设定nn.conv2d中groups参数即可。
  • pytorch自带relu6激励函数。nn.ReLU6()。

2 网络简介

2.1 历史意义

2017年,为了满足移动和嵌入式视觉任务的需要,MobileNet V1构造了一种体量小(参数量是VGG16的1/32)、运算少(计算量是GoogeNet的1/3)网络架构,精度相比VGG仅低了0.9%。

2018年,提出了MobileNet V2。

2019年,提出了MobileNet V3。

2.2 网络亮点

V1版亮点

  1. Depthwise Convolution( DW卷积,大大减少运算量和参数数量)
  2. 增加超参数α(卷积核个数)、β(输入图像的分辨率)

V2版亮点

  1. Inverted Residuals (倒残差结构 )
  2. Linear Bottlenecks

V3版亮点

  1. 更新block(bneck):在V2倒残差结构基础上进行了简单改动。

    1. SE模块:注意力机制。
    2. 更新了激活函数。
  2. 使用NAS(neural architecture search 神经结构搜索)搜索参数
  3. 重新设计耗时层结构

效果:更准确、更高效

2.3 V1网络

DW卷积和PW卷积介绍

传统卷积:

  • 卷积核channel=输入特征channel:这是卷积计算的定义,以前长时间误解卷积核channel就是1,比如输入特征是1024通道,那么单个卷积核也要是1024个通道,单个通道两两匹配做卷积计算,然后1024个通道矩阵的相应位置元素值累加,1024个元素(不是特征图,是特征图中单个元素)累加后,再加一次bias,此时的元素值,就是特征图中的元素值。
  • 卷积核个数=输出的通道数:所以,刚开始1*1卷积的发明,不是做特征提取,而是用来做通道数扩增或降维。

DW卷积:

  • 卷积核通道就1层,而且只和输入的单通道进行卷积。具体DW原理见上图。
  • 这种运算对输入层的每个通道独立进行卷积运算,没有利用不同通道在相同空间位置上的feature信息。因此需要Pointwise Convolution来将这些Feature map进行组合生成新的Feature map。

PW卷积:

  • 作用跟1*1卷积一模一样,在这里换了个名字,作用是控制输出的通道数。具体如上图。

计算量

M是输入特征的通道数,N是输出特征的通道数,通常卷积核是3*3的,使用DW+PW替换常规卷积,分子除以分母约为1/9。

当然,实际上,由于计算机底层计算机制等原因,可能DW卷积计算方式比常规卷积计算更费时间。

网络结构(和VGG差不多,就是卷积层的串联)

注意与解释:

  • conv / s2 :普通卷积;步长为2。
  • 3 x 3 x 3 x 32:卷积核尺寸为 3 x 3;卷积核深度为3;卷积核组数为32(即输出32个特征图)
  • conv dw / s1:DW卷积;步长为1。
  • 3 x 3 x 32 dw:卷积核尺寸为 3 x 3;DW卷积核深度为1,所以这里默认不写;卷积核组数为32。
  • PW卷积就是常规的1x1卷积,所以网络结构中,每个dw卷积后面接一个1x1常规卷积。

在V1版本中,DW部分的卷积核容易废掉,即卷积核参数大部分为0,基本没起作用。V2中有了改善。

效果

2.4 V2网络

倒残差结构

左图是残差结构,右图是倒残差结构,升维降维顺序反过来。

V2论文中有两种连接方式,如下:

论文中,只有stride=1且输入特征矩阵与输出特征矩阵shape相同时才有shortcut连接。

ReLU6激励函数

替换常规ReLU原因是:常规ReLU对低维的特征造成大量损失,而对高维影响才小。

V2网络结构

  • t 是扩展因子:就是DW卷积通道拓展倍数。
  • bottleneck只有s=1时(即bottleneck中第一层DW卷积步长为1),才使用有shortcut分支版本block。(原因:DW卷积步长为2时,特征图尺度会发生变化,没法前后add了)
  • 最后一层 1 x 1 卷积层,因为输入向量也是1x1x1280,所以相当于一个全连接层。

效果

2.5 V3网络

V3算法的 block结构

NL:非线性激活函数的意思。(V2算法中没有用)

后面的1x1卷积层:用于降维,也没有用激活函数。

SE模块(注意力机制)

SE是Squeeze and Excitation的缩写(‘紧缩和激励’),该模块的提出主要是考虑到模型通道之间的相互依赖性。

细节解释

  1. pool:每个通道特征图,使用global pooling,比如全局平均池化,求得特征图所有元素的平均值。

一个例子:

SE-ResNet结合示意图:

重新设计耗时层结构

  1. 减少第一个卷积层的卷积核个数(V2版是32,V2版是16)(我:这也算创新吗,这感觉完全是实验测试发现精度不降,速度能提升。。。)

    1. 效果:精度一样,速度提升2毫秒(约3%提速)
  2. 精简Last State 结构
    1. 效果:速度提升7毫秒(11%提速)

重新设计激活函数

问题:swish激活函数能提升网络精度,但是求导复杂、量化过程也不友好。

解决:发明 h-swish 激活函数,形态和swish相近,但计算和量化好的多。

V3网络结构

3 代码结构(V2版)

  • model.py
  • train.py
  • predict.py

3.1 model.py

from torch import nn
import torch"""
目的:将输入通道数调整为输出通道数的整数倍
ch:卷积核个数(即输出特征图channel)
divisor:一个基数。
_make_divisible就是要把ch调整成divisor的整数倍。
可能原因:有利于并行运算或多机器分布式运算。
"""
def _make_divisible(ch, divisor=8, min_ch=None):"""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 = divisor# 以下一句,相当于四舍五入    new_ch = max(min_ch, int(ch + divisor / 2) // divisor * divisor)# Make sure that round down does not go down by more than 10%.# 确保向下取整时不会超过10%if new_ch < 0.9 * ch:new_ch += divisorreturn new_ch"""
Conv + BN + ReLU6 模块
继承nn.Sequential类,因为后续要使用pytorch官方预训练权重。
groups=1表示普通卷积,其他值将为DW卷积。
"""
class ConvBNReLU(nn.Sequential):def __init__(self, in_channel, out_channel, kernel_size=3, stride=1, groups=1):padding = (kernel_size - 1) // 2#传入3个参数super(ConvBNReLU, self).__init__(nn.Conv2d(in_channel, out_channel, kernel_size, stride, padding, groups=groups, bias=False),nn.BatchNorm2d(out_channel),nn.ReLU6(inplace=True))# 倒残差结构
class InvertedResidual(nn.Module):def __init__(self, in_channel, out_channel, stride, expand_ratio):super(InvertedResidual, self).__init__()hidden_channel = in_channel * expand_ratio #扩展因子t # use_shortcut:是否使用shortcut结构self.use_shortcut = stride == 1 and in_channel == out_channellayers = []if expand_ratio != 1: #如果为1,则不要1*1卷积# 1x1 pointwise convlayers.append(ConvBNReLU(in_channel, hidden_channel, kernel_size=1))# layers.append()是一个个插入。而layers.extend()能批量插入。layers.extend([# 3x3 depthwise convConvBNReLU(hidden_channel, hidden_channel, stride=stride, groups=hidden_channel),# 1x1 pointwise conv(linear 线性激活函数)nn.Conv2d(hidden_channel, out_channel, kernel_size=1, bias=False),nn.BatchNorm2d(out_channel),# 注意:线性激活函数,就相当于y=x,所以也就是BN层后不需要加激活函数了。])self.conv = nn.Sequential(*layers)def forward(self, x):if self.use_shortcut:return x + self.conv(x)else:return self.conv(x)"""
alpha:卷积核个数的倍率
round_nearest:"""
class MobileNetV2(nn.Module):def __init__(self, num_classes=1000, alpha=1.0, round_nearest=8):super(MobileNetV2, self).__init__()block = InvertedResidual# _make_divisible:将输入通道数调整为输出通道数的整数倍# 可能原因:有利于并行运算或多机器分布式运算。input_channel = _make_divisible(32 * alpha, round_nearest)last_channel = _make_divisible(1280 * alpha, round_nearest)inverted_residual_setting = [# t, c, n, s(具体含义,见网络详解图)[1, 16, 1, 1],[6, 24, 2, 2],[6, 32, 3, 2],[6, 64, 4, 2],[6, 96, 3, 1],[6, 160, 3, 2],[6, 320, 1, 1],]features = []# conv1 layerfeatures.append(ConvBNReLU(3, input_channel, stride=2))# building inverted residual residual blockesfor t, c, n, s in inverted_residual_setting:output_channel = _make_divisible(c * alpha, round_nearest)for i in range(n):stride = s if i == 0 else 1features.append(block(input_channel, output_channel, stride, expand_ratio=t))input_channel = output_channel# building last several layersfeatures.append(ConvBNReLU(input_channel, last_channel, 1))# combine feature layersself.features = nn.Sequential(*features)# building classifierself.avgpool = nn.AdaptiveAvgPool2d((1, 1))self.classifier = nn.Sequential(nn.Dropout(0.2),nn.Linear(last_channel, num_classes))# weight initialization 权重初始化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) #bias设为0elif isinstance(m, nn.BatchNorm2d):nn.init.ones_(m.weight) #方差设为1nn.init.zeros_(m.bias) #bias设为0elif isinstance(m, nn.Linear):nn.init.normal_(m.weight, 0, 0.01) #将权重调整为均值为0,方差为0.01的正态分布。nn.init.zeros_(m.bias) #bias设为0def forward(self, x):x = self.features(x)x = self.avgpool(x)x = torch.flatten(x, 1)x = self.classifier(x)return x

3.2 train.py

import torch
import torch.nn as nn
from torchvision import transforms, datasets
import json
import os
import torch.optim as optim
from model import MobileNetV2
# import torchvision.models.mobilenet 点进去里面有mobilenet_v2预训练模型的下载路径。def main():device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")print("using {} device.".format(device))data_transform = {"train": transforms.Compose([transforms.RandomResizedCrop(224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),"val": transforms.Compose([transforms.Resize(256),transforms.CenterCrop(224),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])}data_root = os.path.abspath(os.path.join(os.getcwd(), "../.."))  # get data root pathimage_path = os.path.join(data_root, "data_set", "flower_data")  # flower data set pathassert os.path.exists(image_path), "{} path does not exist.".format(image_path)train_dataset = datasets.ImageFolder(root=os.path.join(image_path, "train"),transform=data_transform["train"])train_num = len(train_dataset)# {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}flower_list = train_dataset.class_to_idxcla_dict = dict((val, key) for key, val in flower_list.items())# write dict into json filejson_str = json.dumps(cla_dict, indent=4)with open('class_indices.json', 'w') as json_file:json_file.write(json_str)batch_size = 16nw = 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,num_workers=nw)validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),transform=data_transform["val"])val_num = len(validate_dataset)validate_loader = torch.utils.data.DataLoader(validate_dataset,batch_size=batch_size, shuffle=False,num_workers=nw)print("using {} images for training, {} images fot validation.".format(train_num,val_num))net = MobileNetV2(num_classes=5)# load pretrain weights# download url: https://download.pytorch.org/models/mobilenet_v2-b0353104.pthmodel_weight_path = "./mobilenet_v2.pth" #imageNet预训练模型,输出节点是1000,所以最后一层在此不能用。assert os.path.exists(model_weight_path), "file {} dose not exist.".format(model_weight_path)pre_weights = torch.load(model_weight_path)# delete classifier weights# 遍历权重字典,看权重名称中是否有“classifier”参数,有表示是最后一层全连接层参数,pre_dict = {k: v for k, v in pre_weights.items() if "classifier" not in k} #排除最后一层名叫"classifier"的全连接层。missing_keys, unexpected_keys = net.load_state_dict(pre_dict, strict=False) # 通过字典载入权重# freeze features weights# 冻结 features结构部分(特征提取部分)权重。for param in net.features.parameters(): # 如果是net.parameters(),则所有网络结构都冻结。param.requires_grad = False # 不求导,也不参数更新。net.to(device)loss_function = nn.CrossEntropyLoss()optimizer = optim.Adam(net.parameters(), lr=0.0001)best_acc = 0.0save_path = './MobileNetV2.pth'for epoch in range(5):# trainnet.train()running_loss = 0.0for step, data in enumerate(train_loader, start=0):images, labels = dataoptimizer.zero_grad()logits = net(images.to(device))loss = loss_function(logits, labels.to(device))loss.backward()optimizer.step()# print statisticsrunning_loss += loss.item()# print train processrate = (step+1)/len(train_loader)a = "*" * int(rate * 50)b = "." * int((1 - rate) * 50)print("\rtrain loss: {:^3.0f}%[{}->{}]{:.4f}".format(int(rate*100), a, b, loss), end="")print()# validatenet.eval()acc = 0.0  # accumulate accurate number / epochwith torch.no_grad():for val_data in validate_loader:val_images, val_labels = val_dataoutputs = net(val_images.to(device))  # eval model only have last output layer# loss = loss_function(outputs, test_labels)predict_y = torch.max(outputs, dim=1)[1]acc += (predict_y == val_labels.to(device)).sum().item()val_accurate = acc / val_numif val_accurate > best_acc:best_acc = val_accuratetorch.save(net.state_dict(), save_path)print('[epoch %d] train_loss: %.3f  test_accuracy: %.3f' %(epoch + 1, running_loss / step, val_accurate))print('Finished Training')if __name__ == '__main__':main()

冻结输出层之外的层参数,训练结果:

作者最高训练到94%。

如果基于预训练模型,所有层都进行更新训练,能达到98.1%。

3.3 predict.py

import torch
from model import MobileNetV2
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as plt
import jsondata_transform = transforms.Compose([transforms.Resize(256),transforms.CenterCrop(224),transforms.ToTensor(),transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])# load image
img = Image.open("../tulip.jpg")
plt.imshow(img)
# [N, C, H, W]
img = data_transform(img)
# expand batch dimension
img = torch.unsqueeze(img, dim=0)# read class_indict
try:json_file = open('./class_indices.json', 'r')class_indict = json.load(json_file)
except Exception as e:print(e)exit(-1)# create model
model = MobileNetV2(num_classes=5)
# load model weights
model_weight_path = "./MobileNetV2.pth"
model.load_state_dict(torch.load(model_weight_path))
model.eval()
with torch.no_grad():# predict classoutput = torch.squeeze(model(img)) #压缩batch维度predict = torch.softmax(output, dim=0) #将输出值转为概率分布。predict_cla = torch.argmax(predict).numpy()
print(class_indict[str(predict_cla)], predict[predict_cla].numpy())
plt.show()

预测输出:

CV算法复现(分类算法6/6):MobileNet(2017年V1,2018年V2,2019年V3,谷歌)相关推荐

  1. NLP专栏简介:数据增强、智能标注、意图识别算法|多分类算法、文本信息抽取、多模态信息抽取、可解释性分析、性能调优、模型压缩算法等

    NLP专栏简介:数据增强.智能标注.意图识别算法|多分类算法.文本信息抽取.多模态信息抽取.可解释性分析.性能调优.模型压缩算法等 专栏链接:NLP领域知识+项目+码源+方案设计 订阅本专栏你能获得什 ...

  2. 算法杂货铺——分类算法之决策树(Decision tree)

    算法杂货铺--分类算法之决策树(Decision tree) 2010-09-19 16:30 by T2噬菌体, 88978 阅读, 29 评论, 收藏, 编辑 3.1.摘要 在前面两篇文章中,分别 ...

  3. 算法杂货铺——分类算法之贝叶斯网络(Bayesian networks)

    算法杂货铺--分类算法之贝叶斯网络(Bayesian networks) 2010-09-18 22:50 by T2噬菌体, 66011 阅读, 25 评论, 收藏, 编辑 2.1.摘要 在上一篇文 ...

  4. k近邻算法(KNN)-分类算法

    k近邻算法(KNN)-分类算法 1 概念 定义:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别. k-近邻算法采用测量不同特征值之间的 ...

  5. 数据挖掘算法——常用分类算法总结

    常用分类算法总结 分类算法 NBC算法 LR算法 SVM算法 ID3算法 C4.5 算法 C5.0算法 KNN 算法 ANN 算法 分类算法 分类是在一群已经知道类别标号的样本中,训练一种分类器,让其 ...

  6. 分类算法列一下有多少种?应用场景?分类算法介绍、常见分类算法优缺点、如何选择分类算法、分类算法评估

    分类算法 分类算法介绍 概念 分类算法 常见分类算法 NBS LR SVM算法 ID3算法 C4.5 算法 C5.0算法 KNN 算法 ANN 算法 选择分类算法 分类算法性能评估 分类算法介绍 概念 ...

  7. 目标检测:YOLO V1、YOLO V2、YOLO V3 算法

    日萌社 人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新) yoloV3模型 目标检测:YOLO V1.YOLO V2.Y ...

  8. ML算法基础——分类算法-决策树、随机森林

    文章目录 1.决策树 1.1 认识决策树 1.2 信息论基础-银行贷款分析 1.2.1 信息论基础-信息熵 1.2.2 决策树的划分依据之一-信息增益 1.3 泰坦尼克号乘客生存分类 1.3.1 sk ...

  9. ML之监督学习算法之分类算法一 ———— k-近邻算法(最邻近算法)

    一.概述 最近邻规则分类(K-Nearest Neighbor)KNN算法 由Cover 和Hart在1968年提出了最初的邻近算法, 这是一个分类(classification)算法 输入基于实例的 ...

  10. 算法杂货铺——分类算法之朴素贝叶斯分类(Naive Bayesian classification)

    FROM: http://www.cnblogs.com/leoo2sk/archive/2010/09/17/1829190.html 0.写在前面的话 我个人一直很喜欢算法一类的东西,在我看来算法 ...

最新文章

  1. 棋盘覆盖问题原理及演示程序
  2. ubuntu下安装极点五笔
  3. Java基础 之软引用、弱引用、虚引用
  4. controlleradvice 参数_Spring MVC之@ControllerAdvice详解
  5. viewpager默认界面_使用默认方法的界面演变–第一部分:方法
  6. php购物车内物品删除,求助 购物车 用session删除 列表的一条
  7. java中程序跳转_java程序中先后台交互的两种实现方式以及页面之间的跳转
  8. 高并发下的HashMap
  9. 拓端tecdat|R语言中ARMA,ARIMA(Box-Jenkins),SARIMA和ARIMAX模型用于预测时间序列数据
  10. 中国信通院发布《区块链赋能新型智慧城市白皮书(2019年)》解读(附全文下载)
  11. 太赞了!分享一个数据科学利器 PyCaret,几行代码搞定从数据处理到模型部署
  12. i7 12650h参数 酷睿i712650h属于什么档次
  13. 判断Linux服务器是否被攻击以及相应的预防措施
  14. Android 无障碍服务自动点击
  15. 使用 Python +OpenCV+Deepface实现人脸、情感和种族检测
  16. java爬虫(本地爬虫和网络爬虫)
  17. 【日常】C盘及电脑内存清理
  18. 风险管理系列课程二:利用图技术优化反洗钱解决方案
  19. Day3-scrapy爬虫下载图片自定义名称
  20. 【解决】农业银行如何取消短信通知

热门文章

  1. Failed to save settings. Please restart Android Studio
  2. spring boot整合spring security笔记
  3. Windows窗口的建立
  4. ajax异步传输数据时return返回总是undefined(转载)
  5. Activiti——管理流程定义(四)
  6. dubbo入门--Hello World
  7. MySQL 学习笔记(9)— 事务控制语句、事务属性以及并发和隔离级别
  8. 神奇的FireFox
  9. 关于HTML代码的转义
  10. 【原创】Linux环境下的图形系统和AMD R600显卡编程(11)——R600指令集