본문 바로가기
딥러닝/프로젝트

[개인]합성곱 신경망을 통한 이미지 분류

by 혜 림 2021. 4. 26.

아래의 자료를 참고하였습니다

 

github.com/deeplearningzerotoall/PyTorch/blob/master/lab-10_4_2_ImageFolder_2.ipynb

 

deeplearningzerotoall/PyTorch

Deep Learning Zero to All - Pytorch. Contribute to deeplearningzerotoall/PyTorch development by creating an account on GitHub.

github.com

 

실은 이번 챕터는 customize한 데이터를 가지고 이미지 분류를 하는 거였다. 

있는 데이터 셋으로 하는 건 별로 재미가 없을 것 같아서 새로 만들어보았다.

혼자 여기저기 잘 놀러다니는 편이라, 이번주에는 어린이 대공원을 갔다왔다.

뮤지컬 극장 바로 옆이 어린이 대공원일줄 누가 알았겠는가.

 

하여간에, 그래서 어린이 대공원을 돌면서

호랑이랑 사자 사진을 좀 찍어볼까 싶었는데 에그머니!

늦게 가서 맹수 없는 맹수마을만 보고왔다. 동물들의 퇴근 시간이었지 뭔가.

 

그래서 하릴없이 꽃들 사진만 찍었다. 

그런데 찍고 있자니 내가 굉장히 감수성이 짙은 할머니가 된 것만 같았다.

 

하여간에 그래서 이번 데이터 셋은 위 꽃들을 구분하는 거였다. 

아직 내 딥러닝 친구는 똑똑하지 않고, 나도 그렇기 때문에 가능하면 비슷한 구도에서 찍었다. 

 

그리고 또 웃긴 생각이 났는데, 바로 내 사진을 테스트 셋에 넣는거다.

킄킄...

 

## customizing data

import torchvision
from torchvision import transforms

from torch.utils.data import DataLoader

from matplotlib.pyplot import imshow
%matplotlib inline

 

사진이 워낙 고화질이다 보니, 그 사이즈를 줄여주는 과정

trans=transforms.Compose([transforms.Resize((64,128))])
train_data=torchvision.datasets.ImageFolder(root='./origin_data',transform=trans)

 

이 과정을 거치면 내 사진들이...

이런 똥퀄로 바뀐다! 내가 쭈그려 앉아서 열심히 찍었던 사진들이...

텐서로 바꾼 이미지를 띄우는 방법은 접은글로 넣겠다

더보기
더보기
img=train_data[0][0].squeeze()
print(train_data[0][1])
imshow(transforms.ToPILImage()(img), interpolation="bicubic")
###
img=train_data[15][0].squeeze()
print(train_data[15][1])
imshow(transforms.ToPILImage()(img), interpolation="bicubic")

그 다음엔 바꿔준 이미지들을 다시 저장해준다. 

for num,value in enumerate(train_data):
    data,label=value
    print(num,data,label)
    
    if label==0:
        data.save('./train_data/purple/%d_%d.jpeg'%(num,label))
    else:
        data.save('./train_data/white/%d_%d.jpeg'%(num,label))

 

 

0: purple, 1: white

 

## learning

 

 

import torch
import torch.nn as nn
import torch.nn.functional as F

import torch.optim as optim
from torch.utils.data import DataLoader

import torchvision
import torchvision.transforms as transforms

 

데이터 셋이 너무 작아서 배치 사이즈도 줄였다. 4정도로...

trans=transforms.Compose([transforms.ToTensor()])
train_data=torchvision.datasets.ImageFolder(root='./train_data',transform=trans)
data_loader = DataLoader(dataset = train_data, batch_size = 4, shuffle = True, num_workers=2)

 

CNN 모델 생성

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.layer1 = nn.Sequential(
            nn.Conv2d(3,6,5),
            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        self.layer2 = nn.Sequential(
            nn.Conv2d(6,16,5),
            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        self.layer3 = nn.Sequential(
            nn.Linear(16*13*29, 120),
            nn.ReLU(),
            nn.Linear(120,2)
        )
        
    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = out.view(out.shape[0], -1)
        out = self.layer3(out)
        return out

기타 최적화, 비용 함수 생성

net=CNN()

optimizer=optim.Adam(net.parameters(),lr=0.00005)
loss_func=nn.CrossEntropyLoss()

아무 생각 말고 돌리기!

total_batch = len(data_loader)

epochs = 15
for epoch in range(epochs):
    avg_cost = 0.0
    for num, data in enumerate(data_loader):
        imgs, labels = data

        optimizer.zero_grad()
        out = net(imgs)
        loss = loss_func(out, labels)
        loss.backward()
        optimizer.step()
        
        avg_cost += loss / total_batch
        
    print('[Epoch:{}] cost = {}'.format(epoch+1, avg_cost))
print('Learning Finished!')

보통  epoch를 줄인다고 해서 비용함수가 막 줄진 않던데 이 경우는 잘 줄어서 기분이가 좋았다!

 

다음으로

테스트 셋도 이미지를 줄여준다.

그렇지만 실은 테스트 셋으로 트레인 셋을 이용했다.

사진을 찍을 사진이 모자랐다...

 

trans=torchvision.transforms.Compose([
    transforms.Resize((64,128)),
    transforms.ToTensor()
])
test_data = torchvision.datasets.ImageFolder(root='./train_data', transform=trans)
test_set = DataLoader(dataset = test_data, batch_size = len(test_data))

그 다음엔 예측해서 정확도를 비교한다

with torch.no_grad():
    for num, data in enumerate(test_set):
        imgs, label = data

        prediction = net(imgs)
        
        correct_prediction = torch.argmax(prediction, 1) == label
        
        accuracy = correct_prediction.float().mean()
        print('Accuracy:', accuracy.item())

정확도는 1이 나왔는데, 생각해보면 테스트 셋으로 트레인 셋을 썼으니 당연한 결과다

 

그래서 test data로 내 사진도 넣어보고 이 허접한 프로젝트는 잠시 마무리하려고 한다.

with torch.no_grad():
    for num, data in enumerate(test_set):
        imgs, label = data

        prediction = net(imgs)
        
        correct_prediction = torch.argmax(prediction, 1) == label
        
        print(prediction)
        print(torch.argmax(prediction, 1))

나는 보라색 꽃이라네!

 

근데 생각해보면 당연한 결과인게

이 사진에서 나는 분홍 꽃을 머리에 꽂고 있다.

 

 

아니면 첫번째 폴더에 있어서 그런가? 

실은 ImageFolder를 잘 모르겠다. 

왜냐하면  test_data도 이미지 폴더를 만들어줘야 하다보니, 그렇게 했는데.. 폴더가 나뉜 것도 딥러닝에 영향이 있는걸까? 

 

그래서 한 폴더에 라벨과 내 사진을 섞었더니

이렇게 나왔다.

 

사진 자체는 

폴더 속에선 혹시 몰라 파일명도 바꿨다... 물론 영향이 없을 것 같긴 했지만 만반의 준비는 해야지

 

 

사진을 더 찍게 된다면 테스트 셋을 바꿔서 리업로드 해볼까 한다..

 


그런데 문제가 생겼다

저렇게 압축된 사진 대신, train data에 있었던 원본을 넣었더니 예측을 제대로 못하는 사태가 일어났다.

그래서 점심시간에 급하게 나가서 꽃 사진을 찍었다..ㅋㅋ

 

대신 이번엔 좀 다양하게 찍어봤다.

기존에 찍었던 하얀색 철쭉도 다시 찍었고, 아예 데이터 셋에 없는 종류의 꽃도 찍어봤고, 마지막으로 빨간색 철쭉도 찍어봤다. 

그런데 너무 처참하게도, 모두 class0(보라색 꽃, 꽃 이름을 모른다)로 예측하는 것이다

 

그래서 기분이 상한 나머지 좀 방황하다가

다시 돌아와 앉아서 합성곱 층의 채널 수를 달리 해봤다.

 

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.layer1 = nn.Sequential(
            nn.Conv2d(3,16,5),
            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        self.layer2 = nn.Sequential(
            nn.Conv2d(16,64,5),
            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        self.layer3 = nn.Sequential(
            nn.Linear(64*13*29, 120),
            nn.ReLU(),
            nn.Linear(120,2)
        )
        
    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)
        out = out.view(out.shape[0], -1)
        out = self.layer3(out)
        return out

그런데 사이즈를 계산하는게 어려워서 헤맸다. 그래서 코드의 도움을 받아서 해결했다.

더보기
더보기
out=net.layer1(train_data[0][0].unsqueeze(0))
print(out.shape)
out=net.layer2(out)
print(out.shape)

 

이렇게 생긴 테스트 셋을

이렇게 예측했다. 아무래도 색이 중요한 영향인걸까? 

나도 갑자기 하얀색 꽃으로 예측됐다. 신기방기 컴퓨터의 생각을 알고 싶다. 

댓글