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)