はじめに
今回は前回に引き続き、下記の内容を解説します。
- Juliaによる深層学習モデルの実装
- JuliaでMNISTを解く・コードの解説
- JuliaとPythonの学習速度の比較
MNISTとは
- MNISTは、手書き数字の画像を集めたデータセットで、画像分類の入門やチュートリアルでよく使用されます
- データの中身はグレースケールの0~9の手書き数字で、6万件の学習データと1万件のテストデータから構成されています
- グレースケールの色の濃淡は0~255の値で表されており、学習する際は、これらの数値を正規化してモデルに入力します

JuliaでMNISTを解く
実際に、サンプルコードを用いて、JuliaによるMNISTの学習とテストをやってみます。こちらにGoogle Colabで実行できるJuliaのサンプルコードを用意してありますので、ダウンロードしてください。
ダウンロードしたipynbファイルをGoogle Colabで開くと、一番上に次のようなセルがあると思いますので、実行してください。

実行が完了したら、上のメニューから「ランタイム」->「ランタイムのタイプを変更」を選択してください。そうすると、Juliaのバージョンが記されたダイアログが出てくるので、「保存」を押してください。

これで、ランタイムがリセットされ、Juliaの実行環境が整いました。
あとは順番に、後に続くセルを実行してください。実行していくと「学習」と書かれたセルがあるかと思いますが、下記のようにlossが減少していく過程が表示されていれば、問題なく学習ができています。

同じく、「テスト」と書かれている以下のセルを実行してください。下記のように正解率が返ってくれば、問題なくテストも実行することができました。

コード解説
MNISTの学習とテストを実行できたと思いますので、次はコードの内容について解説していきます。
データ読み込み

- ここでは、MLDatasetsから、MNISTの学習用データとテスト用データを読み込んでいます
- MLDatasetsは機械学習で使用される一般的なデータを提供するパッケージです
- train_data(Float32)で学習用の入力データ(train_x)と正解データ(train_y)を出力します
- テスト用のデータも同じ要領です
では、実際にMNISTに含まれる手書き数字の画像を表示させてみましょう。

5と書かれた数字のグレースケール画像を表示させることができました。画像にコンバートしないで表示させてみると以下のようになります。

学習時には、このような0~1の数値で表された行列をモデルに入力します。
モデル定義

- ここでは、MNISTを解くための深層学習モデルを定義しています
- レイヤーには全結合層とドロップアウトを用いており、入力層が28×28、中間層が32、出力層が10(0~9までの数値に対する確率)というようになっています
- Chainは、複数のレイヤーや関数をチェーンして、特定の入力で順番に呼び出せるようにします
パラメータ

- 学習時に更新するモデルのパラメータをFlux.paramsに渡しています
- パラメータは目的関数の勾配を計算するために使用されます
最適化関数

- 最適化関数にはADAMオプティマイザーを使用します
損失関数

- 損失関数にはlogitcrossentropyを用います
- 処理的にはcrossentropy(softmax(ŷ), y)と同じですが、crossentropyで個別にsoftmaxを使用するよりも数値的に安定しています
コールバック関数

- バッチごとにlossの結果を返すコールバック関数を定義しています
学習

- train!に損失関数、パラメータ、学習データ、最適化関数を渡すことで、学習を実行することができます
- 先頭に@epochs num_epochsとつけることで、エポック数を設定することができます
- cb = throttle(evalcb, 5)は、各エポック5数秒間に最大1回コールバック関数を実行するように設定しています
- train!はモデルのパラメーターを自動で更新してくれるので、単純なモデルを学習させる場合に便利です
テスト

- ここでは、バッチごとに正解率を計算し、最終的に正解率の平均をとるようにaccuracyという関数を定義しています

- DropoutやBatch Normalizationなどはテスト時には無効にしたいため、testmode!とします
- accuracy関数にtest_dataを渡して実行すると正解率が返ってきます
- test_dataは最初にMLDatasetsから読み込んだ1万件の手書き数字画像です
Pythonとの比較
- ここまでJuliaでMNISTをやってきましたが、ここからはPython(TensorFlow)でも同じコードを作成し、学習速度の比較を行いたいと思います
- 今回比較で使用するPythonコードはこちらにあります
実験設定
速度計測の対象は、学習が完了するまでの時間で、条件の詳細は下記になります。
- 計測対象:学習開始から完了まで
- 計測方法:5回計測し、学習時間の平均を比較対象とする
- 実行環境:Google Colaboratory、GPUはTesla T4を使用する
- Epoch数:10
- 使用データ:MNISTの学習用データ(6万件)
- Juliaでの計測方法

- Pythonでの計測方法

結果
学習速度の結果を下記に示します。
Juliaの学習時間はPythonに比べて3倍程度速い結果となりました。
回数 | Julia | Python |
1回目 | 9.2 | 31.7 |
2回目 | 9.2 | 31.5 |
3回目 | 9.6 | 33.3 |
4回目 | 9.9 | 31.6 |
5回目 | 9.4 | 31.7 |
平均 | 9.46 | 31.96 |
続いて、MNISTのテストデータ(1万件)の正解率を示します。念のため、正解率も出してみましたが、JuliaとPythonで大きな差はなく、どちらもMNISTを解けているようです。
回数 | Julia | Python |
1回目 | 0.966 | 0.962 |
2回目 | 0.965 | 0.966 |
3回目 | 0.968 | 0.963 |
4回目 | 0.966 | 0.967 |
5回目 | 0.969 | 0.960 |
平均 | 0.967 | 0.964 |
おわりに
今回は、JuliaでMNISTを解く深層学習モデルを実装し、学習時間をPythonと比較しました。Juliaの方が3倍速いという結果になりましたが、パッケージの読み込みやコンパイル時間を含めた全体の実行時間では、まだまだPythonの方が速く、使いやすい気がしました。今回はMNISTということでしたが、より複雑な問題であれば、Juliaにも明確な優位性が出てくるのかもしれません。
(K.K)
追記:関連
Julia関連記事については、次にもまとめてあります。