FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

CNTKのチュートリアル(MNIST)の読み解きに挑戦

またまたCNTKである。次に、どこでもかしこでも登場してくるMNISTの認識(手書き文字認識)のチュートリアルに挑戦してみよう。

(僕の思う)CNTKとTensorFlowにおけるネットワーク構成方法の違い

TensorFlowでも、MNISTは最初のチュートリアルで登場してきた。これだ。

MNIST For ML Beginners

TensorFlowでは、ネットワークを解くのに自分でpythonのコードを書いたね。入力から計算方式までを自分で考えて、ディープネットを構成した。

for i in range(1000):
  batch_xs, batch_ys = mnist.train.next_batch(100)
  sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})

しかし、CNTKとTensorFlowの決定的な違いは、このネットワークの構成法のように思う。TensorFlowが手続としてネットワークを構成するのに対して、CNTKはどのような形状のネットワークであるかをconfigファイルとして記述する。 大した違いじゃないと思うけど、TensorFlowはPythonを用いて手続を定義することでネットワークを構成し、CNTKはDSLを用いてネットワークの形状を記述している、ような気がする。

DNN = [
    hiddenDim = 200

    # DNNSigmoidLayer and DNNLayer are defined in Macros.ndl
    h1 = DNNSigmoidLayer(featDim, hiddenDim, featScaled, 1)
    ol = DNNLayer(hiddenDim, labelDim, h1, 1)

    ce = CrossEntropyWithSoftmax(labels, ol)
    err = ErrorPrediction(labels, ol)

    # Special Nodes
    FeatureNodes = (features)
    LabelNodes = (labels)
    CriterionNodes = (ce)
    EvalNodes = (err)
    OutputNodes = (ol)
]

ここらへんの記述方法は、ちゃんと文法を勉強しないとまずい気がする。 ネットワークは、../Config/01_OneHidden.configで定義されている。

    NDLNetworkBuilder = [
        networkDescription = "$ConfigDir$/01_OneHidden.ndl"
    ]

MNISTのデータを取り込む

これはTensorFlowでも同様のスクリプトが用意されている。MNISTのデータをダウンロードして、欲しいフォーマットに変更するのだ。

python mnist_convert.py

中身を見てみると、何のことはない。各データをダウンロードして、テキストとして並べているだけだ。最後にhstackを利用して、1列目がラベル(つまり実際に何の値であるか)、それ以降が28x28のデータを一列で並べている。

$ python mnist_convert.py
Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
Done.
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
Done.
Writing train text file...
Done.
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz
Done.
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz
Done.
Writing test text file...
Done.

$ ls ../Data/Train-28x28.txt
// 1列目がラベル、それ以降が28x28のデータである。
5       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0...
...

学習させる

01_OneHidden.config に、データの形状が記述されている。

  • ラベルの数は10個(つまりネットワークの出口は10個ある
  • それぞれのラベルはlabelsmap.txtに記述されている
  • データの数は784個で構成されている。1列目からスタートする
  • ラベルの数は1つである。0列目からスタートする
    NDLNetworkBuilder = [
        networkDescription = "$ConfigDir$/01_OneHidden.ndl"
    ]

    SGD = [
        epochSize = 60000
        minibatchSize = 32
        learningRatesPerMB = 0.1
        momentumPerMB = 0
        maxEpochs = 30
    ]

    reader = [
        readerType = "UCIFastReader"
        file = "$DataDir$/Train-28x28.txt"

        features = [     // 訓練データの構成
            dim = 784    // 28x28個のデータが並んでいる
            start = 1    // 1列目からスタートする
        ]

        labels = [
            dim = 1        // 訓練ラベルは1つ
            start = 0      // 0列目からスタートする
            labelDim = 10  // 10種類にラベリングされる
            labelMappingFile = "$DataDir$/labelsmap.txt"
        ]
    ]

実行してみる

さて、実行してみると、訓練とテストが実行される。これは01_OneHidden.ndlに、「trainとtestを実行しろ!」と書いてあるからである。

command = train:test

実行すると、../Output/以下にいろいろ生成された。

less ../Output/01_OneHidden_out_train_test.log
# 最後の方
RandomOrdering: 2036 retries for 10000 elements (20.4%) to ensure window condition
RandomOrdering: recached sequence for seed 0: 2009, 1524, ...
Minibatch[1-500]: Samples Seen = 8000    err: ErrorPrediction/Sample = 0.02675    ce: CrossEntropyWithSoftmax/Sample = 0.081793641
Minibatch[501-625]: Samples Seen = 2000    err: ErrorPrediction/Sample = 0.014    ce: CrossEntropyWithSoftmax/Sample = 0.048010155
Final Results: Minibatch[1-625]: Samples Seen = 10000    err: ErrorPrediction/Sample = 0.0242    ce: CrossEntropyWithSoftmax/Sample = 0.075036944    Perplexity = 1.077924
COMPLETED

今回はエラー率2.4%かー。まあまあじゃないの?

テストデータの結果を出力してみる

上記で01_OneHiddenのチュートリアルは終了なのだが、全然面白くないよ!ちゃんと学習結果によって、テストデータが正しく判別できていることが確認できないといやだ! という訳で、Simple2dと同様に、テスト結果を出力するコマンドを作ってみる。といっても、TEST CONFIGの内容にちょっとだけ追加しただけだが...

outputコマンドを作成する

testコマンドに対して、outputPathを追加しただけである。簡単!

#######################################
#  TEST OUTPUT                        #
#######################################

output = [
    action = "write"
    minibatchSize = 16

    NDLNetworkBuilder=[
        networkDescription = "$ConfigDir$/01_OneHidden.ndl"
    ]

    reader = [
        readerType = "UCIFastReader"
        file = "$DataDir$/Test-28x28.txt"

        features = [
            dim = 784
            start = 1
        ]

        labels = [
            dim = 1
            start = 0
            labelDim = 10
            labelMappingFile = "$DataDir$/labelsmap.txt"
        ]
    ]

    outputPath = "$OutputDir$/testoutput.txt"    # Dump output as text
]

outputコマンドをデフォルトで実行するように変更する。

コマンド欄に、"output"を追加するだけである。簡単!

command = train:test:output

実行してみる

../Output/にtestoutput.txtが作成された。

ls ../Output/testoutput.txt.ol.z
-1.41889 -4.48155 1.99092 4.43438 -3.80075 -0.139526 -10.3081 11.6369 -0.992156 2.32791
1.07828 4.79982 13.0368 5.26931 -8.97806 2.02424 2.7303 -11.1134 2.3798 -11.0942
-6.40207 8.5036 1.1787 -0.355491 -1.55176 -0.0385594 -1.07135 0.718961 1.08962 -2.0911
9.35339 -5.23981 1.95068 -3.1295 -1.42687 -0.249888 1.47601 0.0180543 -4.76824 1.43057
-1.65102 -8.40526 1.34139 -4.81253 9.54889 -1.75908 0.0672715 1.70851 -0.857385 3.96584
...

例えば一番最初の行は、Test-28x28.txt(入力ファイル)の結果からして7にラベリングされるのが正解なのだが、7列目の値が11.6369と最も大きい。やった!正解だ!

Excelで答え合わせ

ラベルとCNTKの回答だけ取り出した。

f:id:msyksphinz:20160131020657p:plain

ほとんど正解しており、やはりExcelで計算しても誤差は2.4%くらいだ。

ちなみに

02_Convolution.config はCPUだと遅すぎて20分程度待っても終了せず。。。 03_ConvBatchNorm.config はConvReLUBNLayerがリポジトリ内で定義されておらず実行できず。。。

自分で考えろってことか?

過去の記事

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com

msyksphinz.hatenablog.com