在 Keras 中,ResNet50 有一个奇怪的模式

In Keras, the ResNet50 has a strange pattern

众所周知,在CNN中,只有Convolution、BatchNormalization这几层是有权重的。通常,它们是通过这种方式构建的。 Conv - BN - ReLU - Conv - BN - ReLU 但是,如您所见,下面的结构仍然不寻常。

conv2_block1_0_conv/kernel:0
conv2_block1_0_conv/bias:0
conv2_block1_3_conv/kernel:0
conv2_block1_3_conv/bias:0
conv2_block1_1_bn/gamma:0
conv2_block1_1_bn/beta:0
conv2_block1_1_bn/moving_mean:0
conv2_block1_1_bn/moving_variance:0
conv2_block1_3_bn/gamma:0
conv2_block1_3_bn/beta:0
conv2_block1_3_bn/moving_mean:0
conv2_block1_3_bn/moving_variance:0

您可以通过以下方式找到此结果:

model = tf.keras.application.ResNet50()
#The unusual phenomenon begins with index 18.
model.weights[18]

我建议您在 IDE 中使用调试模式。然后你会发现它更容易。

在下面的行中,ResNet50 具有 stack_fn 用于创建图层的函数

def ResNet50():
.
.
  def stack_fn(x):
    x = stack1(x, 64, 3, stride1=1, name='conv2')
    x = stack1(x, 128, 4, name='conv3')
    x = stack1(x, 256, 6, name='conv4')
    return stack1(x, 512, 3, name='conv5')
.
.

在下面的代码中,stack1用于简化重复的住宅区。

def stack1(x, filters, blocks, stride1=2, name=None):


  x = block1(x, filters, stride=stride1, name=name + '_block1')
  for i in range(2, blocks + 1):
    x = block1(x, filters, conv_shortcut=False, name=name + '_block' + str(i))
  return x

在下面的结构中,block1是ResNet50中的住宅层。

def block1(x, filters, kernel_size=3, stride=1, conv_shortcut=True, name=None):

  bn_axis = 3 if backend.image_data_format() == 'channels_last' else 1

  if conv_shortcut:
    shortcut = layers.Conv2D(
        4 * filters, 1, strides=stride, name=name + '_0_conv')(x)
    shortcut = layers.BatchNormalization(
        axis=bn_axis, epsilon=1.001e-5, name=name + '_0_bn')(shortcut) 
  else:
    shortcut = x

  x = layers.Conv2D(filters, 1, strides=stride, name=name + '_1_conv')(x)
  x = layers.BatchNormalization(
      axis=bn_axis, epsilon=1.001e-5, name=name + '_1_bn')(x)
  x = layers.Activation('relu', name=name + '_1_relu')(x)

  x = layers.Conv2D(
      filters, kernel_size, padding='SAME', name=name + '_2_conv')(x)
  x = layers.BatchNormalization(
      axis=bn_axis, epsilon=1.001e-5, name=name + '_2_bn')(x)
  x = layers.Activation('relu', name=name + '_2_relu')(x)

  x = layers.Conv2D(4 * filters, 1, name=name + '_3_conv')(x)
  x = layers.BatchNormalization(
      axis=bn_axis, epsilon=1.001e-5, name=name + '_3_bn')(x)

  x = layers.Add(name=name + '_add')([shortcut, x]) 
  x = layers.Activation('relu', name=name + '_out')(x)
  return x

我的问题是为什么模型实例与实际结构不同?

更新
抱歉,我之前可能误解了您的问题。 如下图所示,似乎有两个连续的卷积层,我想这就是你的意思。然而,这实际上不是连续的。
ResNet 具有分支结构(残差),这意味着它不是顺序的。但是在 TensorFlow 中,summary 按顺序打印它的层,所以,请注意最后一列,它表示该层在它之前连接到什么 TensorFlow 通过指定哪个层在哪个层之后来说明并行结构。

例如,conv2_block1_0_conv 连接到 pool1_pool
conv2_block1_3_conv 连接到 conv2_block1_2_relu

意思是虽然是并排打印,但不是连续的,是平行结构!

conv2_block1_0_conv和conv2_block1_0_bn在快捷路径上
而 conv2_block1_3_conv 和 conv2_block1_3_bn 在残差路径上

如果您对此部分有更多问题,请随时发表评论,或者如果您有其他问题,请打开一个新的post


model.weights return 模型的权重(名称不言自明)。

Conv - BN - ReLU - Conv - BN - ReLU 是层。
Conv代表Convolutional layer,BN代表Batch Normalization,ReLU是activation。

要获取图层列表,您可以使用 model.layers(return 是图层对象列表)。如果您只是想查看模型结构的摘要,请使用model.summary() 打印结构

例如,ResNet50().summary()给出(部分输出)

odel: "resnet50" __________________________________________________________________________________________________ Layer (type) Output Shape Param #
Connected to
================================================================================================== input_1 (InputLayer) [(None, 224, 224, 3) 0

__________________________________________________________________________________________________ conv1_pad (ZeroPadding2D) (None, 230, 230, 3) 0
input_1[0][0]
__________________________________________________________________________________________________ conv1_conv (Conv2D) (None, 112, 112, 64) 9472
conv1_pad[0][0]
__________________________________________________________________________________________________ conv1_bn (BatchNormalization) (None, 112, 112, 64) 256
conv1_conv[0][0]
__________________________________________________________________________________________________ conv1_relu (Activation) (None, 112, 112, 64) 0
conv1_bn[0][0]
__________________________________________________________________________________________________ pool1_pad (ZeroPadding2D) (None, 114, 114, 64) 0
conv1_relu[0][0]
__________________________________________________________________________________________________ pool1_pool (MaxPooling2D) (None, 56, 56, 64) 0
pool1_pad[0][0]
__________________________________________________________________________________________________ conv2_block1_1_conv (Conv2D) (None, 56, 56, 64) 4160
pool1_pool[0][0]
__________________________________________________________________________________________________ conv2_block1_1_bn (BatchNormali (None, 56, 56, 64) 256
conv2_block1_1_conv[0][0]
__________________________________________________________________________________________________ conv2_block1_1_relu (Activation (None, 56, 56, 64) 0
conv2_block1_1_bn[0][0]