본문 바로가기

lemonheart99/기계학습

CNN과 개 고양이 분류하기

# CNN 개 고양이 분류하기

 

CNN은 1960년대에 신경과학자인 데이비드 휴벨(David Hubel)과 토르스텐 비젤(Torsten Wiesel)이

시각을 담당하는 신경세포를 연구하다가 서로 비슷한 이미지들이 뇌의 특정 부위를 지속적으로 자극하며

서로 다른 이미지는 다른 부위를 자극한다는 사실을 발견하여 발명되었다.

이미지의 각 부분에 뇌의 서로 다른 부분이 반응하여 전체 이미지를 인식하고,

이미지의 특징을 추출하여 우리 뇌가 인식한다는 것이다.

 

CNN은 Convolutional Neural Network의 줄임말로서, 합성곱 신경망이라고도 한다.

이미지나 비디오같은 영상 인식에 특화된 설계로, 병렬 처리가 쉬워서 대규모 서비스에 적용할 수 있고,

최근에는 이미지뿐만 아니라 자연어 처리와 추천 시스템에 응용되기도 한다.

 

컴퓨터는 이미지의 픽셀값을 가로, 세로로 늘여놓은 행렬로 표현할 수 있다.

인공신경망은 다양한 형태의 입력에 적용되기가 힘들기 때문에

조금만 입력에 차이가 있어도 예측률이 급격히 떨어진다.

그렇기 때문에 pytorch의 transforms 도구를 사용하여

학습 데이터를 다양하게 변형한 다음 훈련하여 모델이 다양한 상황에도 적응할 수 있게 해야한다.

 

사람이 이미지를 볼 때 배경, 질감, 움직임 등의 특징을 바로 잡아내는 것처럼

컨볼루션도 계층적으로 인식할 수 있도록 단계마다 이미지의 특징을 추출하는 것을 목적으로 한다.

각각 채도, 윤곽선 검출 등의 역할을 하는 필터를 따로 두고 각각 수행해서 이미지를 인식하는 것이고,

필터를 적용할 때 이미지 왼쪽 위에서 오른쪽 밑까지 밀어가며 곱하고 더하는 것을 컨볼루션(convolution)이라고 한다.

 

필터들을 하나하나 설계하면

다양한 이미지에 모두 적용되고, 사람의 실력에 좌우되며, 크고 복잡한 이미지에는

너무 많은 필터들이 필요로하기 때문에 비효율적이다.

그래서 CNN은 이미지를 추출하는 필터를 스스로 학습하는 것을 목표로 한다.

 

CNN 모델은 컨볼루션 계층(convolution layer), 풀링 계층(pooling layer), 특징들을 모아

최종 분류하는 일반적인 인공 신경망 계층으로 구성된다.

컨볼루션 계층은 이미지의 특징을 추출하고,

풀링 계층은 필터를 거친 여러 특징 중 가장 중요한 특징 하나를 골라낸다.

이 과정에서 덜 중요한 특징을 버리기 때문에 더 작은 차원의 이미지를 얻을 수 있다.

 

컨볼루션 연산은 매우 작은 단위인 필터(filter), 커널(kernel)로 이미지를 보고, 보통 3 x 3, 5 x 5가 쓰인다.

몇 칸씩 움직이면서 볼지를 스트라이드(stride)로 정한다.

stride가 클수록 출력되는 텐서의 크기가 작아지고, 이 결과를 특징 맵(feature map)이라고 한다.

컨볼루션 계층마다 여러 특징 맵이 만들어지고, 다음 단계인 풀링(pooling) 계층으로 넘어간다.

특징 맵의 크기가 너무 크면 학습이 어렵고 과적합의 위험이 증가한다. 위에서 언급했듯이,

풀링은 앞 계층에서 추출한 특징을 값 하나로 추려내서 특징 맵의 크기를 줄이고, 중요한 특징을 강조한다.

필터가 지나갈 때마다 픽셀을 묶어서 평균이나 최댓값을 가져오게 된다.

 

일반적인 CNN 모델의 구조는 아래와 같다.

컨볼루션 → 풀링 →컨볼루션  드롭아웃  풀링  신경망  드롭아웃  신경망

 

 

우리가 할려는 악성코드 탐지가 주어진 파일이 정상코드인지 악성코드인지 분류하는 이진 분류 문제라서

가장 유사한 개 고양이 분류 문제를 시도하려고 했다

cat dog classification chart flow

파이토치로 할려고 했으나 다들 파이토치로는 MNIST만 하고 있었고

개 고양이 분류는 케라스로 하는 사람이 많았다...

거기다 다들 약간씩 하드웨어 등 작업환경이 달라서 딱 내가 원하는 소스코드를 구하지 못했다...

그리고 결국 개 고양이 이미지가 아닌 PE파일을 이미지화 해서 사용해야 되고

해당 데이터 셋은 곧 얻을 수 있을 뿐만 아니라

팀원들과 어떤 툴을 사용할지 상의를 해서 통일 해야되기 때문에

실제 소스코드 구현 및 실행은 잠시 보류했다

 

 

PyTorch를 사용한 MNIST 분류 소스코드를 첨부했다

CNN 알고리즘을 사용한다

잘 작동하는지는 아직 확인 못했다

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import transforms, datasets

USE_CUDA = torch.cuda.is_available()
DEVICE = torch.device("cuda" if USE_CUDA else "cpu")

EPOCHS = 40
BATCH_SIZE = 64

train_loader = torch.utils.data.Dataloader(
    datasets.MNIST("./data", train = True, download = True, transform = transforms.Compose([
        transforms.ToTensor(), transforms.Nomalize((0.1307,),(0.3081,))
    ])), batch_size = BATCH_SIZE, shuffle= True)

test_loader = torch.utils.data.Dataloader(
    datasets.MNIST('./data', train = False, transforms = transforms.Compose([transforms.ToTensor(),
                                                                             transforms.Nomalize((0.1307,),(0.3801,))])),
    batch_size = BATCH_SIZE, shuffle = True)


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size = 5)
        self.conv2 = nn.conv2d(10, 20, kernel_size = 5)
        
        self.conv2_drop = nn.Dropout2d()
        
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)
        
    def forward(self, x):
        x = F.relu(F.max_pool2d(self.conv1(x), 2))
        x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
        x = x.view(-1, 320)
        x = F.relu(self.fc1(x))
        x = F.dropout(x, traning = self.training)
        x = self.fc2x
        return F.log_softmax(x, dim = 1)
    

model = Net().to(DEVICE)
optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum = 0.5)


def train(model, train_loader, optimzer, epoch):
    model.train()
    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(DEVICE), target.to(DEVICE)
        optimizer.zero_grad()
        output = model(data)
        loss = F.cross_entropy(output, target)
        loss.backward()
        optimizer.step()
        
        if batch_idx % 200 == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%]\t Loss: {:.6f}'.format(
                epoch, batch_idx * len(data), len(train_loader.dataset),
                100. * batch_idx / len(train_loader), loss.item()))

          
def evaluate(model, test_loader):
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(DEVICE), target.to(DEVICE)
            output = model(data)
            
            test_loss += F.cross_entropy(output,target, reducatin = 'sum').item()
            
            pred = output.max(1, keepdim = True)[1]
            correct += pred.eq(target.view_as(pred)).sum().item()
        
        test_loss /= len(test_loader.dataset)
        test_accuracy = 100. * correct / len(test_loader.dataset)
        return test_loader, test_accuracy


for epoch in range(1, EPOCHS + 1):
    train(model, train_loader, optimizer, epoch)
    test_loss, test_accuracy = evaluate(model, test_loader)
    
    print('[{}] Test Loss : {:.4f}, Accuracy: {:.2f}%'.format(
        epoch, test_loss, test_accuracy))