ResNet有2個基本的block搓劫,一個是Identity Block塑荒,輸入和輸出的dimension是一樣的党瓮,所以可以串聯(lián)多個浑侥;另外一個基本block是Conv Block姊舵,輸入和輸出的dimension是不一樣的,所以不能連續(xù)串聯(lián)锭吨,它的作用本來就是為了改變特征向量的dimension
因為CNN最后都是要把輸入圖像一點點的轉(zhuǎn)換成很小但是depth很深的feature map蠢莺,一般的套路是用統(tǒng)一的比較小的kernel(比如VGG都是用3*3),但是隨著網(wǎng)絡(luò)深度的增加零如,output的channel也增大(學(xué)到的東西越來越復(fù)雜)躏将,所以有必要在進(jìn)入Identity Block之前,用Conv Block轉(zhuǎn)換一下維度考蕾,這樣后面就可以連續(xù)接Identity Block.
Conv Block:
Identity Block:
其實就是在shortcut path的地方加上一個conv2D layer(1*1 filter size)祸憋,然后在main path改變dimension,并與shortcut path對應(yīng)起來.
ResNetV1-50流程如下, 不使用bottleneck, 且只有resnetv1在initial_conv后面做BN和Relu:
block_sizes=[3, 4, 6, 3]指的是stage1(first pool)之后的4個layer的block數(shù), 分別對應(yīng)res2,res3,res4,res5,
每一個layer的第一個block在shortcut上做conv+BN, 即Conv Block
inputs: (1, 720, 1280, 3)
initial_conv:
conv2d_fixed_padding()
1. kernel_size=7, 先做padding(1, 720, 1280, 3) -> (1, 726, 1286, 3)
2. conv2d kernels=[7, 7, 3, 64], stride=2, VALID 卷積. 7x7的kernel, padding都為3, 為了保證左上角和卷積核中心點對其
(1, 726, 1286, 3) -> (1, 360, 640, 64)
3. BN, Relu (只有resnetv1在第一次conv后面做BN和Relu)
initial_max_pool:
k=3, s=2, padding='SAME', (1, 360, 640, 64) -> (1, 180, 320, 64)
以下均為不使用bottleneck的building_block
block_layer1:
(有3個block, layer間stride=1(上一層做pool了), 64個filter, 不使用bottleneck(若使用bottleneck 卷積核數(shù)量需乘4))
1. 第一個block:
Conv Block有projection_shortcut, 且strides可以等于1或者2
Identity Block沒有projection_shortcut, 且strides只能等于1
`inputs = block_fn(inputs, filters, training, projection_shortcut, strides, data_format)`
shortcut做[1, 1, 64, 64], stride=1的conv和BN, shape不變
然后和主要分支里input做3次卷積后的結(jié)果相加, 一起Relu, 注意block里最后一次卷積后只有BN沒有Relu
input: conv-bn-relu-conv-bn-relu-conv-bn 和shortcut相加后再做relu
shortcut: conv-bn
shortcut: [1, 1, 64, 64], s=1, (1, 180, 320, 64) -> (1, 180, 320, 64)
input做兩次[3, 3, 64, 64], s=1的卷積, shape不變(1, 180, 320, 64) -> (1, 180, 320, 64) -> (1, 180, 320, 64)
inputs += shortcut, 再relu
2. 對剩下的2個block, 每個block操作相同:
`inputs = block_fn(inputs, filters, training, None, 1, data_format)`
shortcut直接和input卷積結(jié)果相加, 不做conv-bn
input做兩次[3, 3, 64, 64], s=1的卷積, shape不變(1, 180, 320, 64) -> (1, 180, 320, 64) -> (1, 180, 320, 64)
inputs += shortcut, 再relu
block_layer2/3/4同block_layer1, 只是每個layer的identity block數(shù)量不同, 卷積核數(shù)量和layer間stride也不同, 不過仍然只有第一個conv block的shortcut做conv-bn
block_layer2: 4個block, 128個filter, layer間stride=2 (因為上一層出來后沒有pool)
1. 第一個block:
對shortcut做kernel=[1, 1, 64, 128], s=2的conv和BN, (1, 180, 320, 64) -> (1, 90, 160, 128)
對主要分支先做kernel=[3, 3, 64, 128], s=2的卷積, padding='VALID', (1, 180, 320, 64) -> (1, 90, 160, 128)
再做kernel=[3, 3, 128, 128], s=1的卷積, padding='SAME', (1, 90, 160, 128) -> (1, 90, 160, 128)
2. 剩下的3個block, 每個block操作相同:
shortcut不操作直接和結(jié)果相加做Relu
對主要分支做兩次[3, 3, 128, 128], s=1的卷積, padding='SAME', (1, 90, 160, 128) -> (1, 90, 160, 128) -> (1, 90, 160, 128)
block_layer3: 6個block, 256個filter, layer間stride=2
1. 第一個block:
對shortcut做kernel=[1, 1, 128, 256], s=2的conv和BN, (1, 90, 160, 128) -> (1, 45, 80, 256)
對主要分支先做kernel=[3, 3, 128, 256], s=2的卷積, padding='VALID', (1, 90, 160, 128) -> (1, 45, 80, 256)
再做kernel=[3, 3, 256, 256], s=1的卷積, padding='SAME', (1, 45, 80, 256) -> (1, 45, 80, 256)
2. 剩下的5個block, 每個block操作相同:
shortcut不操作直接和結(jié)果相加做Relu
對主要分支做兩次[3, 3, 256, 256], s=1的卷積, padding='SAME', (1, 45, 80, 256) -> (1, 45, 80, 256) -> (1, 45, 80, 256)
block_layer4: 3個block, 512個filter, layer間stride=2
1. 第一個block:
對shortcut做kernel=[1, 1, 256, 512], s=2的conv和BN, (1, 45, 80, 256) -> (1, 23, 40, 512)
對主要分支先做kernel=[3, 3, 256, 512], s=2的卷積, padding='VALID', (1, 45, 80, 256) -> (1, 23, 40, 512)
再做kernel=[3, 3, 512, 512], s=1的卷積, padding='SAME', (1, 23, 40, 512) -> (1, 23, 40, 512)
2. 剩下的2個block, 每個block操作相同:
shortcut不操作直接和結(jié)果相加做Relu
對主要分支做兩次[3, 3, 512, 512], s=1的卷積, padding='SAME', (1, 23, 40, 512) -> (1, 23, 40, 512)
avg_pool, 7*7
FC, output1000
softmax
輸出prediction
Keras版結(jié)構(gòu)如下, res2a代表stage2的第1個block, branch1是shortcut path, branch2是main path, branch2a代表是main path的第1個卷積: