blog

CVPR2018 Relation Net解析の全容をフライング・パドルで再現する

フライング・パドルの開発者は、東京航空大学大学院修士課程修了、マシンビジョン・アルゴリズム・エンジニアのXingyu Tongと言います。\n\nRelation NetはCVPR2018の論文です:...

Dec 25, 2020 · 10 min. read
シェア

Relation NetはCVPR 2018の論文です:

arxiv.org/pdf/1711.06...

例えば、絵や本からシマウマを認識した子供や、シマウマが「縞模様の馬」と説明されているのを聞いただけの子供は、難なくシマウマを識別することができます。ディープラーニングでは、モデルのサンプルサイズが小さいために分類結果が悪くなるという問題を解決するために、人間の少サンプル・無サンプル学習能力にヒントを得て、少サンプル学習が再び注目を集めています。

下図は、5-way 1-shotで1サンプルを受け入れる場合のネットワーク構造とフローです。サンプルセットの5つのイメージとクエリセットの1つのイメージが抽出され、埋め込みモデルによってスプライスされ、5つの新しい特徴マップが得られます。その後、Relation Netに供給して関係スコアを計算し、最終的にワンショットベクトルが得られ、最もスコアの高いものが対応するカテゴリを表します。

学習に用いる損失関数も比較的単純で、平均二乗誤差を損失関数として用います。式中、ri,jはイメージiとjの類似度を表し、yiとyjはイメージの実ラベルを表します。

プロペラの複製に基づく

関係ネットワーク

再現の技術的な詳細については、開発者の皆さんと共有することにして、Relation Networkモデルの構造定義をご覧ください:

github.com/pa...

1.関係ネットワークの構築

このモデルは埋め込みモデルと関係モデルの2つの部分から構成され、どちらのネットワークも主にConv+BN+Reluモジュールで構成されています。そこで、まずBaseNetクラスを定義し、その中にconv_bn_layerメソッドを以下のコードで実装します:

class BaseNet:
 def conv_bn_layer(self,
 input,
 num_filters,
 filter_size,
 stride=1,
 groups=1,
 padding=0,
 act=None,
 name=None,
 data_format='NCHW'):
 n = filter_size * filter_size * num_filters
 conv = fluid.layers.conv2d(
 input=input,
 num_filters=num_filters,
 filter_size=filter_size,
 stride=stride,
 padding=padding,
 groups=groups,
 act=None,
 param_attr=ParamAttr(name=name + "_weights", initializer=fluid.initializer.Normal(0,math.sqrt(2\. / n))),
 bias_attr=ParamAttr(name=name + "_bias",
 initializer=fluid.initializer.Constant(0.0)),
 name=name + '.conv2d.output.1',
 data_format=data_format)
 bn_name = "bn_" + name
 return fluid.layers.batch_norm(
 input=conv,
 act=act,
 momentum=1,
 name=bn_name + '.output.1',
 param_attr=ParamAttr(name=bn_name + '_scale',
 initializer=fluid.initializer.Constant(1)),
 bias_attr=ParamAttr(bn_name + '_offset',
 initializer=fluid.initializer.Constant(0)),
 moving_mean_name=bn_name + '_mean',
 moving_variance_name=bn_name + '_variance',
 data_layout=data_format)

Flying Paddleは、静的マップと動的マップの2つのネットワーク定義モードをサポートしており、ここでは静的マップを選択しました。上のコードでは、畳み込みニューラルネットワークで最も頻繁に使われる層であるconv_bn層を定義していますが、batch_norm層の運動量が1に設定されていることに注意してください。

具体的なパラメータの意味は以下の通り:

  • 入力: 畳み込みたいテンソルオブジェクトを渡します;

  • num_filter畳み込みカーネルの数;

  • filter_size畳み込みカーネルのサイズ;

  • stride畳み込みステップサイズ

  • groups: グループ化された畳み込みのグループ数;

  • padding: パディングサイズ。ここでは0に設定し、畳み込みが埋められないようにします;

  • act:BN層後の活性化関数。Noneの場合、活性化関数は使用されません;

  • name: オペレーター・ダイアグラム内のオブジェクトの名前。

次に、関係ネットワークの埋め込みモデル部分を定義します。

class EmbeddingNet(BaseNet):
 def net(self,input):
 conv = self.conv_bn_layer(
 input=input,
 num_filters=64,
 filter_size=3,
 padding=0,
 act='relu',
 name='embed_conv1')
 conv = fluid.layers.pool2d(
 input=conv,
 pool_size=2,
 pool_stride=2,
 pool_type='max')
 conv = self.conv_bn_layer(
 input=conv,
 num_filters=64,
 filter_size=3,
 padding=0,
 act='relu',
 name='embed_conv2')
 conv = fluid.layers.pool2d(
 input=conv,
 pool_size=2,
 pool_stride=2,
 pool_type='max')
 conv = self.conv_bn_layer(
 input=conv,
 num_filters=64,
 filter_size=3,
 padding=1,
 act='relu',
 name='embed_conv3')
 conv = self.conv_bn_layer(
 input=conv,
 num_filters=64,
 filter_size=3,
 padding=1,
 act='relu',
 name='embed_conv4')
 return conv

上記のコードでは、BaseNetクラスを継承するEmbeddingNetクラスを作成し、conv_bn_layerメソッドを継承します。入力イメージテンソルを表すパラメータinputを持つnetメソッドをEmbeddingNetに定義します。

入力は、まずConv + BN + reluモジュールを介して特徴マップembed_conv1を取得し、次に最大プーリング操作、プーリングの役割は、特徴マップを縮小する前提の下で重要な特徴を保持することです、同じ役割の背後にある畳み込みとプーリング操作の役割;embed_conv4形状の特徴マップの最終的な出力は、[-1,64、19,19]4次元の合計、静的なネットワークを作成するときにbatch_sizeが不確実であるため、最初の緯度は、batch_sizeを表します。19,19]は4次元で、最初の緯度はbatch_sizeを表し、静的ネットワークの作成時にbatch_sizeが不確実であるため、-1を含む任意の値になります。最後に、3番目と4番目の次元は、特徴マップの幅と高さを表し、ここでは19x19です。

Relation model codeセクションは以下の通り:

class RelationNet(BaseNet):
 def net(self, input, hidden_size):
 conv = self.conv_bn_layer(
 input=input,
 num_filters=64,
 filter_size=3,
 padding=0,
 act='relu',
 name='rn_conv1')
 conv = fluid.layers.pool2d(
 input=conv,
 pool_size=2,
 pool_stride=2,
 pool_type='max')
 conv = self.conv_bn_layer(
 input=conv,
 num_filters=64,
 filter_size=3,
 padding=0,
 act='relu',
 name='rn_conv2')
 conv = fluid.layers.pool2d(
 input=conv,
 pool_size=2,
 pool_stride=2,
 pool_type='max')
 fc = fluid.layers.fc(conv,size=hidden_size,act='relu',
 param_attr=ParamAttr(name='fc1_weights',
 initializer=fluid.initializer.Normal(0,0.01)),
 bias_attr=ParamAttr(name='fc1_bias',
 initializer=fluid.initializer.Constant(1)),
 )
 fc = fluid.layers.fc(fc, size=1,act='sigmoid',
 param_attr=ParamAttr(name='fc2_weights',
 initializer=fluid.initializer.Normal(0,0.01)),
 bias_attr=ParamAttr(name='fc2_bias',
 initializer=fluid.initializer.Constant(1)),
 )
 return fc

BaseNetクラスからconv_bn_layerメソッドを継承したRelationNetクラスを作成します。ネットメソッドでは、モデルの最初の数層は特徴抽出のためにConv+BN+Reluモジュールを使ったembedingモデルと同様で、最後に2つの完全連結層を使って特徴値を2つのイメージの類似度を表すスカラー関係スコアにマッピングします。

学習プロセスでは、サンプルセットとクエリセットのイメージを埋め込みモデルで埋め込み、[-1,64,19,19]の形状の特徴マップを得ます。

sample_image = fluid.layers.data('sample_image', shape=[3, 84, 84], dtype='float32')
query_image = fluid.layers.data('query_image', shape=[3, 84, 84], dtype='float32')
sample_query_image = fluid.layers.concat([sample_image, query_image], axis=0)
sample_query_feature = embed_model.net(sample_query_image)

sample_batch_size = fluid.layers.shape(sample_image)[0]
query_batch_size = fluid.layers.shape(query_image)[0]

この部分では、イメージテンソルの0次元をbatch_sizeとします。

sample_feature = fluid.layers.slice(
 sample_query_feature,
 axes=[0],
 starts=[0],
 ends=[sample_batch_size])
if k_shot > 1:
# few_shot
 sample_feature = fluid.layers.reshape(sample_feature, shape=[c_way, k_shot, 64, 19, 19])
 sample_feature = fluid.layers.reduce_sum(sample_feature, dim=1)
query_feature = fluid.layers.slice(
 sample_query_feature,
 axes=[0],
 starts=[sample_batch_size],
 ends=[sample_batch_size + query_batch_size])

イメージはスプライスされるので、フィーチャーの後、sample_featureとquery_featureをsample_query_featureのbatch_sizeに対応する0次元でスライスする必要もあります。ここで、K-shotが1より大きい場合は、sample_featureの形状を変更する必要があります。ここで、K-shotが1より大きい場合、sample_featureの形状を変更する必要があり、K-shotに対応する1次元のK-shotテンソルを合計し、その次元を削除すると、sample_featureの形状は[C-way,64,19,19]となります。sample_batch_size の値は C-way とします。

sample_feature_ext = fluid.layers.unsqueeze(sample_feature, axes=0)
query_shape = fluid.layers.concat(
 [query_batch_size, fluid.layers.assign(np.array([1, 1, 1,1]).astype('int32'))])
sample_feature_ext = fluid.layers.expand(sample_feature_ext, query_shape)

サンプルセットの各イメージ特徴量はC型のイメージ特徴量とスプライスする必要があるため、ここでunsqueezeによって新たな次元が追加されます。expandインタフェースのパラメータ要求に従って、新しいquery_shapeテンソルが作成され、 sample_featureテンソルquery_batch_sizeを複製して、 [query_batch_size, sample_batch_size, 64, 19, 19]の形状のテンソルを得ます。

query_feature_ext = fluid.layers.unsqueeze(query_feature, axes=0)
if k_shot > 1:
sample_batch_size = sample_batch_size / float(k_shot)
sample_shape = fluid.layers.concat(
 [sample_batch_size, fluid.layers.assign(np.array([1, 1, 1, 1]).astype('int32'))])
query_feature_ext = fluid.layers.expand(query_feature_ext, sample_shape)

上記と同様に、クエリセットの特徴を新しい次元に追加する必要があり、ここでsample_batch_sizeを数回コピーする必要があります。注目すべきは、k-shotが1より大きい場合、reduce_mean操作が既に行われているため、sample_batch_sizeをk-shotで割って新しいsample_batch_sizeを求め、最後にコピーすることで[sample_batch_size, query_batch_size, 64, 19, 19]のテンソルが得られることです。batch_size, 64, 19, 19] のテンソルが得られます。

query_feature_ext = fluid.layers.transpose(query_feature_ext, [1, 0, 2, 3, 4])
relation_pairs = fluid.layers.concat([sample_feature_ext, query_feature_ext], axis=2)
relation_pairs = fluid.layers.reshape(relation_pairs, shape=[-1, 128, 19, 19])

最後にsample_feature_extとquery_feature_extが同じ形状になるようにtranspose法で転置し、最後に2つの特徴量の形状をスプライスして修正し、[query_batch_size x sample_batch_size, 128, 19, 19]の形状のtensor relation_pairsを得ます。query_batch_size x sample_batch_size, 128, 19, 19]のテンソルrelation_pairs。

relation = RN_model.net(relation_pairs, hidden_size=8)
relation = fluid.layers.reshape(relation, shape=[-1, c_way]) 

損失関数のコードは以下の通り:

one_hot_label = fluid.layers.one_hot(query_label, depth=c_way)
loss = fluid.layers.square_error_cost(relation, one_hot_label)
loss = fluid.layers.reduce_mean(loss)

2.トレーニング戦略

FSLタスクでは、サポート集合のみを学習に使用した場合、テスト集合に対する推論予測も可能ですが、サポート集合のサンプル数が比較的少ないため、分類器の性能は一般的に悪くなります。したがって,分類器の性能が向上するように,一般に訓練集合が訓練に用いられます.ここで,エピソードベースの学習と呼ばれる効果的な手法を紹介します.

エピソードに基づくトレーニングの実施手順は以下の通りです:

  1. Cカテゴリの残りのサンプルの中からランダムに数サンプルをクエリセットとして選択し、学習させます。

学習オプティマイザにはAdam optimisationが選択され、学習率は0.001に設定されました。データ増強には、データの多様性を高めるために、データ読み取り時にサンプルセットとクエリセットの両方のイメージにAutoAugmentが使用されました。

3.再現モデルの検証

検証用データセットは、本論文の実験に使用したminImageNetのみを使用し、100カテゴリ、各カテゴリ600イメージ。100のカテゴリはトレーニング/検証/テストの3つのデータセットに分けられ、それぞれ64、16、20の数です。

この記事では、minImageNetのテストデータセットにおけるモデルの精度について、以下のように言及しています:

5方向1ショットで約50.44、5方向5ショットで約65.32の精度を達成。また、フライングパドルベースのリレーションネットの実装をminImageNetのテストデータセットに使用すると

5ウェイ1ショットの精度:

5ウェイ5ショットの精度:

結果は論文の精度と一致しており、モデルの再現は完璧です。

コードアドレス

github.com/pa...

もし使用中に問題があれば、フライングパドルの公式QQグループ:1108045677に参加することができます。

空飛ぶプロペラについて詳しくお知りになりたい方は、以下の資料をご参照ください。

公式ホームページアドレス

フライングパドルオープンソースフレームワーク:

GitHub。

ギテー。

gitee.com/paddlepaddl...

終了

Read next