機械学習・自然言語処理の勉強メモ

学んだことのメモやまとめ

PyTorch入門③:Logistic Regression

Logistic Regression



今回はLogistic Regression。
下記のチュートリアルを参考に実装した。
github.com

尚、コード全体はここに置いた。
github.com


データセットだが、チュートリアルはMNISTを使っているが、今回はsklearnのdigits datasetを使った。
(ロードが手っ取り早かったので。。)

import

必要なライブラリをインポート

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from torch.autograd import Variable
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
ネットワークの定義

ネットワーク自体は線形モデル。
下記のモデルはただの線形変換。

class LogisticNet(torch.nn.Module):
    def __init__(self, D_in, D_out):
        super(LogisticNet, self).__init__()
        self.linear = nn.Linear(D_in, D_out)

    def forward(self, x):
        lin = self.linear(x)
        return lin

損失関数nn.CrossEntropyLoss()の中で、softmax関数が含まれているので、モデル自体は線形変換でも構わないみたい。
これは、下記のようにsoftmax関数を通して、損失関数nn.NLLLoss()で評価するのと同じ。

class LogisticNet(torch.nn.Module):
    def __init__(self, D_in, D_out):
        super(LogisticNet, self).__init__()
        self.linear = nn.Linear(D_in, D_out)

    def forward(self, x):
        lin = self.linear(x)
        # return lin
        return F.log_softmax(lin)
train

訓練データを使って下記の処理を順番に実行。

  • 訓練データをテンソル変数で定義
  • 勾配初期化
  • 訓練データの出力値計算
  • 出力(予測値)と正解データの損失値の計算
  • 損失値に基づく勾配計算
  • 最適化関数によるパラメータ更新
def train(model, loss_func, optimizer, trX, trY):
    x = Variable(trX, requires_grad=False)
    y = Variable(trY, requires_grad=False)
    optimizer.zero_grad()
    y_pred = model(x)
    loss = loss_func(y_pred, y)
    loss.backward()
    optimizer.step()
    return loss.data[0]
valid

評価データを使って下記の処理を順番に実行。

  • 評価データをテンソル変数で定義
  • 評価データの出力値計算
  • 出力値の最大値のインデックス取得
  • 出力(予測値)と正解データの一致数を計算
  • 一致率(正解数/評価データ数)の計算
def valid(model, loss_func, valX, valY):
    x = Variable(valX, requires_grad=False)
    y = Variable(valY, requires_grad=False)

    outputs = model(x)
    val_loss = loss_func(outputs, y)
    _, predY = torch.max(outputs.data, 1)
    correct = (predY == y.data).sum()
    val_acc = float(correct) / y.size(0)
    return val_loss.data[0], val_acc

torch.maxについて
モデルの出力はデータ数×クラス数の行列。
一方、正解データはデータ数次元のベクトル。
このままでは比べられないので、torch.maxで出力を変換する。
torch.maxは、行列(ベクトル)の最大値とそのインデックスを返す。

下記の行列で確認する。

0.2 1.4 1.7
0.3 1.5 0.7
1.1 1.3 0.4
0.9 2.3 0.5
testX = np.array([[0.2, 1.4, 1.7],[0.3, 1.5, 0.7],[1.1, 1.3, 0.4],[0.9, 2.3, 0.5]])
testY = np.array([[2, 1, 0, 1]])
x = Variable(torch.from_numpy(testX).float(), requires_grad=False)
y = Variable(torch.from_numpy(testY.astype(np.int64)), requires_grad=False)
value, index = torch.max(x.data, 0)  # row
>> value
 1.1000
 2.3000
 1.7000
[torch.FloatTensor of size 3]

>> index
 2
 3
 0
[torch.LongTensor of size 3]
value, index = torch.max(x.data, 1)  # colomn
>> value
 1.7000
 1.5000
 1.3000
 2.3000
[torch.FloatTensor of size 4]

>> index
 2
 1
 1
 1
[torch.LongTensor of size 4]

列方向で最大値を取ったインデックスのベクトルがモデルのラベル予測値に相当する。
これにより、予測時にソフトマックス関数を通す必要がなくなる。

main

データのロードと分割
ここは、sklearnのライブラリを利用する。

digits = load_digits()
data = digits['data']
target = digits['target']
# separate data
trX, teX, trY, teY = train_test_split(data, target, test_size=0.2, random_state=0)

学習の実行
モデルや損失関数などを定義して、繰り返し処理を回す。

n_samples = trX.shape[0]
input_dim = trX.shape[1]
n_classes = 10
model = LogisticNet(input_dim, n_classes)
optimizer = optim.SGD(model.parameters(), lr=0.0001, momentum=0.9)
loss_func = nn.CrossEntropyLoss()

trX = torch.from_numpy(trX).float()
teX = torch.from_numpy(teX).float()
trY = torch.from_numpy(trY.astype(np.int64))
teY = torch.from_numpy(teY.astype(np.int64))

N_EPOCHS = 200

for epoch in range(N_EPOCHS):
    loss = train(model, loss_func, optimizer, trX, trY)
    val_loss, val_acc = valid(model, loss_func, teX, teY)
    print 'val loss:%.3f val acc:%.3f' % (val_loss, val_acc)