Comprensión y análisis del código fuente resnet_v1_50

Enlace del código fuente:
https://github.com/tensorflow/models/blob/master/research/slim/nets/resnet_utils.py
https://github.com/tensorflow/models/blob/master/research/slim/nets /resnet_v1.py

1. Uso de resnet_v1_50 en TensorFlow

Primero, resuma el uso. Los parámetros de resnet_v1_50 en el código fuente son los siguientes:

def resnet_v1_50(inputs,
                 num_classes=None,
                 is_training=True,
                 global_pool=True,
                 output_stride=None,
                 spatial_squeeze=True,
                 store_non_strided_activations=False,
                 min_base_depth=8,
                 depth_multiplier=1,
                 reuse=None,
                 scope='resnet_v1_50'):

en:

  • entrada: conjunto de entrenamiento, su formato es [lote, altura_en, ancho_en, canales]
  • num_classes: El número de tipos de muestras, usado para definir el número de nodos en la capa superior. Si es "Ninguno", el resultado final debe ser [lote, 1,1, 2048], si "spatial_stride=True", el resultado final es [lote, 2048]
  • is_training: si agregar la capa "Batch_Norm" al modelo de entrenamiento
  • global_pool: esta capa se encuentra después de toda la estructura de la red y antes de "num_classes". "Verdadero" significa hacer una agrupación promedio global para la salida de la última capa "neta" de la red. La llamada combinación global significa que la zancada combinada es igual al tamaño de entrada y se obtiene un escalar.
  • aprisionamiento espacial: elimine la dimensión igual a 1 en la lista, como aprisionamiento espacial ([B, 1, 1, C]) = [B, C]
  • store_non_strided_activations: útil en el procesamiento de imágenes de múltiples escalas, puede almacenar salidas de diferentes tamaños

En pocas palabras, después de importar los módulos anteriores, construimos la estructura de red de ResNet50, principalmente ingresando el conjunto de entrenamiento "entrada" y el número de categorías "num_classes". Si "num_classes=None", construimos una arquitectura de red de extractor de características, que solo puede extraer las características de las imágenes, que pueden tener dimensiones muy altas, como 2048; si "num_classes=10", significa que "ingresaremos "Los datos en " se dividen en 10 categorías, y la salida de la última capa de la arquitectura de red es un vector de 10 dimensiones, que puede representar la probabilidad de que la imagen pertenezca a una determinada categoría.

2. El marco de construcción del código fuente resnet_v1_50

"resnet_utils.py" y "resnet_v1.py" son dos módulos que construyen resnet_v1_50 en el código fuente, y las funciones importantes en su interior se han rodeado con cuadros negros. Block define una clase, 'scope' es el atributo del espacio de nombres, 'unit_fn' es una función que maneja el bloque de unidades en la arquitectura de red y args es su parámetro. "stack_blocks_dense" está procesando bloques ResNet_Block. El "cuello de botella" se ocupa de la parte del cuello de botella de la arquitectura de la red, incluida la parte del "atajo" del documento. "resnet_v1" es la arquitectura principal de "ResNet50". En "resnet_v1_block", Block.unit_fn se asigna como cuello de botella.

Echemos un vistazo a los significados de Bloque, cuello de botella y unidad en el código fuente, como se muestra en la Figura 3 a continuación:

Figura 3 ResNet_Block, cuello de botella y unidad

Un "ResNet_Block" representa el conv2_x en la Tabla 1 del documento original, excluyendo la capa de grupo máximo, que es la parte en el cuadro de la línea de puntos azul en la Figura 3; "cuello de botella" representa el contenido, incluida la curva negra en la línea de puntos verde cuadro; y "unidad" significa la tabla 3*1 en el cuadro rojo punteado. Puede ser más fácil de entender comparando la Tabla 1.

El contenido de "resnet_v1" en el código fuente es el siguiente:

def resnet_v1(inputs,
              blocks,
              num_classes=None,
              is_training=True,
              global_pool=True,
              output_stride=None,
              include_root_block=True,
              spatial_squeeze=True,
              store_non_strided_activations=False,
              reuse=None,
              scope=None):
  with tf.variable_scope(scope, 'resnet_v1', [inputs], reuse=reuse) as sc:
    end_points_collection = sc.original_name_scope + '_end_points'
    with slim.arg_scope([slim.conv2d, bottleneck,
                         resnet_utils.stack_blocks_dense],
                        outputs_collections=end_points_collection):
      with (slim.arg_scope([slim.batch_norm], is_training=is_training)
            if is_training is not None else NoOpScope()):
        net = inputs
        if include_root_block:
          if output_stride is not None:
            if output_stride % 4 != 0:
              raise ValueError('The output_stride needs to be a multiple of 4.')
            output_stride /= 4
          net = resnet_utils.conv2d_same(net, 64, 7, stride=2, scope='conv1')
          net = slim.max_pool2d(net, [3, 3], stride=2, scope='pool1')
        net = resnet_utils.stack_blocks_dense(net, blocks, output_stride,
                                              store_non_strided_activations)
        # Convert end_points_collection into a dictionary of end_points.
        end_points = slim.utils.convert_collection_to_dict(
            end_points_collection)

        if global_pool:
          # Global average pooling.
          net = tf.reduce_mean(net, [1, 2], name='pool5', keep_dims=True)
          end_points['global_pool'] = net
        if num_classes:
          net = slim.conv2d(net, num_classes, [1, 1], activation_fn=None,
                            normalizer_fn=None, scope='logits')
          end_points[sc.name + '/logits'] = net
          if spatial_squeeze:
            net = tf.squeeze(net, [1, 2], name='SpatialSqueeze')
            end_points[sc.name + '/spatial_squeeze'] = net
          end_points['predictions'] = slim.softmax(net, scope='predictions')
        return net, end_points
resnet_v1.default_image_size = 224

El contenido antes de la declaración "net = resnet_utils.stack_blocks_dense(net, blocks, output_stride, store_non_strided_activations)" en el código fuente trata el contenido entre la arquitectura principal de ResNet50, incluida la capa de convolución "conv1" en Table1 y la agrupación de " capa conv2_1"; y esta declaración se ocupa de la arquitectura principal de la red: todas las partes desde "conv2_2" hasta "conv4_x", definidas por el parámetro "bloques". Esta instrucción va seguida de "global_pool", "num_classes" y "spatial_squeeze". Solo cuando "num_classes" no es "Ninguno", "spatial_squeeze" tendrá efecto.

El proceso de llamada de la función representada por Table2 y el valor real de algunos parámetros durante el proceso de llamada de ResNet50. La fuente negra en la parte blanca representa los parámetros formales de la función, mientras que la parte azul representa el valor real del parámetro. Los parámetros formales de "resnet_v1_50()" y "resnet_v1()" no están todos enumerados, pero las partes no enumeradas no afectan la comprensión del código fuente.

El proceso de llamada de la función Table2

"resnet_v1_block()" es la estructura principal, que define 4 bloques "Bloque" de ResNet50, incluido el nombre del bloque, el número de unidades contenidas en cada bloque y el paso correspondiente. La profundidad o profundidad_base es en realidad el número de canales del núcleo en la arquitectura de red. "resnet_v1_block()" llama a "resnet_utils.Block()" durante la ejecución para definir la función unit_fn() en cada bloque de bloque, que es igual a cuello de botella(), y asigna valores a algunos de los parámetros. En "conv2_x" es "[{256,64,1},{256,64,1},{256,64,2}]" Cada parámetro entre llaves corresponde a una unidad. La última línea en Table2 es en realidad la llamada de la función en la penúltima línea, que maneja las cosas específicas en cada "cuello de botella()". Salida después del procesamiento, continúe procesando la siguiente unidad y finalmente regrese a "resnet_v1 ()" para procesar el siguiente bloque. Hasta que se completa el procesamiento de la parte de la estructura principal, es decir, se obtiene el valor de retorno de "net = resnet_utils.stack_blocks_dense(net, blocks, output_stride, store_non_strided_activations)", y finalmente se procesa la parte subsiguiente y la salida de la totalidad se devuelve el programa.

 

Supongo que te gusta

Origin blog.csdn.net/Huang_Fj/article/details/100575180
Recomendado
Clasificación