본문 바로가기

프로젝트

무이메이커스_딥러닝을 활용한 인공지능 (AI) 이미지 분류 (Image Classification) 프로젝트

프로젝트 진행 순서

  • 1. 이미지 분류 (Image Classification) 개요
  • 2. 이미지 데이터 전처리 (Image Preprocessing)
  • 3. 데이터에 적합한 딥러닝 모델 생성
  • 4. 모델 평가 및 시각화 (Evaluation and Visualization)
  • 5. 다양한 모델의 적용 및 비교
  • 6. 실생활 적용

 

 

안녕하세요 헬스케어 제품 개발회사 허니컴의 무이메이커스페이스 입니다.

저희는 4차산업혁명을 맞이하여 딥러닝을 접목시킨 제품 개발을 위해 다양한 프로젝트를 수행하고,

이를 활용하여 인공지능 (AI) 을 지닌 다양한 헬스케어 제품을 생산하는데 그 목적이 있습니다.

이번 시간에는 딥러닝을 활용한 이미지 분류 (Image Classification) 프로젝트를 소개하고자 합니다.

목표는 5가지 종류의 꽃에 대한 이미지 (Flower Image) 를 분류해내는 것이고,

데이터셋 (Dataset) 으로는 캐글 (Kaggle) 에서 제공되는 Flower Recognition Dataset 을 사용하였습니다.

개발 환경은 윈도우 (Window), 언어는 파이썬 (Python), 라이브러리는 파이토치 (Pytorch) 를 사용합니다.

 

https://www.kaggle.com/alxmamaev/flowers-recognition

Flowers Recognition

This dataset contains labeled 4242 images of flowers.

www.kaggle.com

총 4242 장으로 구성된 5개 종류의 Flower Dataset

 

1. 이미지 분류 (Image Classification) 개요

이번주 저희의 목표는 컴퓨터 비전 (Computer Vision) 분야에 속하는 이미지에 관련된 문제입니다.

4차산업혁명의 핵심인 딥러닝 기술은 현재까지 해당 분야에서 가장 큰 발전을 이뤘다고 볼 수 있으며,

크게 아래의 3가지 분야로 연구가 진행되고 있습니다.

CS231n 에 소개된 Computer Vision Task

목표는 해당 사진 속 고양이를 컴퓨터가 인식하는 것입니다.

가장 기본적으론 고양이라는 사물의 존재 여부를 분류하는 Classification,

고양이라는 사물의 위치를 찾아냄과 동시에 여러가지 사물을 인식하는 Detection,

고양이라는 사물의 형태를 찾아내는 Segmentation 이 존재하며,

이번 프로젝트는 4차산업혁명과 딥러닝의 가능성을 처음으로 대두시킨 CNN (Convolution Neural Network)

모델을 통한 이미지 분류 (Image Classification) 를 파이썬 (Python) 과 파이토치 (Pytorch) 로 진행하였습니다.

 

 

2. 이미지 데이터 전처리 (Image Preprocessing)

딥러닝 모델이 잘 학습하기 위해선 준비된 데이터들을 모델에 맞게 정제하는 과정이 필요합니다.

이를 데이터 전처리 과정 (Preprocessing) 이라 부르며,

이미지 데이터 의 경우 Scaling, Normalizing, Augmentation 등의 과정을 거칩니다.

전처리 과정에 앞서 캐글에서 제공된 데이터를 살펴보면,

먼저, 제공된 이미지 데이터셋에는 Class 라고 부를 수 있는 5가지 종류의 꽃들이 있고,

딥러닝이 학습하는데 필요한 이미지의 갯수가 Class 별로 1,000 장 내외가 존재합니다.

또한 Class 별 이미지 데이터의 갯수가 달라 Class Imbalance Problem 이 발생할 수 있습니다.

따라서 저희는 파이썬 (Python) 라이브러리 Augmentor 를 사용하여 전처리를 진행하였습니다.

 

pip install Augmentor

https://github.com/mdbloice/Augmentor

 

Augmentor 의 경우 전처리된 이미지 파일을 output 이라는 폴더에 별도로 생성하여 저장합니다.

코드에 경로를 지정해주고 Augmentor Docs 를 참고해 Rotate 와 Zoom 을 준 이미지를 생성했습니다.

결과 비교를 위해 2,000 번과 5,000 개 2가지의 이미지 생성을 진행했습니다.

 

import Augmentor

p = Augmentor.Pipeline('이미지 데이터셋의 경로')

p.rotate(probability=0.7, max_left_rotation=10, max_right_rotation=10)
p.zoom(probability=0.5, min_factor=1.1, max_factor=1.5)
p.sample(2000)
# p.sample(5000)
p.process()

https://augmentor.readthedocs.io/en/master/

 

총 2000 장으로 Augmentation 된 Daisy Class Dataset

 

이후 이미지를 딥러닝의 Input 형태로 바꿔줘야 합니다.

먼저 CNN 모델에 Input 으로 넣기 위해 이미지를 동일한 픽셀을 지닌 이미지로 Resize 하며,

파이토치 (Pytorch) 는 이미지를 Tensor 자료형으로 바꾸고 Dataset 함수를 통해 Loader 형태로 바꿔줍니다.

torchvision.transforms 를 통해 간단하게 가능하며,

이미지 데이터셋의 경우 torchvision.datasets.ImageFolder 를 통해 불러올 수 있습니다.

(Parser 는 나중에 Hyperparameter 등 수치 조절을 쉽게 하기 위해 자주 사용됩니다.)

 

import argparse
import torchvision.transforms as transforms
import torchvision.datasets as dset

def main():
    parser = argparse.ArgumentParser(description='Image')
    parser.add_argument('--Resize',type=int,default=(200,200))
    args = parser.parse_args()

    transform = transforms.Compose([
                                    transforms.Resize(args.Resize),
                                    transforms.ToTensor()
                                    ])

    flower = dset.ImageFolder(root='데이터셋의 경로', transform=transform)

캐글에서 별도의 테스트 데이터셋을 제공하지 않으므로,

성능 측정을 위해 3/4 을 트레이닝 세트로, 1/4 을 테스트 세트로 사용하였습니다.

파이썬 (Python) 의 sklearn 라이브러리를 통해 간단하게 나눠줍니다.

 

import sklearn.model_selection

train_data,test_data,train_label,test_label = sklearn.model_selection.train_test_split(flower,
                                                                                       flower.targets,
                                                                                       stratify=flower.targets,
                                                                                       test_size = 0.25,
                                                                                       random_state=42)

 

 

 

3. 데이터에 적합한 딥러닝 모델 생성

이미지 처리를 위한 대표적인 딥러닝 모델은 CNN (Convolution Neural Network) 입니다.

세계 최대 규모의 이미지 데이터베이스인 이미지넷 (ImageNet) 을 기반으로 한

ImageNet Large Scale Visual Recognition Challenge (ILSVRC) - 2012 에서

CNN 구조의 Alexnet 의 등장으로 딥러닝에 대한 가능성이 대두되며 많은 사람들이 CNN을 활용하기 시작합니다.

이번 프로젝트에서 저희는 직접 CNN 구조를 쌓아올린 모델과 기존에 공개된 모델들을 활용한 Transfer Learning, 2가지 방법으로 프로젝트를 진행하였습니다.

 

cs231n에 소개된 ConvNet Architecture

 

기존 딥러닝의 Neural Network 는 Image 의 Pixel 단위로 학습하므로,

Image 의 공간적인 Pattern 을 가진 특징을 찾지 못하는 한계가 있었습니다.

이를 해결하기 위해 CNN 은 Convolution, ReLU, Pooling 과정을 통해 이미지의 공간적인 특징을 추출하고,

이를 Fully Connected Layer 를 통해 분류하는 2가지 구조로 이루어져 있습니다.

즉, 이미지의 공간적인 정보를 유지시킨 특징들을 추출하여 학습시키는 원리입니다.

파이토치 (Pytorch) 에서는 torch.nn 함수들을 이용하여 Layer 를 쌓습니다.

nn.Conv2d 로 Convolution 을 진행하고,

해당 함수의 입력 인자에는 Input 채널, Output 채널, 필터 사이즈, stride, padding 등을 입력합니다.

Output 채널은

 

로 계산합니다.

또한 딥러닝의 안정성을 위해 파이썬 (Python), 파이토치 (Pytorch) 의 Batch Normalization 을 적용시킵니다.

 

import torch.nn as nn

class CNN(nn.Module):

    def __init__(self):
        super(CNN, self).__init__()
        self.layer = nn.Sequential(
            # 3 x 200 x 200
            nn.Conv2d(3, 32, 3, stride = 1),
            nn.BatchNorm2d(32),
            nn.ReLU(),

            # 32 x 198 x 198
            nn.Conv2d(32, 64, 3, stride = 1),
            nn.BatchNorm2d(64),
            nn.ReLU(),

            # 64 x 196 x 196
            nn.MaxPool2d(2, 2),

            # 64 x 98 x 98
            nn.Conv2d(64, 128, 3, stride = 1),
            nn.BatchNorm2d(128),
            nn.ReLU(),

            # 128 x 96 x 96
            nn.Conv2d(128, 256, 3, stride = 1),
            nn.BatchNorm2d(256),
            nn.ReLU(),

            # 256 x 94 x  94
            nn.MaxPool2d(2, 2),

            # 256 x 47 x 47
            nn.Conv2d(256,512,3, stride=1),
            nn.BatchNorm2d(512),
            nn.ReLU()
        )
        # 512 x 45 x 45

        self.avg_pool = nn.AvgPool2d(45)
        self.classifier = nn.Linear(512,5)

    def forward(self, x):
        feat = self.layer(x)
        flat = self.avg_pool(feat).view(feat.size(0),-1)
        out = self.classifier(flat)
        return out, feat

딥러닝 구조를 보다 보기 쉽게 나타내기 위해 파이썬 (Python) 의 torchsummary 를 사용하였습니다.

 

pip install torchsummary

https://github.com/sksq96/pytorch-summary

 

모델과 Input 데이터의 채널, 사이즈를 입력하면 보기 쉽게 파이썬 (Python) 에 요약하여 출력됩니다.

from torchsummary import summary

    model = CNN().cuda()
    summary(model, (3,200,200))

 

독자적으로 생성한 CNN Architecture

 

마지막으로 딥러닝의 Hyperparameter, Optimizer, Loss Function 을 지정해주면 모델 제작이 끝나게 됩니다.

Hyperparameter 는 기본적으로 Batch Size, Epoch, Learning Rate 가 있으며

앞에서 작성한 Parser 를 활용하면 쉽게 수치를 바꿔가며 실험할 수 있습니다.

Optimizer 와 Loss Function 은 가장 많이 사용되는 Adam 과 Cross Entropy 를 사용하였습니다.

해당 함수들은 모두 파이썬 (Python), 파이토치 (Pytorch) 라이브러리에 존재합니다.

Log Interval 은 학습 과정을 출력하는데 사용되는 수치입니다.

 

import torch.optim

    parser = argparse.ArgumentParser(description='Image')
    parser.add_argument('--Resize',type=int,default=(200,200))

    parser.add_argument('--batch-size', type=int, default=50)
    parser.add_argument('--epochs', type=int, default=1)
    parser.add_argument('--lr', type=float, default=0.1)

    parser.add_argument('--log-interval', type=float, default=10)
    args = parser.parse_args()

    optimizer = torch.optim.Adam(model.parameters(), lr=args.lr)
    loss_func = nn.CrossEntropyLoss()

이제 마지막으로 트레이닝과 테스트 코드를 작성합니다.

파이토치 (Pytorch) 의 경우엔 앞서 만든 데이터셋의 Loader 에서 배치에 따른 이미지, 레이블을 불러 학습합니다.

거의 대부분이 아래와 같은 학습 과정으로 파이썬 (Python) 코드가 진행됩니다.

 

def train(args, model, train_loader, optimizer, epoch, loss_func, min_loss):
    model.train()
    train_loss = 0
    correct = 0
    for batch_index,[image,label] in enumerate(train_loader):
        image = image.cuda()
        label = label.cuda()

        out, feat = model(image)
        loss = loss_func(out,label)
        train_loss += loss.item()
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        pred = out.argmax(dim=1, keepdim=True)  # get the index of the max log-probability
        correct += pred.eq(label.view_as(pred)).sum().item()

        if batch_index % args.log_interval == 0:
            print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
                epoch, batch_index * len(image), len(train_loader.dataset),
                       100. * batch_index / len(train_loader), loss.item()))

    train_loss /= len(train_loader)
    acc = 100. * correct / len(train_loader.dataset)

    if train_loss<min_loss:
        print("Renew Model")
        min_loss = train_loss
        torch.save(model.state_dict(), '모델 저장 경로')

    return train_loss, acc, min_loss

테스트 과정도 트레이닝과 흡사하게 진행됩니다.

주의점은 테스트 데이터셋은 학습에 관여하면 안되므로 loss.backward 와 같은 코드는 작성되선 안됩니다.

 

def test(args, model, test_loader, loss_func):
    model.eval()
    test_loss = 0
    correct = 0
    pred_save = []

    for image, label in test_loader:
        image = image.cuda()
        label = label.cuda()
        out, feat = model(image)
        loss = loss_func(out,label)
        test_loss += loss.item()
        pred = out.argmax(dim=1, keepdim=True)  # get the index of the max log-probability
        correct += pred.eq(label.view_as(pred)).sum().item()
        pred_save.append(pred)

    test_loss /= len(test_loader)
    print('\n Test set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
        test_loss, correct, len(test_loader.dataset), 100. * correct / len(test_loader.dataset)))
    val_acc = 100.*correct/len(test_loader.dataset)

    return test_loss, val_acc, pred_save

 

 

4. 모델 평가 및 시각화 (Evaluation and Visualization)

이번 프로젝트는 5가지 클래스에 대한 분류, 즉 Multi-Class Classification 에 관련된 내용이었으므로,

딥러닝 모델에 대한 성능 평가 지표로 파이썬 (Python) 의 Loss 값과 Accuracy 를 사용했고,

시각화를 위해 Confusion Matrix 와 CAMs (Class Activation Mappings) 방법을 사용하였습니다.

Loss 란 실제 주어진 Class 와 딥러닝이 예측한 결과 값 간의 오차를 나타내며,

딥러닝은 학습을 통해 해당 오차를 줄이기 위한 최적의 가중치를 찾아나가는 과정을 반복합니다.

Accuracy 는 딥러닝이 Classifier 를 통해 최종 Class 를 예측하면,

이 예측 값과 실제 주어진 Class 가 일치했는가의 여부를 나타냅니다.

또한 마지막으로 저희는 Confusion Matrix 를 통해 어떠한 Class 가 잘 분리되었는지,

잘못 되었다면 어떤 Class 의 Image 가 Classification 되지 못했는가를 확인할 수 있고,

CAMs 를 통해 어떤 특징이 CNN 의 특징으로 추출되었는가를 확인하였습니다.

 

Augmentation 을 수행하지 않은 기본 데이터셋에 대한 Loss 값과 Accuracy

 

Augmentation 을 수행하지 않은 기본 데이터셋에 대한 Confusion Matrix

 

첫 번째로, 아무런 전처리 과정을 진행하지 않은 기본 이미지 데이터셋에 대한 결과입니다.

해당 인공지능 (AI) 는 Loss 와 Accuracy 모두 Fluctation 이 심한것을 확인할 수 있고,

50번의 학습 결과도 80% 언저리에 불과하므로 좋지 못합니다.

Confusion Matrix 를 확인하니 장미 이미지가 제대로 분류되지 못하고 있음을 확인할 수 있습니다.

 

Augmentation 을 2000 번 수행한 데이터셋에 대한 Loss 값과 Accuracy

 

다음으론 2000번의 Augmentation 으로 이미지가 늘어난 데이터셋에 대한 결과입니다.

해당 인공지능 (AI) 은 기존 데이터셋에 비해 Fluctation 도 감소하고 성능도 향상됨을 확인할 수 있었습니다.

최종 ACC 성능은 97%로 상당히 높았으나 아직까지 불안정한 Fluctation 을 보입니다.

Augmentation 을 5000 번 수행한 데이터셋에 대한 Loss 값과 Accuracy

 

마지막으로 5000번의 Augmentation 으로 이미지가 늘어난 데이터셋 결과입니다.

30번째 학습에 이르러서는 Fluctation 도 거의 사라지며 지속적으로 97% 이상의 안정적인 결과를 보여줍니다.

(해당 인공지능 (AI) 은 Hyperparameter 를 조금 더 튜닝해준다면 더욱 이상적인 학습 결과가 나올 것입니다.)

 

Augmentation 을 2000번 (왼쪽) 5000 번 (오른쪽) 수행한 데이터셋에 대한 Confusion Matrix

 

그렇다면 이 CNN 딥러닝 모델은 어떤 특징으로 분류를 했는가에 대한 의문이 남습니다.

딥러닝은 구조가 깊어질수록 수많은 연산이 이뤄지므로 그 내부를 인간의 뇌로 이해하기 어렵습니다.

따라서 Black Box 라고 표현되고, 이를 이해하기 쉽도록 시각화 하는 연구도 중요한 핵심 분야입니다.

저희는 CAMs 라는 시각화 방법을 적용하여 특징의 시각화를 진행해보았습니다.

 

시각화를 위한 Class Acitvation Maps (CAMs) 방법

CCAMs 의 컨셉은 CNN 구조에서 분류 역할인 Fully Connected Layer 를 제거하고,

해당 위치에 Global Average Pooling Layer 를 통해 Convolution Layer 가 추출한 특징을 그대로 분류합니다.

따라서 마지막 Convolution Layer 를 통과한 Feature Map 의 가중치를 불러오고

원본 이미지에 가중치에 따른 색칠을 통해 어떤 부분을 특징으로 추출했는지 확인할 수 있습니다.

아래 사진을 통해 비교적 꽃의 특징만을 잘 추출하여 분류했음을 볼 수 있습니다.

 

CAMs 결과

 

결과를 보니 CNN 모델이 주변 배경이 아닌, 꽃들의 특성을 보고 분류함을 확인할 수 있었고,

특히 대량의 Sunflower 사진들은 인간과 유사하게 특징을 잡아 Classification 하는 것을 볼 수 있었습니다.

 

 

 

5. 다양한 모델의 적용 및 비교

독자적인 딥러닝 모델은 좋은 성능을 보였으나, 이는 높은 성능을 위해선 많은 시행착오가 필요하고,

동시에 데이터와 컴퓨터 성능에 따라 학습이 안되거나 오랜시간이 걸릴 여지가 있습니다.

이러한 문제들을 해결하고자 기존 딥러닝 모델과 가중치를 가져와 학습시키는 방법을 사용합니다.

이를 Transfer Learning 이라 하며, 대표적인 4개의 모델을 가져와서 적용하였습니다.

1) AlexNet (https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks)

 

Alexnet 구조

AlexNet 은 ILSVRC - 2012 에서 우승한 모델로, 딥러닝의 폭발적인 발전을 가져온 모델입니다.

해당 인공지능 (AI) 은 파이토치 (Pytorch) 에서 제공되는 AlexNet 모델을 이용하여 학습하였습니다.

Augmentation 된 Data 에 대한 AlexNet 결과

 

2) VggNet (https://arxiv.org/abs/1409.1556)

 

VggNet 구조

 

VggNet 은 ILSVRC - 2014 에서 준우승하였으나 간단한 구조에 따른 변형이 용이하여 널리 사용되는 모델입니다.

해당 인공지능 (AI) 은 파이토치 (Pytorch) 에서 제공되는 VggNet 중 가장 Shallow 한 모델인 Vgg16 을 이용하여 학습하였습니다.

Augmentation 된 Data 에 대한 VggNet 결과

 

 

3) Googlenet (https://arxiv.org/abs/1409.4842)

 

GoogleNet 구조

 

GoogleNet 은 ILSVRC - 2014 에서 우승한 모델로 흔히 Inception 이라고도 불리는 모델입니다.

해당 인공지능 (AI) 은 파이토치 (Pytorch) 에서 제공되는 GoogleNet 을 이용하여 학습하였습니다.

Augmentation 된 Data 에 대한 GoogleNet 결과

 

 

4) ResNet (https://arxiv.org/abs/1512.03385)

ResNet 구조

 

ResNet 은 ILSVRC - 2015 에서 우승한 모델로 Layer 를 매우 깊고 효과적으로 학습하기 위해 고안된 모델입니다.

해당 인공지능 (AI) 은 파이토치 (Pytorch) 에서 제공된 ResNet 중 가장 Shallow 한 모델인 ResNet18 을 이용하여 학습하였습니다.

Augmentation 된 Data 에 대한 ResNet 결과

 

 

4가지 모델은 공통적으로 초기 가중치가 학습되어져 있기 때문에,

독자적인 모델에 비해 긴 시간 학습을 진행하지 않아도 높은 성능이 유지됨을 볼 수 있었으나,

기존 독자적인 모델에 비해 정확도는 떨어지는 양상을 보였습니다.

6. 실생활 적용

이미지 분류는 다양한 영역에서 적용되는 기본적인 사례입니다.

Medical 분야에선 X-ray 와 같은 Image 를 통해 환자의 질병 여부를 판독하는데 사용되기도 하며,

Social Network 에선 Image 의 Tagging 을 자동으로 붙여주기도 하고,

Merchandise 를 구매하기 위해 원하는 상품을 사진으로 찍어 판독을 요청하기도 합니다.

또한 Automobile 영역에서 표지판이나 신호등과 같은 도로 상태를 체크하기도 하며,

이러한 다양한 분야에서 인공지능(AI) 이 사용자의 '눈' 이 되어주는 역할을 할 수 있습니다.

4차산업혁명을 통한 빅데이터와 인공지능 (AI) 붐은 다양한 딥러닝의 발전을 가져왔으나,

아직까지 사람들에게 이는 친숙하지 못한 방법론이며

어떠한 데이터냐에 따라 그 변화가 다양하므로 정답이 존재하지 않습니다.

허니컴 메이커스페이스는 데이터에 따라 다양한 방향으로 인공지능 (AI) 딥러닝 모델을 적용해보며

'정답' 에 근접한 모델을 생성하고,

이를 사람들이 이해하기 쉽도록 시각화와 같은 방법을 통해 설명해나가고자 합니다.

........

시제품 제작 문의