yolov26改进 | 主干/Backbone篇 | 目标检测网络FasterNet轻量化网络助力yolov26改进(提高FPS和检测效率)

📅 2026/8/3 11:56:37
yolov26改进 | 主干/Backbone篇 | 目标检测网络FasterNet轻量化网络助力yolov26改进(提高FPS和检测效率)
开始正文前先向大家推荐我的YOLO专栏系列。本人持续更新 YOLOv8、YOLO11、YOLO26 等热门模型内容覆盖图像分类、目标检测、实例分割、多目标跟踪、姿态估计与关键点检测重点讲解 小目标检测、注意力机制、特征融合、损失函数改进、自定义数据集训练、消融实验及论文代码复现。同时分享如何使用 OpenAI Codex辅助撰写论文、配置实验环境、调试项目、改进模型和分析实验结果。 专栏目前正在进行限时优惠每周更新 5–7篇最新论文机制、YOLO改进方法和实战教程。订阅后可获得包含本人全部改进方案的代码与配置文件并加入专属技术交流群。我也会定期在群内分享 YOLO论文选题、创新点设计、实验方案、论文写作与投稿发表经验欢迎大家订阅交流一、本文介绍本文给大家带来的改进机制是高效轻量级主干网络——FasterNet本文将使用该网络替换YOLOv26原有的特征提取网络。FasterNet旨在减少传统轻量化网络中存在的冗余计算和频繁内存访问问题在保持较高检测精度的同时进一步提升模型在不同硬件设备上的实际推理速度FasterNet的核心结构是部分卷积Partial ConvolutionPConv。该方法仅对部分输入通道执行空间卷积其余通道则直接保留随后再通过通道融合完成特征交互。相比直接对全部通道进行卷积PConv能够有效减少计算量和内存访问次数使网络获得更高的运行效率。凭借这一设计FasterNet不仅能够保持较强的特征提取能力还在图像分类、目标检测和语义分割等多种视觉任务中展现出了较好的性能。经过本人实验将FasterNet作为主干网络后模型在大、中、小三种尺度目标的检测效果上均具有一定提升说明该结构在轻量化和检测精度之间能够取得较好的平衡。本文将首先介绍FasterNet及PConv的主要原理随后结合完整代码和配置文件手把手教大家如何将其添加到YOLOv26中大家也可以根据实际任务在源码中进一步调整网络结构。专栏链接YOLOv26有效涨点专栏包含Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制目录一、本文介绍二、FasterNet原理2.1 FasterNet的基本原理2.2 部分卷积2.3 加速神经网络三、FasterNet的核心代码四、手把手教你添加FasterNet机制4.1 修改一4.2 修改二4.3 修改三4.4 修改四4.5 修改五4.6 修改六4.7 修改七4.8 修改八4.9 修改九五、FasterNet的yaml文件5.1 FasterNet的yaml文件5.2 训练文件的代码六、成功运行记录七、本文总结二、FasterNet原理​论文地址官方论文地址代码地址官方代码地址​2.1 FasterNet的基本原理FasterNet是一种高效的神经网络架构旨在提高计算速度而不牺牲准确性特别是在视觉任务中。它通过一种称为部分卷积PConv的新技术来减少冗余计算和内存访问。这种方法使得FasterNet在多种设备上运行速度比其他网络快得多同时在各种视觉任务中保持高准确率。例如FasterNet在ImageNet-1k数据集上的表现超过了其他模型如MobileViT-XXS展现了其在速度和准确度方面的优势。FasterNet的基本原理可以总结为以下几点1. 部分卷积PConv:FasterNet引入了部分卷积PConv这是一种新型的卷积方法它通过只处理输入通道的一部分来减少计算量和内存访问。2. 加速神经网络: FasterNet利用PConv的优势实现了在多种设备上比其他现有神经网络更快的运行速度同时保持了较高的准确度。下面为大家展示的是FasterNet的整体架构。​它包括四个层次化的阶段每个阶段由一系列FasterNet块组成并由嵌入或合并层开头。最后三层用于特征分类。在每个FasterNet块中PConv层之后是两个点状卷积PWConv层。为了保持特征多样性并实现更低的延迟仅在中间层之后放置了归一化和激活层。2.2部分卷积部分卷积PConv是一种卷积神经网络中的操作旨在提高计算效率。它通过只在输入特征图的一部分上执行卷积操作而非传统卷积操作中的全面应用。这样PConv可以减少不必要的计算和内存访问因为它忽略了输入中认为是冗余的部分。这种方法特别适合在资源有限的设备上运行深度学习模型因为它可以在不牺牲太多性能的情况下显著降低计算需求。下面我为大家展示了FasterNet中的部分卷积PConv与传统卷积和深度卷积/分组卷积的比较​PConv通过仅对输入通道的一小部分应用滤波器同时保持其余通道不变实现了快速和高效的特性提取。PConv的计算复杂度FLOPs低于常规卷积但高于深度卷积/分组卷积这样在减少计算资源的同时提高了运算性能。2.3加速神经网络加速神经网络主要通过优化计算路径、减少模型大小和复杂性、提高操作效率以及使用高效的硬件实现等方式来降低模型的推理时间。这些方法包括简化网络层、使用更快的激活函数、采用量化技术将浮点运算转换为整数运算以及使用特殊的算法来减少内存访问次数等。通过这些策略可以在不损害模型准确性的前提下使神经网络能够更快地处理数据和做出预测。三、FasterNet的核心代码核心代码使用方式看章节四import torch import torch.nn as nn from timm.models.layers import DropPath, trunc_normal_ from functools import partial from typing import List from torch import Tensor import copy import os __all__ [FasterNet] class Partial_conv3(nn.Module): def __init__(self, dim, n_div, forward): super().__init__() self.dim_conv3 dim // n_div self.dim_untouched dim - self.dim_conv3 self.partial_conv3 nn.Conv2d(self.dim_conv3, self.dim_conv3, 3, 1, 1, biasFalse) if forward slicing: self.forward self.forward_slicing elif forward split_cat: self.forward self.forward_split_cat else: raise NotImplementedError def forward_slicing(self, x: Tensor) - Tensor: # only for inference x x.clone() # !!! Keep the original input intact for the residual connection later x[:, :self.dim_conv3, :, :] self.partial_conv3(x[:, :self.dim_conv3, :, :]) return x def forward_split_cat(self, x: Tensor) - Tensor: # for training/inference x1, x2 torch.split(x, [self.dim_conv3, self.dim_untouched], dim1) x1 self.partial_conv3(x1) x torch.cat((x1, x2), 1) return x class MLPBlock(nn.Module): def __init__(self, dim, n_div, mlp_ratio, drop_path, layer_scale_init_value, act_layer, norm_layer, pconv_fw_type ): super().__init__() self.dim dim self.mlp_ratio mlp_ratio self.drop_path DropPath(drop_path) if drop_path 0. else nn.Identity() self.n_div n_div mlp_hidden_dim int(dim * mlp_ratio) mlp_layer: List[nn.Module] [ nn.Conv2d(dim, mlp_hidden_dim, 1, biasFalse), norm_layer(mlp_hidden_dim), act_layer(), nn.Conv2d(mlp_hidden_dim, dim, 1, biasFalse) ] self.mlp nn.Sequential(*mlp_layer) self.spatial_mixing Partial_conv3( dim, n_div, pconv_fw_type ) if layer_scale_init_value 0: self.layer_scale nn.Parameter(layer_scale_init_value * torch.ones((dim)), requires_gradTrue) self.forward self.forward_layer_scale else: self.forward self.forward def forward(self, x: Tensor) - Tensor: shortcut x x self.spatial_mixing(x) x shortcut self.drop_path(self.mlp(x)) return x def forward_layer_scale(self, x: Tensor) - Tensor: shortcut x x self.spatial_mixing(x) x shortcut self.drop_path( self.layer_scale.unsqueeze(-1).unsqueeze(-1) * self.mlp(x)) return x class BasicStage(nn.Module): def __init__(self, dim, depth, n_div, mlp_ratio, drop_path, layer_scale_init_value, norm_layer, act_layer, pconv_fw_type ): super().__init__() blocks_list [ MLPBlock( dimdim, n_divn_div, mlp_ratiomlp_ratio, drop_pathdrop_path[i], layer_scale_init_valuelayer_scale_init_value, norm_layernorm_layer, act_layeract_layer, pconv_fw_typepconv_fw_type ) for i in range(depth) ] self.blocks nn.Sequential(*blocks_list) def forward(self, x: Tensor) - Tensor: x self.blocks(x) return x class PatchEmbed(nn.Module): def __init__(self, patch_size, patch_stride, in_chans, embed_dim, norm_layer): super().__init__() self.proj nn.Conv2d(in_chans, embed_dim, kernel_sizepatch_size, stridepatch_stride, biasFalse) if norm_layer is not None: self.norm norm_layer(embed_dim) else: self.norm nn.Identity() def forward(self, x: Tensor) - Tensor: x self.norm(self.proj(x)) return x class PatchMerging(nn.Module): def __init__(self, patch_size2, patch_stride2, dim, norm_layer): super().__init__() self.reduction nn.Conv2d(dim, 2 * dim, kernel_sizepatch_size2, stridepatch_stride2, biasFalse) if norm_layer is not None: self.norm norm_layer(2 * dim) else: self.norm nn.Identity() def forward(self, x: Tensor) - Tensor: x self.norm(self.reduction(x)) return x class FasterNet(nn.Module): def __init__(self, factor0.5, in_chans3, num_classes1000, embed_dim96, depths(1, 2, 8, 2), mlp_ratio2., n_div4, patch_size4, patch_stride4, patch_size22, # for subsequent layers patch_stride22, patch_normTrue, feature_dim1280, drop_path_rate0.1, layer_scale_init_value0, norm_layerBN, act_layerRELU, fork_featTrue, init_cfgNone, pretrainedNone, pconv_fw_typesplit_cat, **kwargs): super().__init__() if norm_layer BN: norm_layer nn.BatchNorm2d else: raise NotImplementedError if act_layer GELU: act_layer nn.GELU elif act_layer RELU: act_layer partial(nn.ReLU, inplaceTrue) else: raise NotImplementedError if not fork_feat: self.num_classes num_classes self.num_stages len(depths) embed_dim int(embed_dim * factor) self.embed_dim embed_dim self.patch_norm patch_norm self.num_features int(embed_dim * 2 ** (self.num_stages - 1)) self.mlp_ratio mlp_ratio self.depths depths # split image into non-overlapping patches self.patch_embed PatchEmbed( patch_sizepatch_size, patch_stridepatch_stride, in_chansin_chans, embed_dimembed_dim, norm_layernorm_layer if self.patch_norm else None ) # stochastic depth decay rule dpr [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] # build layers stages_list [] for i_stage in range(self.num_stages): stage BasicStage(dimint(embed_dim * 2 ** i_stage), n_divn_div, depthdepths[i_stage], mlp_ratioself.mlp_ratio, drop_pathdpr[sum(depths[:i_stage]):sum(depths[:i_stage 1])], layer_scale_init_valuelayer_scale_init_value, norm_layernorm_layer, act_layeract_layer, pconv_fw_typepconv_fw_type ) stages_list.append(stage) # patch merging layer if i_stage self.num_stages - 1: stages_list.append( PatchMerging(patch_size2patch_size2, patch_stride2patch_stride2, dimint(embed_dim * 2 ** i_stage), norm_layernorm_layer) ) self.stages nn.Sequential(*stages_list) self.fork_feat fork_feat self.forward self.forward_det # add a norm layer for each output self.out_indices [0, 2, 4, 6] for i_emb, i_layer in enumerate(self.out_indices): if i_emb 0 and os.environ.get(FORK_LAST3, None): raise NotImplementedError else: layer norm_layer(int(embed_dim * 2 ** i_emb)) layer_name fnorm{i_layer} self.add_module(layer_name, layer) self.apply(self.cls_init_weights) self.init_cfg copy.deepcopy(init_cfg) if self.fork_feat and (self.init_cfg is not None or pretrained is not None): self.init_weights() self.width_list [i.size(1) for i in self.forward(torch.randn(1, 3, 640, 640))] def cls_init_weights(self, m): if isinstance(m, nn.Linear): trunc_normal_(m.weight, std.02) if isinstance(m, nn.Linear) and m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, (nn.Conv1d, nn.Conv2d)): trunc_normal_(m.weight, std.02) if m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, (nn.LayerNorm, nn.GroupNorm)): nn.init.constant_(m.bias, 0) nn.init.constant_(m.weight, 1.0) def forward_det(self, x: Tensor) - Tensor: # output the features of four stages for dense prediction x self.patch_embed(x) outs [] for idx, stage in enumerate(self.stages): x stage(x) if self.fork_feat and idx in self.out_indices: norm_layer getattr(self, fnorm{idx}) x_out norm_layer(x) outs.append(x_out) return outs if __name__ __main__: # Generating Sample image image_size (1, 3, 640, 640) image torch.rand(*image_size) # Model model FasterNet() out model(image) print(len(out))四、手把手教你添加FasterNet机制4.1 修改一我们复制网络结构代码到“ultralytics/nn”目录下创建一个py文件复制粘贴进去 。​4.2 修改二第二步我们在该目录下创建一个新的py文件名字为__init__.py(用群内的文件的话已经有了无需新建)然后在其内部导入我们的检测头如下图所示。4.3 修改三第三步我门中到如下文件ultralytics/nn/tasks.py进行导入和注册我们的模块(用群内的文件的话已经有了无需重新导入直接开始第四步即可)从今天开始以后的教程就都统一成这个样子了因为我默认大家用了我群内的文件来进行修改4.4 修改四添加如下两行代码​4.5 修改五找到1600多行大概把具体看图片按照图片来修改就行添加红框内的部分注意没有()只是函数名我这里只添加了部分的版本这个还有更多的版本可以添加可以看后面章节五看我给的代码函数头即可。​elif m in {自行添加对应的模型即可下面都是一样的}: m m(*args) c2 m.width_list # 返回通道列表 backbone True4.6 修改六按图修改。​if isinstance(c2, list): m_ m m_.backbone True else: m_ nn.Sequential(*(m(*args) for _ in range(n))) if n 1 else m(*args) # module t str(m)[8:-2].replace(__main__., ) # module type m.np sum(x.numel() for x in m_.parameters()) # number params m_.i, m_.f, m_.type i 4 if backbone else i, f, t # attach index, from index, type4.7 修改七如下的也需要修改全部按照我的来。​代码如下把原先的代码替换了即可。if verbose: LOGGER.info(f{i:3}{str(f):20}{n_:3}{m.np:10.0f} {t:45}{str(args):30}) # print save.extend(x % (i 4 if backbone else i) for x in ([f] if isinstance(f, int) else f) if x ! -1) # append to savelist layers.append(m_) if i 0: ch [] if isinstance(c2, list): ch.extend(c2) if len(c2) ! 5: ch.insert(0, 0) else: ch.append(c2)4.8 修改八修改七和前面的都不太一样需要修改前向传播中的一个部分 已经离开了parse_model方法了。可以在图片中看代码行数没有离开task.py文件都是同一个文件。 同时这个部分有好几个前向传播都很相似大家不要看错了是160多行左右的不同仓库版本可能有些差异同时我后面提供了代码大家直接复制粘贴即可不会修改联系博主获取视频教程。​​代码如下-def _predict_once(self, x, profileFalse, visualizeFalse, embedNone): Perform a forward pass through the network. Args: x (torch.Tensor): The input tensor to the model. profile (bool): Print the computation time of each layer if True. visualize (bool): Save the feature maps of the model if True. embed (list, optional): A list of layer indices to return embeddings from. Returns: (torch.Tensor): The last output of the model. y, dt, embeddings [], [], [] # outputs embed frozenset(embed) if embed is not None else {-1} max_idx max(embed) for m in self.model: if m.f ! -1: # if not from previous layer x y[m.f] if isinstance(m.f, int) else [x if j -1 else y[j] for j in m.f] # from earlier layers if profile: self._profile_one_layer(m, x, dt) if hasattr(m, backbone): x m(x) if len(x) ! 5: # 0 - 5 x.insert(0, None) for index, i in enumerate(x): if index in self.save: y.append(i) else: y.append(None) x x[-1] # 最后一个输出传给下一层 else: x m(x) # run y.append(x if m.i in self.save else None) # save output if visualize: feature_visualization(x, m.type, m.i, save_dirvisualize) if embed and m.i in embed: embeddings.append(nn.functional.adaptive_avg_pool2d(x, (1, 1)).squeeze(-1).squeeze(-1)) # flatten if m.i max_idx: return torch.unbind(torch.cat(embeddings, 1), dim0) return x4.9 修改九我们找到如下文件ultralytics/utils/torch_utils.py按照如下的图片进行修改否则容易打印不出来计算量。​五、FasterNet的yaml文件5.1 FasterNet的yaml文件训练信息YOLO26-Backbone-FasterNet summary: 288 layers, 4,855,884 parameters, 4,855,884 gradients, 11.0 GFLOPs# Ultralytics AGPL-3.0 License - https://ultralytics.com/license # Ultralytics YOLO26 object detection model with P3/8 - P5/32 outputs # Model docs: https://docs.ultralytics.com/models/yolo26 # Task docs: https://docs.ultralytics.com/tasks/detect # Parameters nc: 80 # number of classes end2end: True # whether to use end-to-end mode reg_max: 1 # DFL bins scales: # model compound scaling constants, i.e. modelyolo26n.yaml will call yolo26.yaml with scale n # [depth, width, max_channels] n: [0.50, 0.25, 1024] # summary: 260 layers, 2,572,280 parameters, 2,572,280 gradients, 6.1 GFLOPs s: [0.50, 0.50, 1024] # summary: 260 layers, 10,009,784 parameters, 10,009,784 gradients, 22.8 GFLOPs m: [0.50, 1.00, 512] # summary: 280 layers, 21,896,248 parameters, 21,896,248 gradients, 75.4 GFLOPs l: [1.00, 1.00, 512] # summary: 392 layers, 26,299,704 parameters, 26,299,704 gradients, 93.8 GFLOPs x: [1.00, 1.50, 512] # summary: 392 layers, 58,993,368 parameters, 58,993,368 gradients, 209.5 GFLOPs # 下面 [-1, 1, FasterNet, [0.25]] 参数位置的0.25是通道放缩的系数, YOLOv26N是0.25 YOLOv26S是0.5 YOLOv11M是1. YOLOv26l是1 YOLOv26是1.5大家根据自己训练的YOLO版本设定即可. # YOLO26 backbone backbone: # [from, repeats, module, args] - [-1, 1, FasterNet, [0.25]] # 0-4 P1/2 - [-1, 1, SPPF, [1024, 5, 3, True]] # 5 - [-1, 2, C2PSA, [1024]] # 6 # YOLO26 head head: - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 3], 1, Concat, [1]] # cat backbone P4 - [-1, 2, C3k2, [512, True]] # 9 - [-1, 1, nn.Upsample, [None, 2, nearest]] - [[-1, 2], 1, Concat, [1]] # cat backbone P3 - [-1, 2, C3k2, [256, True]] # 12 (P3/8-small) - [-1, 1, Conv, [256, 3, 2]] - [[-1, 9], 1, Concat, [1]] # cat head P4 - [-1, 2, C3k2, [512, True]] # 15 (P4/16-medium) - [-1, 1, Conv, [512, 3, 2]] - [[-1, 6], 1, Concat, [1]] # cat head P5 - [-1, 2, C3k2, [1024, True, 0.5, True]] # 18 (P5/32-large) - [[12, 15, 18], 1, Detect, [nc]] # Detect(P3, P4, P5)5.2 训练文件的代码可以复制我的运行文件进行运行。import warnings warnings.filterwarnings(ignore) from ultralytics import YOLO if __name__ __main__: model YOLO(yolov8-MLLA.yaml) # 如何切换模型版本, 上面的ymal文件可以改为 yolov8s.yaml就是使用的v8s, # 类似某个改进的yaml文件名称为yolov8-XXX.yaml那么如果想使用其它版本就把上面的名称改为yolov8l-XXX.yaml即可改的是上面YOLO中间的名字不是配置文件的 # model.load(yolov8n.pt) # 是否加载预训练权重,科研不建议大家加载否则很难提升精度 model.train(datarC:\Users\Administrator\PycharmProjects\yolov5-master\yolov5-master\Construction Site Safety.v30-raw-images_latestversion.yolov8\data.yaml, # 如果大家任务是其它的ultralytics/cfg/default.yaml找到这里修改task可以改成detect, segment, classify, pose cacheFalse, imgsz640, epochs150, single_clsFalse, # 是否是单类别检测 batch16, close_mosaic0, workers0, device0, optimizerSGD, # using SGD # resumeruns/train/exp21/weights/last.pt, # 如过想续训就设置last.pt的地址 ampTrue, # 如果出现训练损失为Nan可以关闭amp projectruns/train, nameexp, )六、成功运行记录下面是成功运行的截图已经完成了有1个epochs的训练图片太大截不全第2个epochs了。​七、本文总结到此本文的正式分享内容就结束了在这里给大家推荐我的YOLOv26改进有效涨点专栏本专栏目前为新开的平均质量分98分后期我会根据各种最新的前沿顶会进行论文复现也会对一些老的改进机制进行补充如果大家觉得本文帮助到你了订阅本专栏关注后续更多的更新~专栏链接YOLOv26有效涨点专栏包含Conv、注意力机制、主干/Backbone、损失函数、优化器、后处理等改进机制​​