Deep Learning study

[Pytorch] kaggle cat&dog CNN 으로 분류하기 본문

AI/Pytorch

[Pytorch] kaggle cat&dog CNN 으로 분류하기

HwaniL.choi 2018. 1. 24. 01:09
반응형


pytorch를 써야해서 .. 연습하려고 뭔가 해보려고 하다가 kaggle에 cat dog 데이터셋을 다운받아서 학습시켜보았다! 



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np
import matplotlib.pyplot as plt
import os
import torch
import torchvision
import torchvision.transforms as transforms
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
from __future__ import print_function
import argparse
import csv
import os.path
import torch.nn.parallel
import torch.utils.data as data
import torchvision.datasets as datasets
import torchvision.models as models
from PIL import Image
cs

먼저 import해온것들.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
traindir = os.path.join('../data/data_set/catdog''training')#경로를 병합함 .
testdir = os.path.join('../data/data_set/catdog''testing')
 
class TrainImageFolder(datasets.ImageFolder):
 
    def __getitem__(self, index):
            filename = self.imgs[index]
            if (filename[0].split('/')[6]).split('.')[0== 'cat': label = 0
            else : label = 1
 
            return super(TrainImageFolder, self).__getitem__(index)[0],  label
 
normalize = transforms.Normalize(mean=[0.4850.4560.406], std=[0.2290.2240.225])
 
train_loader = data.DataLoader(
        TrainImageFolder(traindir,
                         transforms.Compose([
                             transforms.RandomResizedCrop(224),
                             transforms.RandomHorizontalFlip(),
                             transforms.ToTensor(),
                             normalize,
                         ])),
                        batch_size=4,
                        shuffle=True,
                        num_workers=4,
                        pin_memory=True)
 
class TestImageFolder(datasets.ImageFolder):
 
    def __getitem__(self, index):
        filename = self.imgs[index]
        real_idx =  int(filename[0].split('/')[6].split('.')[0])    
        return super(TestImageFolder, self).__getitem__(index)[0], real_idx
        
 
 
test_loader = data.DataLoader(
    TestImageFolder(testdir,
                    transforms.Compose([
                        transforms.Resize(256),
                        transforms.CenterCrop(224),
                        transforms.ToTensor(),
                        normalize,
                    ])),
                    batch_size=1,
                    shuffle=False,
                    num_workers=1,
                    pin_memory=False)
cs

진짜 제일 애먹은 부분이다. DataLoader 에 대해서 잘 이해도 안되었고 .. 사실 파이썬을 잘 써보지 않은터라 class개념과 상속한다는 개념이 잘 이해가 되지 않아서 이해하는데 힘들었다. 

힘들었던 부분들을 다시 봐봐야 겠다.


1. Train data를 불러올때 사진에 Label 을 달아줘야 하는데(dog : 1 , cat : 0)고것이 DataLoader는 같은 폴더에 있는 사진들은 같은 Label을 메겨버리는 것이다. 다운받은 25000개의 개고양이사진들은 한 폴더안에 있는데 (각각 12500개), 그것들을 어떻게 다른 폴더로 분리시킬지 생각이 안나서 다른해결방법을 찾아보았다. 그래서 진짜 한참을 구글링을 하고 찾아  헤메다가 __getitem__함수를 오버라이딩해서 해결하는 방법을 찾았다. 'cat'이름을 포함한 사진은 0을 주고 'dog'이름을 포함한 사진은 1을 주어서 해결! 


2. 학교 연구실 리눅스 서버를 통해 학습을 시키고 있는데 리눅스가 특이한것인지.. 사진정렬을 이상하게 해놓았다. Test data 사진들이 1.jpg, 2.jpg, 3.jpg .... 12500.jpg 까지 있는데 , 출력해보니 1.jpg , 10.jpg, 100.jpg, 1000.jpg, 10000.jpg. ........ 9999.jpg 뭐 이런식으로 정렬을 해놓았더란다.. 그래서 이것들을 정렬시킬려고 엄청나게 애를 썼지만.. 불가능한것 같아서 다른방법을 생각해냈다. DataLoader에서 돌리는 iterator는 그대로 놔두고 __getitem__함수에 이미지이름에 해당하는 번호를 real_index로 같이 반환해준다. 추후의 방법은 밑에 저장하는 부분에 적어야겠다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class Net(nn.Module):
    def __init__(self):
        super(Net,self).__init__()
        self.conv1 = nn.Conv2d(3,32,5)
        self.pool = nn.MaxPool2d(2,2)
        self.dout = nn.Dropout(0.2)
        self.conv2 = nn.Conv2d(32,64,3)
        self.conv3 = nn.Conv2d(64,128,5)
        self.conv4 = nn.Conv2d(128,256,5)
        
        self.fc1 = nn.Linear(21*21*256512)
        self.fc2 = nn.Linear(512128)
        self.fc3 = nn.Linear(1282)
    
    def forward(self,x):
        x = self.conv1(x)
        x = self.dout(x)
        x = self.pool(F.relu(x))
        x = self.conv2(x)
        x = self.dout(x)
        x = self.pool(F.relu(x))
        x = self.pool(F.relu(self.conv3(x)))
        x = self.dout(x)
        x = F.relu(self.conv4(x))
        x = x.view(x.size(0),-1)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x
 
net = Net()
 
if torch.cuda.device_count() > 1:
  print("Let's use", torch.cuda.device_count(), "GPUs!")
  net = nn.DataParallel(net)
 
if torch.cuda.is_available():
   net.cuda()
 
import torch.optim as optim
 
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr = 0.004)
 
for epoch in range(45):
    running_loss = 0.0
    acc = 0.
    correct = 0
    for i, data in enumerate(train_loader, 0):
        inputs, labels = data
        inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda())
        
        optimizer.zero_grad()
        outputs = net(inputs)
        #print(outputs)
        loss = criterion(outputs, labels)
            
        loss.backward()
        optimizer.step()
    
        running_loss += loss.data[0]
        
        prediction = torch.max(outputs.data,1)[1]
        
        correct += prediction.eq(labels.data.view_as(prediction)).cpu().sum()
            
        if i%2000 == 1999:
            print('[%d, %5d] loss: %.6f acc : %.6f' % (epoch + 1, i+1, running_loss/2000100*correct/((i+1)*4)))
            running_loss = 0.0
            
print('Finished Training')
cs


여기서도 사실상 애를 많이 먹었다. 여러 모델을 시험해보고 내가 알고있는것만으로 짜보려니 힘들었다. 그치만 여차저차해서 만들어낸것이 이것이다. 4개의 convolutional layer와 3개의 sampling layer 그리고 마지막의 3개의 fully connected layer 를 만들었다. 마지막의 출력은 0,1 둘중 하나이므로 2이다.


여튼 그렇게해고 연구실 서버에 4개의 GPU를 쓰기위해서 DataParallel을 설정해준다. (사실 4개를 다 쓰고있는건지 나는 모르겠다.. 그냥 pytorch tutorial에 나와있는거 가져왔따...)


마지막으로 tarin_loader에서 데이터를 읽어와 학습을 시킨다. 

25000장의 학습 데이터 이미지를 총 45번 돌렸다.


원래 시간이 오래걸리는건지는 모르겠지만 6시간이 걸려서 학습을 끝냈다.  


이건 그 걸과이다.

91%정도의 Accuracy를 가진다. 성과는 그렇게 좋진 않았다. 보통 98,99%나오던데... 모델을 다시짜야하나, 학습을 더 많이 시켜야 하는건가. 아니면 내가모르는 알고리즘이 있는건가.... 

다른 ResNet??? transfer learning? 이름만 알지 아직 공부해본적 없어서.. 공부하고 여러가지로 다시 해봐야겠다. 고생고생끝에 학습을 마쳤다. 


이제는 Test data로 검증하는 작업을 해야한다. 하지만 kaggle 에서 Test data에대한 Label은 제공하지 않고있다. 다만 그결과를 .csv파일로 출력하여 kaggle홈피에 제출하면 어느정도로 맞췄는지 알려준다.


그래서 밑의 코드는 그 작업을 한것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import csv
from operator import itemgetter
net.eval()
 
data_list = []
= open('output.csv','w',newline='')
csvWriter = csv.DictWriter(f,['id','label'])
csvWriter.writerows([{'id' : 'id','label':'label'}])
correct = 0
i=0
for data in test_loader:
    inputs, idx= data
    print(idx)
    outputs = net(Variable(inputs.cuda()))    
    
    prediction = torch.max(outputs.data, 1)[1]
    
    data_list.append({'id':Variable(idx).data[0],'label': Variable(prediction).data[0]})
 
 
temp = sorted(data_list,key=itemgetter('id'))
 
csvWriter.writerows(temp)
f.close()
 
 
 
cs


이것도 많이 고생한 부분이다. . ㅠㅠ 사실 고생하지 않은 부분이 없는것 같다. 

일단은 처음에 실수했던게 내가 dropout을 설정해놨기 때문에 모델평가를 할 때는 net.eval()을 실행하여 evaluate모드로 설정해주어야 한다. ㅠㅠ

여튼 설정해주고 파일 열어주고.. 파이썬의 dictionary를 이용하여 index와 테스트 결과인 label을 달아준다. 


위에서 말한 Test data의 real_idx를 반환한것이 여기서 쓰인다. 그냥 DataLoader가 던져주는 이미지들을 차례대로 수행해버리면 아까처럼 1 , 10 ,100 , 1000, 10000, 10001, .... 이순서대로 인덱스가 1, 2, 3, 4, 5.... 이렇게 메겨지기 때문에 사진에 대한 올바른 index가 아니다. 따라서 kaggle홈피에 파일을올렸을때 제대로 채점이 되지 않는다. 


test_loader에서 이미지(inputs) 와 real_idx(idx)를 받아오고 inputs를 모델에 넣어 prediction을 뽑아내고 결과들을 real_idx와함께 data_list에 차곡차곡 넣어준다. 


그렇게 나온 결과들을 index 순서로 정렬을 시켜준다. (왜냐하면 들어가있는 순서는 1, 10, 100, .... 이기때문에,,)

그리고 csv파일로 써주면 끝!! 


채점결과는 썩 좋진않다.. leaderboard보면 보통 다들 99%정도 나온것같다 ㅠㅠ 


오른쪽의 숫자는 log_loss였나.. ? 여튼 손실정도를 보여주는것이다. 처음에 실수도 많이하고 했을때는 17.4인가 나왔는데 .. 많이 줄었다.(사실 17 이면 그냥 찍어서 나온 50%정도의 확률이다.. ㅎㅎ.)  다른모델을 공부해 봐야겠다.


pytorch를 처음 써보기도 하고 , 파이썬에 익숙하지도 않고 그러다보니 너무힘들었다. 제일 힘든건 함수에 대한 이해 였다. 무엇을 return하는지 , parameter로는 무엇을 쓰는지, 내부적으로 어떻게 수정을 할 수 있는지 (오버라이딩 같은거.. ) .. 정말 하나도 모르니까 답답해 미칠 지경이였지만 http://pytorch.org 에서 하나하나 찾아보고, 구글링, pytorch kr 페이스북 페이지 등 에서 해결책을 찾아나갔다.. 정말 힘들었다. 


별거아닌 것이지만 처음이라 너무 어려웠던것 같다 ㅠ


더 열심히 해봐야지..

반응형
Comments