resnetで最も重要なことは、コードの残余ブロックです。
def forward(self,x)
identity = x
out = 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
out += identity
out = self.relu(out)
return out
残りのブロック構造:
左側のものはBasicBlockと呼ばれ、右側のものはボトルネックと呼ばれます
BasicBlock
class BasicBlock(nn.Module):
expansion =1
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(BasicBlock,self).__init__()
self.conv1 = conv3x3(inplanes,planes,stride)
self.bn1 = nn.BatchNorm2d(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv3x3(planes,planes)
self.bn2 = nn.BatchNorm2d(planes)
self.downsample = downsample
self.stride =stride
def forward(self,x)
identity = x
out = 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
out += identity
out = self.relu(out)
return out
ボトルネック
Res18とRes34はBasicBlockを使用し、残りはボトルネックを使用することに注意してください
resnet18:ResNet(BasicBlock、[2、2、2、2])
resnet34:ResNet(BasicBlock、[3、4、6、3])
resnet50:ResNet(ボトルネック、[3、4、6、3])
resnet101:ResNet(Bottleneck、[3、4、23、3])
resnet152:ResNet(Bottleneck、[3、8、36、3])
ボトルネックの各残差構造の出力次元は入力次元の4倍であるため、展開= 4
クラスBottleneck(nn.Module):
拡張= 4
def __init __(self、inplanes、planes、stride = 1、downsample = None):
super(Bottleneck、self).__ init __()
self.conv1 = conv1x1(inplanes、planes)
self.bn1 = nn.BatchNorm2d(planes)
self。 conv2 = conv3x3(planes、planes、stride)
self.bn2 = nn.BatchNorm2d(planes)
self.conv3 = conv1x1(planes、planes * self.expansion)
self.bn3 = nn.BatchNorm2d(planes * self.expansion)
self。 relu = nn.ReLU(inplace = True)
self.downsample =ダウン
サンプルself.stride = stride
def forward(self、x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
self.downsampleがNoneでない場合:
identity = self.downsample(x)
out + =アイデンティティ
out = self.relu(out)
戻る
ResNetクラス
いくつかの重要なポイント:
1.残差構造の前に、最初に元の224 x 224画像を処理し、7 x 7の大きな畳み込みカーネル、BN、ReLU、および最大プーリングの後に56 x 56 x64の特徴マップを取得します
。 layer2、layer3、layer4は、最初のステージが特徴マップを縮小せず、残りはステージの最初のレイヤーでステップ2の3 x 3畳み込みを使用して、特徴マップの長さと幅を縮小することがわかります。半分に
、ダウンサンプリングアップグレード残留構造、直接に1×1の畳み込みプラスBN、次いでBasicBlockクラスとボトルネック・クラスの入力._make_layer機能3.得るために使用される
前記最後のプーリング層は、適応平均値を使用し論文のグローバル平均プーリングの代わりにプール
クラスResNet(nn.Module):
def __init __(self、block、layers、num_classes = 1000、zero_init_residual = False):
super(ResNet、self).__ init __()
self.inplanes = 64
self.conv1 = nn.Conv2d(3、64、kernel_size = 7、stride = 2、パディング= 3、
バイアス= False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace = True)
self.maxpool = nn.MaxPool2d(kernel_size = 3、stride = 2、パディング= 1)
self.layer1 = self._make_layer(block、64、layers [0])
self.layer2 = self._make_layer(block、128、layers [1]、stride = 2)
self.layer3 = self._make_layer(block 、256、layers [2]、stride = 2)
self.layer4 = self._make_layer(block、512、layers [3]、stride = 2)
self.avgpool = nn.AdaptiveAvgPool2d((1、1))
self.fc = nn.Linear(512 * block.expansion、num_classes)
デフ_make_layer(自己、ブロック、プレーン、ブロック、ストライド= 1):
ダウンサンプリング=なし
の場合ストライド= 1またはself.inplanes =プレーン* block.expansion!:
ダウンサンプリング= nn.Sequential(
conv1x1(self.inplanes、プレーン* block.expansion、stride)、
nn.BatchNorm2d(planes * block.expansion)、
)
layers = []
layers.append(block(self.inplanes、planes、stride、
downsample ))self.inplanes = planes * block.expansion
for _ in range(1、blocks):
layers.append(block(self.inplanes、飛行機))
nn.Sequential(* layers)を返します
def forward(self、x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x)
x = x.view(x.size(0)、-1)
x = self.fc(x)
xを返す