またまたCNTKである。次に、どこでもかしこでも登場してくるMNISTの認識(手書き文字認識)のチュートリアルに挑戦してみよう。
(僕の思う)CNTKとTensorFlowにおけるネットワーク構成方法の違い
TensorFlowでも、MNISTは最初のチュートリアルで登場してきた。これだ。
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の回答だけ取り出した。
ほとんど正解しており、やはりExcelで計算しても誤差は2.4%くらいだ。
ちなみに
02_Convolution.config はCPUだと遅すぎて20分程度待っても終了せず。。。 03_ConvBatchNorm.config はConvReLUBNLayerがリポジトリ内で定義されておらず実行できず。。。
自分で考えろってことか?