Pytorchでニューラルネットを構築して多クラス分類を試みる
概要
今回はPytorchでニューラルネットでの多クラス分類を試してみました。
Pytorchはkerasやchainerに並ぶDeep Learningフレームワークです。特に研究用途でよく使われているようです。
Pytorchに関しては以下の記事が詳しいので是非参考にしてください。
ウェブには画像データを使ったMNISTをやってる記事はいくつか見つかる一方で、もっと簡単なデータで試した事例がなかったのでirisデータを使って多クラス分類のニューラルネットをやってみようというのがモチベーションです。
MNISTで参考になった記事は以下。
コード
今回はirisデータを使いますので、まずはirisデータを読み込みます。 一旦データフレームにしているのは、個人的に加工しやすい形だと感じるからです。
import pandas as pd from sklearn import datasets iris = datasets.load_iris() df = pd.DataFrame(iris.data, columns=iris.feature_names) df['target'] = iris.target_names[iris.target]
次にクラスのラベルを数値に変換したり、 学習データと検証データに分けて、型をPytorchで扱えるものに変換します。
import torch import numpy as np #ラベルを数値化 y = np.array(df['target'].astype('category').cat.codes).astype(float) X = np.array(df.iloc[:, :4]) #学習データと検証データを分割 from sklearn.model_selection import train_test_split train_X, val_X, train_y, val_y = train_test_split( X, y, test_size = 0.2, random_state=71) # tensor型に変換 train_X = torch.Tensor(train_X) val_X = torch.Tensor(val_X) train_y = torch.LongTensor(train_y) val_y = torch.LongTensor(val_y)
次にネットワークの定義です。
import torch.nn as nn import torch.nn.functional as F from torch.autograd import Variable torch.manual_seed(71) #seed固定、ネットワーク定義前にする必要ありそう class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.fc1 = nn.Linear(4, 100) self.fc2 = nn.Linear(100, 50) self.fc3 = nn.Linear(50, 3) def forward(self, x): x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return F.log_softmax(x, dim = 1) model = Net() print(model)
以下のように出力されると思いますが、
Net( (fc1): Linear(in_features=4, out_features=100, bias=True) (fc2): Linear(in_features=100, out_features=50, bias=True) (fc3): Linear(in_features=50, out_features=3, bias=True) )
シンプルな3層のニューラルネットワークです。 活性化関数はreluを使っていて最後の出力の活性化関数がsoftmaxです。
学習は以下で行います。
ここではデータを流すときにmini batchは作っていないのですが、本来はbatch毎にデータを流す方がいいのかもしれないです(ご指摘ください)。
#学習 import torch.optim as optim optimizer = optim.SGD(model.parameters(), lr=0.02) train_loss = [] train_accu = [] i = 0 model.train() #学習モード for epoch in range(100): data, target = Variable(train_X), Variable(train_y)#微分可能な型 optimizer.zero_grad() #勾配初期化 output = model(data) #データを流す loss = F.nll_loss(output, target) #loss計算 loss.backward() #バックプロパゲーション train_loss.append(loss.data.item()) optimizer.step() # 重み更新 prediction = output.data.max(1)[1] #予測結果 accuracy = prediction.eq(target.data).sum().numpy() / len(train_X) #正解率 train_accu.append(accuracy) if i % 10 == 0: print('Train Step: {}\tLoss: {:.3f}\tAccuracy: {:.3f}'.format(i, loss.data.item(), accuracy)) i += 1 print('Train Step: {}\tLoss: {:.3f}\tAccuracy: {:.3f}'.format(i, loss.data.item(), accuracy))
結果は以下のように出て、stepが進むと精度が上がっていく様子が見られます。 学習がうまくいっていそうです。
Train Step: 0 Loss: 1.152 Accuracy: 0.317 Train Step: 10 Loss: 0.954 Accuracy: 0.683 Train Step: 20 Loss: 0.835 Accuracy: 0.683 Train Step: 30 Loss: 0.711 Accuracy: 0.683 Train Step: 40 Loss: 0.616 Accuracy: 0.692 Train Step: 50 Loss: 0.547 Accuracy: 0.717 Train Step: 60 Loss: 0.496 Accuracy: 0.783 Train Step: 70 Loss: 0.457 Accuracy: 0.867 Train Step: 80 Loss: 0.425 Accuracy: 0.917 Train Step: 90 Loss: 0.399 Accuracy: 0.925 Train Step: 100 Loss: 0.377 Accuracy: 0.942
最後に検証用データで精度を確認します。
#精度検証 model.eval() #推論モード outputs = model(Variable(val_X)) _, predicted = torch.max(outputs.data, 1) print('Accuracy: {:.3f}'.format(predicted.eq(val_y).sum().numpy() / len(predicted)))
正解率は、
Accuracy: 0.867
いい感じに学習できていますね!
今回使ったコードの全体は以下になります。
pytorch_nueral_net.ipynb · GitHub
所感
やはりkerasよりも少し難しいと思いました。
kerasではネットワークを定義してデータをfitさせれば完了ですが、Pytorchはネットワークを定義したあとにデータを流して、ロスを計算してパラメータ更新して等の処理を明示的に書く必要があります(結局用意された関数実行するだけなのでそんなに手間ではないのですが)。そこが慣れるまで大変そうかなと思います。
ただ一方でkerasよりも自由度はあると思いますし、最新の論文がPytorchで実装されていたりするということで、使いこなす価値はあると思うというのが個人的な印象です。
それでは間違い等ありましたらご指摘お願いいたします。