一张图片经过神经网络进行特征提取后,能够得到很多特征图。

在特征图中会有一些相似性很高,这就是神经网络中存在的特征图冗杂的情况(如图中扳手相连的两幅特征图)。
作者认为可以对其中的一个特征图进行(Cheap Operations)简单的线性运算从而生成更多相似特征图,从而可以使用更少的参数生成更多的特征图,将相似的特征图认为是彼此的Ghost。

作者用Ghost Module代替传统卷积,首先采用普通的1x1卷积对输入图片进行通道数的压缩,然后再进行深度可分离卷积(逐层卷积)得到更多的特征图,然后将不同的特征图concat到一起,组合成新的output。
Ghost Module构建代码
class GhostModule(nn.Module):
def __init__(self, inp, oup, kernel_size=1, ratio=2, dw_size=3, stride=1, relu=True):
super(GhostModule, self).__init__()
#ratio一般会指定成2,保证输出特征层的通道数等于exp
self.oup = oup
init_channels = math.ceil(oup / ratio)
new_channels = init_channels*(ratio-1)
#利用1x1卷积对输入进来的特征图进行通道的浓缩,获得特征通缩
#跨通道的特征提取
self.primary_conv = nn.Sequential(
nn.Conv2d(inp, init_channels, kernel_size, stride, kernel_size//2, bias=False), #1x1卷积的输入通道数为GhostModule的输出通道数oup/2
nn.BatchNorm2d(init_channels), #1x1卷积后进行标准化
nn.ReLU(inplace=True) if relu else nn.Sequential(), #ReLU激活函数
)
#在获得特征浓缩后,使用逐层卷积,获得额外的特征图
#跨特征点的特征提取 一般会设定大于1的卷积核大小
self.cheap_operation = nn.Sequential(
nn.Conv2d(init_channels, new_channels, dw_size, 1, dw_size//2, groups=init_channels, bias=False), #groups参数的功能就是将普通卷积转换成逐层卷据
nn.BatchNorm2d(new_channels),
nn.ReLU(inplace=True) if relu else nn.Sequential(),
)
def forward(self, x):
x1 = self.primary_conv(x)
x2 = self.cheap_operation(x1)
#将1x1卷积后的结果和逐层卷积后的结果进行堆叠
out = torch.cat([x1,x2], dim=1)
return out[:,:self.oup,:,:]
1x1卷积的输入通道数为GhostModule的输出通道数oup/2,因为最终输出特征层由1x1卷积后的结果和逐层卷积后的结果进行堆叠组成的,进行逐层卷积时特征层的通道数不变,如果要保证最终输出特征层的通道数是一个固定值,那么就要使得1x1卷积后的结果的通道数为最终输出特征层的1/2。
Ghost BottleNeck是由Ghost Module组成的瓶颈结构
Ghost BottleNeck整体架构和Residual Block非常相似,也可以直接认为是将Residual Block中的普通卷积操作替换成Ghost Module得到。

左图中主干部分用用两个Ghost Module(GM)串联组成,其中第一个GM扩大通道数,第二个GM将通道数降低到与输入通道数一致;残差边部分与ResNet一样。由于S=1,因此不会对输入特征层的高和宽进行压缩,其功能为加深网络的深度。
右图中主干部分的两个GM之间加入了一个stride=2的Deepwise卷积,可以将特征图高和宽进行压缩,使其大小降为输入的1/2;在残差边部分,也会添加一个步长为2x2的深度可分离卷积和1x1的普通卷积,以保证Add操作可以对齐。由于S=2,因此会对输入特征层的高和宽进行压缩,其功能为改变输入特征层的形状。
实际应用中,为了进一步提高效率,GhostModule中的所有常规卷积都用pointwise卷积代替。
Ghost BottleNeck构建代码:
class GhostBottleneck(nn.Module):
def __init__(self, in_chs, mid_chs, out_chs, dw_kernel_size=3, stride=1, act_layer=nn.ReLU, se_ratio=0.):
super(GhostBottleneck, self).__init__()
has_se = se_ratio is not None and se_ratio > 0.
self.stride = stride
#首先利用一个ghost模块进行特征提取
#此时指定的通道数会比较大,可以看作是逆残差结构,进行通道数上升
self.ghost1 = GhostModule(in_chs, mid_chs, relu=True)
if self.stride > 1: #根据步长判断是否使用深度可分离卷积对输入特征图进行高和宽的压缩
#如果要进行特征图的高宽压缩,则进行逐层卷积
self.conv_dw = nn.Conv2d(mid_chs, mid_chs, dw_kernel_size, stride=stride,
padding=(dw_kernel_size-1)//2,
groups=mid_chs, bias=False)
self.bn_dw = nn.BatchNorm2d(mid_chs)
if has_se: #判断是否使用注意力机制模块
self.se = SqueezeExcite(mid_chs, se_ratio=se_ratio)
else:
self.se = None
#再次利用一个ghost模块进行特征提取
self.ghost2 = GhostModule(mid_chs, out_chs, relu=False)
#判断步长是否等1、输入通道和输出通道是否一样
if (in_chs == out_chs and self.stride == 1):
self.shortcut = nn.Sequential()
else: #如果不一样则利用深度可分离卷积和1x1卷积调整通道数,保证主干部分和残差边部分能够进行相加
self.shortcut = nn.Sequential(
nn.Conv2d(in_chs, in_chs, dw_kernel_size, stride=stride,
padding=(dw_kernel_size-1)//2, groups=in_chs, bias=False),
nn.BatchNorm2d(in_chs),
nn.Conv2d(in_chs, out_chs, 1, stride=1, padding=0, bias=False),
nn.BatchNorm2d(out_chs),
)
def forward(self, x): #前向传播
residual = x
x = self.ghost1(x)
if self.stride > 1:
x = self.conv_dw(x)
x = self.bn_dw(x)
if self.se is not None:
x = self.se(x)
x = self.ghost2(x)
x += self.shortcut(residual)
return x

整个Ghostnet都是由Ghost Bottlenecks进行组成的。
当一张图片输入到Ghostnet当中时,首先进行一个16通道的普通1x1卷积块(卷积+标准化+激活函数)。
之后就开始Ghost Bottlenecks的堆叠了,利用Ghost Bottlenecks,最终获得了一个7x7x160的特征层(当输入是224x224x3的时候)。
然后利用一个1x1的卷积块进行通道数的调整,此时可以获得一个7x7x960的特征层。
之后进行一次全局平均池化,然后再利用一个1x1的卷积块进行通道数的调整,获得一个1x1x1280的特征层。
最后平铺后进行全连接就可以进行分类了。
class GhostNet(nn.Module):
def __init__(self, cfgs, num_classes=1000, width=1.0, dropout=0.2):
super(GhostNet, self).__init__()
# setting of inverted residual blocks
self.cfgs = cfgs
self.dropout = dropout
# building first layer 对输入图片进行第一个卷积标准化+激活函数
output_channel = _make_divisible(16 * width, 4)
#以应用到yolov4为例 416,416,3 -> 208,208,16 压缩高和宽,通道数扩张
self.conv_stem = nn.Conv2d(3, output_channel, 3, 2, 1, bias=False) #s=2,output_channel=16
self.bn1 = nn.BatchNorm2d(output_channel)
self.act1 = nn.ReLU(inplace=True)
input_channel = output_channel
# building inverted residual blocks 构建瓶颈结构
stages = []
block = GhostBottleneck
for cfg in self.cfgs: #对配置列表进行循环
layers = []
for k, exp_size, c, se_ratio, s in cfg:
output_channel = _make_divisible(c * width, 4)
hidden_channel = _make_divisible(exp_size * width, 4)
#根据cfg里面的内容构建瓶颈结构
layers.append(block(input_channel, hidden_channel, output_channel, k, s,
se_ratio=se_ratio))
#计算下一个瓶颈结构的输入
input_channel = output_channel
stages.append(nn.Sequential(*layers))
#卷积标准化+激活函数
output_channel = _make_divisible(exp_size * width, 4)
stages.append(nn.Sequential(ConvBnAct(input_channel, output_channel, 1)))
input_channel = output_channel
#根据构建好的block序列模型
self.blocks = nn.Sequential(*stages)
#构建分类才能够
# building last several layers
output_channel = 1280
self.global_pool = nn.AdaptiveAvgPool2d((1, 1))
self.conv_head = nn.Conv2d(input_channel, output_channel, 1, 1, 0, bias=True)
self.act2 = nn.ReLU(inplace=True)
self.classifier = nn.Linear(output_channel, num_classes)
def forward(self, x):
#第一个卷积标注化+激活函数
x = self.conv_stem(x)
x = self.bn1(x)
x = self.act1(x)
#瓶颈结构特征提取
x = self.blocks(x)
#构建分类岑那个
x = self.global_pool(x)
x = self.conv_head(x)
x = self.act2(x)
x = x.view(x.size(0), -1)
if self.dropout > 0.:
x = F.dropout(x, p=self.dropout, training=self.training)
x = self.classifier(x)
return x
def ghostnet(**kwargs):
"""
Constructs a GhostNet model
"""
cfgs = [
# k, t, c, SE, s
# k代表卷积核大小,表示跨特征点的特征提取能力
# t代表第一个ghost模块的通道数大小,它的值一般比较大一点
# c代表瓶颈结构最终的输出通道数
# SE代表是否使用注意力机制,如果不为0就是用注意力机制
# s代表步长,如果s=2就会对输入特征层进行高和宽的压缩
# stage1
# 208,208,16 -> 208,208,16
[[3, 16, 16, 0, 1]],
# stage2
# 208,208,16 -> 104,104,24
[[3, 48, 24, 0, 2]],
[[3, 72, 24, 0, 1]],
# stage3
# 104,104,24 -> 52,52,40
[[5, 72, 40, 0.25, 2]],
[[5, 120, 40, 0.25, 1]],
# stage4
# 52,52,40 -> 26,26,80 -> 26,26,112
[[3, 240, 80, 0, 2]],
[[3, 200, 80, 0, 1],
[3, 184, 80, 0, 1],
[3, 184, 80, 0, 1],
[3, 480, 112, 0.25, 1],
[3, 672, 112, 0.25, 1]
],
# stage5
# 26,26,112 -> 13,13,160
[[5, 672, 160, 0.25, 2]],
[[5, 960, 160, 0, 1],
[5, 960, 160, 0.25, 1],
[5, 960, 160, 0, 1],
[5, 960, 160, 0.25, 1]
]
]
return GhostNet(cfgs, **kwargs)
需要利用主干特征提取网络获得的三个有效特征进行加强特征金字塔的构建
取出stage3、stage4、stage5的输出:
class GhostNet(nn.Module):
def __init__(self, pretrained=True):
super(GhostNet, self).__init__()
model = ghostnet()
if pretrained:
state_dict = torch.load("model_data/ghostnet_weights.pth")
model.load_state_dict(state_dict)
del model.global_pool
del model.conv_head
del model.act2
del model.classifier
del model.blocks[9]
self.model = model
def forward(self, x):
x = self.model.conv_stem(x)
x = self.model.bn1(x)
x = self.model.act1(x)
feature_maps = []
for idx, block in enumerate(self.model.blocks):
x = block(x)
if idx in [2,4,6,8]:
feature_maps.append(x)
return feature_maps[1:]
参考文献:
原作者解读
Ghostnet网络介绍与构建
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案
我在这方面尝试了很多URL,在我遇到这个特定的之前,它们似乎都很好:require'rubygems'require'nokogiri'require'open-uri'doc=Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))putsdoc这是结果:/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in`open_http':404NotFound(OpenURI::HT
深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG
(本文是网络的宏观的概念铺垫)目录计算机网络背景网络发展认识"协议"网络协议初识协议分层OSI七层模型TCP/IP五层(或四层)模型报头以太网碰撞路由器IP地址和MAC地址IP地址与MAC地址总结IP地址MAC地址计算机网络背景网络发展 是最开始先有的计算机,计算机后来因为多项技术的水平升高,逐渐的计算机变的小型化、高效化。后来因为计算机其本身的计算能力比较的快速:独立模式:计算机之间相互独立。 如:有三个人,每个人做的不同的事物,但是是需要协作的完成。 而这三个人所做的事是需要进行协作的,然而刚开始因为每一台计算机之间都是互相独立的。所以前面的人处理完了就需要将数据
一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su
安全产品安全网关类防火墙Firewall防火墙防火墙主要用于边界安全防护的权限控制和安全域的划分。防火墙•信息安全的防护系统,依照特定的规则,允许或是限制传输的数据通过。防火墙是一个由软件和硬件设备组合而成,在内外网之间、专网与公网之间的界面上构成的保护屏障。下一代防火墙•下一代防火墙,NextGenerationFirewall,简称NGFirewall,是一款可以全面应对应用层威胁的高性能防火墙,提供网络层应用层一体化安全防护。生产厂家•联想网御、CheckPoint、深信服、网康、天融信、华为、H3C等防火墙部署部署于内、外网编辑额,用于权限访问控制和安全域划分。UTM统一威胁管理(Un
TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是
Linux操作系统——网络配置与SSH远程安装完VMware与系统后,需要进行网络配置。第一个目标为进行SSH连接,可以从本机到VMware进行文件传送,首先需要进行网络配置。1.下载远程软件首先需要先下载安装一款远程软件:FinalShell或者xhell7FinalShellxhell7FinalShell下载:Windows下载http://www.hostbuf.com/downloads/finalshell_install.exemacOS下载http://www.hostbuf.com/downloads/finalshell_install.pkg2.配置CentOS网络安装好