記事の内容
今回は、深層学習(ディープラーニング)を手軽に実装するためのライブラリである「pytorch」を紹介します。
実際に、深層学習入門の定番である、手書き文字認識にチャレンジしてみます。
・深層学習の基礎はなんとなくわかっている
・pytorchを使った実装をざっと確認したい
こんな方におすすめの記事になります。
pytorchをつかうための簡単な説明と、実装をまとめます。それでは、目次をご覧ください。
深層学習をざっくりと
教師あり学習で、深層学習をする場合、二つのフェイズにわけて実装する。学習と推論だ。
学習フェイズでは、出力結果と期待される結果に誤差がうまれる。この誤差を表すのが、誤差関数(損失関数)だ。この誤差を小さくするために、結合パラメータを調整する方法が、勾配降下法である。深層学習では、全部ではないが複数個ずつデータを使うミニバッチ学習が一般的である。
学習フェイズの流れ
深層学習の学習フェイズにおけるざっくりとした流れ。
1 推論フェイズと同様に入力から出力を求める
2 期待する出力と実際の出力の誤差関数をもとめる
3 誤差に対する各重みの微分値をバックプロパゲーションで求める
4 重みの微分値に応じて、重みを更新する
5 1に戻る
PyTorch
1 データの前処理
2 DataLoaderの作成
3 ネットワークの構築
4 誤差関数と最適化手法の設定
5 学習と推論の設定
6 学習と推論の実行
この流れに沿って、実装してみる。
今回、この記事で紹介する説明とコードは、おもに「つくりながら学ぶ 深層強化学習」という本を参考にしています。
1 データの前処理
データは、おきまりの「手書き数字の画像データMNIST」を使用。
# 手書き数字の画像データMNISTをダウンロード from sklearn.datasets import fetch_openml mnist = fetch_openml('mnist_784', version=1, data_home=".") # data_homeは保存先を指定
# 1. データの前処理(画像データとラベルに分割し、正規化) print(mnist.data[2020]) X = mnist.data / 255 # 0~255を0~1に正規化 y = mnist.target # 注意‼ MNISTのデータセットの変更により、ラベルが数値データになっていないので、NumPyの配列の数値型に変換 import numpy as np y = np.array(y) y = y.astype(np.int32) print(X[2020]) print(y) type(y[0]) #変換後の型を確認
2 DataLoaderへの変換
使用するデータを「DataLoader」という変数へ変換してあげる必要がある。
# 2. DataLoderの作成 import torch from torch.utils.data import TensorDataset, DataLoader from sklearn.model_selection import train_test_split # 2.1 データを訓練とテストに分割(6:1) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=1/7, random_state=0) # 2.2 データをPyTorchのTensorに変換 X_train = torch.Tensor(X_train) X_test = torch.Tensor(X_test) y_train = torch.LongTensor(y_train) y_test = torch.LongTensor(y_test) # 2.3 データとラベルをセットにしたDatasetを作成 ds_train = TensorDataset(X_train, y_train) ds_test = TensorDataset(X_test, y_test) # 2.4 データセットのミニバッチサイズを指定した、Dataloaderを作成 loader_train = DataLoader(ds_train, batch_size=64, shuffle=True) #訓練データはランダムにしたい loader_test = DataLoader(ds_test, batch_size=64, shuffle=False)
3 ネットワークの構築
kerasの書き方に近い。
# 3. ネットワークの構築 # Keras風の書き方 from torch import nn model = nn.Sequential() model.add_module('fc1', nn.Linear(28*28*1, 100)) #28*28*1=784の入力、出力は100 model.add_module('relu1', nn.ReLU()) model.add_module('fc2', nn.Linear(100, 100)) #入力100、出力100 model.add_module('relu2', nn.ReLU()) model.add_module('fc3', nn.Linear(100, 10)) #出力は、0~9の10 print(model)
4 誤差関数と最適化手法
# 4. 誤差関数と最適化手法の設定 from torch import optim # 誤差関数の設定 loss_fn = nn.CrossEntropyLoss() # クロスエントロピー誤差 # 勾配降下法(Adam) optimizer = optim.Adam(model.parameters(), lr=0.01)
5 学習と推論
1 epochの間に、64個ずつデータを使用してミニバッチ学習を進める。全データを使用したら、1 epoch終了。
注意点:実行する際には、それぞれ学習モード、推論モードにネットワークを設定する必要がある。
# 5-1. 学習1回でやることを定義します def train(epoch): model.train() # 学習モードに切り替える # データローダーから1ミニバッチずつ取り出して計算する for data, targets in loader_train: optimizer.zero_grad() # 一度計算された勾配結果を0にリセット outputs = model(data) # 入力dataをinputし、出力を求める loss = loss_fn(outputs, targets) # 出力と訓練データの正解との誤差を求める loss.backward() # 誤差のバックプロパゲーションを求める optimizer.step() # バックプロパゲーションの値で重みを更新する print("epoch{}:終了\n".format(epoch))
# 5-2. 推論1回でやることを定義します # Chainerのtrainer.extend(extensions.Evaluator())に対応するものはない def test(): model.eval() # ネットワークを推論モードに切り替える correct = 0 # データローダーから1ミニバッチずつ取り出して計算する with torch.no_grad(): # 微分は推論では必要ない for data, targets in loader_test: outputs = model(data) # 入力dataをinputし、出力を求める # 推論する _, predicted = torch.max(outputs.data, 1) # 確率が最大のラベルを求める correct += predicted.eq(targets.data.view_as(predicted)).sum() # 正解と一緒だったらカウントアップ # 正解率を出力 data_num = len(loader_test.dataset) # データの総数 print('\nテストデータの正解率: {}/{} ({:.0f}%)\n'.format(correct, data_num, 100. * correct / data_num))
6 実行
3 epochの学習してみる。
# 6. 学習と推論の実行 for epoch in range(3): train(epoch) test()
今回の場合、実行結果は、「テストデータの正解率: 9610/10000 (96%)」だった。