Amazon Web Services ブログ

柔軟性の高いディープラーニングのために簡単に使用できるプログラミングインターフェイス Gluon のご紹介

本日は、AWS と Microsoft が、どのディープラーニングフレームワークを選択するかにかかわらず、すべての開発者向けに機械学習テクノロジーの速度、柔軟性、アクセス性を向上させることを主眼とした新しい仕様を発表しました。この連携による最初の結果が、新しい Gluon インターフェイスです。これはあらゆるスキルレベルの開発者がディープラーニングモデルのプロトタイプ作成、構築、トレーニングを行えるようにする、Apache MXNet のオープンソースライブラリです。このインターフェイスにより、トレーニング速度を犠牲にすることなく、ディープラーニングモデルの作成プロセスを大幅に簡略化できます。

Gluon の 4 つの重要な利点と、それを示すサンプルコードを示します。

(1) シンプルで理解しやすいコード

Gluon では、シンプル、明瞭、簡潔なコードを使ってニュートラルネットワークを定義できます。事前定義されたレイヤー、オプティマイザ、イニシャライザを含む、プラグアンドプレイのニュートラルネットワーク構築要素のフルセットを入手できます。これにより、基盤となる複雑な実装詳細の多くが排除されます。次の例では、わずか数行のコードでシンプルなニュートラルネットワークを定義する方法を示しています。

# 最初のステップはモデルの初期化です
net = gluon.nn.Sequential()
# Then, define your model architecture
with net.name_scope():
    net.add(gluon.nn.Dense(128, activation="relu")) # 最初のレイヤー - 128 ノード
    net.add(gluon.nn.Dense(64, activation="relu")) # 2 番目のレイヤー – 64 ノード
    net.add(gluon.nn.Dense(num_outputs)) # Output layer

次の図に、ニュートラルネットワークの構造を示します。

詳細については、こちらのウォークスルーに移動して、Gluon ニュートラルネットワーク構成要素を使って multilayer perceptron (MLP) と呼ばれるシンプルなニュートラルネットワークを作成する方法を参照してください。より高度なユースケース向けに、ニュートラルネットワークのパーツをゼロから作成することも簡単です。Gluon では、ニュートラルネットワークで定義済みのカスタムコンポーネントを組み合わせて利用することができます。

(2) 柔軟性の高い構造

ニュートラルネットワークモデルのトレーニングは計算コストが高く、場合によっては数日または数週間もかかることがあります。多くのディープラーニングフレームワークでは、モデルを厳格に定義し、それをトレーニングアルゴリズムから分離することにより、この時間を短縮しています。この厳格な手法では多くの複雑さが加わるとともに、デバッグが困難になります。

Gluon のアプローチはこれとは異なります。トレーニングアルゴリズムとニュートラルネットワークモデルを組み合わせて、パフォーマンスを犠牲にすることなく開発プロセスに柔軟性をもたらします。このアプローチの中心となるのが Gluon の trainer メソッドで、これはモデルのトレーニングに使用されます。 trainer メソッドは MXNet によって決まります。 autograd ライブラリは、派生 (グラデーションなど) を自動的に計算するために使用されます。導関数とは、変数の変更率を測定するための数値計算です。これはトレーニングアルゴリズムに必要な入力です。 autograd ライブラリは、これらの数値計算を効率的に実装し、Gluon の柔軟性を実現するために不可欠です。トレーニングアルゴリズムを定義して、ネストされたシンプルな for ループを定義できるようになりました。そのためには、 autograd および trainerを組み込みます。

epochs = 10

for e in range(epochs):
    for i, batch in enumerate(train_data):
        data = batch.data[0]
        label = batch.label[0]
        with autograd.record(): # 派生の記録を開始
            output = net(data) # 前方反復
            loss = softmax_cross_entropy(output, label)
            loss.backward()
        trainer.step(data.shape[0])

この柔軟性のある構造により、コードが直観的になり、簡単にデバッグできるとともに、より高度なモデルのための扉を開きます。使い慣れたネイティブな Python 言語構造 ( for ループや if ステートメントなど) をニュートラルネットワーク内またはアルゴリズムの一部として使用できます。モデルとアルゴリズムを組み合わせることで、モデル内の各コード行が実行され、バグを発生させている特定の行の識別が簡単になります。

(3) 動的グラフ

特定のシナリオでは、トレーニングプロセス中にニュートラルネットワークモデルの形とサイズの変更が必要になる場合があります。これが特に必要となるのは、ニュートラルネットワークにフィードされたデータ入力が可変である場合です。これは、入力された各文の長さが異なる可能性がある自然言語処理 (NLP) において一般的です。Gluon では、ニュートラルネットワークの定義を動的にすることができます。つまり、オンザフライで、必要な構造で Python のネイティブな制御フローを使用して構築できます。

たとえば、これらの動的なニュートラルネットワーク構造により、2015 年に Kai Sheng Tai 氏、Richard Socher 氏、Chris Manning 氏によって導入された NLP での主要な開発である、ツリー構造の Long Short-Term Memory (LSTM) モデルをより簡単に構築できます。ツリー LSTM は、文のペアに同じ意味があるかどうかを確認できる強力なモデルです。両方の文が基本的に同じ意味である、次の例を考えてみます。

  • 「マイケルは選手の先頭でフットボールを投げた。」
  • 「ボールはマイケルによって投げられ、目標に届かなかった。」

繰り返しのニュートラルネットワーク (1 つの一般的なシーケンス学習モデル) で文をフィードし、分類を行うことは可能です。ただし、ツリー LSTM の主な洞察によれば、多くの場合、以前の知識を使用することで言語の問題が発生しています。たとえば、文は文法構造を示し、この構造を文から抽出するための強力なツールがあります。ツリー構造のニュートラルネットワークで単語をつなげることができ、その構造により文の既知の文法ツリー構造が再現されます。これを次の図に示します。

(スタンフォード自然言語処理グループ)

例ごとに異なるニュートラルネットワーク構造をオンザフライで構築する必要があります。従来のフレームワークでこれを行うことは困難ですが、Gluon では問題なく処理できます。次のコードスニペットでは、モデルトレーニングのそれぞれの前方反復にループを組み込み、 autograd および trainer の簡素化から利点を得る方法を示しています。これにより、モデルは文のツリー構造を使用でき、その構造に基づいた学習が可能になります。

def forward(self, F, inputs, tree):
    children_outputs = [self.forward(F, inputs, child)
                        for child in tree.children]
    #モデルの定義およびトレーニングプロセス中に各入力文の
    #構文構造に基づいてニュートラルネットワークを作成
    …

(4) 高性能

Gluon が提供する柔軟性により、ニュートラルネットワークモデルのプロトタイプ作成と実験が簡単になります。次に、柔軟性よりも速度が重要になる場合 (すべてのトレーニングデータをフィードする準備ができた場合など)、Gluon インターフェイスにより簡単にニュートラルネットワークモデルをキャッシュし、高いパフォーマンスを実現してメモリフットプリントを減らすことができます。これにより、プロトタイプ作成を完了し、大きなデータセットでテストする準備ができた後でニュートラルネットワークをセットアップするときに、微調整のみで済みます。前に説明したように、 Sequential を使用してニュートラルネットワークレイヤーをスタックする代わりに、 HybridSequential を使用する必要があります。その機能は Sequential と同じですが、基になる最適化エンジンを呼び出して、モデルのアーキテクチャの一部またはすべてを表すことができます。

net = nn.HybridSequential()
with net.name_scope():
    net.add(nn.Dense(128, activation="relu")) # 最初のレイヤー - 128 ノード
    net.add(nn.Dense(64, activation="relu")) # 2 番目のレイヤー – 64 ノード
    net.add(nn.Dense(10)) # 出力レイヤー

次に、 HybridSequential をコンパイルして最適化するには、その hybridize メソッドを呼び出します。

net.hybridize()

これで、モデルをトレーニングするときに、ネイティブ MXNet インターフェイスでほぼ同じ高いパフォーマンスを得て、メモリ使用量を減らすことができます。

Gluon の使用開始

Gluon の使用を開始するには、最新バージョンの MXNet のインストールの簡単なステップに従うか、クラウド上でディープラーニング Amazon Machine Image (AMI) を起動できます。次に、前に説明したさまざまなコンポーネントを使用して、multilayer perceptron と呼ばれる簡単な 2 レイヤーのニュートラルネットワークを構築およびトレーニングする方法を説明します。Python バージョン 3.3 以降を使用し、Jupyter ノートブックを使用してこの例を実装することをお勧めします。

最初に、MXNet をインポートして gluon ライブラリを、他の必要なライブラリ ( autograd および ndarray) に加えて入手します。

import mxnet as mx
from mxnet import gluon, autograd, ndarray

次に、データを取得し、前処理を実行します。一般的に使用される MNIST データセットをインポートします。これには、手書きの数字のイメージの大きなコレクションと、イメージの正しいラベルが含まれます。また、画像を配列にリシェイプして簡単な処理を可能にし、配列を MXNet ネイティブな NDArray オブジェクトクラスに変換します。

# ネイティブ MXNet ユーティリティ関数を使用して MNIST をインポートする
data = mx.test_utils.get_mnist()

# トレーニングデータをセットアップして画像をリシェイプする
train_data = data['train_data'].reshape((-1, 784))
train_label = data['train_label']

# テストデータをセットアップして画像をリシェイプする
test_data = data['test_data'].reshape((-1, 784))
test_label = data['test_label']

# データを NDArrays に変換する
train_data_mx = mx.nd.array(train_data)
train_label_mx = mx.nd.array(train_label)
test_data_mx = mx.nd.array(test_data)
test_label_mx = mx.nd.array(test_label)

次に、イテレーターを作成してトレーニングデータを保持します。イテレーターは、大きなデータセットを走査するための有益なオブジェクトクラスです。その前に、最初にバッチサイズを設定する必要があります。それにより、トレーニングアルゴリズムの反復中にニュートラルネットワークが処理するデータの量が定義されます。この場合は 32 です。

batch_size = 32
train_data = mx.io.NDArrayIter(train_data_mx, train_label_mx, batch_size,  
                                 shuffle=True)           

ここで、実際のニュートラルネットワークを定義できます。2 つのレイヤーを作成します。最初のレイヤーは 128 ノードで、2 番目は 64 ノードです。これらは両方とも、rectified linear unit (ReLU) と呼ばれるアクティベーション関数を組み込んでいます。アクティベーション関数が重要なのは、これによりモデルが入力と出力間で非線形のリレーションシップを表すことができるためです。また、可能な出力の合計数に相当するノード数で出力レイヤーをセットアップする必要もあります。MNIST のケースでは、可能な出力は 10 件のみです。これは、画像が数値を表し、それらは 10 個のみ (0~9) であるためです。

# 最初のステップはモデルの初期化です
net = gluon.nn.Sequential()
# Then, define your model architecture
with net.name_scope():
    net.add(gluon.nn.Dense(128, activation="relu")) # 最初のレイヤー - 128 ノード
    net.add(gluon.nn.Dense(64, activation="relu")) # 2 番目のレイヤー – 64 ノード
    net.add(gluon.nn.Dense(10)) # 外部レイヤー

モデルのトレーニングプロセスを開始する前に、モデルのパラメータを初期化し、損失関数およびモデルオプティマイザ関数をセットアップする必要があります。

# 標準偏差 0.05 の正規分布からモデルのすべてのパラメータの
# ランダム値で開始します
net.collect_params().initialize(mx.init.Normal(sigma=0.05))

# ソフトマックスクロスエントロピー関数を使用して 
# モデルが正しい答えをどれだけ予測できるか測定します
softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()

# 確率的勾配降下法 (SGD) トレーニングアルゴリズムを使用して
# 学習レートのハイパーパラメータを .1 に設定します
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': .1})

ここでモデルのトレーニングアルゴリズムを定義します。反復ごとに、4 つのステップがあります。(1) データのバッチを渡す。(2) ニュートラルネットワークモデルと実際値 (損失など) により生成された出力の差を計算する。(3) autograd を使用して、損失への影響に関してモデルのパラメータの派生を計算する。(4) trainer メソッドを使用して、損失を減らす方法でパラメータを最適化する。epoch の数を 10 に設定します。つまり、トレーニングデータセットを 10 回繰り返します。

epochs = 10

for e in range(epochs):
    for i, batch in enumerate(train_data):
        data = batch.data[0]
        label = batch.label[0]
        with autograd.record(): # 派生の記録を開始
            output = net(data) # 前方反復
            loss = softmax_cross_entropy(output, label)
            loss.backward()
        trainer.step(data.shape[0])

これでニュートラルネットワークモデルをトレーニングしたので、確保しておいたテストデータを使用して正確さを見てみましょう。予測された値を実際の値と比較して正確性を計算します。

acc = mx.metric.Accuracy()# 正確性メトリクスを初期化する
output = net(test_data_mx) # ニュートラルネットワーク全体でテストデータを実行する
predictions = ndarray.argmax(output, axis=1) # テストデータの予測
acc.update(preds=predictions, labels=test_label_mx) # 正確性を計算する
print(acc)

Gluon インターフェイスとディープラーニングの詳細については、この包括的な一連のチュートリアルを参照できます。これらのチュートリアルでは、ディープラーニングの概要から最先端のニュートラルネットワークモデルを実装する方法まで紹介しています。


今回のブログの投稿者について

Vikram Madan は AWS Deep Learning の上級製品マネージャーです。特にオープンソースの Apache MXNet エンジンに注目し、ディープラーニングエンジンを使いやすくする製品を担当しています。長距離のランニングやドキュメンタリー鑑賞が趣味です。