resnet_v1_50 ソース コードの理解と分析

ソースコードのリンク:
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. TensorFlow での resnet_v1_50 の使用法

まず使い方をまとめると、ソースコード中のresnet_v1_50のパラメータは以下の通りです。

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'):

の:

  • 入力: トレーニング セット、形式は [バッチ、高さ_入力、幅_入力、チャンネル]
  • num_classes: サンプルの種類の数。上位層のノードの数を定義するために使用されます。「None」の場合、最終出力は [batch,1,1,2048] になる必要があります。「spatial_stride=True」の場合、最終出力は [batch,2048] になります。
  • is_training: 学習モデルに「Batch_Norm」レイヤーを追加するかどうか
  • global_pool: この層は、ネットワーク構造全体の後、「num_classes」の前に位置します。「True」は、ネットワークの最後の「ネット」層の出力に対してグローバル平均プーリングを実行することを意味します。いわゆるグローバル プーリングとは、プールされたストライドが入力サイズと等しく、スカラーが取得されることを意味します。
  • spatial_squeeze: spatial_squeeze([B,1,1,C])=[B,C] など、リスト内の 1 に等しい次元を削除します。
  • store_non_strided_activations: マルチスケールの画像処理に役立ち、さまざまなサイズの出力を保存できます。

簡単に言うと、上記のモジュールをインポートした後、主にトレーニングセット「input」とカテゴリ数「num_classes」を入力して、ResNet50 のネットワーク構造を構築しました。「num_classes=None」の場合、2048 などの非常に高次元の画像の特徴のみを抽出できる特徴抽出器のネットワーク アーキテクチャを構築します。「num_classes=10」の場合、「入力」することを意味します。 「」のデータは 10 のカテゴリに分割されており、ネットワーク アーキテクチャの最後の層の出力は 10 次元のベクトルであり、画像が特定のカテゴリに属する​​確率を表すことができます。

2. resnet_v1_50 ソースコード構築の枠組み

「resnet_utils.py」と「resnet_v1.py」は、ソースコード内でresnet_v1_50を構築する2つのモジュールであり、内部の重要な関数は黒いボックスで囲まれています。Block はクラスを定義し、「scope」は名前空間属性、「unit_fn」はネットワーク アーキテクチャ内の単位ブロックを処理する関数、args はそのパラメータです。「stack_blocks_dense」は ResNet_Block ブロックを処理しています。「ボトルネック」では、論文の「ショートカット」部分を含む、ネットワーク アーキテクチャのボトルネック部分を扱います。「resnet_v1」は「ResNet50」の主要なアーキテクチャです。「resnet_v1_block」ではBlock.unit_fnがボトルネックとして割り当てられています。

以下の図 3 に示すように、ソース コード内のブロック、ボトルネック、ユニットの意味を見てみましょう。

図 3 ResNet_Block、ボトルネック、ユニット

「ResNet_Block」は原論文の表1のconv2_xから最大プール層を除いた部分(図3の青い点線枠の部分)を表し、「ボトルネック」は緑の点線の黒い曲線を含む内容を表します。 「ユニット」は赤い点線のボックス内の 3*1 テーブルを意味します。表1と比較してみるとわかりやすいと思います。

ソースコードの「resnet_v1」の内容は以下のとおりです。

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

ソース コードのステートメント「net = resnet_utils.stack_blocks_dense(net, blocks,output_stride, store_non_strided_activations)」より前の内容は、Table1 の「conv1」畳み込み層と「 conv2_1" 層; このステートメントは、ネットワークの主要なアーキテクチャ、つまりパラメーター "blocks" によって定義される "conv2_2" から "conv4_x" までのすべての部分を扱います。このステートメントの後には、「global_pool」、「num_classes」、「spatial_squeeze」が続きます。「num_classes」が「None」でない場合にのみ、「spatial_squeeze」が有効になります。

表 2 で表される関数の呼び出しプロセスと、ResNet50 の呼び出しプロセス中の一部のパラメーターの実際の値。白い部分の黒いフォントは関数の仮パラメータを表し、青い部分はパラメータの実際の値を表します。「resnet_v1_50()」と「resnet_v1()」の仮パラメータはすべて記載されておりませんが、記載されていない部分はソースコードの理解に影響しません。

Table2関数の呼び出し処理

「resnet_v1_block()」は、ResNet50 の 4 つの「Block」ブロックを定義するメイン構造で、ブロックの名前、各ブロックに含まれるユニットの数、対応するストライドが含まれます。ネットワーク アーキテクチャ内のカーネルのチャネル数。「resnet_v1_block()」は、実行中に「resnet_utils.Block()」を呼び出して、bottleneck()と同等の各ブロック内のunit_fn()関数を定義し、一部のパラメータに値を代入します。 is "[{256,64,1},{256,64,1},{256,64,2}]" 中括弧内の各パラメーターは単位に対応します。表 2 の最後の行は、実際には最後から 2 番目の行にある関数の呼び出しであり、各「bottleneck()」の特定のものを処理します。処理後に出力し、次のユニットの処理を続け、最後に「resnet_v1()」に戻って次のブロックを処理します。主要構造部分の処理が完了するまで、つまり「net = resnet_utils.stack_blocks_dense(net,blocks,output_stride,store_non_strided_activations)」の戻り値が得られ、最後に後続部分の処理が行われ、全体の出力が得られます。プログラムが返されます。

 

おすすめ

転載: blog.csdn.net/Huang_Fj/article/details/100575180