JULY 2018

VOLUME 33 NUMBER 7

テストの実行 - CNTK を使用した DNN による画像分類の紹介

によってJames McCaffrey

James McCaffrey に心より感謝いたします。画像の分類は、たとえば"apples"または"oranges"または"bananas"を含む 1 つとして、写真を識別する入力イメージが属しているカテゴリは何を決定する必要があります。 画像の分類の 2 つの最も一般的なアプローチは、標準のディープ ニューラル ネットワーク (DNN) を使用してまたは、畳み込みニューラル ネットワーク (CNN) を使用しています。この記事で後ほど説明します、DNN の方法、CNTK ライブラリを使用します。

今回の目標を理解するため、図 1 をご覧ください。デモ プログラムは、National Standards Institute の変更とテクノロジ (MNIST) のデータセットのサブセットのイメージ分類モデルを作成します。デモのトレーニング データセットは、手書きの数字の 1,000 個のイメージで構成されます。各画像は 28 x 28 ピクセル幅 (784 ピクセル単位) 高であり、0 ~ 9 の数字を表します。

DNN を cntk を使用して画像の分類
DNN を cntk を使用して、図 1 イメージ分類

デモ プログラムは、784 入力ノード (各ピクセルの 1 つ)、2 つの隠し処理レイヤー (それぞれ 400 のノードがある) 10 個の出力ノード (可能な桁ごとに 1 つ) と標準的なニューラル ネットワークを作成します。10,000 のイテレーションを使用して、モデルがトレーニングされます。損失 (トレーニング エラーとも呼ばれます) が緩やかに変化が低下し、予測精度が増しますが、トレーニングの動作を示します。

トレーニングの完了後、デモには、100 項目のテスト データセットに、トレーニング済みモデルが適用されます。モデルの精度は 100 テスト イメージの 84 が不適切に分類するための 84.00%、です。

この記事では、中級以上のプログラミング スキルが C ファミリ言語とを持っていても CNTK やニューラル ネットワークについて詳しく理解するとしますが前提としています。デモ は Python を使用してコーディングしています。しかし、Python の基礎知識がなくても、それほど苦労することなく内容を理解できるでしょう。ここには、デモ プログラムのソース コードをすべて掲載しています。2 つのデータ ファイルの使用はこの記事に付属するダウンロードから入手します。

データについて

完全な MNIST データセットは、60,000 のイメージをテストするためのトレーニングおよび 10,000 のイメージで構成されます。通常はある程度は、トレーニング セットは、すべてのピクセル値を保持する 1 つ使用し、関連付けられたラベル値 (0 ~ 9) を保持する 1 つを使用する、2 つのファイルにない含まれます。テスト イメージは、2 つのファイルにも格納されます。

さらに、4 つのソース ファイルは、独自のバイナリ形式で格納されます。深層ニューラル ネットワークを使用する場合は、使用できるフォームにデータを取得する時間がかかり、困難なほとんどの場合は。図 2トレーニングの最初のイメージの内容を示しています。重要なポイントは、各イメージが 784 (ピクセル単位) を各ピクセルは 00 h (10 進数 0) ~ FFh (255 の 10 進数) の値は。

MNIST 画像
図 2 MNIST 画像

デモ プログラムを記述する前に、バイナリのソース ファイルの読み取りを CNTK リーダー オブジェクトによって簡単に使用できるテキスト ファイルにそのコンテンツのサブセットを書き込むユーティリティ プログラムを作成します。ファイル mnist_train_1000_cntk.txt ようになります。

|digit 0 0 0 0 0 1 0 0 0 0 |pixels 0 .. 170 52 .. 0
|digit 0 1 0 0 0 0 0 0 0 0 |pixels 0 .. 254 66 .. 0
etc.

CNTK 形式への生の MNIST バイナリ データの取得は、単純なは。ユーティリティ プログラムのソース コードが見つかります: bit.ly/2ErcCbwします。

1,000 行のデータがあり、それぞれ 1 つのイメージを表します。タグ"| 桁"と"| ピクセル"、値の予測および予測値の開始を示します。桁ラベルには 1 ビットの位置に数字が示されているエンコードされた 1 つのホットです。そのため、上記のコードでは、最初の 2 つのイメージを表す「5」と「1」です。 データの各行には、0 ~ 255 の範囲は、それぞれの 784 のピクセル値があります。ファイル mnist_test_100_cntk.txt は、100 個のイメージを備え、同じ CNTK に適した形式を使用します。

最もニューラル ネットワークの問題の予測値を正規化したいです。データ ファイル内のピクセル値を直接正規化するには、代わりに、デモ プログラムは、すぐにわかりますをその場でデータを正規化します。

デモ プログラム

スペースを節約するために少し編集した完全なデモ プログラムを図 3 に示します。通常のエラー チェックはすべて削除しています。領域を節約する通常 4 ではなく 2 つの空白文字でインデントします。行の連結に Python によって「\」文字が使用されることに注意してください。

図 3 の完全なデモ プログラムの一覧

# mnist_dnn.py
# MNIST using a 2-hidden layer DNN (not a CNN)
# Anaconda 4.1.1 (Python 3.5.2), CNTK 2.4
import numpy as np
import cntk as C
def create_reader(path, input_dim, output_dim, rnd_order, m_swps):
  x_strm = C.io.StreamDef(field='pixels', shape=input_dim,
    is_sparse=False)
  y_strm = C.io.StreamDef(field='digit', shape=output_dim,
    is_sparse=False)
  streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
  deserial = C.io.CTFDeserializer(path, streams)
  mb_src = C.io.MinibatchSource(deserial, randomize=rnd_order,
    max_sweeps=m_swps)
  return mb_src
# ===================================================================
def main():
  print("\nBegin MNIST classification using a DNN \n")
  train_file = ".\\Data\\mnist_train_1000_cntk.txt"
  test_file  = ".\\Data\\mnist_test_100_cntk.txt"
  C.cntk_py.set_fixed_random_seed(1)
  input_dim = 784  # 28 x 28 pixels
  hidden_dim = 400
  output_dim = 10  # 0 to 9
  X = C.ops.input_variable(input_dim, dtype=np.float32)
  Y = C.ops.input_variable(output_dim)  # float32 is default
  print("Creating a 784-(400-400)-10 ReLU classifier")
  with C.layers.default_options(init=\
    C.initializer.uniform(scale=0.01)):
    h_layer1 = C.layers.Dense(hidden_dim, activation=C.ops.relu,
      name='hidLayer1')(X/255) 
    h_layer2 = C.layers.Dense(hidden_dim, activation=C.ops.relu,
      name='hidLayer2')(h_layer1)
    o_layer = C.layers.Dense(output_dim, activation=None,
      name='outLayer')(h_layer2)
  dnn = o_layer               # train this
  model = C.ops.softmax(dnn)  # use for prediction
  tr_loss = C.cross_entropy_with_softmax(dnn, Y)
  tr_eror = C.classification_error(dnn, Y)
  max_iter = 10000   # num batches, not epochs
  batch_size = 50   
  learn_rate = 0.01
  learner = C.sgd(dnn.parameters, learn_rate)
  trainer = C.Trainer(dnn, (tr_loss, tr_eror), [learner]) 
  # 3. create reader for train data
  rdr = create_reader(train_file, input_dim, output_dim,
    rnd_order=True, m_swps=C.io.INFINITELY_REPEAT)
  mnist_input_map = {
    X : rdr.streams.x_src,
    Y : rdr.streams.y_src
  } 
  # 4. train
  print("\nStarting training \n")
  for i in range(0, max_iter):
    curr_batch = rdr.next_minibatch(batch_size, \
      input_map=mnist_input_map)
    trainer.train_minibatch(curr_batch)
    if i % int(max_iter/10) == 0:
      mcee = trainer.previous_minibatch_loss_average
      macc = (1.0 - trainer.previous_minibatch_evaluation_average) \
        * 100
      print("batch %4d: mean loss = %0.4f, accuracy = %0.2f%% " \
        % (i, mcee, macc))
  print("\nTraining complete \n")
  # 5. evaluate model on test data
  rdr = create_reader(test_file, input_dim, output_dim,
    rnd_order=False, m_swps=1)
  mnist_input_map = {
    X : rdr.streams.x_src,
    Y : rdr.streams.y_src
  }
  num_test = 100
  test_mb = rdr.next_minibatch(num_test, input_map=mnist_input_map)
  test_acc = (1.0 - trainer.test_minibatch(test_mb)) * 100
  print("Model accuracy on the %d test items = %0.2f%%" \
    % (num_test,test_acc)) 
  print("\nEnd MNIST classification using a DNN \n")
if __name__ == "__main__":
  main()

Mnist_dnn.py デモは、create_reader 1 つのヘルパー関数です。すべての制御ロジックは、1 つのメイン関数では。CNTK は若い、継続的な開発中は、(このケースで 2.4) が使用されているバージョンの詳細を示すコメントを追加することをお勧めです。

CNTK のインストールは、Python の世界に慣れていない場合に、少し難しいことができます。まず、Anaconda Python のディストリビューションが、必要な Python インタープリターを含む、NumPy や SciPy など必要なパッケージおよび pip などの便利なユーティリティをインストールします。今回は、Python 3.5 が含まれる Anaconda3 4.1.1 (64 ビット) を使用します。Anaconda をインストールすると、としてインストールする CNTK Python パッケージ、スタンドアロン システムではなく、pip ユーティリティを使用します。一般的なシェルから、以下のコマンドを使用します。

>pip install https://cntk.ai/PythonWheel/CPU-Only/cntk-2.4-cp35-cp35m-win_amd64.whl

ファイルを示すホイール ファイルで"cp35"は、Python 3.5 で使用するために注意してください。注意してください。見た CNTK のインストール エラーのほとんどは、Anaconda CNTK のバージョン互換性がないのためにされました。

リーダーの関数のシグネチャは、create_reader (パス、input_dim、output_dim、rnd_order、m_swps) です。Path パラメーターは、CNTK 形式では、トレーニングまたはテスト ファイルを指します。Rnd_order パラメーターは、トレーニングの進行状況を加えずに動くようにするランダムな順序でトレーニング データを処理するためのトレーニング データを True に設定されるブール フラグです。パラメーターが設定を False にテスト データを読み取るときに順序が重要でないために、モデルの精度を評価するからです。M_swps パラメーター (「最大スイープ」) は、(繰り返し処理できる) ため、トレーニング データとテスト データの評価を 1 に設定する定数 INFINITELY_REPEAT に設定されます。

モデルを作成する

今回のデモでは、ディープ ニューラル ネットワークを準備します。

train_file = ".\\Data\\mnist_train_1000_cntk.txt"
test_file  = ".\\Data\\mnist_test_100_cntk.txt"
C.cntk_py.set_fixed_random_seed(1)
input_dim = 784
hidden_dim = 400
output_dim = 10
X = C.ops.input_variable(input_dim, dtype=np.float32)
Y = C.ops.input_variable(output_dim)  # 32 is default

通常、再現可能な結果になるように、CNTK グローバル乱数シードを明示的に設定することをお勧めします。入力ノードと出力ノードの数はそれぞれのデータによって決まりますが、隠し処理ノードの数は自由パラメーターなので試行錯誤しながら決めていかなければなりません。CNTK の既定値は、32 ビット変数を使用しては 64 ビットを使用して得られる有効桁数がパフォーマンスの低下を見合わないため、ニューラル ネットワークの一般的な発生します。

ニューラル ネットワークは次のように作成します。

with C.layers.default_options(init=
  C.initializer.uniform(scale=0.01)):
  h_layer1 = C.layers.Dense(hidden_dim,
    activation=C.ops.relu, name='hidLayer1')(X/255) 
  h_layer2 = C.layers.Dense(hidden_dim,
  activation=C.ops.relu, name='hidLayer2')(h_layer1)
  o_layer = C.layers.Dense(output_dim, activation=None,
    name='outLayer')(h_layer2)
dnn = o_layer               # train this
model = C.ops.softmax(dnn)  # use for prediction

Python の with ステートメントは、一連の一般的な引数を複数の関数に適用する構文ショートカットです。ここで-0.01 と +0.01 間のランダムな値にすべてのネットワークの重みを初期化するために使用されます。X オブジェクトは、イメージの 784 入力値を保持します。実際の入力値は [0.0, 1.0] の範囲内では、255 で除算して各値を正規化したことを確認します。

 最初の隠し層への入力として入力値を正規化する行為。最初の隠し層の出力は、2 つ目の非表示層への入力として機能します。次に、2 つ目の非表示層の出力は、出力層に送信されます。2 つの隠れ層は、イメージ分類の場合は、多くの場合よりも高い機能標準 tanh 活性化関数を ReLU (正規化線形ユニット数) ライセンス認証を使用します。

出力ノードには活性化関数を適用していません。CNTK のトレーニング関数は、活性化されていない値そのものを想定しているためです。これが、CNTK の特異な点です。Dnn は、便利なエイリアスです。モデル オブジェクトにはソフトマックス活性化関数が含まれるため、トレーニング後の予測に使用できます。Python は参照によって代入、ため、dnn オブジェクトのトレーニング、モデル オブジェクトもトレーニングします。

ニューラル ネットワークのトレーニング

ニューラル ネットワークは、以下のようにトレーニングに向けて準備します。

tr_loss = C.cross_entropy_with_softmax(dnn, Y)
tr_eror = C.classification_error(dnn, Y)
max_iter = 10000 
batch_size = 50   
learn_rate = 0.01
learner = C.sgd(dnn.parameters, learn_rate)
trainer = C.Trainer(dnn, (tr_loss, tr_eror), [learner])

トレーニングの損失 (tr_loss) オブジェクトは、CNTK にトレーニングするときにエラーを測定する方法を指示します。クロス エントロピ誤差は、通常、分類の問題の最適な選択です。トレーニング中またはトレーニング後に自動的に正しくない予測の割合を計算するトレーニングの分類エラー (tr_eror) オブジェクトを使用できます。損失関数を指定することが必要ですが、分類の誤差関数の指定は省略可能。

トレーニングのイテレーションの最大数の値を学習率一度にトレーニングするバッチ内の項目の数には、試行錯誤によって決定する必要がありますすべて無償のパラメーターです。アルゴリズム、として learner オブジェクトと、学習器を使用して、ニューラル ネットワークの重みとバイアスの値の適切な値を検索するオブジェクトと trainer オブジェクトを考えることができます。確率的勾配降下 (sgd) 学習器は、簡単な問題に適してが、ほとんどのプリミティブのアルゴリズムです。代替手段には、アダプティブ瞬間の推定 (adam) と平均平方根伝達 (rmsprop) が含まれます。

以下のステートメントでは、トレーニング データのリーダー オブジェクトが作成されます。

rdr = create_reader(train_file, input_dim, output_dim,
  rnd_order=True, m_swps=C.io.INFINITELY_REPEAT)
mnist_input_map = {
  X : rdr.streams.x_src,
  Y : rdr.streams.y_src
}

Create_reader のコードを調べる場合図 3、データ ファイルで使用されるタグの名前 (「ピクセル」と「桁」) を指定しているが表示されます。Create_reader と DNN 画像分類問題の定型コードとして reader オブジェクトを作成するコードを検討することができます。変更する必要があるが、タグの名前とマッピング ディクショナリ (mnist_input_map) の名前です。

すべての準備ができたら、トレーニングが実行されるように図 4します。

図 4 トレーニング

print("\nStarting training \n")
for i in range(0, max_iter):
  curr_batch = rdr.next_minibatch(batch_size, \
    input_map=mnist_input_map)
  trainer.train_minibatch(curr_batch)
  if i % int(max_iter/10) == 0:
    mcee = trainer.previous_minibatch_loss_average
    macc = (1.0 - \
      trainer.previous_minibatch_evaluation_average) \
        * 100
    print("batch %4d: mean loss = %0.4f, accuracy = \
      %0.2f%% " % (i, mcee, macc))

デモ プログラムでは、各イテレーションのトレーニング項目の 1 つのバッチが処理されるよう設計されています。多くのニューラル ネットワーク ライブラリでは、「エポック」という用語を使用して、すべてのトレーニング項目の 1 つのパススルーを参照してください。この例で、1,000 のトレーニング項目があるし、バッチ サイズが 50 に設定されているためエポックの 1 つになります 20 のイテレーション。

トレーニングのイテレーションの数を固定する代わりに、損失と誤差がいくつかのしきい値を下回るときに、トレーニングを停止します。トレーニング中には損失と誤差を表示することが重要です。トレーニングの失敗は珍しいことことではなく、よくあることです。クロス エントロピ誤差は、直接解釈が困難ですが、ほど小さくなる傾向値を表示します。(「25% 間違った」) の平均分類誤差を表示するには、代わりに、デモを計算し、平均の分類の精度を出力します ("75%") を修正私見ですより自然な指標であります。

モデルの評価と使用

イメージの分類器のトレーニングが完了したら、通常はしますがテスト データでトレーニング済みモデルを評価します。ように、デモが分類の精度を計算図 5します。

図 5 コンピューティング分類の精度

rdr = create_reader(test_file, input_dim, output_dim,
  rnd_order=False, m_swps=1)
mnist_input_map = {
  X : rdr.streams.x_src,
  Y : rdr.streams.y_src
}
num_test = 100
test_mb = rdr.next_minibatch(num_test,
  input_map=mnist_input_map)
test_acc = (1.0 - trainer.test_minibatch(test_mb)) * 100
print("Model accuracy on the %d test items = %0.2f%%" \
  % (num_test,test_acc)))

新しいデータ リーダーを作成します。トレーニングで使用したリーダーとは異なり、新しいリーダーは、データをランダムな順序で移動せず、掃引数は 1 に設定します。Mnist_input_map dictionary オブジェクトを再作成されます。よくある間違いと元のリーダーを使用する — がマッピングを再作成する必要があるために、rdr オブジェクトが変更されました。Test_minibatch 関数は、この場合、全体の 100 項目のテスト セットには、そのミニバッチ引数の平均分類誤差を返します。

通常は、トレーニング後またはトレーニング中にデータを保存することをお勧めします。CNTK では、保存はようになります。

mdl_name = ".\\Models\\mnist_dnn.model"
model.save(mdl_name)

既定の CNTK v2 形式を使用してこれが保存されます。代わりに Open Neural Network Exchange (ONNX) 形式を使用することもできます。Dnn オブジェクト (出力のアクティブ化なし) ではなく (ソフトマックス活性化関数) でモデル オブジェクトを保存するたい一般的に注意してください。別のプログラムで、保存したモデルを以下の行のようにメモリに読み込むことができます。

mdl_name = ".\\Models\\mnist_dnn.model"
model = C.ops.functions.Function.load(mdl_name)

読み込んだら、ちょうどトレーニングが完了したばかりのようにモデルを使用できます。デモ プログラムでは、予測を行う際にトレーニング後のモデルを使用しません。この予測コードのように可能性があります。

input_list = [0.55] * 784  # [0.55, 0.55, . . 0.55]
input_vec = np.array(input_list, dtype=np.float32)
pred_probs = model.eval(input_vec)
pred_digit = np.argmax(pred_probs)
print(pred_digit)

Input_list が、それぞれ 0.55 (正規化されたデータにフィードする必要がありますので、正規化されたデータでモデルがトレーニングされてを思い出してください) の値を持つダミー 784 ピクセル値を入力します。ピクセルの値は、NumPy 配列にコピーされます。Eval 関数への呼び出しが 10 個の値の配列その合計に 1.0 戻って疎確率として解釈できます。Argmax 関数は、最大の値は、数字の予測と同じでは便利なことのインデックス (0 ~ 9) を返します。すてきでしょう?

まとめ

ディープ ニューラル ネットワークを使用して単純な画像の分類の最も一般的なアプローチを使用します。ただし、Dnn には、少なくとも 2 つの主な制限事項があります。最初に、Dnn は、膨大な数のピクセルの画像に適切にスケールはありません。次に、Dnn しない明示的に考慮に入れて画像のピクセルのジオメトリ。たとえば、MNIST 画像では、2 番目のピクセルのすぐ下にあるピクセルは入力ファイル内の最初のピクセルから 28 の位置。

これらの制限事項が原因とその他の理由は、すぎる、畳み込みニューラル ネットワーク (CNN) の使用は、画像の分類の一般的なようになりました。とはいえ、DNN を使用して、タスクが簡単と多くの場合、単に (またはそれ以上) の単純な画像の分類、CNN を使用するよりも効果的です。


Dr.James McCaffrey は、ワシントン州レドモンドの Microsoft Research に勤務しています。これまでに、Internet Explorer、Bing などの複数のマイクロソフト製品にも携わってきました。Dr.McCaffrey の連絡先は jammc@microsoft.com (英語のみ) です。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Chris Lee、Ricky Loynd、Ken Tran に心より感謝いたします。


この記事について MSDN マガジン フォーラムで議論する