Flowers transfer learning example

앞장에서는 수행한 Retaining시에 Batch size가 8이상 크면 컴퓨터의 사양에 따라서 메모리가 부족한 경우도 생길 수도 있습니다. 혹은 이미 생겨서 그 크기를 조정한 경우도 있을 수 있습니다. 즉, 네트워크와 입력 dataset이 크면 학습에 큰 컴퓨터 자원이 소모됩니다.

따라서, pretrained된 network는 거의 모든 부분을 그대로 사용하고, 학습이 필요한 부분은 마지막 Fully connnected layer일 뿐이니, 마지막 layer 앞전 값을 입력으로 하고 마지막 단만을 학습하는 방법을 생각할 수 있습니다.

이번 장에서는 이부분을 구현하도록 하겠습니다.

일단, 밑의 명령어를 수행시켜서, 실행디렉토리 밑에 꽃 이미지 압축파일을 풀어 놓습니다.


In [1]:
!if [ ! -d "/tmp/flower_photos" ]; then curl http://download.tensorflow.org/example_images/flower_photos.tgz | tar xz -C /tmp ;rm /tmp/flower_photos/LICENSE.txt; fi

In [2]:
%matplotlib inline

1. extract feature & save feature data

model를 생성하고 predict하여 이미지로 부터 transfer values를 생성할 것입니다.


In [3]:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torchvision
from torchvision import datasets, transforms
from torch.autograd import Variable
import matplotlib.pyplot as plt
import numpy as np

is_cuda = torch.cuda.is_available() # cuda 사용가능시, True
traindir = '/tmp/flower_photos'

normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

batch_size = 1 
train_loader = torch.utils.data.DataLoader(
    datasets.ImageFolder(traindir,
                         transforms.Compose([
                             transforms.RandomSizedCrop(224),
                             transforms.RandomHorizontalFlip(),
                             transforms.ToTensor(),
                             normalize,])),
    batch_size=batch_size,
    shuffle=True)

cls_num = len(datasets.folder.find_classes(traindir)[0])

model = torchvision.models.resnet152(pretrained = True)
# remove last fully-connected layer
model = nn.Sequential(*list(model.children())[:-1])

In [4]:
model.eval()
features = []
targets = []
for image, target in train_loader:
    image, target = Variable(image, volatile=True), Variable(target, volatile=True)
    if is_cuda :  image, target = image.cuda(), target.cuda() 
    feature = model(image).data
    features.append(feature)
    targets.append(target.data.squeeze())

In [5]:
features = torch.cat(features, 0).squeeze()
targets = torch.cat(targets, 0)
torch.save(features, 'flower_feature.pth')
torch.save(targets, 'flower_label.pth')

retrainning fullly connected layer

1. feature dataset 만들기


In [6]:
# load feature datasets
features = torch.load('flower_feature.pth')
targets = torch.load('flower_label.pth')

batch_size = 500

features_datasest = torch.utils.data.TensorDataset(features, targets)
feature_loader = torch.utils.data.DataLoader(
    features_datasest,   
    batch_size=batch_size,
    shuffle=True)

2. 사전 설정

  • model

2048개의 특징을 받아서 5개의 꽃 종류로 표시하는 fully connect layer를 만듭니다.

  • loss
  • opimizer

In [7]:
# remove last fully-connected layer
fcmodel = nn.Linear(2048, cls_num)

loss_fn = nn.CrossEntropyLoss() 
optimizer = torch.optim.Adam(fcmodel.parameters(), lr=1e-4, weight_decay=1e-4)

if is_cuda : model.cuda(), loss_fn.cuda()

3. Trainning loop

* (입력 생성)
* model 생성
* loss 생성
* zeroGrad
* backpropagation
* optimizer step (update model parameter)

```


In [8]:
# trainning
fcmodel.train()
train_loss = []
train_accu = []
i = 0
for epoch in range(1000):
    for feature, target in feature_loader:
        feature, target = Variable(feature), Variable(target.squeeze()) # 입력image Target 설정 
        if is_cuda :  feature, target = feature.cuda(), target.cuda() 
        output = fcmodel(feature) # model 생성
        loss = loss_fn(output, target) #loss 생성
        optimizer.zero_grad() # zero_grad
        loss.backward() # calc backward grad
        optimizer.step() # update parameter
        
        pred = output.data.max(1)[1]
        accuracy = pred.eq(target.data).sum()/batch_size
        
        train_loss.append(loss.data[0])
        train_accu.append(accuracy)

        if i % 300 == 0:
            print(i, loss.data[0])
        i += 1


0 1.6089081764221191
300 0.4521520137786865
600 0.32684481143951416
900 0.2902452051639557
1200 0.24725505709648132
1500 0.2048124223947525
1800 0.22823375463485718
2100 0.17747755348682404
2400 0.17375829815864563
2700 0.16366168856620789
3000 0.14638055860996246
3300 0.1580706685781479
3600 0.13060815632343292
3900 0.10086245834827423
4200 0.12406690418720245
4500 0.10393000394105911
4800 0.1344904899597168
5100 0.10150565952062607
5400 0.1082053929567337
5700 0.08285167813301086
6000 0.09925411641597748
6300 0.06993661820888519
6600 0.08772073686122894
6900 0.0636705681681633
7200 0.07196623086929321
7500 0.07124580442905426
7800 0.07379543036222458

In [9]:
plt.plot(train_accu)


Out[9]:
[<matplotlib.lines.Line2D at 0x7fa73046de48>]

In [10]:
plt.plot(train_loss)


Out[10]:
[<matplotlib.lines.Line2D at 0x7fa7303a0898>]

In [11]:
checkpoint_filename = 'flowerfeature_resnet152.ckpt'

## save a parameter 
torch.save(fcmodel.state_dict(), checkpoint_filename)

4. Predict & Evaluate

이제 학습된 마지막단 fully connected layter를 기존의 모델에 연결하여, prediction을 수행하여 봅니다. batch size를 더 크게 할 수 있어서 그러진 8장의 예제보다 좀 더 정확한 93%정도 수준의 정확도를 보여줍니다.


In [12]:
import torch
import torchvision
import torch.nn as nn
from torchvision import datasets, transforms
from torch.autograd import Variable

traindir = '/tmp/flower_photos'
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
cls_num = len(datasets.folder.find_classes(traindir)[0])

test_loader = torch.utils.data.DataLoader(
    datasets.ImageFolder(traindir,
                         transforms.Compose([
                             transforms.RandomSizedCrop(224),
                             transforms.RandomHorizontalFlip(),
                             transforms.ToTensor(),
                             normalize,])),
    shuffle=True)

model = torchvision.models.resnet152(pretrained = True)
# make new fully connected layer 
fcmodel = nn.Linear(2048, cls_num)
# load saved parameter into fc layer
checkpoint_filename = 'flowerfeature_resnet152.ckpt'
checkpoint = torch.load(checkpoint_filename)
fcmodel.load_state_dict(checkpoint)
#connect fc layer to resnet152
model.fc = fcmodel

model.eval()
correct = 0

from itertools import islice
for image, target in islice(test_loader, 100):
    if is_cuda :  image, target = image.cuda(), target.cuda() 
    image, target = Variable(image, volatile=True), Variable(target)
    output = model(image)
    prediction = output.data.max(1)[1]
    correct += prediction.eq(target.data).sum()

#print('\nTest set: Accuracy: {:.2f}%'.format(100. * correct / len(test_loader.dataset)))
print('\nTest set: Accuracy: {:.2f}%'.format(100. * correct / 100))


Test set: Accuracy: 96.00%

In [ ]: