pytorchでlogistic回帰をやってみる

設定

例によって設定から。今回はみんな大好きirisで試します。

1
2
3
4
5
6
7
8
9
from sklearn.datasets import load_iris

import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F
import torch.optim as optim

import matplotlib.pyplot as plt

データのインポート

まずは読み込んで、、、

1
2
## irisのデータセットのload
iris = load_iris()

どんなデータだったかみたい場合は、iris.DESCRで確認しましょう。print()を使った方が読みやすいと思います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
print(iris.DESCR)
#.. _iris_dataset:
#
#Iris plants dataset
#--------------------
#
#**Data Set Characteristics:**
#
# :Number of Instances: 150 (50 in each of three classes)
# :Number of Attributes: 4 numeric, predictive attributes and the class
# :Attribute Information:
# - sepal length in cm
# - sepal width in cm
# - petal length in cm
# - petal width in cm
# - class:
# - Iris-Setosa
# - Iris-Versicolour
# - Iris-Virginica

Setosa, Versicolour, Virginicaの3種別あるで、多クラス分類ですね。

データをpytorchが読めるようにする

中身を見てみましょうか。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
## 説明変数用(numpy形式で格納)
iris.data[:5] ## はじめの5行分だけ眺めてみます

#array([[5.1, 3.5, 1.4, 0.2],
# [4.9, 3. , 1.4, 0.2],
# [4.7, 3.2, 1.3, 0.2],
# [4.6, 3.1, 1.5, 0.2],
# [5. , 3.6, 1.4, 0.2]])

## こっちは目的変数用(こっちもnumpy形式ですね)
iris.target
#array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
# 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
# 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
# 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
# 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
# 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
# 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])

値が0, 1, 2という値ですね。
多クラス分類を考える時、このようなラベル名をそのまま当てはめる場合とベクトルに直さないとライブラリが使えなくなったりするので、要注意ですね。つまり、

のようにベクトル化することが必要な場合とがあります。

1
2
train_X = torch.Tensor(iris.data)       ## torch.Tensor型: Pytorch版のnumpy形式
train_y = torch.LongTensor(iris.target) ## 目的変数が上記のような0,1,2のような整数型の場合はtorch.LongTensorを利用(無邪気にtorch.Tensorとすると学習の時に怒られます)

通常、学習データ、評価データを分割して過学習(overfit)してないか?とか見るのが通常ですが、
今回は取り敢えずモデルが作れるところを主軸にしたいので敢えて学習しかしてません。

モデルの骨格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
## まずはNeural Networkを使うよってことでnn.Moduleを継承します
class Net(nn.Module):
## 個々で
def __init__(self):
## nn.Moduleを継承するよってことで。
super(Net, self).__init__()
## 第1層:4つの変数を100個の変数に展開します
self.fc1 = nn.Linear(4, 100)
## 第2層:100個→50個に
self.fc2 = nn.Linear(100, 50)
## 第3層:50個を最終的なラベルの数3に分類させます
self.fc3 = nn.Linear(50, 3)
def forward(self, x):
## 第1層と第2層をreluをかました後に繋ぎます
x = F.relu(self.fc1(x))
## 第2層と第3層をreluをかました後に繋ぎます
x = F.relu(self.fc2(x))
x = self.fc3(x)
## 最後にlog softmax(ココは普通にF.softmax()に変えても処理は回ります。)で出力
return F.log_softmax(x, dim = 1)

モデルの骨格が出来たのでメモリに載せます(インスタンス化)

1
2
3
4
5
6
7
8
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)
#)

学習させます!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
## 誤差の収束方法をSGD(Stochastic Gradient Descent), lr(learning rate)を設定
optimizer = optim.SGD(model.parameters(), lr=0.02)
## エポック数(何回学習させるか)
epochs = 1000
## 後の可視化用に取っておく
loss_log, accuracy_log = [], []

data, target = Variable(train_X), Variable(train_y)
for epoch in range(epochs):
## pytorchでは勾配を蓄積する仕組みなので更新前に初期化しておきます
optimizer.zero_grad()
## feed forward(つまり予測させます)
output = model(data)
## 予実差からの誤差を決めます。nll_lossは、Negative Log Likelihood(負の対数尤度)ですね。
loss = F.nll_loss(output, target)

## Back Propagation
loss.backward()
## 各層内のパラメータの更新
optimizer.step()

## 正解率の計算
prediction = output.data.max(1)[1]
accuracy = prediction.eq(target.data).sum().numpy() / len(iris.data)

## 記録を取っておきます
loss_log.append(loss.item())
accuracy_log.append(accuracy)

if epoch % 100 == 0:
print('Train Step: {}\tLoss: {:.3f}\tAccuracy: {:.3f}'.format(epoch, loss.data.item(), accuracy))

#Train Step: 0 Loss: 1.092 Accuracy: 0.360
#Train Step: 100 Loss: 0.366 Accuracy: 0.960
#Train Step: 200 Loss: 0.203 Accuracy: 0.973
#Train Step: 300 Loss: 0.133 Accuracy: 0.980
#Train Step: 400 Loss: 0.105 Accuracy: 0.980
#Train Step: 500 Loss: 0.091 Accuracy: 0.987
#Train Step: 600 Loss: 0.083 Accuracy: 0.980
#Train Step: 700 Loss: 0.077 Accuracy: 0.980
#Train Step: 800 Loss: 0.073 Accuracy: 0.980
#Train Step: 900 Loss: 0.071 Accuracy: 0.980

学習過程を図示します

1
2
3
4
5
6
7
8
9
plt.figure()
plt.plot(range(epochs), loss_log, label='train loss')
plt.legend()
plt.title('Loss transition via epochs')

plt.figure()
plt.plot(range(epochs), accuracy_log, label='accuracy')
plt.legend()
plt.title('Accuracy transition via epochs')

Lossの変化
Accuracyの変化

References