[深度学习网络从入门到入土] 残差网络ResNet

📅 发布时间:2026/7/15 23:58:16 👁️ 浏览次数:
[深度学习网络从入门到入土] 残差网络ResNet
[深度学习网络从入门到入土] 残差网络ResNet个人导航知乎https://www.zhihu.com/people/byzh_rcCSDNhttps://blog.csdn.net/qq_54636039注本文仅对所述内容做了框架性引导具体细节可查询其余相关资料or源码参考文章各方资料文章目录[深度学习网络从入门到入土] 残差网络ResNet个人导航参考资料背景架构(公式)1. BasicBlockResNet18/342. BottleneckResNet50/101/1523. Shortcut 类型创新点1. 残差连接Skip Connection2. 可训练超深网络3. 结构简洁但极强为什么 ResNet 能训练 152 层代码实现项目实例参考资料Deep Residual Learning for Image Recognition.背景在 2014–2015 年深度 CNN 进入“越深越好”的阶段AlexNet8 层VGG16–19 层GoogLeNet22 层问题来了当网络超过 20 层后训练误差反而上升这不是过拟合而是优化困难degradation problemresnet横空出世:让网络学习“残差”而不是直接学习映射传统网络H ( x ) F ( x ) H(x)F(x)H(x)F(x)ResNetH ( x ) F ( x ) x H(x) F(x) xH(x)F(x)x架构(公式)1.BasicBlockResNet18/34Conv → BN → ReLU Conv → BN Shortcut → ReLUy ReLU ( F ( x ) x ) F ( x ) W 2 σ ( W 1 x ) y \text{ReLU}(F(x) x) \\ F(x) W_2 \sigma(W_1 x)yReLU(F(x)x)F(x)W2​σ(W1​x)2.BottleneckResNet50/101/152当网络变得非常深时使用瓶颈结构1×1(降维) → 3×3(提取特征) → 1×1(升维)F ( x ) W 3 σ ( W 2 σ ( W 1 x ) ) F(x) W_3 \sigma(W_2 \sigma(W_1 x))F(x)W3​σ(W2​σ(W1​x))3.Shortcut 类型情况1尺寸相同y F ( x ) x y F(x) xyF(x)x情况2尺寸不同下采样y F ( x ) W s x W s 1 × 1 Conv y F(x) W_s x \\ \color{purple}{W_s 1\times1 \text{ Conv}}yF(x)Ws​xWs​1×1Conv创新点1.残差连接Skip Connection允许梯度直接传播2. 可训练超深网络152 层首次成功训练3. 结构简洁但极强成为后续几乎所有视觉网络的基础(DenseNet, U-Net)为什么 ResNet 能训练 152 层残差网络的理论基础∂ y ∂ x ∂ F ( x ) ∂ x 1 \frac{\partial y}{\partial x} \frac{\partial F(x)}{\partial x} 1∂x∂y​∂x∂F(x)​1即梯度中始终存在 “1” 项梯度不会消失网络可以直接传递恒等映射代码实现importtorchimporttorch.nnasnnimporttorch.nn.functionalasFfrombyzh.ai.Butilsimportb_get_paramsclassBasicBlock(nn.Module): 给 ResNet18/34 用 expansion1def__init__(self,in_ch,out_ch,stride1):super().__init__()self.conv1nn.Conv2d(in_ch,out_ch,3,stride,1,biasFalse)self.bn1nn.BatchNorm2d(out_ch)self.conv2nn.Conv2d(out_ch,out_ch,3,1,1,biasFalse)self.bn2nn.BatchNorm2d(out_ch)self.shortcutnn.Sequential()ifstride!1orin_ch!out_ch:self.shortcutnn.Sequential(nn.Conv2d(in_ch,out_ch,1,stride,biasFalse),nn.BatchNorm2d(out_ch))defforward(self,x):outtorch.relu(self.bn1(self.conv1(x)))outself.bn2(self.conv2(out))outself.shortcut(x)outtorch.relu(out)returnoutclassBottleneck(nn.Module): 给 ResNet50/101/152 用 expansion4# 输出通道 out_ch * 4def__init__(self,in_ch,out_ch,stride1):super().__init__()# 1x1 降维self.conv1nn.Conv2d(in_ch,out_ch,kernel_size1,biasFalse)self.bn1nn.BatchNorm2d(out_ch)# 3x3 特征提取这里做 stride 下采样self.conv2nn.Conv2d(out_ch,out_ch,kernel_size3,stridestride,padding1,biasFalse)self.bn2nn.BatchNorm2d(out_ch)# 1x1 升维self.conv3nn.Conv2d(out_ch,out_ch*self.expansion,kernel_size1,biasFalse)self.bn3nn.BatchNorm2d(out_ch*self.expansion)self.shortcutnn.Sequential()ifstride!1orin_ch!out_ch*self.expansion:self.shortcutnn.Sequential(nn.Conv2d(in_ch,out_ch*self.expansion,kernel_size1,stridestride,biasFalse),nn.BatchNorm2d(out_ch*self.expansion))defforward(self,x):outtorch.relu(self.bn1(self.conv1(x)))outtorch.relu(self.bn2(self.conv2(out)))outself.bn3(self.conv3(out))outself.shortcut(x)outtorch.relu(out)returnoutclassResNet(nn.Module): input shape: (N, 3, 224, 224) def__init__(self,block,layers,num_classes1000):super().__init__()self.in_ch64self.conv1nn.Conv2d(3,64,7,2,3,biasFalse)self.bn1nn.BatchNorm2d(64)self.maxpoolnn.MaxPool2d(3,2,1)self.layer1self._make_layer(block,64,layers[0])self.layer2self._make_layer(block,128,layers[1],stride2)self.layer3self._make_layer(block,256,layers[2],stride2)self.layer4self._make_layer(block,512,layers[3],stride2)self.avgpoolnn.AdaptiveAvgPool2d((1,1))self.fcnn.Linear(512*block.expansion,num_classes)def_make_layer(self,block,out_ch,blocks,stride1):layers[]layers.append(block(self.in_ch,out_ch,stride))self.in_chout_ch*block.expansionfor_inrange(1,blocks):layers.append(block(self.in_ch,out_ch))returnnn.Sequential(*layers)defforward(self,x):xtorch.relu(self.bn1(self.conv1(x)))xself.maxpool(x)xself.layer1(x)xself.layer2(x)xself.layer3(x)xself.layer4(x)xself.avgpool(x)xtorch.flatten(x,1)xself.fc(x)returnxclassB_ResNet18_Paper(ResNet): input shape: (N, 3, 224, 224) def__init__(self,num_classes1000):blockBasicBlock layers[2,2,2,2]super().__init__(blockblock,layerslayers,num_classesnum_classes)classB_ResNet34_Paper(ResNet): input shape: (N, 3, 224, 224) def__init__(self,num_classes1000):blockBasicBlock layers[3,4,6,3]super().__init__(blockblock,layerslayers,num_classesnum_classes)classB_ResNet50_Paper(ResNet): input shape: (N, 3, 224, 224) def__init__(self,num_classes1000):blockBottleneck layers[3,4,6,3]super().__init__(blockblock,layerslayers,num_classesnum_classes)classB_ResNet101_Paper(ResNet): input shape: (N, 3, 224, 224) def__init__(self,num_classes1000):blockBottleneck layers[3,4,23,3]super().__init__(blockblock,layerslayers,num_classesnum_classes)classB_ResNet152_Paper(ResNet): input shape: (N, 3, 224, 224) def__init__(self,num_classes1000):blockBottleneck layers[3,8,36,3]super().__init__(blockblock,layerslayers,num_classesnum_classes)if__name____main__:# ResNet18netB_ResNet18_Paper(num_classes1000)atorch.randn(50,3,224,224)resultnet(a)print(result.shape)print(f参数量:{b_get_params(net)})# 11_689_512# ResNet34netB_ResNet34_Paper(num_classes1000)atorch.randn(50,3,224,224)resultnet(a)print(result.shape)print(f参数量:{b_get_params(net)})# 21_797_672# ResNet50netB_ResNet50_Paper(num_classes1000)atorch.randn(50,3,224,224)resultnet(a)print(result.shape)print(f参数量:{b_get_params(net)})# 25_557_032# ResNet101netB_ResNet101_Paper(num_classes1000)atorch.randn(50,3,224,224)resultnet(a)print(result.shape)print(f参数量:{b_get_params(net)})# 44_549_160# ResNet152netB_ResNet152_Paper(num_classes1000)atorch.randn(50,3,224,224)resultnet(a)print(result.shape)print(f参数量:{b_get_params(net)})# 60_192_808项目实例库环境:numpy1.26.4 torch2.2.2cu121 byzh-core0.0.9.21 byzh-ai0.0.9.53 byzh-extra0.0.9.12 ...ResNet18训练MNIST数据集:# copy all the codes from here to runimporttorchimporttorch.nn.functionalasFfromuploadToPypi_ai.byzh.ai.Bdataimportb_stratified_indicesfrombyzh.ai.BtrainerimportB_Classification_Trainerfrombyzh.ai.BdataimportB_Download_MNIST,b_get_dataloader_from_tensor# from uploadToPypi_ai.byzh.ai.Bmodel.study_cnn import B_ResNet18_Paperfrombyzh.ai.Bmodel.study_cnnimportB_ResNet18_Paperfrombyzh.ai.Butilsimportb_get_device##### hyper params #####epochs10lr1e-3batch_size32deviceb_get_device(use_idle_gpuTrue)##### data #####downloaderB_Download_MNIST(save_dirD:/study_cnn/datasets/MNIST)data_dictdownloader.get_data()X_traindata_dict[X_train_standard]y_traindata_dict[y_train]X_testdata_dict[X_test_standard]y_testdata_dict[y_test]num_classesdata_dict[num_classes]num_samplesdata_dict[num_samples]indicesb_stratified_indices(y_train,num_samples//5)X_trainX_train[indices]X_trainF.interpolate(X_train,size(224,224),modebilinear)X_trainX_train.repeat(1,3,1,1)y_trainy_train[indices]indicesb_stratified_indices(y_test,num_samples//5)X_testX_test[indices]X_testF.interpolate(X_test,size(224,224),modebilinear)X_testX_test.repeat(1,3,1,1)y_testy_test[indices]train_dataloader,val_dataloaderb_get_dataloader_from_tensor(X_train,y_train,X_test,y_test,batch_sizebatch_size)##### model #####modelB_ResNet18_Paper(num_classesnum_classes)##### else #####optimizertorch.optim.Adam(model.parameters(),lrlr)criteriontorch.nn.CrossEntropyLoss()##### trainer #####trainerB_Classification_Trainer(modelmodel,optimizeroptimizer,criterioncriterion,train_loadertrain_dataloader,val_loaderval_dataloader,devicedevice)trainer.set_writer1(./runs/resnet18/log.txt)##### run #####trainer.train_eval_s(epochsepochs)##### calculate #####trainer.draw_loss_acc(./runs/resnet18/loss_acc.png,y_limFalse)trainer.save_best_checkpoint(./runs/resnet18/best_checkpoint.pth)trainer.calculate_model()