400 128 6709

行业新闻

PPYOLOE详解第二弹:你真的知道什么是Neck嘛?

发布时间:2025-07-31点击次数:
本文先介绍双阶段检测模型Neck,以FPN论文为例讲4种结构。再讲单阶段检测模型,回顾YOLOv1到v3中Neck的发展,最后重点介绍PANet及PPYOLOE的Neck结构,还给出了相关代码,展现了目标检测网络Neck的演变与特点。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

ppyoloe详解第二弹:你真的知道什么是neck嘛? -

上次介绍完了PPYOLOE的Backbone 那么这次就到目标检测网络的第二部分--Neck了

相比较Backbone被称为主干(骨干)网络比较明确,但是neck的提出就不太明确了,我第一次认识到这个词还是在看YOLOv4网络的时候,YOLOv3中我甚至都没注意到这里,因为YOLOv3中Neck就只是使用来的一个上采样的方式来将主干网络的不同输出进行融合,但是到了YOLOv4中 Neck的占比变得更丰富了,它的作用甚至逐渐的和Head与Backbone相提并论了起来,由于PPYOLOE中的neck创新点也不是很多,单独说又显得有点水,那么这里我们就向大家介绍几种不同的Neck以及YOLO系列中Neck的发展历史,最后再讲YOLO Neck!

So just do it

相信如果对目标检测有一些了解的人应该会知道目标检测网络分为点阶段与双阶段检测模型,那么我们先简单讲一下与本文关系不太大的双阶段检测模型都有哪些Neck吧!

零:双阶段检测模型Neck

这里我们以《Feature Pyramid Networks for Object Detection》这篇论文为例子来介绍几个Neck,FPN这篇论文中一共提出了4个结构

PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        
  1. 使用图像金字塔构建特征金字塔。在每个图像比例上独立计算特征,速度较慢。
  2. 最近的检测系统选择只使用单尺度特征,以实现更快的检测。
  3. 另一种方法是重用Conventas计算的金字塔特征层次(如果它是特征化的图像金字塔)。
  4. 特征金字塔网络(FPN)与(b)和(c)一样快速,但更精确。

0.1:第一种

PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        

第一种是对每一种尺度的图像都进行特征提取,因此能够产生多尺度的特征表示,由于所有登记的特征图都具有不同的语义信息因此检测精度较高,但是他的推理时间相较于其他网络会大幅增加,同时占用大量的资源,不好实现。

0.2: 第二种

PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        

第二种就是只利用最高层的特征图进行预测,像是经典的FasterRCNN网络就是使用的这种简单的方式,也就是几乎没有Neck什么事情,这种方法速度虽然比较快,但是他的检测精度相比较其他几种方法来说还是有待提高

0.3: 第三种

PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        

第三种网络与第一种网络类似,但是他放弃了使用底层特征图,而是在较高层次搭建金字塔结构,这就导致一些比较低级的语义信息被错过,而这对于小目标检测来说是极其重要的

0.4: 第四种

PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        

插曲

特征金字塔结构,我有一个项目是在详细的介绍,这里不过多赘述,如想详细了解可以看我的之前的项目ResNet+FPN详解

一:单阶段检测模型

1.1: YOLOv1

原论文 You Can Only look once 

YOLOv1是与SSD和Faster RCNN同时代的产物,但是YOLOv1在刚刚出来的时候真的不是很惊艳,因为在检测精度上它与Faster RCNN相差太远了,在“能耗比”上也比不过同时期的SSD,而且这时的YOLOv1采用的是上文中的b模式,也就是只用最高层的特征图进行预测,所以其实YOLOv1中并没有什么Neck,这里也就不过多赘述了,上面有原论文有兴趣的可以去读一下,这里就放一张YOLOv1的原图供大家参考

PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        

这里的图片源自B站up主霹雳吧啦Wz

Openflow Openflow

一键极速绘图,赋能行业工作流

Openflow 88 查看详情 Openflow

1.2:YOLOv2

原论文 YOLO9000: Better, Faster, Stronger

PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        

*** YOLOv2的论文原名叫YOLO9000,这是因为作者通过引入anchor等操作让YOLO能够检测超过9000种,正如YOLO9000的名字一样 Better,Faster,Stronger,作者在网络中也开始使用Backbone中间层次的特征图,从而利用到了中间层的语义信息,通过上图可以这里YOLOv2是将中间的分支先通过一个基础卷积块把通道数从512变为64,后通过PassThrough Layer将通道数改为256,长宽各减一半,然后与最高层的特征图在深度维度进行concat拼接,这里YOLOv2已经开始使用下采样的的方式来进行语义融合,这里我认为可以算是YOLO的Neck初具雏形,但在YOLOv2中它仍然只有一个检测头,所以它应该是类似于FPN***

这里的图片源自B站up主霹雳吧啦Wz

1.3: YOLOv3

YOLOv3: An Incremental Improvement

PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        

YOLOv3的论文名称很有意思,渐进式改进,所以可能作者只是想把YOLO当做一个日常升级?自己还藏了一手?所以也有的同学会戏称原YOLO系列的作者为藏一手司机。在YOLOv3中Backbone已经被替换成了DarkNet53,这次的Backbone输出分支也从2变成了3个,同时预测头也增加到了3个。这里我们就将Backbone的三个分支从低到高以此称为C0 [B,256,64,64],C1[B,512,32,32],C2[B,1024,16,16]。首先网络将C2的输出经过一系列卷积之后,传出两个分支C2-1,C2-2,C2-1分支用于预测头,C2-2经过上采样技术用于与C1的输出进行Concat拼接用于语义融合,并在经过一系列卷积之后再次传出两个分支C1-1、C1-2,C1-1分支作用在第二个预测头上,C1-2分支再次经过一个上采样技术然后与C0的释出进行Concat拼接用于语义融合,并在经过一系列卷积之后,诶~这回到头了,不分支了,直接接一个检测头,哈哈哈哈

Neck:我滴任务完成了

PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        

YOLOv3相比较YOLOv2他增加了Backbone输出,增加了预测头分支,但是他的语义融合方式从下采样改成了上采样,所以这里有没有可能就是,他真的留了一手,真的准备在自己的YOLOv4中让Neck称为一个真正的Neck?但是很可惜由于种种原因YOLO的原作者放弃了更新YOLO,留一手司机究竟留没留一手,谁也不知道了。

这里的图片源自B站up主霹雳吧啦Wz

二:正式介绍PANet

比较新的几篇YOLO的Neck都是采用了PAN结构,像是PPYOLOv2,YOLO-X,YOLOv5(待定)以及本文要介绍PPYOLOE都采用了这个结构

PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        

首先从Backbone获取C5输出需要经过CSPStage,

PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        
# 1. C5会先分出两个分支
	# 1.1. 分支一     直接经过一个1x1卷积把通道数从1024变成384.
	# 1.2. 分支二     首先经过一个1x1的卷积将通道数从1024变为384,然后经过3个BasicBlock结构其中在第二个BasicBlock结构后加入一个SPP结构# 2. 将分支一与分支二进行concat拼接# 3. 再经过一个ConvBNLayer结构,说句实话我也不知道这是在干吗因为这个ConvBN是1x1卷积,而且他的输出通道数与输入通道数都是768,所以我也有点疑惑。# 4. 分出两支一个为C5-1,另外一个为C5-2
        PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        
 5. 然后将C5-1再经过一个ConvBNLayer层将通道数从768转换为384,然后通过interpolate函数进行上采样将宽高x2,通道数不变。 6. 然后与C4进行Concat拼接,称为C4-0,此时C4-0通道数为896,宽高都为32.
        PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        
# 7. C4-0会先分出两个分支
	# 1.1. 分支一     直接经过一个1x1卷积把通道数从896变成192.
	# 1.2. 分支二     首先经过一个1x1的卷积将通道数从896变为192,然后经过3个BasicBlock结构# 8. 将分支一与分支二进行concat拼接# 9. 再经过一个ConvBNLayer结构。# 10. 分出两支一个为C4-1,另外一个为C4-2
        PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        
 11. 然后将C5-1再经过一个ConvBNLayer层将通道数从384转换为192,然后通过interpolate函数进行上采样将宽高x2,通道数不变。 12. 然后与C3进行Concat拼接,称为C3-0,此时C3-0通道数为448,宽高都为64.
        PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        
# 13. C3-0会先分出两个分支
	# 1.1. 分支一     直接经过一个1x1卷积把通道数从448变成96.
	# 1.2. 分支二     首先经过一个1x1的卷积将通道数从448变为96,然后经过3个BasicBlock结构# 14. 将分支一与分支二进行concat拼接# 15. 再经过一个ConvBNLayer结构。# 16. 分出两支一个为C3-1,另外一个为C3-2# 17. C3-2直接作用于预测头上
        PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        
 11. 然后将C3-1再经过一个ConvBNLayer层将宽高除以二 12. 然后与C4-2进行Concat拼接,称为P4-0,此时P4-0通道数为576,宽高都为32.
        PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        
# 13. P4-0会先分出两个分支
	# 1.1. 分支一     直接经过一个1x1卷积把通道数从576变成192.
	# 1.2. 分支二     首先经过一个1x1的卷积将通道数从576变为192,然后经过3个BasicBlock结构# 14. 将分支一与分支二进行concat拼接# 15. 再经过一个ConvBNLayer结构。# 16. 分出两支一个为P4-1,P4-2# 17. P4-2直接作用于预测头上
        PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        
 11. 然后将P4-1再经过一个ConvBNLayer层将宽高除以二 12. 然后与C5-2进行Concat拼接,称为P5-0,此时P4-0通道数为1152,宽高都为16.
        PPYOLOE详解第二弹:你真的知道什么是Neck嘛? -        
# 13. P5-0会先分出两个分支
	# 1.1. 分支一     直接经过一个1x1卷积把通道数从1152变成384.
	# 1.2. 分支二     首先经过一个1x1的卷积将通道数从1152变成384,然后经过3个BasicBlock结构# 14. 将分支一与分支二进行concat拼接# 15. 再经过一个ConvBNLayer结构。# 17. 然后直接作用于预测头上
   

PPYOLOE Neck 所有代码

代码地址 PaddleDetection/ppdet/modeling/necks/custom_pan.py

In [9]
!git clone -b develop https://gitee.com/paddlepaddle/PaddleDetection.git

%cd PaddleDetection/
!python setup.py install
!pip install -r requirements.txt
    In [ ]
!python ./ppdet/modeling/necks/custom_pan.py
   
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at##     http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.import paddleimport paddle.nn as nnimport paddle.nn.functional as Ffrom ppdet.core.workspace import register, serializablefrom ppdet.modeling.layers import DropBlockfrom ppdet.modeling.ops import get_act_fnfrom ppdet.modeling.backbones.cspresnet import ConvBNLayer, BasicBlockfrom ppdet.modeling.shape_spec import ShapeSpecimport numpy as np

__all__ = ['CustomCSPPAN']class SPP(nn.Layer):
    def __init__(self,
                 ch_in,
                 ch_out,
                 k,
                 pool_size,
                 act='swish',
                 data_format='NCHW'):
        super(SPP, self).__init__()
        self.pool = []
        self.data_format = data_format        for i, size in enumerate(pool_size):
            pool = self.add_sublayer(                'pool{}'.format(i),
                nn.MaxPool2D(
                    kernel_size=size,
                    stride=1,
                    padding=size // 2,
                    data_format=data_format,
                    ceil_mode=False))
            self.pool.append(pool)
        self.conv = ConvBNLayer(ch_in, ch_out, k, padding=k // 2, act=act)    def forward(self, x):
        outs = [x]        for pool in self.pool:
            outs.append(pool(x))        if self.data_format == 'NCHW':
            y = paddle.concat(outs, axis=1)        else:
            y = paddle.concat(outs, axis=-1)

        y = self.conv(y)        return yclass CSPStage(nn.Layer):
    def __init__(self, block_fn, ch_in, ch_out, n, act='swish', spp=False):
        super(CSPStage, self).__init__()

        ch_mid = int(ch_out // 2)
        self.conv1 = ConvBNLayer(ch_in, ch_mid, 1, act=act)
        self.conv2 = ConvBNLayer(ch_in, ch_mid, 1, act=act)
        self.convs = nn.Sequential()
        next_ch_in = ch_mid        for i in range(n):
            self.convs.add_sublayer(                str(i),                eval(block_fn)(next_ch_in, ch_mid, act=act, shortcut=False))            if i == (n - 1) // 2 and spp:
                self.convs.add_sublayer(                    'spp', SPP(ch_mid * 4, ch_mid, 1, [5, 9, 13], act=act))
            next_ch_in = ch_mid
        self.conv3 = ConvBNLayer(ch_mid * 2, ch_out, 1, act=act)    def forward(self, x):
        y1 = self.conv1(x)
        y2 = self.conv2(x)
        y2 = self.convs(y2)
        y = paddle.concat([y1, y2], axis=1)
        y = self.conv3(y)        return yclass CustomCSPPAN(nn.Layer):
    __shared__ = ['norm_type', 'data_format', 'width_mult', 'depth_mult', 'trt']    def __init__(self,
                 in_channels=[256, 512, 1024],
                 out_channels=[768,768/2,768/4],
                 norm_type='bn',
                 act='relu',
                 stage_fn='CSPStage',
                 block_fn='BasicBlock',
                 stage_num=1,
                 block_num=3,
                 drop_block=False,
                 block_size=3,
                 keep_prob=0.9,
                 spp=True,
                 data_format='NCHW',
                 width_mult=1.0,
                 depth_mult=1.0,
                 trt=False):

        super(CustomCSPPAN, self).__init__()
        out_channels = [max(round(c * width_mult), 1) for c in out_channels]
        block_num = max(round(block_num * depth_mult), 1)
        act = get_act_fn(
            act, trt=trt) if act is None or isinstance(act,
                                                       (str, dict)) else act
        self.num_blocks = len(in_channels)
        self.data_format = data_format
        self._out_channels = out_channels
        in_channels = in_channels[::-1]
        fpn_stages = []
        fpn_routes = []        for i, (ch_in, ch_out) in enumerate(zip(in_channels, out_channels)):            if i > 0:
                ch_in += ch_pre // 2

            stage = nn.Sequential()            for j in range(stage_num):
                stage.add_sublayer(                    str(j),                    eval(stage_fn)(block_fn,
                                   ch_in if j == 0 else ch_out,
                                   ch_out,
                                   block_num,
                                   act=act,
                                   spp=(spp and i == 0)))            if drop_block:
                stage.add_sublayer('drop', DropBlock(block_size, keep_prob))

            fpn_stages.append(stage)            if i < self.num_blocks - 1:
                fpn_routes.append(
                    ConvBNLayer(
                        ch_in=ch_out,
                        ch_out=ch_out // 2,
                        filter_size=1,
                        stride=1,
                        padding=0,
                        act=act))

            ch_pre = ch_out

        self.fpn_stages = nn.LayerList(fpn_stages)
        self.fpn_routes = nn.LayerList(fpn_routes)

        pan_stages = []
        pan_routes = []        for i in reversed(range(self.num_blocks - 1)):
            pan_routes.append(
                ConvBNLayer(
                    ch_in=out_channels[i + 1],
                    ch_out=out_channels[i + 1],
                    filter_size=3,
                    stride=2,
                    padding=1,
                    act=act))

            ch_in = out_channels[i] + out_channels[i + 1]
            ch_out = out_channels[i]
            stage = nn.Sequential()            for j in range(stage_num):
                stage.add_sublayer(                    str(j),                    eval(stage_fn)(block_fn,
                                   ch_in if j == 0 else ch_out,
                                   ch_out,
                                   block_num,
                                   act=act,
                                   spp=False))            if drop_block:
                stage.add_sublayer('drop', DropBlock(block_size, keep_prob))

            pan_stages.append(stage)

        self.pan_stages = nn.LayerList(pan_stages[::-1])
        self.pan_routes = nn.LayerList(pan_routes[::-1])    def forward(self, blocks, for_mot=False):
        blocks = blocks[::-1]
        fpn_feats = []        for i, block in enumerate(blocks):            if i > 0:
                block = paddle.concat([route, block], axis=1)            print(self.fpn_stages[i])
            route = self.fpn_stages[i](block)
            fpn_feats.append(route)            if i < self.num_blocks - 1:
                route = self.fpn_routes[i](route)
                route = F.interpolate(
                    route, scale_factor=2., data_format=self.data_format)

        pan_feats = [fpn_feats[-1], ]
        route = fpn_feats[-1]        for i in reversed(range(self.num_blocks - 1)):
            block = fpn_feats[i]
            route = self.pan_routes[i](route)
            block = paddle.concat([route, block], axis=1)
            route = self.pan_stages[i](block)
            pan_feats.append(route)        return pan_feats[::-1]    @classmethod
    def from_config(cls, cfg, input_shape):
        return {'in_channels': [i.channels for i in input_shape], }    @property
    def out_shape(self):
        return [ShapeSpec(channels=c) for c in self._out_channels]if __name__=='__main__':
    model = CustomCSPPAN()
    path = './infer/neck/CustomCSPPAN_SPP_True'
    # weight_path = '123.pdparams'
    # static = paddle.load(weight_path)
    x2 = paddle.to_tensor(np.random.rand(1,1024,16,16),dtype='float32')
    x1 = paddle.to_tensor(np.random.rand(1,512,32,32),dtype='float32')
    x0 = paddle.to_tensor(np.random.rand(1,256,64,64),dtype='float32')
    out = [x0,x1,x2]
    out = model(out)    # model.eval()
    # paddle.jit.s*e(model,path,(out,False))
   

以上就是PPYOLOE详解第二弹:你真的知道什么是Neck嘛?的详细内容,更多请关注其它相关文章!


# 一言  # 无极荣耀注册桌仁seo  # 呈贡网站建设推广制作  # 成都网谷seo  # 快速seo优化软件  # 柳州独特seo营销招聘  # 电商网站的推广策略包括  # 江苏抖音关键词排名  # 湖南网站如何建设  # 桔子团购网站建设与运营  # 网站建设目的  # 都是  # 数为  # 两支  # 都为  # python  # 是在  # 会先  # 头上  # 中文网  # 你真  # type  # fig  # asic  # red  # ai  # b站  # apache  # git 


相关栏目: 【 行业新闻62819 】 【 科技资讯67470


相关推荐: 田渊栋新作:打开1层Transformer黑盒,注意力机制没那么神秘  DeepMind用AI重写排序算法;将33B大模型塞进单个消费级GPU  深企派遣无人机救援队赴京津冀开展防汛救灾任务  赋能金融新生态,多家银行创新应用成果亮相世界人工智能大会  软通动力多项AI创新产品及应用亮相2025世界人工智能大会  马斯克WAIC2025演讲全文:AI将对人类文明产生深远影响  谷歌将使用公开信息训练 AI 模型,构建更强大的自家产品  扎克伯格吐槽苹果Vision Pro:社交落后Meta太多,无法建设元宇宙  人工智能助力精准学习,猿辅导小猿学练机满足学生个性化学习需求  昌吉市利用无人机实现全天候河道动态巡检  软银、淡马锡、沙特阿美突击入股,“协作机器人第一股”节卡股份:强敌环伺,持续失血是常态  微软新出热乎论文:Transformer扩展到10亿token  2025 WAIC|美团无人机发布第四代新机型  五项人工智能尚未能够实现的任务  WHEE上线时间介绍  加速电网转型升级推进新型电力系统建设  拓普龙7188ML:轻便壁挂式工控机箱,为人工智能应用场景提供有力保障  国内阅读行业首款对话式AI应用“阅爱聊”封闭内测  为什么很多人对纽约《人工智能招聘法》感到生气?  Spotify计划推出AI驱动的音乐播放器功能  微软推出 LLaVA-Med AI 模型,可对医学病理案例进行分析  郭帆导演成功利用AI技术制作的《流浪地球3》预告片在央视热播,引发巨大反响  不止“文心一言”,消息称百度将推出全新 AI 对话软件“万话”  通用医疗人工智能如何革新医疗行业?  图像生成过程中遭「截胡」:稳定扩散的失败案例受四大因素影响  OpenAI首席执行官表态支持欧盟AI监管  郭帆:AI发展日新月异,或是弯道超车好莱坞的最好机会  移远通信率先完成多场5G NTN技术外场验证,为卫星物联网应用落地提速  “风乌”气象大模型科学家团队:用AI预报极端天气未来不是梦!  华为发布两款AI存储新品  月薪6万,哪些AI岗位在抢人?  陈根:ChatGPT和人类合作开发机器人  Goodnotes 6推出,带来多项全新AI功能,让电子笔记更智能  微软更新服务协议,以防止通过AI服务进行逆向工程和数据抓取  讯飞听见会写“会议摘要”功能全面升级,AI更懂你的关注点  Midjourney创始人:AI应该成为人类思想的延伸  13条咒语挖掘GPT-4最大潜力,Github万星AI导师火了,网友:隔行再也不隔山了  生成式人工智能来了,如何保护未成年人? | 社会科学报  配 3D 机器人头像,谷歌展示全新安卓 LOGO  “思享荟”沙龙热议AIGC与元宇宙 复旦大学赵星畅谈深度数字化  Adobe旗下Illustrator引入生成式AI工具Firefly  轻量级的深度学习框架Tinygrad  GPT-4是如何工作的?哈佛教授亲自讲授  自动驾驶汽车避障、路径规划和控制技术详解  售价14.99万起!小米汽车部分信息疑遭AI曝光,内部人士回应:网传图片明显经过处理,不可轻信  腾讯汤道生:大模型只是起点,产业落地是AI更大的应用场景  重塑未来生活的五项技术趋势  大疆 Air 3 无人机售价和实物照片曝光  高通发布长期产品计划,为工业和企业物联网产品提供全新组合方案  “长沙造”无人机,领先的不止植保 

400 128 6709
E-mail

contact@tlftec.cn

扫一扫,添加微信

©  云南淘乐房科技有限公司 版权所有  滇ICP备2025071560号  

云南淘乐房科技有限公司 云南淘乐房科技有限公司 云南淘乐房科技有限公司 云南淘乐房科技有限公司 云南淘乐房科技有限公司 云南淘乐房科技有限公司 云南淘乐房科技有限公司 云南淘乐房科技有限公司 云南淘乐房科技有限公司 云南淘乐房科技有限公司