首页
/ Transformers 中 ResNet 实现全解析:从 ResNet v1.5 配置到图像分类与 Backbone 特征提取

Transformers 中 ResNet 实现全解析:从 ResNet v1.5 配置到图像分类与 Backbone 特征提取

2026-09-07 14:37:52作者:何举烈Damon

本文围绕 Hugging Face Transformers 的 ResNet 官方模型文档(docs/source/en/model_doc/resnet.md)展开,系统讲解 ResNet 残差网络的设计思想、Transformers 对 "ResNet v1.5" 变体的具体实现、ResNetConfig 的全部关键配置项,以及如何用 ResNetForImageClassification 做图像分类、用 ResNetBackbone 提取多尺度特征图。读完本文,你将能够独立构建/加载 ResNet 模型、按 ResNet-18/34/50/101/152 规格定制配置,并理解其 Stem、Basic/Bottleneck 残差层与 Stage 的组织方式。

1. 背景:ResNet 与 Transformers 采用的 "v1.5" 变体

ResNet 出自论文 Deep Residual Learning for Image Recognition(Kaiming He 等人,2015)。其核心贡献是残差连接(residual connection):将每一层重新表述为"以层输入为参考学习残差函数",而非学习无参考函数,从而使得数百甚至上千层的深层网络可被有效训练,并凭借极大的表示深度在 2015 年 ILSVRC & COCO 竞赛中拿下 ImageNet 分类、检测、定位以及 COCO 检测与分割的冠军。

Transformers 中的实现与原始论文有一个刻意的小差异(文档原文强调):下采样(stride=2)被放在 bottleneck 的 3x3 卷积上,而不是第一个 1x1 卷积上。这一变体由 Nvidia 提出并推广,业界通称为 "ResNet v1.5"。这一差异在源码中由 downsample_in_bottleneck 开关控制(见下文 modeling_resnet.pyResNetBottleNeckLayer 的实现)。

论文摘要(原文收录于文档)指出:在 ImageNet 上评估了深度至 152 层(比 VGG 深 8 倍但复杂度更低)的残差网络,集成模型在 ImageNet 测试集上取得 3.57% 错误率;在 CIFAR-10 上还验证了 100 层与 1000 层网络的可行性。

该模型由社区贡献者 Francesco 于 2022-03-14 提交至 Transformers,官方预训练检查点命名为 microsoft/resnet-50microsoft/resnet-18 等(文档中 ResNetConfig 的 autodoc 即以 microsoft/resnet-50 为示例检查点)。

2. 模块组成:从 Conv 原子到完整模型

阅读 src/transformers/models/resnet/ 目录可以看到实现分为三部分:

modeling_resnet.py 中的构建层级自底向上为:

  1. ResNetConvLayermodeling_resnet.py#L39-L69):Conv2d + BatchNorm2d + 激活函数 三件套,默认 relupadding = kernel_size // 2
  2. ResNetEmbeddings(Stem,#L72-L93):单块"激进卷积"——7x7stride=2 卷积将 num_channels 映射到 embedding_size,随后 3x3stride=2MaxPool2d 进一步下采样;并会校验输入通道数与 config.num_channels 一致;
  3. ResNetShortCut#L96-L110):1x1 卷积 + BatchNorm 的跳跃连接,用于在通道数或步长变化时把残差投影到正确尺寸(通道数与步长均不变时退化为 nn.Identity);
  4. ResNetBasicLayer#L113-L136):两个 3x3 卷积组成的经典残差块,供 ResNet-18/34 使用;
  5. ResNetBottleNeckLayer#L139-L178):1x1 降维 -> 3x3 -> 1x1 升维 的瓶颈块,reduction=4。注意其中两个 stride 分支:
ResNetConvLayer(in_channels, reduces_channels, kernel_size=1,
                stride=stride if downsample_in_bottleneck else 1),
ResNetConvLayer(reduces_channels, reduces_channels,
                stride=stride if not downsample_in_bottleneck else 1),

downsample_in_bottleneck=True 时下采样前移到第一个 1x1 卷积(经典 v1.0),默认 False 时留在 3x3 卷积上(v1.5,Transformers 的默认行为,与文档描述一致); 6. ResNetStage#L181-L216):按 config.layer_type 选用 Bottleneck 或 Basic 层,首层带步长、后续层等通道堆叠 depth 层; 7. ResNetEncoder#L219-L257):依次串起所有 stage。第一个 stage 的步长由 downsample_in_first_stage 决定(stride=21),其余 stage 固定 stride=2。若 output_hidden_states=True,会记录每个 stage 前后的中间特征(共 num_stages + 1 个张量)。

ResNetModel 在 embedder 与 encoder 之外再加一个 AdaptiveAvgPool2d((1, 1)) 池化器,输出 last_hidden_state(最终特征图)、pooler_output(全局平均池化结果)与可选的 hidden_states

3. ResNetConfig:完整配置参数说明

ResNetConfig 继承 BackboneConfigMixinPreTrainedConfigmodel_type = "resnet"。结合 configuration_resnet.py#L52-L75 的源码,完整参数如下:

参数 默认值 说明
num_channels 3 输入图像通道数,需与 Stem 卷积的输入一致
embedding_size 64 Stem 卷积输出通道数(stage1 的输入通道)
hidden_sizes (256, 512, 1024, 2048) 各 stage 输出通道数,即 ResNet-50 及以上规格
depths (3, 4, 6, 3) 各 stage 中残差块的层数
layer_type "bottleneck" "basic"(ResNet-18/34)或 "bottleneck"(ResNet-50 及以上),非法取值会由 validate_layer_type 抛错
hidden_act "relu" 残差块激活函数
downsample_in_first_stage False True 时第一个 stage 也用 stride=2 下采样(经典配置)
downsample_in_bottleneck False True 时瓶颈块在第一个 1x1 卷积上下采样(v1.0),否则在 3x3 卷积上(v1.5)
num_labels / id2label / label2id 分类头相关,由 PreTrainedConfig 提供

__post_init__ 中,配置会自动生成 stage_names = ["stem", "stage1", "stage2", "stage3", "stage4"],并通过 set_output_features_output_indices 解析 out_indices / out_features(Backbone 输出哪些特征图)。

文档给出的最小示例(可直接运行,随机权重):

from transformers import ResNetConfig, ResNetModel

# 初始化 resnet-50 风格的配置(默认值即为 resnet-50 规格)
configuration = ResNetConfig()

# 从该配置初始化模型(随机权重)
model = ResNetModel(configuration)

# 访问模型配置
configuration = model.config

规格对照表convert_resnet_to_pytorch.py#L128-L147 中定义了官方转换脚本使用的六种规格,可作为自定义配置的权威参照:

规格 depths hidden_sizes layer_type
resnet18 [2, 2, 2, 2] [64, 128, 256, 512] basic
resnet26 [2, 2, 2, 2] [256, 512, 1024, 2048] bottleneck
resnet34 [3, 4, 6, 3] [64, 128, 256, 512] basic
resnet50 [3, 4, 6, 3] [256, 512, 1024, 2048] bottleneck
resnet101 [3, 4, 23, 3] [256, 512, 1024, 2048] bottleneck
resnet152 [3, 8, 36, 3] [256, 512, 1024, 2048] bottleneck

可见 basic 规格(18/34)通道数减半(最高 512),bottleneck 规格(26 及以上)统一为 256/512/1024/2048,深度由 depths 区分。此外该脚本还从 huggingface/label-files 数据集拉取 ImageNet-1k 的 id2label(1000 类),说明官方检查点的分类头均对应 1000 类 ImageNet。

4. ResNetForImageClassification:图像分类用法

文档 Resources 一节明确:ResNetForImageClassificationimage-classification 管线支持,配套示例脚本为 examples/pytorch/image-classification/run_image_classification.py,任务指南见 docs/source/en/tasks/image_classification.md

模型本体在 modeling_resnet.py#L340-L384:内部实例化一个 ResNetModel,分类头为 nn.Flatten() + nn.Linear(hidden_sizes[-1], num_labels)num_labels=0 时退化为 Identity,可当作特征提取器使用)。forward 的签名为:

def forward(
    self,
    pixel_values: torch.FloatTensor | None = None,
    labels: torch.LongTensor | None = None,
    output_hidden_states: bool | None = None,
    return_dict: bool | None = None,
    **kwargs,
) -> ImageClassifierOutputWithNoAttention:
  • pixel_values:图像张量;
  • labels:形状 (batch_size,),取值范围 [0, num_labels-1]。提供后按 Cross-Entropy 计算 lossnum_labels > 1 时);
  • 返回 ImageClassifierOutputWithNoAttention,含 losslogitshidden_states

推理端的最小闭环(检查点 microsoft/resnet-50,与测试用例 tests/models/resnet/test_modeling_resnet.py#L268-L291 一致):

import torch
from transformers import ResNetForImageClassification, AutoImageProcessor

model = ResNetForImageClassification.from_pretrained("microsoft/resnet-50")
processor = AutoImageProcessor.from_pretrained("microsoft/resnet-50")

inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
    outputs = model(**inputs)

# ImageNet-1k 检查点的 logits 形状为 (1, 1000)
print(outputs.logits.shape)

集成测试中,对固定测试图像的 logits 前三个值做了切片比对([-11.1069, -9.7877, -8.3777],CPU 与 CUDA 一致,容差 2e-4),可作为复现环境时的数值校验手段。

权重转换:如需自行转换 timm 权重,可用 convert_resnet_to_pytorch.py。其命令行参数为:

python convert_resnet_to_pytorch.py \
    --model_name resnet50 \
    --pytorch_dump_folder_path ./output \
    --push_to_hub False

脚本内部用 Tracker 记录前向传播中实际参与计算的叶子模块(Conv2d/BatchNorm2d 等),按顺序把 timm 模型参数拷入 Transformers 模型,并在推送前断言 torch.allclose(from_model(x), our_model(x).logits) 保证输出一致。

5. ResNetBackbone:面向 DETR / MaskFormer 的多尺度特征提取

除分类头外,文档的 autodoc 体系还覆盖 ResNetBackboneBackboneMixin 实现,用于 DETR、MaskFormer 等下游框架)。其 forward 的官方示例(见 modeling_resnet.py#L415-L440):

from transformers import AutoImageProcessor, AutoBackbone

processor = AutoImageProcessor.from_pretrained("microsoft/resnet-50")
model = AutoBackbone.from_pretrained(
    "microsoft/resnet-50",
    out_features=["stage1", "stage2", "stage3", "stage4"],
)

inputs = processor(image, return_tensors="pt")
outputs = model(**inputs)

feature_maps = outputs.feature_maps
list(feature_maps[-1].shape)  # [1, 2048, 7, 7](224x224 输入时)

实现上,ResNetBackbone.forward 强制以 output_hidden_states=True 前向,拿到 encoder 中每个 stage 的特征,再按 out_featuresstage_names 中匹配并挑选对应的特征图;out_features=None 时退化为只输出最后一层。测试用例 test_modeling_resnet.py#L117-L144 同时验证了两种情形下的特征图形状与 channels 属性(如 stage2 特征为 [B, hidden_sizes[1], 4, 4]out_features=None 时为 [B, hidden_sizes[-1], 1, 1])。

此外,从 Auto 映射可确认各入口均已注册(auto/modeling_auto.py):AutoModel -> ResNetModelAutoModelForImageClassification -> ResNetForImageClassificationAutoBackbone -> ResNetBackbone;图像处理器方面,resnet 模型族复用 ConvNext 的 image processor(auto/image_processing_auto.py#L141),这解释了为什么 microsoft/resnet-50 直接搭配 AutoImageProcessor 即可工作。

6. 权重初始化与其他实现细节

  • 初始化ResNetPreTrainedModel._init_weightsmodeling_resnet.py#L268-L287)对 Conv2d 使用 Kaiming 正态(fan_out + relu)、对 Linear 使用 Kaiming 均匀并给 bias 按 1/sqrt(fan_in) 界内均匀采样、对 BatchNorm 置 weight=1, bias/mean=0, var=1,与 PyTorch 官方 reset_parameters 语义对齐;
  • 无注意力:模型声明 has_attentions = False,测试中相应跳过了 inputs_embeds、embedding 增删、FFN chunking 等文本模型用例(test_modeling_resnet.py#L190-L242);
  • 管线映射image-feature-extraction -> ResNetModelimage-classification -> ResNetForImageClassificationtest_modeling_resnet.py#L169-L173);
  • 空间下采样总倍数:从测试断言 image_size // 32 可知,默认配置下(downsample_in_first_stage=False)Stem 下采样 4 倍 + 3 个后续 stage 各 2 倍,最终特征图边长为输入的 1/32;若开启 downsample_in_first_stage,则首 stage 额外下采样 1 倍(对应经典 v1.0 布局)。

7. 小结

Transformers 的 ResNet 文档与实现构成了一条完整的链路:以 ResNet v1.5(stride 位于 3x3 卷积)为默认架构,用 depths + hidden_sizes + layer_type 三个参数覆盖 ResNet-18 到 ResNet-152 全部规格;ResNetModel 提供带全局池化的基础输出,ResNetForImageClassification 加上 1000 类 ImageNet 分类头并被 image-classification 训练脚本与管线支持,ResNetBackbone 则通过 out_features 暴露 stage1~stage4 多尺度特征图供检测/分割框架复用。测试套件 tests/models/resnet/test_modeling_resnet.py 覆盖了形状校验、hidden states 数量(stage 数 + 1)与 microsoft/resnet-50 的 logits 数值比对,可直接用于验证本地环境的正确性。

登录后查看全文
热门项目推荐
相关项目推荐