Ultralytics:解读ResNetBlock模块
📅 2026/7/26 9:37:33
👁️ 阅读次数
📝 编程学习
Ultralytics:解读ResNetBlock模块
- 前言
- 相关介绍
- Ultralytics 简介
- 前提条件
- 实验环境
- ResNetBlock(ResNet 基础残差块)
- 代码实现
- 功能
- 初始化参数
- 前向方法
- 使用示例
- 流程示意图
- 代码解读
- 注意事项
- 优缺点
- 优点
- 缺点
- 参考文献
前言
- 由于本人水平有限,难免出现错漏,敬请批评改正。
- 更多精彩内容,可点击进入Python日常小操作专栏、OpenCV-Python小应用专栏、YOLO系列专栏、自然语言处理专栏、人工智能混合编程实践专栏或我的个人主页查看
- YOLOs-CPP:一个免费开源的YOLO全系列C++推理库(以YOLO26为例)
- PaddleOCR:Win10上安装使用PPOCRLabel标注工具
- 目标检测:使用自己的数据集微调DEIMv2进行物体检测
- 图像分割:PyTorch从零开始实现SegFormer语义分割
- 图像超分:使用自己的数据集微调Real-ESRGAN-x4plus进行超分重建
- 图像生成:PyTorch从零开始实现一个简单的扩散模型
- Stable Diffusion:使用自己的数据集微调 Stable Diffusion 3.5 LoRA 文生图模型
- 图像超分:使用自己的数据集微调Real-ESRGAN-x2plus进行超分重建
- Anomalib:使用Anomalib 2.1.0训练自己的数据集进行异常检测
- Anomalib:在Linux服务器上安装使用Anomalib 2.1.0
- 人工智能混合编程实践:C++调用封装好的DLL进行异常检测推理
- 人工智能混合编程实践:C++调用封装好的DLL进行FP16图像超分重建(v3.0)
- 隔离系统Python:源码编译3.11.8到自定义目录(含PGO性能优化)
- 在线机的Python环境迁移到离线机上
- Nuitka 将 Python 脚本封装为 .pyd 或 .so 文件
- Ultralytics:使用 YOLO11 进行速度估计
- Ultralytics:使用 YOLO11 进行物体追踪
- Ultralytics:使用 YOLO11 进行物体计数
- Ultralytics:使用 YOLO11 进行目标打码
- 人工智能混合编程实践:C++调用Python ONNX进行YOLOv8推理
- 人工智能混合编程实践:C++调用封装好的DLL进行YOLOv8实例分割
- 人工智能混合编程实践:C++调用Python ONNX进行图像超分重建
- 人工智能混合编程实践:C++调用Python AgentOCR进行文本识别
- 通过计算实例简单地理解PatchCore异常检测
- Python将YOLO格式实例分割数据集转换为COCO格式实例分割数据集
- YOLOv8 Ultralytics:使用Ultralytics框架训练RT-DETR实时目标检测模型
- 基于DETR的人脸伪装检测
- YOLOv7训练自己的数据集(口罩检测)
- YOLOv8训练自己的数据集(足球检测)
- YOLOv5:TensorRT加速YOLOv5模型推理
- YOLOv5:IoU、GIoU、DIoU、CIoU、EIoU
- 玩转Jetson Nano(五):TensorRT加速YOLOv5目标检测
- YOLOv5:添加SE、CBAM、CoordAtt、ECA注意力机制
- YOLOv5:yolov5s.yaml配置文件解读、增加小目标检测层
- Python将COCO格式实例分割数据集转换为YOLO格式实例分割数据集
- YOLOv5:使用7.0版本训练自己的实例分割模型(车辆、行人、路标、车道线等实例分割)
- 使用Kaggle GPU资源免费体验Stable Diffusion开源项目
- Stable Diffusion:在服务器上部署使用Stable Diffusion WebUI进行AI绘图(v2.0)
- Stable Diffusion:使用自己的数据集微调训练LoRA模型(v2.0)
相关介绍
Ultralytics 简介
Ultralytics 基于多年的计算机视觉和人工智能基础研究,创建了最先进的 (SOTA) YOLO 模型。我们的模型不断更新性能和灵活性,快速、准确且易于使用。他们擅长对象检测、跟踪、实例分割、语义分割、图像分类和姿势估计任务。
- 官方文档:https://docs.ultralytics.com/
- 官方代码:https://github.com/ultralytics/ultralytics.git
前提条件
- 熟悉Python、Pytorch
实验环境
Package Version ------------------------ ------------ Python3.11.8 absl-py2.4.0 accelerate1.13.0 annotated-doc0.0.4 anyio4.13.0 calflops0.3.2 certifi2026.4.22 charset-normalizer3.4.7 click8.3.3 colorama0.4.6 contourpy1.3.3 cycler0.12.1 filelock3.29.0 flatbuffers25.12.19 fonttools4.62.1 fsspec2026.4.0 grpcio1.80.0 h110.16.0 hf-xet1.5.0 httpcore1.0.9 httpx0.28.1 huggingface_hub1.14.0 idna3.15Jinja23.1.6 kiwisolver1.5.0 Markdown3.10.2 markdown-it-py4.2.0 MarkupSafe3.0.3 matplotlib3.10.9 mdurl0.1.2 ml_dtypes0.5.0 mpmath1.3.0 networkx3.6.1 numpy1.26.4 nvidia-cublas-cu1212.8.3.14 nvidia-cuda-cupti-cu1212.8.57 nvidia-cuda-nvrtc-cu1212.8.61 nvidia-cuda-runtime-cu1212.8.57 nvidia-cudnn-cu129.7.1.26 nvidia-cufft-cu1211.3.3.41 nvidia-cufile-cu121.13.0.11 nvidia-curand-cu1210.3.9.55 nvidia-cusolver-cu1211.7.2.55 nvidia-cusparse-cu1212.5.7.53 nvidia-cusparselt-cu120.6.3 nvidia-nccl-cu122.26.2 nvidia-nvjitlink-cu1212.8.61 nvidia-nvtx-cu1212.8.55 onnx1.19.0 onnxruntime-gpu1.26.0 onnxslim0.1.94 opencv-python4.6.0.66 packaging26.2pillow12.2.0 pip24.0polars1.40.1 polars-runtime-321.40.1 protobuf7.34.1 psutil7.2.2 pycocotools2.0.11 Pygments2.20.0 pyparsing3.3.2 python-dateutil2.9.0.post0 PyYAML6.0.3 regex2026.5.9 requests2.34.1 rich15.0.0 safetensors0.7.0 scipy1.16.0 setuptools65.5.0 shellingham1.5.4 six1.17.0 sympy1.14.0 tabulate0.10.0 tensorboard2.20.0 tensorboard-data-server0.7.2 tokenizers0.22.2 torch2.7.1+cu128 torchaudio2.7.1+cu128 torchvision0.22.1+cu128 tqdm4.67.3 transformers5.8.1 triton3.3.1 typer0.25.1 typing_extensions4.15.0 ultralytics8.4.58 ultralytics-thop2.0.19 urllib32.7.0 Werkzeug3.1.8ResNetBlock(ResNet 基础残差块)
ResNetBlock是 ResNet 架构中经典的残差块(Basic Block)的变体,它通过三个卷积层(1×1 → 3×3 → 1×1)实现特征变换,并引入残差连接以缓解深层网络的梯度消失问题。与标准 ResNet 的 BasicBlock(两个 3×3 卷积)不同,这里采用了类似瓶颈设计的结构:先通过 1×1 卷积降低通道数(可选),再经过 3×3 卷积提取空间特征,最后通过 1×1 卷积将通道数扩展到目标维度(通常为输入的 4 倍)。该模块在目标检测、分类等任务中广泛使用。
代码实现
importcv2importmathimporttorchimporttorch.nnasnnimporttorch.nn.functionalasFimportnumpyasnpimportmatplotlib.pyplotaspltdefautopad(k,p=None,d=1):# kernel, padding, dilation"""Pad to 'same' shape outputs."""ifd>1:k=d*(k-1)+1ifisinstance(k,int)else[d*(x-1)+1forxink]# actual kernel-sizeifpisNone:p=k//2ifisinstance(k,int)else[x//2forxink]# auto-padreturnpclassConv(nn.Module):"""Standard convolution module with batch normalization and activation. Attributes: conv (nn.Conv2d): Convolutional layer. bn (nn.BatchNorm2d): Batch normalization layer. act (nn.Module): Activation function layer. default_act (nn.Module): Default activation function (SiLU). """default_act=nn.SiLU()# default activationdef__init__(self,c1,c2,k=1,s=1,p=None,g=1,d=1,act=True):"""Initialize Conv layer with given parameters. Args: c1 (int): Number of input channels. c2 (int): Number of output channels. k (int): Kernel size. s (int): Stride. p (int, optional): Padding. g (int): Groups. d (int): Dilation. act (bool | nn.Module): Activation function. """super().__init__()self.conv=nn.Conv2d(c1,c2,k,s,autopad(k,p,d),groups=g,dilation=d,bias=False)self.bn=nn.BatchNorm2d(c2)self.act=self.default_actifactisTrueelseactifisinstance(act,nn.Module)elsenn.Identity()defforward(self,x):"""Apply convolution, batch normalization and activation to input tensor. Args: x (torch.Tensor): Input tensor. Returns: (torch.Tensor): Output tensor. """returnself.act(self.bn(self.conv(x)))defforward_fuse(self,x):"""Apply convolution and activation without batch normalization. Args: x (torch.Tensor): Input tensor. Returns: (torch.Tensor): Output tensor. """returnself.act(self.conv(x))classResNetBlock(nn.Module):"""ResNet block with standard convolution layers."""def__init__(self,c1:int,c2:int,s:int=1,e:int=4):"""Initialize ResNet block. Args: c1 (int): Input channels. c2 (int): Output channels. s (int): Stride. e (int): Expansion ratio. """super().__init__()c3=e*c2 self.cv1=Conv(c1,c2,k=1,s=1,act=True)self.cv2=Conv(c2,c2,k=3,s=s,p=1,act=True)self.cv3=Conv(c2,c3,k=1,act=False)self.shortcut=nn.Sequential(Conv(c1,c3,k=1,s=s,act=False))ifs!=1orc1!=c3elsenn.Identity()defforward(self,x:torch.Tensor)->torch.Tensor:"""Forward pass through the ResNet block."""returnF.relu(self.cv3(self.cv2(self.cv1(x)))+self.shortcut(x))功能
- 残差学习:通过
shortcut连接,将输入直接加到输出,让网络学习残差F(x) = H(x) - x,有效缓解梯度消失。 - 瓶颈设计:使用 1×1 卷积先降维(
c1 → c2),再用 1×1 卷积升维(c2 → c3),中间 3×3 卷积在较低通道数下提取空间特征,减少计算量。 - 空间下采样:通过
s参数控制 3×3 卷积的步长,实现特征图的空间降采样(当s>1时)。 - 通道扩展:输出通道数
c3 = e * c2,通常e=4,使得输出通道数为输入通道数的4 * (c2/c1)倍。
初始化参数
| 参数 | 类型 | 说明 |
|---|---|---|
c1 | int | 输入特征图的通道数 |
c2 | int | 中间通道数(也是 3×3 卷积的输入/输出通道) |
s | int | 3×3 卷积的步长(默认 1),用于控制空间尺寸 |
e | int | 扩展比(默认 4),输出通道c3 = e * c2 |
cv1:1×1 卷积,c1 → c2,包含 BN + SiLU(act=True)。cv2:3×3 卷积,c2 → c2,步长s,填充 1(保持尺寸),包含 BN + SiLU。cv3:1×1 卷积,c2 → c3,无激活(act=False),仅包含 BN。shortcut:若s != 1或c1 != c3,则使用 1×1 卷积(步长s,无激活)调整输入以匹配输出;否则为恒等映射。
前向方法
forward(x):输入x,输出[B, c3, H_out, W_out]。
计算流程:
x = self.cv1(x)→[B, c2, H, W]x = self.cv2(x)→ 若s=1,尺寸不变;若s>1,尺寸减半(H/s, W/s)x = self.cv3(x)→[B, c3, H_out, W_out]shortcut = self.shortcut(x_original)→ 调整输入到相同形状- 相加后经 ReLU 激活:
return relu(x + shortcut)
使用示例
if__name__=='__main__':# 1. 随机输入B,c1,H,W=2,32,64,64x=torch.randn(B,c1,H,W)# 2. 创建 ResNetBlock(输入32,中间64,输出256,步长1)block=ResNetBlock(c1=32,c2=64,s=1,e=4)# 3. 前向传播withtorch.no_grad():out=block(x)print("输入形状:",x.shape)# [2, 32, 64, 64]print("输出形状:",out.shape)# [2, 256, 64, 64](因为 c3=4*64=256)# 4. 步长为2的下采样块block_ds=ResNetBlock(c1=32,c2=64,s=2,e=4)withtorch.no_grad():out_ds=block_ds(x)print("步长2输出形状:",out_ds.shape)# [2, 256, 32, 32]# 5. 使用真实图像演示(扩展为多通道)img_path="cat_640x640.png"img_bgr=cv2.imread(img_path)ifimg_bgrisnotNone:img_gray=cv2.cvtColor(cv2.resize(img_bgr,(64,64)),cv2.COLOR_BGR2GRAY)img_tensor=torch.from_numpy(img_gray).float().unsqueeze(0).unsqueeze(0)# [1,1,64,64]# 扩展通道至 c1=32x_img=img_tensor.repeat(1,32,1,1)model_img=ResNetBlock(c1=32,c2=32,s=1,e=4)# 输出128通道withtorch.no_grad():out_img=model_img(x_img)inp_ch0=x_img[0,0].cpu().numpy()out_ch0=out_img[0,0].cpu().numpy()defnorm(arr):return(arr-arr.min())/(arr.max()-arr.min()+1e-8)plt.figure(figsize=(12,5),constrained_layout=True)plt.subplot(1,3,1)plt.imshow(img_gray,cmap='gray')plt.title("Original")plt.axis("off")plt.subplot(1,3,2)plt.imshow(norm(inp_ch0),cmap='gray')plt.title("Input Ch0")plt.axis("off")plt.subplot(1,3,3)plt.imshow(norm(out_ch0),cmap='gray')plt.title("ResNetBlock Output Ch0")plt.axis("off")plt.savefig("resnetblock_demo.png",dpi=150)print("可视化已保存为 resnetblock_demo.png")
输出示例:
输入形状: torch.Size([2, 32, 64, 64]) 输出形状: torch.Size([2, 256, 64, 64]) 步长2输出形状: torch.Size([2, 256, 32, 32]) 可视化已保存为 resnetblock_demo.png流程示意图
代码解读
__init__:c3 = e * c2:计算输出通道数。cv1:1×1 卷积,将输入通道c1映射到c2,包含 BN + SiLU。cv2:3×3 卷积,步长s,提取空间特征,包含 BN + SiLU。cv3:1×1 卷积,将c2扩展到c3,无激活(act=False),仅 BN。shortcut:若需要调整尺寸或通道,使用 1×1 卷积(步长s,无激活)进行投影;否则为恒等映射。
forward:- 主路径:
cv1 → cv2 → cv3。 shortcut路径处理输入。- 两者相加后经 ReLU 输出。
- 主路径:
注意事项
- 通道数关系:
c3 = e * c2,通常e=4,但可调整。若c1 == c3且s==1,则 shortcut 为恒等映射;否则使用 1×1 卷积。 - 步长控制:
s仅作用于cv2,因此空间下采样发生在 3×3 卷积中,而 1×1 卷积不改变空间尺寸。 - 激活函数:前两个卷积使用 SiLU,第三个无激活,最后整体使用 ReLU。这与标准 ResNet 的 ReLU 后置一致。
- BatchNorm 顺序:所有
Conv内部已包含 BN,且act=False时仍有 BN,因此cv3的输出经过 BN 但不经激活。 - 与标准 ResNet 的区别:标准 ResNet 的 BasicBlock 使用两个 3×3 卷积,这里增加了 1×1 卷积,形成瓶颈结构,参数量更少(对于大模型更高效)。
优缺点
优点
- 残差学习:有效缓解梯度消失,支持训练极深网络。
- 参数高效:瓶颈设计(1×1 降维 → 3×3 → 1×1 升维)比直接两个 3×3 卷积参数量更少。
- 灵活配置:可通过
s控制下采样,e控制通道扩展。 - 即插即用:可嵌入多种网络架构,如 ResNet、YOLO 等。
缺点
- 引入额外卷积:相比 BasicBlock,多一个 1×1 卷积,增加了计算量(虽然减少了参数量,但前向计算量可能增加)。
- 后置 ReLU:残差相加后 ReLU,与标准 ResNet 一致,但可能影响负值信息的传递(但一般可接受)。
- shortcut 需投影:当步长或通道变化时,需额外 1×1 卷积,增加参数。
在 YOLO 系列或 RT-DETR 的骨干网络中,ResNetBlock可作为基础残差单元,用于构建深层特征提取网络。使用时可根据任务调整c2和e,以控制模型容量。若需更轻量,可减小e或降低c2。
参考文献
[1] https://docs.ultralytics.com/
[2] https://github.com/ultralytics/ultralytics.git
- 由于本人水平有限,难免出现错漏,敬请批评改正。
- 更多精彩内容,可点击进入Python日常小操作专栏、OpenCV-Python小应用专栏、YOLO系列专栏、自然语言处理专栏、人工智能混合编程实践专栏或我的个人主页查看
- YOLOs-CPP:一个免费开源的YOLO全系列C++推理库(以YOLO26为例)
- PaddleOCR:Win10上安装使用PPOCRLabel标注工具
- 目标检测:使用自己的数据集微调DEIMv2进行物体检测
- 图像分割:PyTorch从零开始实现SegFormer语义分割
- 图像超分:使用自己的数据集微调Real-ESRGAN-x4plus进行超分重建
- 图像生成:PyTorch从零开始实现一个简单的扩散模型
- Stable Diffusion:使用自己的数据集微调 Stable Diffusion 3.5 LoRA 文生图模型
- 图像超分:使用自己的数据集微调Real-ESRGAN-x2plus进行超分重建
- Anomalib:使用Anomalib 2.1.0训练自己的数据集进行异常检测
- Anomalib:在Linux服务器上安装使用Anomalib 2.1.0
- 人工智能混合编程实践:C++调用封装好的DLL进行异常检测推理
- 人工智能混合编程实践:C++调用封装好的DLL进行FP16图像超分重建(v3.0)
- 隔离系统Python:源码编译3.11.8到自定义目录(含PGO性能优化)
- 在线机的Python环境迁移到离线机上
- Nuitka 将 Python 脚本封装为 .pyd 或 .so 文件
- Ultralytics:使用 YOLO11 进行速度估计
- Ultralytics:使用 YOLO11 进行物体追踪
- Ultralytics:使用 YOLO11 进行物体计数
- Ultralytics:使用 YOLO11 进行目标打码
- 人工智能混合编程实践:C++调用Python ONNX进行YOLOv8推理
- 人工智能混合编程实践:C++调用封装好的DLL进行YOLOv8实例分割
- 人工智能混合编程实践:C++调用Python ONNX进行图像超分重建
- 人工智能混合编程实践:C++调用Python AgentOCR进行文本识别
- 通过计算实例简单地理解PatchCore异常检测
- Python将YOLO格式实例分割数据集转换为COCO格式实例分割数据集
- YOLOv8 Ultralytics:使用Ultralytics框架训练RT-DETR实时目标检测模型
- 基于DETR的人脸伪装检测
- YOLOv7训练自己的数据集(口罩检测)
- YOLOv8训练自己的数据集(足球检测)
- YOLOv5:TensorRT加速YOLOv5模型推理
- YOLOv5:IoU、GIoU、DIoU、CIoU、EIoU
- 玩转Jetson Nano(五):TensorRT加速YOLOv5目标检测
- YOLOv5:添加SE、CBAM、CoordAtt、ECA注意力机制
- YOLOv5:yolov5s.yaml配置文件解读、增加小目标检测层
- Python将COCO格式实例分割数据集转换为YOLO格式实例分割数据集
- YOLOv5:使用7.0版本训练自己的实例分割模型(车辆、行人、路标、车道线等实例分割)
- 使用Kaggle GPU资源免费体验Stable Diffusion开源项目
- Stable Diffusion:在服务器上部署使用Stable Diffusion WebUI进行AI绘图(v2.0)
- Stable Diffusion:使用自己的数据集微调训练LoRA模型(v2.0)
编程学习
技术分享
实战经验