あかすくぱるふぇ

同人サークル「あかすくぱるふぇ」のブログです。

pytorchでVGGをfinetuneし、cifar-100の分類を行います。
コードは以下の通り。
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms, models
from torch.autograd import Variable
from PIL import Image

class Trainer():
def __init__(self, model, optimizer, train_loader):
self.model = model
self.optimizer = optimizer
self.train_loader = train_loader

def train_epoch(self):
self.model.train()
for batch_idx, (data, target) in enumerate(self.train_loader):
data, target = Variable(data.cuda()), Variable(target.cuda())
self.optimizer.zero_grad()
output = self.model(data)
criterion = nn.CrossEntropyLoss().cuda()
loss = criterion(output, target)
loss.backward()
optimizer.step()
print('[{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.data[0]))

def test(model, test_loader):
model.eval()
correct = 0
for batch_idx, (data, target) in enumerate(test_loader):
data, target = Variable(data.cuda(), requires_grad=False), Variable(target.cuda())
output = model(data)
pred = output.data.max(dim=1)[1]
correct += pred.eq(target.data).cpu().sum()

print('Accuracy: %d %%' % (
100 * correct / len(test_loader.dataset)))


if __name__ == '__main__':

# ネットワーク生成
model = models.vgg11(pretrained=True)

# ネットワークの変形
for p in model.features.parameters():
p.requires_grad = False
model.classifier = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 100)
)
model.cuda()

# cifar100 datasetの準備
transform = transforms.Compose([
transforms.Resize((224, 224), interpolation=Image.BICUBIC),
transforms.ToTensor()
])
train_set = datasets.CIFAR100(root='../data', train=True, transform=transform, download=True)
test_set = datasets.CIFAR100(root='../data', train=False, transform=transform, download=True)
batch_size = 50
train_loader = torch.utils.data.DataLoader(
train_set, batch_size=batch_size, shuffle=True, num_workers=1, pin_memory=True)
test_loader = torch.utils.data.DataLoader(
test_set, batch_size=batch_size, shuffle=True, num_workers=1, pin_memory=True)

# optimizer, trainer生成
optimizer = optim.SGD(model.classifier.parameters()), lr=0.01, momentum=0.5)
trainer = Trainer(model, optimizer, train_loader)

# 学習
epoch_num = 10
for epoch in range(epoch_num):
print('epoch :', epoch)
trainer.train_epoch()
test(model, test_loader)



わかりにくいと思われるところを解説します。
# ネットワークの変形
for p in model.features.parameters():
p.requires_grad = False

# optimizer, trainer生成
optimizer = optim.SGD(model.classifier.parameters(), lr=0.01, momentum=0.5)
conv層(model.features)のパラメータを固定し、全結合層(model.classifier)のみをfinetuneする設定です。
optimizerは、より汎用的に以下のように書くこともできます。
optimizer = optim.SGD(filter(lambda p: p.requires_grad, model.parameters()), lr=0.01, momentum=0.5)
全層のうち、requires_gradがTrueの層のみをfinetuneするという意味です。



model.classifier = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 100)
)

# cifar-100 datasetの準備
transform = transforms.Compose([
transforms.Resize((224, 224), interpolation=Image.BICUBIC),
transforms.ToTensor()
])
VGGの元々の定義(vgg.py)を参考に、全結合層を書き換えます。
出力層のユニット数を1000(Imagenetのクラス数)から100(cifar-100のクラス数)に変更するだけです。
また、cifar-100の画像は32×32なのですが、VGGは224×224の入力画像を想定しているので、
リサイズして入力するようにします。
(※古いversionのpytorchにはtransforms.Resize()が入ってません。ソースビルドしてください)

もしくは全結合層の入力サイズを入力画像サイズに応じて変更しても構いません。
例えば、Resize((128, 128), ...)として、入力画像サイズを128×128にし、
nn.Linear(512 * 4 * 4, 4096)として、全結合層の入力サイズを4×4にすることなどができます。

以上です。

pytorchのpretrainモデルで画像分類しようと思いググったのですが、
単純に画像分類させる方法が見当たらなかったので、
メモ代わりにコードを貼っておきます。
import numpy as np
import cv2
import torch
import torchvision.models as models
from torch.autograd import Variable

if __name__ == '__main__':

# ネットワーク生成
model = models.vgg16(pretrained=True)
model.cuda()

# 画像入力
img = cv2.imread('phone.jpg')
img = cv2.resize(img, (224, 224))
img = img / float(255)
img = img.transpose(2, 0, 1)

# データ変換
data = torch.from_numpy(img[np.newaxis, :, :, :])
data = Variable(data.float().cuda())

# テスト
model.eval()
output = model(data)
output_numpy = (output.data).cpu().numpy()
sorted = np.argsort(output_numpy)[0][::-1]
print(sorted)
iPhoneの画像を入力し、sortedの中身とimagenetのリストを比べると、
761 : remote control
590 : hand-held computer
688 : oscilloscope
487 : cellular phone
と、なんとなくそれっぽい分類が上位に来ていることがわかります。

以上です。

「主成分分析(無相関化)」と「最小二乗法(二次形式の最小値最大値算出)」は共に
対称行列の対角化から導出される。
これらの関係について整理してみた。

まずは「主成分分析(無相関化)」と「最小二乗法(二次形式の最小値最大値算出)」を、
対称行列の対角化という『便利な道具』を用いてそれぞれ導出してみる。

対角化とは、対称行列Aの固有ベクトルを並べた行列Uによって、
U'AU=Λ
とする変換である(Λは対角成分に固有値を並べた行列)。

無相関化は、データxの共分散行列Σの固有ベクトルを並べた行列Uによって、
データxをy=U'xと変換することによって、共分散行列をU'ΣU=Λとし、成分同士を無相関とする。
無相関化されたデータから固有値の大きい成分のみを抽出すると主成分分析となる。
※はじパタp.41参照

二次形式の最小値最大値の場合は、(共分散行列ではなく)二次形式(x, Ax)を
(x, Ax)=(Ux', AUx')=(x', U'AUx')=(x', Λx')
と二次形式の標準形(二乗のみの線形結合)へ変形できることから、
二次形式(x, Ax)の最小値(最大値)はAの最小(最大)固有値だと導出する。
※金谷先生応用数学教室p.185

最小二乗法は二次形式の最小値がAの最小固有値であることを利用する。
http://akasuku.blog.jp/archives/67001223.html

これら2つの問題は、対角化という便利な道具を用いて導出される別々の問題のように見えるが、
実際には強く関連している。
なぜなら、主成分分析とは、「主成分の分散を最大化する」ような変換だからである。
導出は面倒なので、金谷先生応用数学教室p.195参照のこと。

↑このページのトップヘ