多層ニューラルネットワークのHello Worldとして、深層学習ライブラリのPyTorchを使ってアヤメのデータを分類してみましょう。実際に自分の手で多層ニューラルネットワークを作り、深層学習の基本的な構造が体験できます。深層学習には他の機械学習とはまた違った奥深さがあります。ぜひ、エキサイティングなコーディング時間を楽しみましょう!
##前提環境
簡便のため環境はクラウドを利用します。ここではAmazon Web ServicesのDeep Learning AMIを利用し、関連パッケージがプリインストールされた状態でインスタンス作成ができ便利です。また、OSはUbuntu、Deep Learning AMIのバージョンは5を利用します。なお、GPUインスタンスはp2.xlargeを選択しました。インスタンスを起動させてSSHでサーバに接続したら、まずPythonの環境をPyTorch用に切り替えましょう。
$ source activate pytorch_p36
それでは、pythonを起動し、PyTorchが利用できるか確かめてみましょう。
$ python -i
>>> import torch
>>>
エラーが出力されなければPyTorchが利用できる状態です。
##データセット
機械学習のサンプルデータセットとしてよく用いられる、フィッシャーのアヤメデータを使います。サンプル数は150あり、4つの実数からなる特徴量と、アヤメの種類に対応する正解インデックスです。Pythonの機械学習ライブラリであるscikit-learnを利用すると、簡単にデータセットを読み込みことができます。
from sklearn import datasets
...
iris = datasets.load_iris()
前処理として、正解ラベルをone-hot-vector表現のベクトルに変換します。
y = np.zeros((len(iris.target), 1 + iris.target.max()), dtype=int)
y[np.arange(len(iris.target)), iris.target] = 1
次に、教師データの25%をテストデータとし、予測精度の評価のためにとっておきます。残りを訓練データとして使います。慣例に従い、Xを特徴量、yを正解として表します。
from sklearn.model_selection import train_test_split
...
X_train, X_test, y_train, y_test = train_test_split(iris.data, y, test_size=0.25)
また、これらをPyTorchで取り扱える形式に変換します。
x = Variable(torch.from_numpy(X_train).float(), requires_grad=True)
y = Variable(torch.from_numpy(y_train).float())
##ネットワーク定義
ここでは、シンプルな多層ニューラルネットワークとして、2つの隠れ層を持つネットワークを作ります。概念図は以下です。
ここで、上図のfc1は入力に重みとバイアスによる線形変換を行う関数であり、これにより隠れ層を定義していると理解できます。入力と出力が全結合(fully connected)であるためfc1としています。また、fc2、fc3も同様です。これらの関数は順伝播の際にforwardメソッド内で実行され、活性化関数のReLU関数に与えられます。
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(4, 10)
self.fc2 = nn.Linear(10, 8)
self.fc3 = nn.Linear(8, 3)
def forward(self, x):
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
##学習
実際の学習に際して、以下のコードによりネットワークのインスタンス化、パラメータ更新手法の指定、学習率の指定、コスト関数の指定を行います。
net = Net()
optimizer = optim.SGD(net.parameters(), lr=0.01)
criterion = nn.MSELoss()
そして、実際の学習として3000エポックのループを回し、パラメータを更新していきます。
for i in range(3000):
optimizer.zero_grad()
output = net(x)
loss = criterion(output, y)
loss.backward()
optimizer.step()
##評価
学習の評価には、冒頭に退避しておいたテスト用データを使うことができます。ここでは、ネットワークは確率分布のような実数ベクトルが出力されるので、最大値のインデックスと正解のインデックスを比較することで、予測が正しいかがわかります。したがって、正解個数をテストデータ数で割ることで予測精度を計算することができます。
outputs = net(Variable(torch.from_numpy(X_test).float()))
_, predicted = torch.max(outputs.data, 1)
y_predicted = predicted.numpy()
y_true = np.argmax(y_test, axis=1)
accuracy = (int)(100 * np.sum(y_predicted == y_true) / len(y_predicted))
print('accuracy: {0}%'.format(accuracy))
以下が実行結果です。
$ python -i iris-net.py
accuracy: 97%
>>>
また、架空の未知のデータ(5.6, 4.3, 1.5, 0.35)に対する予測ラベルを計算してみましょう。
>>> predict((5.6, 4.3, 1.5, 0.35))
0
##ソースコード
import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
# network definition
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc1 = nn.Linear(4, 10)
self.fc2 = nn.Linear(10, 8)
self.fc3 = nn.Linear(8, 3)
def forward(self, x):
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
iris = datasets.load_iris()
y = np.zeros((len(iris.target), 1 + iris.target.max()), dtype=int)
y[np.arange(len(iris.target)), iris.target] = 1
X_train, X_test, y_train, y_test = train_test_split(iris.data, y, test_size=0.25)
x = Variable(torch.from_numpy(X_train).float(), requires_grad=True)
y = Variable(torch.from_numpy(y_train).float())
net = Net()
optimizer = optim.SGD(net.parameters(), lr=0.01)
criterion = nn.MSELoss()
for i in range(3000):
optimizer.zero_grad()
output = net(x)
loss = criterion(output, y)
loss.backward()
optimizer.step()
# test
outputs = net(Variable(torch.from_numpy(X_test).float()))
_, predicted = torch.max(outputs.data, 1)
y_predicted = predicted.numpy()
y_true = np.argmax(y_test, axis=1)
accuracy = (int)(100 * np.sum(y_predicted == y_true) / len(y_predicted))
print('accuracy: {0}%'.format(accuracy))
# utility function to predict for an unknown data
def predict(X):
X = Variable(torch.from_numpy(np.array(X)).float())
outputs = net(X)
return np.argmax(outputs.data.numpy())
##まとめ
ここでは、多層ニューラルネットワークのHello Worldとして、シンプルなネットワーク構造による深層学習を実装しました。応用として、コンピュータビジョンや自然言語処理などの実タスクでは、畳み込みネットワーク、LSTMやGANなど、より複雑なモデルが利用されます。しかし、ベースとしている誤差逆伝播法やパラメータ更新などは同じです。ぜひ次のステップとして、手書き数字の認識などにチャレンジしてみてはいかがでしょうか。