このチュートリアルではkaokoreデータに対して
- データの読み込み
- データのプロットと確認
- pytorchを用いたCNNモデルの作成、学習
- 誤識別したデータの確認
- 提出データの作成
を行います
まずは必要なライブラリをインポートしましょう。
import numpy as np
import pandas as pd
import torch
from torch import nn, optim
import torchvision
from torchvision import models
import torchvision.transforms as transforms
import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline
環境
! python --version
Python 3.7.4
print('numpy', np.__version__)
print('pandas', pd.__version__)
print('matplotlib', matplotlib.__version__)
print('torch', torch.__version__)
print('torchvision', torchvision.__version__)
numpy 1.17.4
pandas 1.0.2
matplotlib 3.1.3
torch 1.4.0
torchvision 0.5.0
データの読み込み
まずはデータの読み込みをしてみましょう。
このコンペでは画像とラベルの組み合わせからなる学習データ(train_data.npz
)と
画像のみを含むテストデータ(test_data.npz
)が提供されています。
npzファイルをnp.load
関数を用いて読み込みます。
data_dir = '../compe_data/'
train_X, train_Y = np.load(data_dir + 'train_data.npz').values()
読み込まれたデータは(データ数, 高さ, 幅, チャンネル)
の順番で格納されています。
pytorchで利用できる形式である(データ数, チャンネル, 高さ, 幅)
の順に変更します。
train_X = train_X.transpose((0, 3, 1, 2))
pytorchではDataLoader
を用いることで、バッチごとにデータを取り出すことができます。
train_data = torch.utils.data.TensorDataset(torch.from_numpy(train_X).float(), torch.from_numpy(train_Y.astype(np.int64)))
train_loader = torch.utils.data.DataLoader(train_data, batch_size=64, shuffle=True)
データをプロットして確認
作成したtrain_loader
を用いていくつかデータを表示してみます。ここではmatplotlib
を用いてプロットをします。
class_names = ('noble', 'warrior', 'incarnation', 'commoner')
def imshow(data, title=None):
"""Imshow for Tensor."""
data = data.numpy().transpose((1, 2, 0))
data = (data / 255)
data = np.clip(data, 0, 1)
plt.imshow(data)
if title is not None:
plt.title(title)
inputs, classes = next(iter(train_loader))
out = torchvision.utils.make_grid(inputs[:4])
imshow(out, title=[class_names[x] for x in classes[:4]])
pytorchを用いたCNNモデルの作成、学習
DNNのフレームワークであるpytorchを用いてCNNを作成して分類をおこなっていきます。
まず、GPUを用いてCNNの学習を行いたいのでCUDAが利用可能な場合にはCUDAを有効にします。
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
torchvisionはpytorchのパッケージで画像認識において頻繁に使われるネットワークアーキテクチャや学習済みモデル、データセット、前処理などの機能を提供しています。
今回はtorchvisionに含まれるresnet18
というモデルを用いて学習を行います。
model = models.resnet18()
今回は4クラスの分類を行うのでネットワークの最終層の出力の次元数を変更します。
num_ftrs = model.fc.in_features
model.fc = nn.Linear(num_ftrs, 4)
作成したmodel
をGPUへ転送します。
model = model.to(device)
損失関数と最適化アルゴリズムを設定します。分類問題のためクロスエントロピー損失関数を利用し、
最適化アルゴリズムにはSGDを利用します。
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
ここから、実際にモデルの学習を行っていきます。
to
メソッドによって入力とラベルをGPUへ転送しています。
optimizer.zero_grad()
を呼び出して、ネットワークの各パラメータに保存されている勾配情報を初期化します。
モデルの出力outputs
と正答ラベルlabels
から
損失関数の値を計算します。
loss.backward()
によって誤差逆伝播を行い各パラメータの勾配を更新します。
optimizer.step()
によって更新された勾配を元にパラメータの値を更新します。
log_interval = 10
for epoch in range(10):
running_loss = 0.0
for i, data in enumerate(train_loader, 0):
inputs, labels = data
inputs /= 127.5
inputs -= 1.0
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if i % log_interval == (log_interval - 1):
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / (log_interval)))
running_loss = 0.0
print('Finished Training')
[1, 10] loss: 1.333
[1, 20] loss: 1.195
[1, 30] loss: 1.009
[1, 40] loss: 1.346
[1, 50] loss: 1.267
[1, 60] loss: 1.018
[1, 70] loss: 1.146
[1, 80] loss: 1.160
[1, 90] loss: 1.046
[1, 100] loss: 1.219
[1, 110] loss: 1.264
[1, 120] loss: 1.119
[1, 130] loss: 1.164
[1, 140] loss: 1.089
[1, 150] loss: 1.310
[1, 160] loss: 1.244
[1, 170] loss: 1.220
Finished Training
学習したモデルを用いて学習データに対する正答率を見てみましょう。
モデルの出力はあるクラスに属する確率値のようなものなのでtorch.max
関数を用いて各出力で最も大きい値を持つインデックスを取得します。
この予測されたラベルを用いて正答率を計算します。
correct = 0
total = 0
wrong_data = []
with torch.no_grad():
for data in train_loader:
images, labels = data
images /= 127.5
images -= 1.0
images, labels = images.to(device), labels.to(device)
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('accuracy', 100 * correct / total)
accuracy 79.59975178405213
学習データにおける正答率は80%程度でした。
誤識別したデータを確認
続いて誤識別したデータを確認します。
正答率を求めたときとは逆に、予測されたラベルが正答ラベルとことなるデータのインデックスを取得し、プロットします。
grid = torchvision.utils.make_grid(images[predicted != labels].cpu()[:5])
grid += 1.0
grid *= 127.5
imshow(grid, title=['predict:'+ str(pred.item())+' actual:' + str(truth.item()) for pred, truth in zip(predicted[predicted != labels][:10], labels[predicted != labels].data)][:5])
このモデルでは、後姿を誤認識してしまい”化身”と認識してしまうことがあるようです。
提出データの作成
最後にテストデータを用いて提出データを作成します。
学習データと同様にtest_data.npz
を読み込み、DataLoader
を作成します。
test_X = np.load(data_dir + 'test_data.npz')['arr_0']
test_X = test_X.transpose((0, 3, 1, 2))
test_data = torch.utils.data.TensorDataset(torch.from_numpy(test_X).float())
test_loader = torch.utils.data.DataLoader(test_data, batch_size=64, shuffle=True)
predicts = []
with torch.no_grad():
for x in test_loader:
x = x[0]
x /= 127.5
x -= 1.0
x = x.to(device)
out = model(x)
_, predicted = torch.max(out.data, 1)
predicts.append(predicted.cpu().numpy())
提出データはid
,y
のカラムを持つcsvファイルとします。
sub = pd.DataFrame(np.concatenate(predicts), columns=['y'])
sub.index.name = 'id'
sub.index += 1
sub.to_csv('submission.csv')