Help us understand the problem. What is going on with this article?

PyTorchでシンプルな多層ニューラルネットワークを作ろう

More than 1 year has passed since last update.

多層ニューラルネットワークの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

ソースコード

iris-net.py

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など、より複雑なモデルが利用されます。しかし、ベースとしている誤差逆伝播法やパラメータ更新などは同じです。ぜひ次のステップとして、手書き数字の認識などにチャレンジしてみてはいかがでしょうか。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした