概要
衣類の画像データセット Fashion-MNIST を題材に、Pytorch で CNN モデルの構築、学習、及び推論を行う方法を学ぶチュートリアルです。
環境
コード全体は GitHub にあります。
このコードは、以下の環境で実行しました。
- OS
- Ubuntu: 18.04
- ライブラリ
- pytorch: 1.3.1
- torchvision: 0.4.2
- GPU の実行環境
- GPU: GeForce GTX 1080
- CUDA: 10.1
- CuDNN: 7
- CPU の実行環境
- CPU: Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
- メモリ: 16G
必要なモジュールを import する。
Fashion-MNIST
Fashion-MNIST は、オンラインショップ Zalando 上の衣類の画像から作成されたデータセットです。 6万枚の学習データ、1万枚のテストデータで構成されています。 各サンプルは大きさが 28×28 のグレースケール画像で、10クラスのいずれかがラベル付けされています。 機械学習アルゴリズムのベンチマークとして広く使われてきた MNIST を参考にして、作成されました。
![](/wp/wp-content/uploads/2020/03/pytorch-cnn-based-classification-model-with-fashion-mnist_01.jpg)
Fashin-MNIST を読み込む。
Fashin-MNIST は torchvision の datasets.FashionMNIST クラスで提供されています。
- 引数
root
: データセットを保存するディレクトリのパスを指定します。train
:True
を指定した場合は学習データ、False
を指定した場合はテストデータをダウンロードします。transform
: 画像に対して行う前処理を指定します。今回は、transforms.ToTensor のみを指定しました。 この Transformer は、画素値を範囲が [0, 255] の uint8 型から範囲が [0, 1] の float32 型にし、Pytorch で扱う Tensor に変換を行います。download
:True
を指定した場合は、データセットがローカルにない場合は、ネットからダウンロードします。
FashionMNIST オブジェクトを作成したら、utils.data.DataLoader に渡して、DataLoader を作成します。 この DataLoader は、指定したデータセットからデータを取得し、ミニバッチを作成して返す役割があります。
Fashion-MNIST の中身を確認する。
FashionMNIST
クラスの以下の属性で、クラス一覧や各画像、ラベルを取得できます。
FashionMNIST.classes
(list): クラス名一覧FashionMNIST.class_to_idx
(dict): クラス名、値がクラス ID の辞書FashionMNIST.data
(Tensor): 画像一覧FashionMNIST.targets
(Tensor): ラベル一覧
クラス一覧は以下のようになっています。
クラス ID | クラス名 (英語) | クラス名 (日本語) |
---|---|---|
0 | T-shirt/top | Tシャツ/トップス |
1 | Trouser | パンツ/ボトムズ |
2 | Pullover | セーター |
3 | Dress | ドレス |
4 | Coat | コート |
5 | Sandal | サンダル |
6 | Shirt | シャツ |
7 | Sneaker | スニーカー |
8 | Bag | バッグ |
9 | Ankle boot | ブーツ |
matplotlib で各クラスのサンプルを1つずつ表示して、確認します。
![](/wp/wp-content/uploads/2020/03/pytorch-cnn-based-classification-model-with-fashion-mnist_02.jpg)
CNN モデルを作成する
Pytorch で今回使用する以下の CNN モデルを作成していきます。
No | 層 | 層のパラメータ | 出力の形状 (B, C, H, W) |
---|---|---|---|
1 | 畳み込み層 | 出力数: 32 カーネルサイズ: (3, 3) ストライド: (1, 1) パディング: (1, 1) |
(None, 32, 28, 28) |
2 | ReLU | (None, 32, 28, 28) | |
4 | Max Pooling | カーネルサイズ: (2, 2) ストライド: (2, 2) |
(None, 32, 14, 14) |
3 | 畳み込み 層 | 出力数: 64 カーネルサイズ: (3, 3) ストライド: (1, 1) パディング: (1, 1) |
(None, 64, 14, 14) |
2 | ReLU | (None, 64, 14, 14) | |
4 | Max Pooling | カーネルサイズ: (2, 2) ストライド: (2, 2) |
(None, 64, 7, 7) |
6 | Flatten | (None, 3136) | |
5 | ドロップアウト | 脱落率: 0.5 | (None, 3136) |
7 | 全結合層 | 出力数: 128 | (None, 128) |
8 | ReLU | (None, 128) | |
9 | ドロップアウト | 脱落率: 0.5 | (None, 128) |
10 | 全結合層 | 出力数: 10 | (None, 10) |
11 | Log Softmax | (None, 10) |
コード
解説
Pytorch でモデルを作成するには、まず torch.nn.Module クラスを継承したクラスを作ります。
__init__()
内でそのモデルで使用する層を作成します。
畳み込み層
畳み込み層は torch.nn.Conv2d で作成します。 Keras と違い、Pytorch では入力のテンソルのチャンネル数も明示的に指定する必要があります。
- 引数
- in_channels: 入力のチャンネル数
- out_channels: 出力のチャンネル数
- kernel_size: カーネルサイズ
- stride: ストライド (デフォルトは1)
- stride: パディング (デフォルトは0)
Max Pooling
Max Pooling は torch.nn.MaxPool2d で作成します。
- 引数
- kernel_size: カーネルサイズ
- stride: ストライド (デフォルトは kernel_size と同じ値)
- stride: パディング (デフォルトは0)
全結合層
全結合層は torch.nn.Linear で作成します。
- 引数
- in_channels: 入力のチャンネル数
- out_channels: 出力のチャンネル数
活性化関数
ReLU は torch.nn.ReLU、log softmax は torch.nn.LogSoftmax で作成します。
ドロップアウト
ドロップアウトは torch.nn.Dropout で作成します。
- 引数
- p: 脱落率 (デフォルトは0.5)
損失関数を作成する。
出力層の活性化関数を log softmax としたので、それに合わせて、損失関数は torch.nn.NLLLoss を選択します。
デバイスを選択する
計算を実行するデバイスを選択します。 デフォルトでは、計算は CPU で実行するようになっていますが、CUDA が利用可能な場合は GPU で実行するようにします。 GPU が使用可能かどうかは torch.cuda.is_available() の値で確認できます。
GPU で実行する場合、計算に必要なテンソルはすべて GPU メモリ上にある必要があるので、モデルを Module.to() で GPU に転送します。 CPU 実行の場合は、メモリ上にすでにデータがあるため、なにも行われません。
最適化手法を選択する
モデルの重みパラメータの更新方法を選択します。 今回は Adam という手法て最適化を行います。
モデルで学習が必要なパラメータの一覧は Module.parameters() で取得できるので、それを torch.optim.Adam に渡して、Optimizer を作成します。
学習を行う関数を作成する
1エポック分の学習を行う処理を関数にします。
コード
解説
Module.train() で学習モードに設定します。 Batch Normalization や Dropout などは学習時と評価時で挙動が変わるため、明示的に設定する必要があります。
DataLoader はイテレータになっており、1回のループごとに、バッチサイズ分のデータ及びラベルを返します。データセットのすべてのサンプルを返した場合はループを抜けるようになっています。
ループ内では以下の順番で処理を行います。
モデル同様、データ及びラベルを計算を実行するデバイスに転送します。
データをモデルに渡して、順伝搬を行います。
損失を計算します。
Pytorch では、逆伝搬によって勾配を計算したあと、前回計算した勾配がある場合、それに今回計算した勾配を加算するようになっています。この仕様は RNN の学習では便利ですが、今回の CNN の学習では勾配を累加する必要はないので、逆伝搬を行う前に、勾配を Optimizer.zero_grad() で初期化します。
逆伝搬を行い、勾配を計算します。
Optimizer で計算した勾配を元に、モデルの重みパラメータを更新します。
学習の履歴を確認するために、精度を計算します。
モデルの出力のうち、確率の最も高いクラスを予測ラベルとします。
予測ラベル pred_target
と正解ラベル target
を比較し、一致する数、つまり、正答数を計算します。
全サンプル数は len(data_loader.dataset)
で取得できるので、この値で除算することで損失の平均及び精度を計算します。
評価を行う関数を作成する
モデルが過学習を起こしていないか確認するために、各エポックごとにテストデータに対する損失の平均及び精度を計算する関数も用意します。
コード
解説
Module.eval()) で学習モードに設定します。 Batch Normalization や Dropout などは学習時と評価時で挙動が変わるため、明示的に設定する必要があります。
コンテキストマネージャー torch.no_grad() を使うと、そのコンテキスト中での計算では、勾配計算に必要な情報をメモリ上に保存しなくなります。 評価時は勾配を計算する必要がないため、メモリ節約のために、このコンテキスト中で計算を行います。
学習する。
実際に学習を行ってみます。 エポック数は50とし、各エポックごとに学習、評価を順番に実行します。
損失関数の値の推移をグラフ化する。
matplotlib で損失、精度の推移を描画します。
![](/wp/wp-content/uploads/2020/03/pytorch-cnn-based-classification-model-with-fashion-mnist_03.jpg)
50 エポック分学習し、テストデータに対する精度が 93% のモデルを作ることができました。
まとめ
Fashion-MNIST を題材に、Pytorch で CNN モデルの構築、学習、及び推論を行う方法について見てきました。 Pytorch は今回のように単純なモデルからより複雑なモデルまで、シンプルにコーディングできる柔軟性に優れたライブラリです。
コメント