文章目录

  • 论文阅读
  • 代码实现
    • model
    • train
    • predict
  • 实验结果

论文阅读

感谢p导

论文链接:MobileNetV2: Inverted Residuals and Linear Bottlenecks

主要亮点是提出了带线性瓶颈层的倒残差结构

回顾MobileNet V1,主要是将普通Conv转换为dw和pw,但是在dw中训练出来可能会很多0,也就是depthwise部分得到卷积核会废掉,即卷积核参数大部分为0,因为权重数量可能过少,再加上Relu激活函数的原因

V2为了解决这方面的问题,提出去掉在低维特征图上面的Relu操作

在模型中 各层的特征图,其中有很多冗余信息,我们可以 通过降维(1*1卷积,width multiplier)来实现提取其中的manifold of insterest,Relu也是可以用来去除冗余信息,但是可能会出现大量的信息缺失问题(见论文Fig1),这就是降维和非线性之间的矛盾,本文中提出首先将特征图进行升维,之后使用Relu来进行非线性的激活,这样就可以发挥出Relu的非线性作用(神经网络的作用就是其非线性)


Relu丢失信息,见Fig1,通过Relu(T×X),得到相应的Y,之后对使用T的广义逆矩阵对T做相应操作,将Y映射回与X相同维度,可以发现,Relu对低维特征有大量的损失。

因此,通过上述描述,我们得到了两个属性


因此,论文提出了先升维后降维,且在最后一个低维特征图中不使用Relu6,来做线性变换,也就是带线性瓶颈层的倒残差结构

论文中对于在哪里使用残差连接,使用Linear还是non-linear做了实验对比

代码实现




V2的模型主要是有线性瓶颈层的倒残差结构,两种,一个有残差连接,一个没有残差链接,写出这种模块进行堆叠即可

model

写一个通用的ConvBnRelu6类
写一个residual类
堆建模型即可
有一个width multiplier的超参数,设置一个函数,将channel与该超参数相乘的结果设置为相近的8的整数倍

from torch import nn
import torchdef _make_divisible(ch, divisor=8, min_ch=None):
# ???这里不是很懂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_ch# conv+bn+relu6
class ConvBNReLU(nn.Sequential):def __init__(self, in_channel, out_channel, kernel_size=3, stride=1, groups=1):padding = (kernel_size - 1) // 2super(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#stride==1,out=in时候才使用残差链接self.use_shortcut = stride == 1 and in_channel == out_channellayers = []if expand_ratio != 1:# 1x1 pointwise conv 升维layers.append(ConvBNReLU(in_channel, hidden_channel, kernel_size=1))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),])self.conv = nn.Sequential(*layers)def forward(self, x):if self.use_shortcut:return x + self.conv(x)else:return self.conv(x)class MobileNetV2(nn.Module):def __init__(self, num_classes=1000, alpha=1.0, round_nearest=8):super(MobileNetV2, self).__init__()block = InvertedResidualinput_channel = _make_divisible(32 * alpha, round_nearest)last_channel = _make_divisible(1280 * alpha, round_nearest)inverted_residual_setting = [# expansion factor, out_channels, num, first_layer stride[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 = []# 第一层features.append(ConvBNReLU(3, input_channel, stride=2))# 倒残差结构for 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))# *可迭代对象,展开其中元素self.features = nn.Sequential(*features)# 全局平均池化self.avgpool = nn.AdaptiveAvgPool2d((1, 1))self.classifier = nn.Sequential(nn.Dropout(0.2),nn.Linear(last_channel, num_classes))# weight initializationfor 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(self, x):x = self.features(x)x = self.avgpool(x)x = torch.flatten(x, 1)x = self.classifier(x)return x

train

import os
import sys
import jsonimport torch
import torch.nn as nn
import torch.optim as optim
from torchvision import transforms, datasets
from tqdm import tqdmfrom model_v2 import MobileNetV2def main():device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")print("using {} device.".format(device))batch_size = 16epochs = 5nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workersprint('Using {} dataloader workers every process'.format(nw))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, "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"])validate_dataset = datasets.ImageFolder(root=os.path.join(image_path, "val"),transform=data_transform["val"])train_num = len(train_dataset)val_num = len(validate_dataset)    train_loader = torch.utils.data.DataLoader(train_dataset,batch_size=batch_size, shuffle=True)validate_loader = torch.utils.data.DataLoader(validate_dataset,batch_size=batch_size, shuffle=False)print("using {} images for training, {} images for validation.".format(train_num,val_num))# {'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) # create modelnet = MobileNetV2(num_classes=5)# torchvision.models.mobilenet# download url: https://download.pytorch.org/models/mobilenet_v2-b0353104.pthmodel_weight_path = "./MobileNetV2.pth"assert os.path.exists(model_weight_path), "file {} dose not exist.".format(model_weight_path)pre_weights = torch.load(model_weight_path, map_location=device)# delete classifier weightspre_dict = {k: v for k, v in pre_weights.items() if net.state_dict()[k].numel() == v.numel()}missing_keys, unexpected_keys = net.load_state_dict(pre_dict, strict=False)# 浅层特征具有一定通用性,且减少训练时间,冻结feature层特征for param in net.features.parameters():param.requires_grad = Falsenet.to(device)# define loss functionloss_function = nn.CrossEntropyLoss()# construct an optimizerparams = [p for p in net.parameters() if p.requires_grad]optimizer = optim.Adam(params, lr=0.0001)best_acc = 0.0save_path = './MobileNetV2.pth'train_steps = len(train_loader)for epoch in range(epochs):# trainnet.train()running_loss = 0.0train_bar = tqdm(train_loader, file=sys.stdout)for data in train_bar: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()train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,epochs,loss)# validatenet.eval()acc = 0.0  # accumulate accurate number / epochwith torch.no_grad():val_bar = tqdm(validate_loader, file=sys.stdout)for val_data in val_bar:val_images, val_labels = val_dataoutputs = net(val_images.to(device))# loss = loss_function(outputs, test_labels)predict_y = torch.max(outputs, dim=1)[1]acc += torch.eq(predict_y, val_labels.to(device)).sum().item()val_bar.desc = "valid epoch[{}/{}]".format(epoch + 1,epochs)val_accurate = acc / val_numprint('[epoch %d] train_loss: %.3f  val_accuracy: %.3f' %(epoch + 1, running_loss / train_steps, val_accurate))if val_accurate > best_acc:best_acc = val_accuratetorch.save(net.state_dict(), save_path)print('Finished Training')if __name__ == '__main__':main()

predict

import os
import jsonimport torch
from PIL import Image
from torchvision import transforms
import matplotlib.pyplot as pltfrom model_v2 import MobileNetV2def main():device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")data_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 imageimg_path = "../tulip.jpg"assert os.path.exists(img_path), "file: '{}' dose not exist.".format(img_path)img = Image.open(img_path)plt.imshow(img)# [N, C, H, W]img = data_transform(img)# expand batch dimensionimg = torch.unsqueeze(img, dim=0)# read class_indictjson_path = './class_indices.json'assert os.path.exists(json_path), "file: '{}' dose not exist.".format(json_path)json_file = open(json_path, "r")class_indict = json.load(json_file)# create modelmodel = MobileNetV2(num_classes=5).to(device)# load model weightsmodel_weight_path = "./MobileNetV2.pth"model.load_state_dict(torch.load(model_weight_path, map_location=device))model.eval()with torch.no_grad():# predict classoutput = torch.squeeze(model(img.to(device))).cpu()predict = torch.softmax(output, dim=0)predict_cla = torch.argmax(predict).numpy()print_res = "class: {}   prob: {:.3}".format(class_indict[str(predict_cla)],predict[predict_cla].numpy())plt.title(print_res)for i in range(len(predict)):print("class: {:10}   prob: {:.3}".format(class_indict[str(i)],predict[i].numpy()))plt.show()if __name__ == '__main__':main()

实验结果

【论文阅读】MobileNet V2——MobileNetV2: Inverted Residuals and Linear Bottlenecks相关推荐

  1. 《MobileNetV2: Inverted Residuals and Linear Bottlenecks》论文学习笔记

    文章目录 论文基本信息 研究背景 读完摘要后对上述问题的回答 读完论文后对上述问题的回答 什么是MobileNet V2? 为啥要在前边加一个Pointwise convolution? 什么是反向残 ...

  2. 轻量化网络(二)MobileNetV2: Inverted Residuals and Linear Bottlenecks

    论文链接 Pytorch实现 Tensorflow实现 Mobilenet V2是谷歌在Mobilenet V1上的进一步改进,第一版参考文章,是Mobilenet系列的第二篇.该文章以深度可分离卷积 ...

  3. 论文阅读:MobileNetV2: Inverted Residuals and Linear Bottlenecks(MobileNetV2)

    文章目录 1 摘要 2 存在问题 3 亮点 3.1 Linear Bottlenecks 3.2 Inverted residual block 3.4 总的结构体 4 部分结果 4.1 部分数据对比 ...

  4. 论文精读:MobileNetV2: Inverted Residuals and Linear Bottlenecks

    论文地址:https://arxiv.org/pdf/1801.04381.pdf 模型结构简单,重点是理解模型设计的动机,并记录一下卷积的通用知识,已经熟知的知识就不再记录了,详细读原文. Abst ...

  5. 轻量型网络之MobileNetV2: Inverted Residuals and Linear Bottlenecks论文学习

    0.摘要 针对残差结构提出来倒残差结构(Inverted Residuals),由于使用的是1x1卷积在resnet中也叫瓶颈层,所以这个模块最终叫做具有线性瓶颈的倒残差结构(the inverted ...

  6. 论文分享 MobileNetV2: Inverted Residuals and Linear Bottlenecks

    摘要 在本文中,我们描述了一种新的移动架构 MobileNetV2,它提高了移动模型在多个任务和基准测试以及不同模型大小范围内的最新性能.我们还描述了在我们称为 SSDLite 的新框架中将这些移动模 ...

  7. MobilenetV2学习笔记 --- MobileNetV2: Inverted Residuals and Linear Bottlenecks

    论文:https://arxiv.org/abs/1801.04381 代码:https://github.com/tonylins/pytorch-mobilenet-v2 此外给出Mobilene ...

  8. MobileNetV2: Inverted Residuals and Linear Bottlenecks

    推荐阅读:https://blog.csdn.net/kangdi7547/article/details/81431572 16->96->24, 为什么中间有96怎么确定, 由t来确定 ...

  9. MobileNet v2中 Inverted Residual 和 Linear Bottleneck 是怎么回事

    MobileNet v2中 Inverted Residual 和 Linear Bottleneck 是怎么回事 flyfish MobileNet v1的深度可分离卷积 Block的结构 先从Mo ...

  10. 【MobileNet V2】《MobileNetV2:Inverted Residuals and Linear Bottlenecks》

    CVPR-2018 caffe 版本的代码:https://github.com/shicai/MobileNet-Caffe/blob/master/mobilenet_v2_deploy.prot ...

最新文章

  1. iOS 后台下载及管理库
  2. 视频程式化的基于帧差异的时间损失
  3. sqlite的速度_内存不够用还要速度快,终于找到可以基于 File 的 Cache 了
  4. PennyLane | 用于量子计算机可微分编程的跨平台Python库
  5. osgi实战学习之路:8. Service-3之ServiceTracker
  6. 《学习opencv》笔记——矩阵和图像处理——cvGEMM,cvGetCol,cvGetCols and cvGetDiag
  7. 深入Android 【六】 —— 界面构造
  8. Infobright 数据仓库心得总结
  9. curl命令java_Java中的curl命令
  10. Tiff – 值得你体验一下的可视化的字体对比工具
  11. ios重签名shell脚本_iOS逆向——shell重签名及代码注入
  12. 花式模拟【栈结构】做“日志分析”(洛谷P1165题题解,Java语言描述)
  13. iphone-常用的对视图图层(layer)的操作
  14. 用python让excel飞起来 pdf_讯飞智能键盘K710 一款无网络实力依然在线的黑科技产品...
  15. 谷歌要构建自己的区块链技术
  16. jquery1.6.2源码拆分一
  17. dosbox 实现程序编译
  18. go-fastDFS 分布式文件系统搭建(实现梳理)
  19. 黑苹果常用 工具+Kext+ACPI+UEFI驱动 下载
  20. Legion:基于Haskell开发的极简区块链服务器

热门文章

  1. windows桌面待办事项_提醒待办事项app哪个好用?苹果手机上有什么好用的提醒便签软件吗...
  2. Perl 6 - CPAN
  3. 程序员的浪漫之——情侣日常小程序
  4. python编程基础-task5-面向对象的编程
  5. 毕业设计 高校排课系统
  6. 聊一聊Java中的线程中断
  7. 字节架构师发布“绝版”Java并发实现原理剖析JDK源码
  8. 几组数据的相关性python_几的定义
  9. PBR流程介绍和模型规范
  10. 寓教于乐:12个学习编程的游戏化平台