一、网络介绍
ResNet网络模型主要为在网络训练模型中,增加残差训练机制,按照”结果不会变的更坏“的处理原则,同时避免深度神经网络中梯度消失与过拟合的问题。
二、残差原理
三、源码解读
以ResNet18为例
1、BasicBlock == 为ResNet中的基础block模块,[2,2,2,2]为 网络中相关层循环的次数
# 当模型初始化时 weights != None时自动进行权重文件检索,不存在权重文件则进行权重文件下载。
def resnet18(*, weights: Optional[ResNet18_Weights] = None, progress: bool = True, **kwargs: Any) -> ResNet:weights = ResNet18_Weights.verify(weights) # 验证初始化权重文件是否存在# 当权重文件检索完毕后 进入_resnet 模块中,同时传入(BasicBlock, [2, 2, 2, 2], weights, progress, **kwargs) 相关参数return _resnet(BasicBlock, [2, 2, 2, 2], weights, progress, **kwargs)
2、进入_resnet函数
def _resnet(block: Type[Union[BasicBlock, Bottleneck]],layers: List[int],weights: Optional[WeightsEnum],progress: bool,**kwargs: Any,
) -> ResNet:if weights is not None:# 当权重文件 不等于 空值时,将模型输出的n_class 更改为 weights中的n_class数量_ovewrite_named_param(kwargs, "num_classes", len(weights.meta["categories"]))# 初始化模型model = ResNet(block, layers, **kwargs)if weights is not None:# 为模型加载相关权重参数model.load_state_dict(weights.get_state_dict(progress=progress))# 返回模型return model
3、初始化模型,进入ResNet模块中,同时传入(block, layers, **kwargs),其中block为BasicBlock,layers为[2, 2, 2, 2]
class ResNet(nn.Module):def __init__(self,block: Type[Union[BasicBlock, Bottleneck]],layers: List[int],num_classes: int = 1000,zero_init_residual: bool = False,groups: int = 1,width_per_group: int = 64,replace_stride_with_dilation: Optional[List[bool]] = None,norm_layer: Optional[Callable[..., nn.Module]] = None,) -> None:super().__init__()_log_api_usage_once(self)if norm_layer is None:norm_layer = nn.BatchNorm2dself._norm_layer = norm_layerself.inplanes = 64 # 定义初始输入通道为64self.dilation = 1if replace_stride_with_dilation is None:replace_stride_with_dilation = [False, False, False]self.groups = groupsself.base_width = width_per_group# 头部函数 用于读取图像数据 self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3, bias=False) # 常规7*7卷积核进行输入图像卷积 输入3 输出 64 kernel 7self.bn1 = norm_layer(self.inplanes) # 常规的batchNorm2dself.relu = nn.ReLU(inplace=True) # Relu函数self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) # 重叠池化# 核心层函数 == 构建 ResNet 网络模型的主体结构self.layer1 = self._make_layer(block, 64, layers[0]) # 将(BasicBlock,64,2) 传入_make_layer 函数中 == 生成第一层layer"""········跳转········"""self.layer2 = self._make_layer(block, 128, layers[1], stride=2, dilate=replace_stride_with_dilation[0]) # 将(BasicBlock,128,2,2,False) 传入_make_layer 函数中 == 生成第二层layer"""········跳转········"""self.layer3 = self._make_layer(block, 256, layers[2], stride=2, dilate=replace_stride_with_dilation[1])self.layer4 = self._make_layer(block, 512, layers[3], stride=2, dilate=replace_stride_with_dilation[2])self.avgpool = nn.AdaptiveAvgPool2d((1, 1))self.fc = nn.Linear(512 * block.expansion, num_classes)for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):nn.init.constant_(m.weight, 1)nn.init.constant_(m.bias, 0)if zero_init_residual:for m in self.modules():if isinstance(m, Bottleneck) and m.bn3.weight is not None:nn.init.constant_(m.bn3.weight, 0) # type: ignore[arg-type]elif isinstance(m, BasicBlock) and m.bn2.weight is not None:nn.init.constant_(m.bn2.weight, 0) # type: ignore[arg-type]def _make_layer( # self, # 将(BasicBlock,128,2,2,False) 传入_make_layer 函数中 == 生成第二层layerblock: Type[Union[BasicBlock, Bottleneck]], # BasicBlock planes: int, # 128blocks: int, # 2stride: int = 1, # 2dilate: bool = False,) -> nn.Sequential:norm_layer = self._norm_layer # 图像标准化采用class中函数downsample = None # 降采样默认为Noneprevious_dilation = self.dilation # 膨胀系数 默认为Flase# 将(BasicBlock,64,2) 传入_make_layer 函数中 == 生成第一层layerif dilate: # 第一层 Falseself.dilation *= stride # 不执行stride = 1 # 不执行if stride != 1 or self.inplanes != planes * block.expansion: # 不执行 # 第二轮 stride = 2 ,执行降采样工作 == 采用1*1卷积进行处理,输入 64 输出 128 步长 2 downsample = nn.Sequential(conv1x1(self.inplanes, planes * block.expansion, stride),norm_layer(planes * block.expansion),)# 创建主体层layers = [] # 创建层列表 用于存放 layerslayers.append(block( # 将(BasicBlock,64,2) 传入_make_layer 函数中 == 生成第一层layer# 将(BasicBlock,128,2,2,False) 传入_make_layer 函数中 == 生成第二层layerself.inplanes, planes, stride, downsample, self.groups, self.base_width, previous_dilation, norm_layer # 对BasicBlock中的函数进行解析,创建第一层layers))self.inplanes = planes * block.expansion # 定义输入通道 为 64 * 1 # 第二层 定义修改输入通道为128 for _ in range(1, blocks): # blocks = 2 layers.append(block(self.inplanes, # 128planes, # 128groups=self.groups, base_width=self.base_width,dilation=self.dilation,norm_layer=norm_layer,))return nn.Sequential(*layers) # 对layers列表进行解包,返回生成第一层 顺序容器
跳转 BasicBlock函数
class BasicBlock(nn.Module):expansion: int = 1 # 默认初始膨胀系数def __init__(self,inplanes: int,planes: int,stride: int = 1,downsample: Optional[nn.Module] = None,groups: int = 1,base_width: int = 64,dilation: int = 1,norm_layer: Optional[Callable[..., nn.Module]] = None,) -> None:super().__init__()if norm_layer is None:norm_layer = nn.BatchNorm2dif groups != 1 or base_width != 64:raise ValueError("BasicBlock only supports groups=1 and base_width=64")if dilation > 1:raise NotImplementedError("Dilation > 1 not supported in BasicBlock")# 将(BasicBlock,64,2 ) 传入_make_layer 函数中 == 生成第一层layerself.conv1 = conv3x3(inplanes, planes, stride) # 第一层 常规 3*3卷积 输入64 输出 64 步长为1 self.bn1 = norm_layer(planes) # 标准化函数self.relu = nn.ReLU(inplace=True) # 激活函数self.conv2 = conv3x3(planes, planes) # 输入 64 输出 64 步长1 self.bn2 = norm_layer(planes) # 标准化函数self.downsample = downsample # 降采样默认为Noneself.stride = stride # stride 默认为1 def forward(self, x: Tensor) -> Tensor:identity = xout = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)if self.downsample is not None:identity = self.downsample(x)out += identityout = self.relu(out)return out # 返回第一层 _make_layer 函数
其余相同= 最终生成ResNet网络模型
四、模型特点
在模型进行全连接输出时,去除了原本存在的dropout函数,全连接进行激活后进行输出。