Chainerを使ったディープラーニング【第4回:GPUコンテナで機械学習する】

AI初学者にとってハードルのひとつになっているGPU環境構築をできるだけ効率的に行うために、コンテナを有効活用することを目的に連載をスタートしました。前回はディープラーニングのフレームワークとしてメジャーなTensorFlowとKerasを使い、手書き文字の分類を行いました。今回はこちらもメジャーなフレームワークのひとつであるChainer(チェイナー)を使って、前回同様に手書き文字画像の分類を行います。

この連載について

本連載は、全6回のシリーズを通してできるだけ効率的に、GPUの環境構築を行うためにコンテナの活用を行っていきます。「機械学習やディープラーニングをGPUで実行してみたいけど難しそう…」など導入にハードルを感じられている方に、コンテナを活用することで、環境構築に要する工数を圧倒的に削減し、即座に課題に取り組むことができるメリットを感じていただきます。そのために必要な知識や操作方法を、当社のGPUサーバーを使い解説していきます。
連載を読み終えるころには、TensorFlowやPyTorchなどのメジャーなフレームワークを使った演習ができるようになっているはずです。

本連載は、こちらの手順で進めています

・GPUコンテナとは何か?何が便利なのか?(第1回)

AI初学者がGPUを使って機械学習やディープラーニングに取り組みたい場合、環境構築に想像以上の工数が発生することがあります。セットアップ作業に要する時間を極力削減するためにコンテナ技術を適用し、コンテナ内からGPUを利用するための準備と手順について紹介します。

●GPUコンテナとは何か?何が便利なのか?(第1回)
https://www.kagoya.jp/howto/cloud/gpu-container1/

・TensorFlowとKerasによるディープラーニング①(第2回)

OSS(オープンソースソフトウエア)の機械学習ライブラリの中からTensorFlow(テンソルフロー)、Keras(ケラス)を取り上げ、これらが稼働するコンテナを作成し、コンテナ内からGPUを指定する方法について紹介します。TensorFlowはKerasを取り込む形で公開されていて、ディープラーニングをする際の使い勝手の良さから、多くのユーザーに利用されています。この回ではコンテナ内のTensorFlowでGPUを利用できる状態まで確認します。

●GPUコンテナで画像解析?準備編?(第2回)
https://www.kagoya.jp/howto/cloud/gpu-container2/

・TensorFlowとKerasによるディープラーニング②(第3回)

TensorFlow(テンソルフロー)やTheano(テアノ)/CNTK(Cognitive Toolkit)の複数のバックエンドとして利用可能なKeras(ケラス)を取り上げ、TensorFlowとKerasを使ったディープラーニングを行います。ここではAI初学者の方が親しみやすい課題を扱うことを意図し、TensorFlowの公式ガイドに記載されている「初心者のための TensorFlow 2.0 入門」のチュートリアルを取り上げました。

●GPUコンテナで画像解析?実践編?(第3回)
https://www.kagoya.jp/howto/cloud/gpu-container3/

・Chainerを使ったディープラーニング(第4回)今回の記事

ディープラーニングのフレームワークとして有名なChainer(チェイナー)の利用方法を紹介します。コンテナからPythonのプログラム実行結果を通して確認します。C言語に比べて処理時間がかかると言われているPythonですが、数値計算を効率的に行うための拡張モジュールであるNumPy(ナムパイ)も利用します。今回扱う題材としてChainerのチュートリアルを取り上げます。

・Pytorchでニューラルネットワーク(第5回)

Pythonの機械学習用フレームワークであるPytorch(パイトーチ)を取り上げます。PyTorchではTensor(テンソル)という型で行列を表現します。Tensorは多次元配列を扱うためのデータ構造であり、GPUをサポートしていることから、Pytorchが稼働するコンテナを利用し、GPUによる高速処理を行う手順について紹介します。

・OpenPoseによる関節点抽出・姿勢推定(第6回)

カメラ画像のAI画像認識と言えば「顔認証」を思い浮かべる人が多いと思いますが、最近は一歩進み、人が映った静止画や動画から関節点抽出・姿勢推定に取り組むケースが増えています。人体、顔、手足などのキーポイントを画像から検出する技術がディープラーニングにより、実用レベルまで向上しているからです。この回ではOpenPose(オープンポーズ)というライブラリをGPU上で動かすコンテナを使い、動画ファイルの関節点抽出手順を紹介します。

前回(第3回)の振り返り

前回は、Docker環境でGPUコンテナからTensroFlowとKerasを動かし、手書き文字の認識を行いました。
【 第3回の連載記事はこちら https://www.kagoya.jp/howto/cloud/gpu-container3/

MNIST(エムニスト)を使った手書き文字の判別処理

AI初学者の方が親しみやすい課題を扱うことを意図し、TensorFlowの公式サイトに記載されている「初心者のための TensorFlow 2.0 入門」のチュートリアルを取り上げました。
【 https://www.tensorflow.org/tutorials/quickstart/beginner?hl=ja 】

チュートリアルで行っている以下の内容を実践しました

  1. 手書き文字画像を分類するためのニューラルネットワークのモデルを定義する
  2. 構築したモデルに対し、トレーニング用画像を使い、学習を行う
  3. テスト用画像を使い、モデルを性能評価する

MNISTとは

0から9の手書き数字文字の画像データセットです。機械学習の領域でサンプルとして利用されることが多い使い勝手のよいデータ集であり、トレーニング用データ60,000枚とテスト用データ10,000枚が含まれます。MNISTのデータは、手書き文字の画像データが何の数字かを示す「ラベルデータ」と縦・横 各々28ピクセルの「画像データ」から構成されています。

ニューラルネットワークのモデルを定義

MNISTの画像を分類するためのニューラルネットワークを構築します。このチュートリアルでは「Sequentialモデル」と呼ばれる、各層を順番につなげていくモデルを定義しています。

上記のモデルで予測する

前段で定義したモデルを使って予測を行います。この時点ではトレーニング前です。
以下、詳しい説明は省略していますので、前回の記事を参照してください。

モデルのトレーニング(学習)を行います

学習モデルに対し、トレーニングデータ、教師ラベルデータを与え、画像とラベルの対応関係を学習します。モデルに対し、テスト用データセットの予測を行わせ、予測結果とテスト用教師ラベルを照合します。エポック回数を5と設定し、model.fit関数を使い、トレーニングを開始します。

正解率の評価

モデルの検証を行います。5回のエポックでモデルの正解率が示されましたが、トレーニングで利用しなかったテスト用データとテスト用教師ラベルデータを使い、誤差と正解率を求めます。

検証結果は上記のように誤差が約 0.07正解率は約 0.98 と表示されました。
5回の学習で高い正解率が出たことで、ここで学習は終了しています。

テスト用画像で推論

学習が終わったので、最後にトレーニング済モデルを使って推論処理しますが、ソフトマックス関数をモデル全体に対して指定し、テスト用画像5つについて推論します。

以下のような結果になりました。

配列の中身を確認します。
1つ目のテスト画像に対する推論結果は、数字の7である確率が約99%と最も高くなりました。(1番目の配列の並び順に数字の0,1,2,3,4,5,6,7,8,9である確率を示しており、9.99731362e-01 と一番高い確率を示しているのが、数字の7に対応したノードであるから)
以下、同様に見ていきます。
2つ目のテスト画像の推論結果は数字の2である確率が約99%
3つ目のテスト画像の推論結果は数字の1である確率が約99%
4つ目のテスト画像の推論結果は数字の0である確率が約99%
5つ目のテスト画像の推論結果は数字の4である確率が約99%
と算出されました。

テスト用データセットの画像ファイルの中身の一部は下記のようなっています。

左端から右方向に1つ目から5つ目の画像と推論結果を照らし合わせると、7、2、1、0、4で合致していることがわかりました。

公式ガイドのチュートリアルに沿って、手書き文字認識のモデル構築、学習、推論という一連の流れを見てきました。
コンテナ内部でフレームワークを利用することで、ほとんどプログラミングすることなく、関数の呼び出しとパラメータの設定により、ディープラーニングできることが確認できました。

Chainerを使った機械学習

前回、TensorFlowKerasを使って手書き文字の分類を行いましたが、今回はChainerの開発元であるPFN社のサイトにあるChainerチュートリアルに沿って機械学習を行います。

Chainer(チェイナー)とは

今回、とりあげる Chainer はPFN(Preferred Networks:プリファードネットワークス)という日本の企業が開発したPythonベースのディープラーニング向けフレームワークです。オープンソースソフトウエアであることや、複雑なニューラルネットワークを比較的シンプルに定義できることなど、使い勝手の良さから日本の多くのユーザーから支持されてきました。

今回行うことの概要

AI初学者の方が親しみやすい課題を扱うことを意図し、Chainerの公式サイトに記載されているChainerチュートリアルの中から14.Chainerの基礎を取り上げました。
チュートリアルの内容を補足しながら進めていきますので、下記の記載事項もあわせて見ていただけると理解が深まると思います。
【参考サイト:https://tutorials.chainer.org/ja/14_Basics_of_Chainer.html?hl=ja 】

チュートリアルで行っている以下の内容を実践します。

  1. ニューラルネットワークのモデルを定義する
  2. 構築したモデルに対し、トレーニング用データを使い、学習を行う
  3. テスト用データを使い、モデルを性能評価する

Chainerのコンテナを準備する

早速、docker hubからchainerのコンテナイメージをpullしましょう。
下記のURLから pip install コマンドで chainerをインストールします。
Dockerhub 参照サイト:https://hub.docker.com/r/chainer/chainer/

コンテナイメージを確認します。

chainerのコンテナ内からnvidia-smiを実行し、GPUが認識できているかを確認します。

上記のようにGPU関連の情報が表示されることが確認できました。

Pythonのライブラリをインストールする

あらためてChainerのコンテナイメージからbashをたちあげ、pip3 listコマンドでインストール済のパッケージ一覧を表示します。

pipのアップグレードを促すメッセージが表示されたので実施します。(アップグレードの必要がない方は無視して先に進んでください)

アップグレードが終わったので、もう一度pip3 listコマンドでインストール済のパッケージ一覧を表示します。

今回の演習では、計算結果をグラフ化するために「matplotlib」というグラフ描画ライブラリを利用しますので、このタイミングでインストールしておきます。

同様に「scikit-learn」というPythonの機械学習ライブラリを利用しますのでインストールします。

pip3 listコマンドでインストール済のパッケージ一覧を表示します。「matplotlib」「scikit-learn」関連のライブラリがインストールされたことが確認できました。

コンテナからPythonを起動し、chainerを実行する環境の情報を表示します。

OSやChainer、Numpyのバージョン情報などが表示されます。CuPy(GPUを使いNumPy互換の機能を提供するライブラリ)も利用できることがわかります。

機械学習用のデータセットを準備する

今回の演習では、scikit-learnに付属している『Iris plants dataset』を使い、3種類のあやめ(アイリス)の花の測定値から、どの品種に属するかを分類する機械学習モデルを構築します。
参考:『Iris plants dataset』の仕様はこちら

使用する『Iris plants dataset』を読み込んで、内容について確認しましょう。
sklearn.datasets モジュールにある load_iris() 関数を実行することでデータセットを読み込むことができます。

load_iris() 関数の説明のページを参照すると、詳細説明がDESCRという引数で照会できると記載されていますので、確認します。

**Data Set Characteristics:**と書かれた部分に注目します。9行目に150 個のインスタンス(あやめのデータ)が存在し、各々50個ずつの3つのクラスから構成されていると記載されています。
10行目、11行目の記載内容を見ると、このデータセットには「Iris-Setosa(ヒオウギアヤメ)」「Iris-Versicolour(アイリスバージカラー)」「Iris-Virginica(アイリスバージニカ)」という3種類のあやめの測定データが含まれており、「sepal(がく片)」「petal(花弁)」の「length(長さ)」「width(幅)」を計測したデータ(単位:センチメートル)が含まれていることがわかります。load_iris()関数で150 個のあやめのがく片の長さ・幅、花弁の長さ・幅のデータと、各々のあやめの種類を示すクラス IDを取得し、それぞれ xt という変数で受け取り、各々の配列の形を表示します。
【参考ページ:Visualizing Datasets
Iris

データの形式を Chainer に合わせます。
Chainer で学習用モデルにデータを引き渡すために、入力値のデータ型を numpy.float32(32ビット浮動小数点数)に、分類問題では目標値のデータ型を numpy.int32(32ビットの符号付き整数) にする必要があります。 NumPy配列ndarrayではデータ型dtypeを保持しており、これらの型に合わせるため astype() 関数を使ってデータ型を変更します。

データセットを分割する

学習モデルの正解率を正しく判断するために、トレーニング用とテスト用に分割する必要があります。トレーニング用に準備したデータをテスト用にも使うと、既知のデータであることから、正解率が高くなってしまうため、それを避けるためにデータセットを「トレーニング用」と「テスト用」に分割します。ここでは前述の2つに加え「検証用」の3つにデータセットを分割します。
まずは全体を『「トレーニング用データセット」 「検証用データセット」』と『「テスト用データセット」』の2つに分割します。データセットを分割するためにscikit-learntrain_test_split()関数を使用します。
このとき「テスト用データセット」のサイズを全体の30%にするために関数の引数 test_size=0.3 と指定します。これで、「トレーニング用データセット」と「検証用データセット」を合わせたデータが全体の70%、テスト用データセットが30%に分割されます。 引数 random_state=0 は、乱数のシード値を指定しない設定です。train_test_split()関数を使い、データセットを分割する処理を複数回行った際に、毎回異なる数字の並びになるように実行するたびランダムに分割したい場合は乱数のシード値を指定しません。

続いて「トレーニング用データセット」と「検証用データセット」に分割します。 「検証用データセット」のサイズは「トレーニング用データセット」「検証用データセット」を合わせたサイズの30%になるようtest_size=0.3と指定します。

ニューラルネットワークのモデル定義

続けて、あやめの種類を分類するためのニューラルネットワークを構築します。今回とりあげたChainerのチュートリアルでは「Sequentialモデル」と呼ばれる、各層を順番につなげていくモデルを定義しています。
chainerでモデルの定義をするにあたって、chainer.links(パラメータを持つ関数) 、chainer.functions(パラメータを持たない関数) という関数を利用できるよう準備します。
import chainer.links as Limport chainer.functions as F のように別名を付与する記述の仕方については、chainerを使う際のお作法のようなものと考えましょう。

先ず、入力次元数が 3、出力次元数が 2 の全結合層を L.Linear クラスで定義します。第1引数は入力信号数、第2引数は出力信号数です。全結合層とは、すべてのノードが次の層のすべてのノードにつながっている層なので、3個のノードから2個のノードに接続する記述は以下になります。

次に Sequential クラスと Linear クラス、relu() を使ってネットワークを定義します。
入力層、中間層、出力層の3つの層について下記の設定を行います。
Iris のデータは入力変数が 4 つ(「sepal(がく片)」「petal(花弁)」の「length(長さ)」「width(幅)」)ですので、最初の全結合層の入力次元数は 4 になります。
2つ目の全結合層の入力次元数は任意の値を指定可能ですが、ここでは 10 とします。
Iris のクラス数は 3 (あやめの種類が3つ)なので、最後の全結合層の出力次元数は 3 です。
活性化関数としてReLU関数(ランプ関数)を適用すると、0以下なら「0」を、0より大きければ「入力値そのもの」が返ってきますので、マイナス値を切り捨て、特徴がはっきりとしたデータとして扱うことが可能になります。

最適化手法を決定する

機械学習でトレーニングを行うための最適化手法を決めますが、この演習では、確率的勾配降下法 (SGD:Stochastic Gradient Descent) を利用します。 Chainer には chainer.optimizers にいくつかの最適化手法を利用するためのクラスが用意されていて、確率的勾配降下法は SGD という名前で定義されています。

学習率 lr を 0.01と指定し、前述のchainer.optimizers.SGDを利用し、optimizer という名前のインスタンスを作成します。

インスタンス optimizer に対し、前段でネットワークの定義をした net をセットして、net のパラメータが確率的勾配降下法によって更新されるようにします。

ネットワークのトレーニングを実行する

ここまでに定義したネットワークを使ってトレーニングを行いますが、そのためのエポック数とバッチサイズを各々 30 と 16 に設定します。元になるデータセットのサイズ自体が小さいことから、バッチサイズも 16 と小さく設定し、30回の学習回数でトレーニングを行います。

トレーニングの実行
トレーニングは以下の処理を繰り返します。

  • 訓練用のバッチを準備
  • 予測値を計算し、目的関数を適用 (順伝播)
  • 勾配を計算 (逆伝播)
  • パラメータを更新

これに加えて、トレーニングがうまくいっているか判断するために「トレーニング用データ」を利用した分類精度と「検証データ」を利用した目的関数の値と分類精度を計算します。
numpy を別名 np でインポートした後、トレーニングを行います。

トレーニングが終了したので、目的関数の出力値(交差エントロピー誤差)と分類精度を各々グラフ化します。先ず目的関数の出力値を表示します。

4行目の matplotlib.use(‘Agg’) は、私はこの演習をCLI(Command Line Interface)環境で作業しているため実行結果をウィンドウ表示できないので、matplotlib のバックエンドをAggにすることで回避し、あとで plt.savefig() で任意の画像ファイルに保存しています。GUI環境で作業されている方は無視してください。

目的関数の出力値(交差エントロピー誤差)をグラフ化したものは以下になります。
fig1_Loss.png

横軸がエポック数、縦軸が目的関数の出力値です。トレーニングが進むにつれて目的関数の出力値(誤差)が減っていることから、トレーニングがうまくいっていることがわかります。
次に、分類精度(accuracy)のグラフを作成します。

fig2_Accuracy.png
横軸がエポック数、縦軸が分類精度です。こらのグラフでもトレーニングが進むにつれて分類精度が上昇していることから、トレーニングがうまくいっていることがわかります。

テストデータを用いた評価
前段でトレーニングが終わったので、トレーニング済ネットワークを使い、テスト用データに対する評価を行います。先ず、テスト用データで予測を行いますが、検証用データのとき同様 chainer.using_config(‘train’, False)chainer.using_config(‘enable_backprop’, False) を使います。

予測ができたら分類精度を計算し、確認します。

ネットワークの保存
一通り作業が終了したので、トレーニング済のネットワークを my_iris.net という名前で保存します。

念のため、保存されたかを確認します。

トレーニング済ネットワークを用いた推論処理

最後にトレーニング済ネットワークを用いて、テスト用データに対する推論処理を行います。
先ず、保存したネットワーク my_iris.net を読み込みます。 そのためにトレーニング済ネットワークと同様のクラスのインスタンスを作成します。

このインスタンスに対し、トレーニング済ネットワークのパラメータを読み込ませます。

トレーニング済ネットワークの準備ができたら、実際に推論処理を行います。推論するときには検証用データのとき同様、 chainer.using_config(‘train’, False)chainer.using_config(‘enable_backprop’, False) を使用します。

テスト用データに対する推論処理が終了したらテスト用データの 0 番目のサンプルの予測結果を確認します。
分類では以下のようにして、予測されたラベルを出力できます。

予測結果は 2 であることが確認できました。

Chainerを使って、あやめを分類をするためにモデル定義、学習、推論という一連の流れを見てきました。今回はここで終了です。
前回同様にフレームワークを利用することで効率的にディープラーニングできることが理解できたのではないかと思います。

今回の演習で使用したデータセットはデータの件数、パラメータが少ないため、CPUで実行しても短時間で処理が終了しましたが、大規模なデータセットであったり、中間層が多いようなネットワークの場合には処理時間が長くなります。そのようなときはGPUを利用することで高速化が可能になります。
Chainerのチュートリアルの応用編(15. Chainer の応用)にはGPUを使ったトレーニングの方法が記載されていますので、ご参照ください。
参考:15. Chainer の応用

次回予告 第5回:Pytorchでニューラルネットワーク

これまで利用してきたフレームワークと比べると比較的新しいフレームワークであるPytorch(パイトーチ)の利用方法を紹介します。

GPUサーバー


他の連載記事を読む

Tesla GPUサーバー

人気記事ランキングトップ10

コンテナサービス

なんでもできるVPS すぐにインスタンスの追加が可能!

カテゴリ一覧

新着記事Pick Up!