suzuzusu日記

(´・ω・`)

GPyTorchで多クラス分類

GPyTorchとは

PyTorch上で実装されたガウス過程(GP)のライブラリである. ハイパーパラメータをPyTorchのシステムを使用してGPUなどで最適化できるのが特徴である. GPは逆行列を計算する際に \mathcal{O}(N^ 3) の計算量がかかり,スケーラビリティに難があるが,PyTorchのシステムを使ってそれを補おうとしている.

GPでは尤度を変更することで回帰や二値分類,多クラス分類が可能となる. 今回はアヤメ(iris)データセットを用いて3クラスの多クラス分類をGPyTorchで分類してみる.

コード

# -*- coding: utf-8 -*-

import torch
import gpytorch
import numpy as np

from gpytorch.models import AbstractVariationalGP
from gpytorch.variational import CholeskyVariationalDistribution
from gpytorch.mlls.variational_elbo import VariationalELBO

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split


class GPMultiClassificationModel(AbstractVariationalGP):
    def __init__(self, num_dim=2, grid_bounds=(-10., 10.), grid_size=64):
        variational_distribution = gpytorch.variational.CholeskyVariationalDistribution(
            num_inducing_points=grid_size, batch_size=num_dim
        )
        variational_strategy = gpytorch.variational.AdditiveGridInterpolationVariationalStrategy(
            self, grid_size=grid_size, grid_bounds=[grid_bounds], num_dim=num_dim,
            variational_distribution=variational_distribution, mixing_params=False, sum_output=False
        )
        super().__init__(variational_strategy)
        self.mean_module = gpytorch.means.ConstantMean()
        self.covar_module = gpytorch.kernels.ScaleKernel(gpytorch.kernels.RBFKernel())

    def forward(self, x):
        mean_x = self.mean_module(x)
        covar_x = self.covar_module(x)
        latent_pred = gpytorch.distributions.MultivariateNormal(mean_x, covar_x)
        return latent_pred

device = 'cuda' if torch.cuda.is_available() else 'cpu'

iris = load_iris()
# 特徴量は2次元
# testデータは3割
(train_x, test_x, train_y, test_y) = train_test_split(iris.data[:, [0, 2]].astype(np.float32), iris.target, test_size=0.3, random_state=0,)
# 3クラス分類
n_labels = len(np.unique(train_y))

train_x = torch.from_numpy(train_x).to(device)
train_y = torch.from_numpy(train_y).to(device)
test_x = torch.from_numpy(test_x).to(device)
test_y = torch.from_numpy(test_y).to(device)

model = GPMultiClassificationModel(num_dim=2).to(device)

# 多クラス分類なのでSoftmaxを使用する
likelihood = gpytorch.likelihoods.SoftmaxLikelihood(num_classes=n_labels, num_features=2).to(device)

# 周辺尤度
mll = VariationalELBO(likelihood, model, train_y.numel())

optimizer = torch.optim.Adam(model.parameters(), lr=0.1)

# train
model.train()
likelihood.train()
training_iter = 10000
for i in range(training_iter):
    optimizer.zero_grad()
    output = model(train_x)
    loss = -mll(output, train_y)
    loss.backward()
    print('\rIter %d/%d - Loss: %.3f' % (i + 1, training_iter, loss.item()), end='')
    optimizer.step()
print('')

# test
model.eval()
likelihood.eval()
correct = 0.0
# sample数を64とする
with torch.no_grad(), gpytorch.settings.num_likelihood_samples(64):
    data = test_x
    target = test_y 
    output = likelihood(model(data))
    pred = output.probs.mean(0).argmax(-1)
    correct += pred.eq(target.view_as(pred)).cpu().sum()
    acc = correct.numpy() / float(len(test_x)) * 100.0
    print('Accuracy:', acc)

出力結果

Iter 10000/10000 - Loss: 0.397
Accuracy: 86.66666666666667

gist

gist.github.com

参考