ResNetResidual Network的核心思想其实就一句话通过“残差连接skip connection解决深层网络训练困难问题。但你问的是“结构 block 如何映射到代码”我们直接从结构 → block → 代码实现一层层拆开讲清楚。一、ResNet整体结构以 ResNet-50 为例一个标准 ResNet 可以抽象为输入 ↓ Conv17x7 BN ReLU MaxPool ↓ Stage1多个残差块 ↓ Stage2多个残差块 ↓ Stage3多个残差块 ↓ Stage4多个残差块 ↓ Global AvgPool ↓ FC ↓ 输出 核心中间全是“残差块Residual Block堆叠”二、Residual Block残差块本质残差块核心公式[y F(x) x] 含义x输入F(x)卷积网络学到的“残差”输出 输入 学到的变化1️⃣ BasicBlock用于 ResNet18 / 34结构x │ ├── Conv3x3 → BN → ReLU │ ├── Conv3x3 → BN │ └── identity ↓ ReLU 特点2层卷积输入输出通道一致2️⃣ Bottleneck用于 ResNet50/101/152结构x │ ├── 1x1 Conv降维 ├── 3x3 Conv特征提取 ├── 1x1 Conv升维 │ └── identity ↓ ReLU 通道变化in_channels → mid → mid → out_channels 一般关系out_channels 4 * mid_channels三、关键问题尺寸不一致怎么办如果stride ≠ 1或 通道数不同 就不能直接x F(x)需要一个downsample捷径分支identity Conv1x1(x)四、代码如何对应PyTorch我们直接给你一个标准可复用写法1️⃣ BasicBlock实现importtorchimporttorch.nnasnnclassBasicBlock(nn.Module):expansion1# 输出通道倍数def__init__(self,in_channels,out_channels,stride1):super().__init__()self.conv1nn.Conv2d(in_channels,out_channels,3,stride,1,biasFalse)self.bn1nn.BatchNorm2d(out_channels)self.relunn.ReLU(inplaceTrue)self.conv2nn.Conv2d(out_channels,out_channels,3,1,1,biasFalse)self.bn2nn.BatchNorm2d(out_channels)# shortcutself.downsampleNoneifstride!1orin_channels!out_channels:self.downsamplenn.Sequential(nn.Conv2d(in_channels,out_channels,1,stride,biasFalse),nn.BatchNorm2d(out_channels))defforward(self,x):identityx outself.conv1(x)outself.bn1(out)outself.relu(out)outself.conv2(out)outself.bn2(out)ifself.downsample:identityself.downsample(x)outidentity outself.relu(out)returnout2️⃣ Bottleneck实现ResNet50核心classBottleneck(nn.Module):expansion4def__init__(self,in_channels,mid_channels,stride1):super().__init__()out_channelsmid_channels*4self.conv1nn.Conv2d(in_channels,mid_channels,1,biasFalse)self.bn1nn.BatchNorm2d(mid_channels)self.conv2nn.Conv2d(mid_channels,mid_channels,3,stride,1,biasFalse)self.bn2nn.BatchNorm2d(mid_channels)self.conv3nn.Conv2d(mid_channels,out_channels,1,biasFalse)self.bn3nn.BatchNorm2d(out_channels)self.relunn.ReLU(inplaceTrue)self.downsampleNoneifstride!1orin_channels!out_channels:self.downsamplenn.Sequential(nn.Conv2d(in_channels,out_channels,1,stride,biasFalse),nn.BatchNorm2d(out_channels))defforward(self,x):identityx outself.relu(self.bn1(self.conv1(x)))outself.relu(self.bn2(self.conv2(out)))outself.bn3(self.conv3(out))ifself.downsample:identityself.downsample(x)outidentity outself.relu(out)returnout3️⃣ ResNet主干如何搭关键函数make_layerdefmake_layer(block,in_channels,out_channels,blocks,stride):layers[]# 第一个block可能需要降采样layers.append(block(in_channels,out_channels,stride))for_inrange(1,blocks):layers.append(block(out_channels*block.expansion,out_channels))returnnn.Sequential(*layers)4️⃣ ResNet50结构对应self.layer1make_layer(Bottleneck,64,64,3,stride1)self.layer2make_layer(Bottleneck,256,128,4,stride2)self.layer3make_layer(Bottleneck,512,256,6,stride2)self.layer4make_layer(Bottleneck,1024,512,3,stride2) 对应论文StageBlock数layer13layer24layer36layer43五、你需要建立的“映射思维”看到代码要能脑补结构1️⃣block.expansion 决定输出通道是否 ×42️⃣stride 控制是否降采样feature map 是否变小3️⃣downsample 控制shortcut 是否变形4️⃣make_layer 本质就是多个 block 堆叠六、一句话总结非常关键 ResNet代码 block定义结构 make_layer堆叠规则 主干拼接