ApacheMXNet-グルーオン

もう1つの最も重要なMXNetPythonパッケージはGluonです。この章では、このパッケージについて説明します。Gluonは、DLプロジェクトに明確で簡潔でシンプルなAPIを提供します。これにより、Apache MXNetは、トレーニング速度を損なうことなく、DLモデルのプロトタイプ作成、構築、トレーニングを行うことができます。

ブロック

ブロックは、より複雑なネットワーク設計の基礎を形成します。ニューラルネットワークでは、ニューラルネットワークの複雑さが増すにつれて、ニューロンの単一層から全体層の設計に移行する必要があります。たとえば、ResNet-152のようなNN設計は、次のもので構成されているため、非常にかなりの規則性があります。blocks 繰り返される層の。

以下の例では、単純なブロック、つまり多層パーセプトロンのブロックをコードで記述します。

from mxnet import nd
from mxnet.gluon import nn
x = nd.random.uniform(shape=(2, 20))
N_net = nn.Sequential()
N_net.add(nn.Dense(256, activation='relu'))
N_net.add(nn.Dense(10))
N_net.initialize()
N_net(x)

Output

これにより、次の出力が生成されます。

[[ 0.09543004 0.04614332 -0.00286655 -0.07790346 -0.05130241 0.02942038
0.08696645 -0.0190793 -0.04122177 0.05088576]
[ 0.0769287 0.03099706 0.00856576 -0.044672 -0.06926838 0.09132431
0.06786592 -0.06187843 -0.03436674 0.04234696]]
<NDArray 2x10 @cpu(0)>

レイヤーの定義から1つ以上のレイヤーのブロックの定義に進むために必要な手順-

Step 1 −ブロックはデータを入力として受け取ります。

Step 2−これで、ブロックは状態をパラメーターの形式で保存します。たとえば、上記のコーディング例では、ブロックに2つの非表示レイヤーが含まれており、そのパラメーターを格納する場所が必要です。

Step 3−次のブロックは、フォワード関数を呼び出してフォワード伝播を実行します。フォワード計算とも呼ばれます。最初のフォワードコールの一部として、ブロックはパラメータを遅延的に初期化します。

Step 4−最後に、ブロックは後方関数を呼び出し、入力を参照して勾配を計算します。通常、このステップは自動的に実行されます。

シーケンシャルブロック

シーケンシャルブロックは、データが一連のブロックを流れる特殊な種類のブロックです。この場合、各ブロックは前の1つの出力に適用され、最初のブロックは入力データ自体に適用されます。

方法を見てみましょう sequential クラスは動作します-

from mxnet import nd
from mxnet.gluon import nn
class MySequential(nn.Block):
   def __init__(self, **kwargs):
      super(MySequential, self).__init__(**kwargs)

   def add(self, block):
      self._children[block.name] = block
   def forward(self, x):
   for block in self._children.values():
      x = block(x)
   return x
x = nd.random.uniform(shape=(2, 20))
N_net = MySequential()
N_net.add(nn.Dense(256, activation
='relu'))
N_net.add(nn.Dense(10))
N_net.initialize()
N_net(x)

Output

出力はここに与えられます-

[[ 0.09543004 0.04614332 -0.00286655 -0.07790346 -0.05130241 0.02942038
0.08696645 -0.0190793 -0.04122177 0.05088576]
[ 0.0769287 0.03099706 0.00856576 -0.044672 -0.06926838 0.09132431
0.06786592 -0.06187843 -0.03436674 0.04234696]]
<NDArray 2x10 @cpu(0)>

カスタムブロック

上で定義したシーケンシャルブロックとの連結を簡単に超えることができます。ただし、カスタマイズを行う場合は、Blockクラスは、必要な機能も提供します。ブロッククラスには、nnモジュールで提供されるモデルコンストラクターがあります。そのモデルコンストラクターを継承して、必要なモデルを定義できます。

次の例では、 MLP class をオーバーライドします __init__ Blockクラスの関数を転送します。

それがどのように機能するか見てみましょう。

class MLP(nn.Block):

   def __init__(self, **kwargs):
      super(MLP, self).__init__(**kwargs)
      self.hidden = nn.Dense(256, activation='relu') # Hidden layer
      self.output = nn.Dense(10) # Output layer


   def forward(self, x):
      hidden_out = self.hidden(x)
      return self.output(hidden_out)
x = nd.random.uniform(shape=(2, 20))
N_net = MLP()
N_net.initialize()
N_net(x)

Output

コードを実行すると、次の出力が表示されます。

[[ 0.07787763 0.00216403 0.01682201 0.03059879 -0.00702019 0.01668715
0.04822846 0.0039432 -0.09300035 -0.04494302]
[ 0.08891078 -0.00625484 -0.01619131 0.0380718 -0.01451489 0.02006172
0.0303478 0.02463485 -0.07605448 -0.04389168]]
<NDArray 2x10 @cpu(0)>

カスタムレイヤー

ApacheMXNetのGluonAPIには、適度な数の事前定義されたレイヤーが付属しています。しかし、まだある時点で、新しいレイヤーが必要になる場合があります。GluonAPIで新しいレイヤーを簡単に追加できます。このセクションでは、新しいレイヤーを最初から作成する方法を説明します。

最も単純なカスタムレイヤー

Gluon APIで新しいレイヤーを作成するには、最も基本的な機能を提供するBlockクラスから継承するクラスを作成する必要があります。事前定義されたすべてのレイヤーを、直接または他のサブクラスを介して継承できます。

新しいレイヤーを作成するために実装する必要がある唯一のインスタンスメソッドは forward (self, x)。このメソッドは、順伝播中にレイヤーが何をするかを正確に定義します。前にも説明したように、ブロックのバックプロパゲーションパスは、ApacheMXNet自体によって自動的に実行されます。

以下の例では、新しいレイヤーを定義します。また、実装しますforward() 入力データを[0、1]の範囲に当てはめることによって正規化する方法。

from __future__ import print_function
import mxnet as mx
from mxnet import nd, gluon, autograd
from mxnet.gluon.nn import Dense
mx.random.seed(1)
class NormalizationLayer(gluon.Block):
   def __init__(self):
      super(NormalizationLayer, self).__init__()

   def forward(self, x):
      return (x - nd.min(x)) / (nd.max(x) - nd.min(x))
x = nd.random.uniform(shape=(2, 20))
N_net = NormalizationLayer()
N_net.initialize()
N_net(x)

Output

上記のプログラムを実行すると、次の結果が得られます-

[[0.5216355 0.03835821 0.02284337 0.5945146 0.17334817 0.69329053
0.7782702 1. 0.5508242 0. 0.07058554 0.3677264
0.4366546 0.44362497 0.7192635 0.37616986 0.6728799 0.7032008

 0.46907538 0.63514024]
[0.9157533 0.7667402 0.08980197   0.03593295 0.16176797 0.27679572
 0.07331014 0.3905285 0.6513384 0.02713427 0.05523694 0.12147208
 0.45582628 0.8139887 0.91629887 0.36665893 0.07873632 0.78268915
 0.63404864 0.46638715]]
 <NDArray 2x20 @cpu(0)>

ハイブリダイゼーション。

これは、ApacheMXNetが順方向計算のシンボリックグラフを作成するために使用するプロセスとして定義できます。ハイブリダイゼーションにより、MXNetは、計算シンボリックグラフを最適化することにより、計算パフォーマンスを向上させることができます。から直接継承するのではなくBlock実際、既存のレイヤーを実装しているときに、ブロックが HybridBlock

これの理由は次のとおりです-

  • Allows us to write custom layers: HybridBlockを使用すると、命令型プログラミングとシンボリックプログラミングの両方でさらに使用できるカスタムレイヤーを作成できます。

  • Increase computation performance− HybridBlockは、MXNetが計算パフォーマンスを向上できるようにする計算シンボリックグラフを最適化します。

この例では、HybridBlockを使用して、上で作成したサンプルレイヤーを書き直します。

class NormalizationHybridLayer(gluon.HybridBlock):
   def __init__(self):
      super(NormalizationHybridLayer, self).__init__()

   def hybrid_forward(self, F, x):
      return F.broadcast_div(F.broadcast_sub(x, F.min(x)), (F.broadcast_sub(F.max(x), F.min(x))))

layer_hybd = NormalizationHybridLayer()
layer_hybd(nd.array([1, 2, 3, 4, 5, 6], ctx=mx.cpu()))

Output

出力は以下のとおりです。

[0. 0.2 0.4 0.6 0.8 1. ]
<NDArray 6 @cpu(0)>

ハイブリダイゼーションはGPUでの計算とは何の関係もなく、CPUとGPUの両方でハイブリッドネットワークと非ハイブリッドネットワークをトレーニングできます。

BlockとHybridBlockの違い

比較すると Block クラスと HybridBlock、私たちはそれを見るでしょう HybridBlock すでに forward() メソッドが実装されました。 HybridBlock を定義します hybrid_forward()レイヤーの作成中に実装する必要があるメソッド。F引数は、forward() そして hybrid_forward()。MXNetコミュニティでは、F引数はバックエンドと呼ばれます。Fはどちらかを参照できますmxnet.ndarray API (命令型プログラミングに使用)または mxnet.symbol API (シンボリックプログラミングに使用)。

ネットワークにカスタムレイヤーを追加するにはどうすればよいですか?

カスタムレイヤーを個別に使用する代わりに、これらのレイヤーは事前定義されたレイヤーで使用されます。どちらでも使用できますSequential または HybridSequentialシーケンシャルニューラルネットワークからのコンテナ。前にも説明したように、Sequential コンテナはブロックから継承し、 HybridSequential から継承 HybridBlock それぞれ。

以下の例では、カスタムレイヤーを使用して単純なニューラルネットワークを作成します。からの出力Dense (5) レイヤーはの入力になります NormalizationHybridLayer。の出力NormalizationHybridLayer の入力になります Dense (1) 層。

net = gluon.nn.HybridSequential()
with net.name_scope():
net.add(Dense(5))
net.add(NormalizationHybridLayer())
net.add(Dense(1))
net.initialize(mx.init.Xavier(magnitude=2.24))
net.hybridize()
input = nd.random_uniform(low=-10, high=10, shape=(10, 2))
net(input)

Output

次の出力が表示されます-

[[-1.1272651]
 [-1.2299833]
 [-1.0662932]
 [-1.1805027]
 [-1.3382034]
 [-1.2081106]
 [-1.1263978]
 [-1.2524893]
 
 [-1.1044774]

 [-1.316593 ]]
<NDArray 10x1 @cpu(0)>

カスタムレイヤーパラメーター

ニューラルネットワークでは、レイヤーには一連のパラメーターが関連付けられています。レイヤーの内部状態であるウェイトと呼ばれることもあります。これらのパラメータは異なる役割を果たします-

  • これらは、バックプロパゲーションのステップで学習したいものです。

  • これらは、フォワードパス中に使用したい定数である場合があります。

プログラミングの概念について話すと、ブロックのこれらのパラメーター(重み)は保存され、 ParameterDict それらの初期化、更新、保存、およびロードを支援するクラス。

以下の例では、次の2つのパラメーターセットを定義します。

  • Parameter weights−これは訓練可能であり、その形状は建設段階では不明です。これは、順伝播の最初の実行時に推測されます。

  • Parameter scale−これは値が変化しない定数です。パラメータの重みとは反対に、その形状は構築中に定義されます。

class NormalizationHybridLayer(gluon.HybridBlock):
   def __init__(self, hidden_units, scales):
      super(NormalizationHybridLayer, self).__init__()
      with self.name_scope():
      self.weights = self.params.get('weights',
      shape=(hidden_units, 0),
      allow_deferred_init=True)
      self.scales = self.params.get('scales',
         shape=scales.shape,
         init=mx.init.Constant(scales.asnumpy()),
         differentiable=False)
      def hybrid_forward(self, F, x, weights, scales):
         normalized_data = F.broadcast_div(F.broadcast_sub(x, F.min(x)),
         (F.broadcast_sub(F.max(x), F.min(x))))
         weighted_data = F.FullyConnected(normalized_data, weights, num_hidden=self.weights.shape[0], no_bias=True)
         scaled_data = F.broadcast_mul(scales, weighted_data)
return scaled_data