AI/AI 기초

[AI 기초] 8. 딥러닝

kk_______yy 2024. 1. 30. 18:32

8.1 더 깊게

8.1.1 더 깊은 신경망으로

[그림 8-1]과 같이 구성된 CNN을 만들어보자 (VGG 신경망 참고)

[그림 8-1] 손글씨 숫자를 인식하는 심층 CNN

  • 지금까지 구현한 신경망보다 층이 깊음
  • 여기에서 사용하는 합성곱 계층은 모두 3x3 크기의 작은 필터, 층이 깊어지면서 채널수 늘어남
    (합성곱 계층의 채널 수는 앞 계층에서부터 순서대로 16, 16, 32, 32, 64, 64로 늘어감)
  • 그림과 같이 풀링 계층을 추가하여 중간 데이터의 공간 크기를 점차 줄여감
  • 마지막 단의 완전연결 계층에서는 드롭아웃 계층을 사용

 

이 신경망의 특징

  • 3x3의 작은 필터를 사용한 합성곱 계층
  • 활성화 함수는 ReLU
  • 완전연결 계층 뒤에 드롭아웃 계층 사용
  • Adam을 사용해 최적화
  • 가중치 초깃값은 'He의 초깃값'
  • 정확도 99.38%

(신경망 구현 소스 코드)

더보기

신경망 구현 소스 코드 : deep_convnet.py

(학습 시간이 오래 걸리므로, 학습된 가중치 매개변수 모델)

https://github.com/youbeebee/deeplearning_from_scratch/blob/master/ch8.%EB%94%A5%EB%9F%AC%EB%8B%9D/deep_convnet_params.pkl

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import pickle
import numpy as np
from collections import OrderedDict
from common.layers import *


class DeepConvNet:
    """정확도 99% 이상의 고정밀 합성곱 신경망

    네트워크 구성은 아래와 같음
        conv - relu - conv- relu - pool -
        conv - relu - conv- relu - pool -
        conv - relu - conv- relu - pool -
        affine - relu - dropout - affine - dropout - softmax
    """
    def __init__(self, input_dim=(1, 28, 28),
                 conv_param_1 = {'filter_num':16, 'filter_size':3, 'pad':1, 'stride':1},
                 conv_param_2 = {'filter_num':16, 'filter_size':3, 'pad':1, 'stride':1},
                 conv_param_3 = {'filter_num':32, 'filter_size':3, 'pad':1, 'stride':1},
                 conv_param_4 = {'filter_num':32, 'filter_size':3, 'pad':2, 'stride':1},
                 conv_param_5 = {'filter_num':64, 'filter_size':3, 'pad':1, 'stride':1},
                 conv_param_6 = {'filter_num':64, 'filter_size':3, 'pad':1, 'stride':1},
                 hidden_size=50, output_size=10):
        # 가중치 초기화===========
        # 각 층의 뉴런 하나당 앞 층의 몇 개 뉴런과 연결되는가(TODO: 자동 계산되게 바꿀 것)
        pre_node_nums = np.array([1*3*3, 16*3*3, 16*3*3, 32*3*3, 32*3*3, 64*3*3, 64*4*4, hidden_size])
        wight_init_scales = np.sqrt(2.0 / pre_node_nums)  # ReLU를 사용할 때의 권장 초깃값
        
        self.params = {}
        pre_channel_num = input_dim[0]
        for idx, conv_param in enumerate([conv_param_1, conv_param_2, conv_param_3, conv_param_4, conv_param_5, conv_param_6]):
            self.params['W' + str(idx+1)] = wight_init_scales[idx] * np.random.randn(conv_param['filter_num'], pre_channel_num, conv_param['filter_size'], conv_param['filter_size'])
            self.params['b' + str(idx+1)] = np.zeros(conv_param['filter_num'])
            pre_channel_num = conv_param['filter_num']
        self.params['W7'] = wight_init_scales[6] * np.random.randn(64*4*4, hidden_size)
        self.params['b7'] = np.zeros(hidden_size)
        self.params['W8'] = wight_init_scales[7] * np.random.randn(hidden_size, output_size)
        self.params['b8'] = np.zeros(output_size)

        # 계층 생성===========
        self.layers = []
        self.layers.append(Convolution(self.params['W1'], self.params['b1'], 
                           conv_param_1['stride'], conv_param_1['pad']))
        self.layers.append(Relu())
        self.layers.append(Convolution(self.params['W2'], self.params['b2'], 
                           conv_param_2['stride'], conv_param_2['pad']))
        self.layers.append(Relu())
        self.layers.append(Pooling(pool_h=2, pool_w=2, stride=2))
        self.layers.append(Convolution(self.params['W3'], self.params['b3'], 
                           conv_param_3['stride'], conv_param_3['pad']))
        self.layers.append(Relu())
        self.layers.append(Convolution(self.params['W4'], self.params['b4'],
                           conv_param_4['stride'], conv_param_4['pad']))
        self.layers.append(Relu())
        self.layers.append(Pooling(pool_h=2, pool_w=2, stride=2))
        self.layers.append(Convolution(self.params['W5'], self.params['b5'],
                           conv_param_5['stride'], conv_param_5['pad']))
        self.layers.append(Relu())
        self.layers.append(Convolution(self.params['W6'], self.params['b6'],
                           conv_param_6['stride'], conv_param_6['pad']))
        self.layers.append(Relu())
        self.layers.append(Pooling(pool_h=2, pool_w=2, stride=2))
        self.layers.append(Affine(self.params['W7'], self.params['b7']))
        self.layers.append(Relu())
        self.layers.append(Dropout(0.5))
        self.layers.append(Affine(self.params['W8'], self.params['b8']))
        self.layers.append(Dropout(0.5))
        
        self.last_layer = SoftmaxWithLoss()

    def predict(self, x, train_flg=False):
        for layer in self.layers:
            if isinstance(layer, Dropout):
                x = layer.forward(x, train_flg)
            else:
                x = layer.forward(x)
        return x

    def loss(self, x, t):
        y = self.predict(x, train_flg=True)
        return self.last_layer.forward(y, t)

    def accuracy(self, x, t, batch_size=100):
        if t.ndim != 1 : t = np.argmax(t, axis=1)

        acc = 0.0

        for i in range(int(x.shape[0] / batch_size)):
            tx = x[i*batch_size:(i+1)*batch_size]
            tt = t[i*batch_size:(i+1)*batch_size]
            y = self.predict(tx, train_flg=False)
            y = np.argmax(y, axis=1)
            acc += np.sum(y == tt)

        return acc / x.shape[0]

    def gradient(self, x, t):
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.last_layer.backward(dout)

        tmp_layers = self.layers.copy()
        tmp_layers.reverse()
        for layer in tmp_layers:
            dout = layer.backward(dout)

        # 결과 저장
        grads = {}
        for i, layer_idx in enumerate((0, 2, 5, 7, 10, 12, 15, 18)):
            grads['W' + str(i+1)] = self.layers[layer_idx].dW
            grads['b' + str(i+1)] = self.layers[layer_idx].db

        return grads

    def save_params(self, file_name="params.pkl"):
        params = {}
        for key, val in self.params.items():
            params[key] = val
        with open(file_name, 'wb') as f:
            pickle.dump(params, f)

    def load_params(self, file_name="params.pkl"):
        with open(file_name, 'rb') as f:
            params = pickle.load(f)
        for key, val in params.items():
            self.params[key] = val

        for i, layer_idx in enumerate((0, 2, 5, 7, 10, 12, 15, 18)):
            self.layers[layer_idx].W = self.params['W' + str(i+1)]
            self.layers[layer_idx].b = self.params['b' + str(i+1)]

 

훈련용 코드 : train_deepnet.py

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from deep_convnet import DeepConvNet
from common.trainer import Trainer

(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)

network = DeepConvNet()  
trainer = Trainer(network, x_train, t_train, x_test, t_test,
                  epochs=20, mini_batch_size=100,
                  optimizer='Adam', optimizer_param={'lr':0.001},
                  evaluate_sample_num_per_epoch=1000)
trainer.train()

# 매개변수 보관
network.save_params("deep_convnet_params.pkl")
print("Saved Network Parameters!")

 

[그림 8-2] 인식하지 못한 이미지들 : 각 사진의 왼쪽 위는 정답 레이블, 오른쪽 알는 이 신경망의 추론 결과

 

인간도 판단하기 어려운 이미지가 많음

 

8.1.2 정확도를 더 높이려면

<What is the class of this image?> 웹 사이트

  • 다양한 데이터셋을 대상으로 그동안 논문 등에서 발표한 기법들의 정확도 순위를 정리해둠

[그림 8-3] MNIST 데이터셋에 대한 각 기법의 순위(2016년 기준)

 

  • 상위권은 CNN 기초로 한 기법들이 점령

 

* NOTE

  • MNIST 데이터셋은 층을 깊게 하지 않고도 최고 수준의 결과가 나옴
  • 대규모 일반 사물 인식엣는 문제가 훨씬 복잡해지므로 층을 깊게 하면 정확도 끌어올릴 수 있음

 

[그림 8-3]의 상위 기법들을 참고하면 정확도 더 높일 수 있는 기술이나 힌트 발견할 수 있음

  • ex) 앙상블 학습, 학습률 감소, 데이터 확장 등 특히 데이터 확장은 손쉽고 정확도 개선에 효과적

 

데이터 확장(data augmentation)

  • 입력 이미지(훈련 이미지)를 알고리즘을 동원해 '인위적'으로 확장
  • 입력 이미지를 회전하거나 세로로 이동하는 등 미세한 변화를 주어 이미지 개수 늘림
  • 이미지 일부 자르는 crop, 좌우 뒤집는 flip, 밝기 등의 외형 변화, 확대 축소 등 스케일 변화
  • 데이터가 몇 개 없을 때 특히 효과적

[그림 8-4] 데이터 확장의 예

 

8.1.3 깊게 하는 이유

'층을 깊게 하는 것'이 왜 중요한가

  • 이론적인 근거는 아직 부족
  • 그래도 실험 결과를 바탕으로 몇 가지 설명

 

ILSVRC로 대표되는 대규모 이미지 인식 대회의 결과

  • 상위 기법 대부분 딥러닝 기반
  • 신경망을 더 깊게 만드는 방향의 경향성

 

이점

  • 신경망의 매개변수 수가 줄어듦
  • 층이 깊은 신경망은 깊지 않은 것보다 적은 매개변수로 같은 (혹은 그 이상) 수준의 표현력

 

[그림 8-5] 5x5 합성곱 연산의 예

 

주목할 점 : 출력 데이터의 각 노드가 입력 데이터의 어느 영역으로부터 계산되었는가

  • [그림 8-5]의 예에서 각각의 출력 노드는 입력 데이터의 5x5 크기 영역에서 계산

 

[그림 8-6] 3x3 합성곱 계층을 2회 반복한 예

3x3 합성곱 연산을 2회 반복하는 경우

  • 중간 데이터는 5x5 영역에서 계산
  • 출력 노드 하나는 중간 데이터의 3x3 영역에서 계산

 

5x5 합성곱 연산 1회는 3x3 합성곱 연산을 2회 수행하여 대체

  • 전자의 매개변수 25개(5x5), 후자 18개(2x3x3) → 층을 반복할수록 적어짐
  • 같은 크기의 영역을 1회의 합성곱 연산으로 '보기' 위해서는 7x7 크기의 필터, 즉 매개변수 49개 필요

 

* NOTE : 작은 필터를 겹처 신경망을 깊게 할 때의 장점

  • 매개변수 수를 줄여 넓은 수용 영역(receptive field)을 소화할 수 있음
    (수용 영역 : 뉴런에 변화를 일으키는 국소적인 공간 영역)
  • 층을 거듭하면서 ReLU 등의 활성화 함수를 합성곱 계층 사이에 끼움으로써 신경망의 표현력 개선
    → 활성화 함수가 신경망에 '비선형' 힘을 가하고, 비선형 함수가 겹치면서 더 복잡한 것도 표현할 수 있게 됨

층을 깊게 하여 학습 데이터의 양을 줄여 학습을 고속으로 수행할 수 있음

→ 직관 : 7.6, 단순한 패턴에 뉴런이 반응하고, 층이 깊어지면서 텍스처와 사물의 일부와 같이 점차 더 복잡한 것에 반응

 

ex) '개'를 인식하는 문제

  • 얕은 신경망은 합성곱 계층이 개의 특징 대부분을 한 번에 '이해'해야 함
  • 개의 특징을 이해하려면 변화가 풍부하고 많은 학습 데이터가 필요
    → 결과적으로 학습 시간이 오래 걸림
  • 층을 깊게 하면 정보를 계층적으로 전달할 수 있음

 

신경망을 깊게 하면 학습해야 할 문제를 계층적으로 분해할 수 있음

  • = 각 층이 학습해야 할 문제를 더 단순한 문제로 대체할 수 있음

 

 

8.2 딥러닝의 초기 역사

  • 딥러닝이 큰 주목을 받게 된 계기는 이미지 인식 기술을 겨루는 ILSVRC의 2012년 대회
  • AlexNet이 압도적인 성적으로 우승하며, 이미지 인식에 대한 접근법을 뒤흔듬

 

8.2.1 이미지넷

이미지넷

  • 100만 장이 넘는 이미지를 담고 있는 데이터셋
  • 각 이미지에는 레이블(클래스 이름)이 붙어있음
  • 분류(classification) : 1,000개의 클래스를 제대로 분류하는지 겨룸
  • 톱-5 오류 : 확률이 가장 높다고 생각하는 후보 클래스 5개 안에 정답이 포함되지 않은, 즉 5개 모두가 틀린 비율

 

[그림 8-7] 대규모 데이터셋 ImageNet의 데이터들

 

[그림 8-8] ILSVRX 최우수 팀의 성적 추이 : 세로축은 오류율, 가로축은 연도, 가로축의 괄호 안은 팀 이름(또는 기법 이름)

 

  • AlexNet이 오류율을 크게 낮췄고, 이후 딥러닝을 활용한 기법이 꾸준히 정확도를 개선해 옴

 

8.2.2 VGG

VGG 합성곱 계층과 풀링 계층으로 구성되는 '기본적'인 CNN

 

[그림 8-9] VGG

  • 3x3의 작은 필터를 사용한 합성곱 계층을 연속으로 거침
  • 2~4회 연속으로 풀링 계층을 두어 크기를 절반으로 줄이는 처리 반복
  • 마지막에 완전연결 계층을 통과시켜 결과 출력

* NOTE

  • VGG는 2014년 대회 2위, 성능은 1위 GoogLeNet에 뒤지지만, VGG는 구성이 간단하여 응용이 좋음
  • 많은 기술자가 VGG 기반의 신경망 즐겨 사용

 

8.2.3 GoogLeNet

그림의 사각형이 합성곱 계층과 풀링 계층 등의 계층 나타냄

[그림 8-10] GoogLeNet

  • 그림은 구성이 복잡해 보이지만, CNN과 다르지 않음
  • GoogLeNet은 세로 방향 깊이뿐 아니라 가로 방향도 깊다는 점이 특징

 

GoogLeNet에는 가로 방향에 '폭'이 있음 = 인셉션 구조

[그림 8-11] GoogLeNet의 인셉션 구조

 

인셉션 구조

  • 크기가 다른 필터(와 풀링)를 여러 개 적용하여 그 결과를 결합
  • 1x1의 합성곱 연산은 채널 쪽으로 크기를 줄이는 것, 매개변수 제거와 고속 처리에 기여

 

8.2.4 ResNet

ResNet(Residual Network)

  • 마이크로 소프트 팀이 개발한 네트워크
  • 특징 : 지금까지보다 층을 더 깊게 할 수 있는 특별한 '장치'

 

딥러닝의 학습에서 층이 지나치게 깊으면 학습이 잘 되지 않고 성능이 떨어지는 문제

 

ResNet의 해결책

  • 스킵 연결(skip connection) 도입
  • 층의 깊이에 비례해 성능을 향상시킬 수 있게 함
    (물론 층을 깊게 하는 데에 여전히 한계)

 

스킵 연결

  • 입력 데이터를 합성곱 계층을 건너뛰어 출력에 바로 더하는 구조 [그림 8-12]
  • [그림 8-12]에서는 입력 x를 연속한 두 합성곱 계층을 건너뛰어 출력에 바로 연결
  • 이게 없었다면 두 합성곱 계층의 출력 F(x)
  • 스킵 연결로 F(x) + x 가 되는 것이 핵심
  • 스킵 연결은 층이 깊어져도 학습을 효율적으로 할 수 있도록 함
    → 역전파 때 스깁 연결이 신호 감쇠를 막아주기 때문

 

[그림 8-12] ResNet의 구성요소 : weight layer는 합성곱 계층

 

* NOTE

  • 스킵 여결은 입력 데이터를 '그대로' 흘리는 것
  • 역전파 때도 상류의 기울기를 그대로 하류로 보냄
  • 상류의 기울기에 아무런 수정도 가하지 않고 '그대로' 흘림
    → 스킵 연결로 기울기가 작아지거나 지나치게 커질 걱정 없이 앞 층에 '의미 있는 기울기' 전해짐
  • 층을 깊게 할수록 기울기가 작아지는 소실 문제를 스킵 연결이 줄여줌

 

 

[그림 8-13] ResNet : 블록이 3x3인 합성곱 계층에 대응. 층을 건너뛰는 스킵 연결이 특징이다.

ResNet의 스킵 연결

  • ResNet은 VGG 신경망을 기반으로 스킵 연결을 도입하여 층을 깊게 함
  • ResNet은 합성곱 계층을 2개 층마다 건너뛰면서 층을 깊게 함
  • 실험 결과 150층 이상으로 해도 정확도 오르는 모습

 

* NOTE : 전이 학습(transfer learning)

  • 이미지넷이 제공하는 거대한 데이터셋으로 학습한 가중치 값들은 실제 제품에 활용해도 효과적
  • 학습된 가중치(또는 그 일부)를 다른 신경망에 복사, 그 상태로 재학습을 수행
  • 전이 학습은 보유한 데이터셋이 적을 때 특히 유용

ex)

  • VGG와 구성이 같은 신경망 준비
  • 미리 학습된 가중치를 초깃값으로 설정
  • 새로운 데이터셋을 대상으로 재학습(fine tuning) 수행

 

8.3 더 빠르게(딥러닝 고속화)

  • 딥러닝에서 대량의 연산 수행
  • GPU(Graphics Processing Unit)를 활용해 대량의 연산 고속으로 처리

 

8.3.1 풀어야 할 숙제

[그림 8-14] Alexnet의 forward 처리 시각 층의 시간 비율 : 왼쪽이 GPU, 오른쪽이 CPU를 사용한 경우. conv는 합성곱 계층, pool은 풀링 계층, fc는 완전연결 계층, norm은 정규화 계층

  • AlexNet에서는 오랜 시간을 합성곱 계층에서 소요

 

합성곱 계층에서 이뤄지는 연산을 어떻게 고속으로 효율적으로 하느냐가 딥러닝의 과제

= '단일 곱셈 - 누산' 고속 계산

 

8.3.2 GPU를 활용한 고속화

GPU 컴퓨팅

  • 병렬 수치 연산을 고속으로 처리, 그 압도적인 힘을 다양한 용도로 활용하자
  • GPU로 범용 수치 연산을 수행하는 것

[그림 8-15] AlexNet의 학습 시간을 '16코어 제온 CPU'와 엔비디아 '타이탄 GPU'에서 비교한 결과

 

  • CPU에서는 40여 일이나 걸리지만 GPU로는 6일까지 단축
  • cuDNN이라는 딥러닝에 최적화된 라이브러리를 이용하면 더욱 빨라짐
  • GPU는 주로 엔비디아, AMD 두 회사가 제공
  • 대부분의 딥러닝 프레임워크는 엔비디아 GPU에서만 혜택
    → 엔비디아 GPU 컴퓨팅용 통합 개발 환경인 CUDA를 사용하므로
    → cuDNN은 CUDA 위에서 동작하는 라이브러리

* NOTE

  • 합성곱 계층에서 행하는 연산은 im2col을 이용해 큰 행렬의 곱으로 변환
  • 이러한 im2col의 방식은 GPU로 구현하기에도 적합
  • GPU는 '작은' 단위로 계산하기보다는 큰 덩어리를 한 번에 계산하는 데 유리
  • IM2COL의 거대한 행렬의 곱으로 한 번에 계산하여 GKKPU의 잠재력 끌어냄

 

8.3.3 분산 학습

  • 여전히 시간이 걸림
  • 딥러닝 학습을 수평 확장(scale out)하자는 아이디어(=분산 학습)

 

다수의 GPU와 기기로 계산을 분산

  • 구글 텐서플로, 마이크로소프트 CNTK는 분산 학습에 역점을 두고 개발
  • 거대한 데이터센터의 저지연, 고처리량 네트워크 위에서 이 프레임워크들의 분산 학습 놀라운 효과

 

[그림 8-16] 텐서플로의 분산 학습 성능 : 가로는 GPU의 수, 세로는 GPU 1개일 때와 비교한 비율

= 분산 학습의 놀라운 효과

 

'계산을 어떻게 분산시키느냐'는 몹시 어려운 문제

  • 텐서플로 같은 뛰어난 프레임워크에 맡기는 것이 좋음

 

8.3.4 연산 정밀도와 비트 줄이기

메모리 용량과 버스 대역폭 등이 병목이 될 수 있음

  • 메모리 용량 면에서는 대량의 가중치 매개변수와 중간 데이터를 메모리에 저장해야 함
  • 버스 대역폭 면에서는 GPU(or CPU)의 버스를 흐르는 데이터가 많아져 한계를 넘어서면 병목

→ 네트워크로 주고받는 데이터의 비트수는 최소로 만드는 것이 바람직

 

 

컴퓨터는 주로 64비트나 32비트 부동소수점 수 사용해 실수 표현

  • 많은 비트 사용할수록 계산 비용, 메모리 사용량 늘어남
  • 버스 대역폭에 부담

 

 

딥러닝은 높은 수치 정밀도 요구하지 않음

  • 32비트 단정밀도 vs 16비트 배정밀도 vs 16비트 반정밀도
  • 16비트 반정밀도만 사용해도 학습에 문제 없다고 알려짐

 

넘파이는 16비트 반정밀도 부동소수점 지원

  • 정확도 떨어지지 않음
  • (단, 스토리지로서 16비트라는 틀, 연산 자체는 16비트로 수행하지 않음)

 

(구현 코드)

더보기
# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 부모 디렉터리의 파일을 가져올 수 있도록 설정
import numpy as np
import matplotlib.pyplot as plt
from deep_convnet import DeepConvNet
from dataset.mnist import load_mnist


(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)

network = DeepConvNet()
network.load_params("deep_convnet_params.pkl")

sampled = 1000 # 고속화를 위한 표본추출
x_test = x_test[:sampled]
t_test = t_test[:sampled]

print("caluculate accuracy (float64) ... ")
print(network.accuracy(x_test, t_test))

# float16(반정밀도)로 형변환
x_test = x_test.astype(np.float16)
for param in network.params.values():
    param[...] = param.astype(np.float16)

print("caluculate accuracy (float16) ... ")
print(network.accuracy(x_test, t_test))

 

8.4 딥러닝의 활용

  • 딥러닝은 사물 인식뿐 아니라 온갖 문제에 적용할 수 있음
  • 이미지, 음성, 자연어 등 수많은 분야에서 뛰어난 성능

 

 8.4.1 사물 검출

[그림 8-17] 사물 검출의 예

  • 이미지 어딘가에 있을 사물의 위치 알아냄
  • 한 이미지에 여러 사물 존재할 수 있음

 

CNN을 기반으로 한 사물 검출 문제 기법

  • R-CNN(Regions with Convolutional Neural Network)

[그림 8-18] R-CNN의 처리 흐름

  1. 사물이 위치한 영역을 (어떤 방법으로) 찾아냄
  2. 추출한 각 영역에 CNN을 적용하여 클래스를 분류
    (이미지를 사각형으로 변형하거나 분류할 때 서포트 벡터 머신(SVM)을 사용)

 

후보영역 추출

  • = 사물처럼 보이는 물체를 찾아 처리
  • 컴퓨터 비전 분야에서 발전해온 다양한 기법 사용
  • 최근에는 이 후모 영역 추출도 CNN으로 처리하는 Faster R-CNN 기법도 등장
    → 모든 일을 하나의 CNN에서 처리하므로 아주 빠름

 

8.4.2 분할

분할(segmentation)

: 이미지를 픽셀 수준에서 분류하는 문제

  • 다음과 같이 픽셀 단위로 객체마다 채색된 지도(supervised) 데이터를 사용해 학습
  • 추론할 때 입력 이미지의 모든 픽셀을 분류

[그림 8-19] 분할의 예 : 왼쪽이 입력 이미지, 오른쪽이 지도용 이미지

 

신경망을 이용해 분할하는 가장 단순한 방법

  • 모든 픽셀 각각을 추론하는 것

 

ex)  어떤 직사각형 영역의 중심 픽셀의 클래스를 분류하는 신경망을 만들어, 모든 픽셀을 대상으로 하나씩 추론 작업

  • 픽셀 수만큼 forward 처리 해야 하여 긴 시간 소요

 

낭비를 줄여주는 기법으로 FCN(Fully Convolutional Network) 고안

  • = 한 번의 forward 처리로 모든 픽셀의 클래스 분류

 

 

[그림 8-20] FCN의 전체 그림

 

Fully Convolutional Network

직역 : 합성곱 계층만으로 구성된 네트워크

 

차이점1

  • 일반적인 CNN → 완전연결 계층 이용
  • FCN → 완전연결 계층을 '같은 기능을 하는 합성곱 계층'으로 바꿈

차이점2

  • 사물 인식에서 사용한 신경망의 완전연결 계층
    → 중간 데이터의 공간 볼륨(다차원 형태)를 1차원으로 변환하여 한 줄로 늘어선 노드들이 처리
  • FCN : 공간 볼륨을 유지한 채 마지막까지 처리

특징

  • 마지막에 공간 크기를 확대하는 처리 도입
  • 확대 처리로 줄어든 중간 데이터를 입력 이미지와 같은 크기까지 단번에 확대
    = 이중 선형 보간, 이를 역합성곱 연산으로 구현

* NOTE

  • 완전연결 계층에서는 출력이 모든 입력과 연결
  • 이와 같은 구성을 합성곱 계층으로도 구현할 수 있음

ex) 32x10x10인 데이터에 대한 완전연결 계층 → 필터 크기가 32x10x10인 합성곱 계층으로 대체

  • 완전연결 계층의 출력 노드가 100개라면 합성곱 계층에서는 기존의 32x10x10 필터를 100개 준비하면 완전히 같은 처리

 

8.4.3 사진 캡션 생성

  • 컴퓨터 비전과 자연어를 융합한 재미있는 연구

[그림 8-21] 딥러닝으로 사진 캡션을 생성하는 예

  • NIC(Nural Image Caption) 모델이 대표적

 

[그림 8-22] NIC의 전체 구성

 

NIC

  • [그림 8-22]와 같이 심층 CNN과 자연어를 다루는 순환 신경망(Recurrent Neural Network, RNN)으로 구성
  • RNN은 순환적 관계를 갖는 신경망으로 자연어나 시계열 데이터 등의 연속된 데이터를 다룰 때 많이 사용
  • RNN은 CNN이 추출한 특징을 초깃값으로 해서 텍스트를 '순환적'으로 생성
  • 2개의 신경망(CNN, RNN)을 조합한 간단한 구성
  • 멀티모달 처리 : 여러 종류의 정보를 조합하고 처리하는 것

* NOTE : RNN

  • R은 Recurrent(순환적)을 의미
  • 신경망의 순환적 네트워크 구조
  • 순환적인 구조 : 이전에 생성한 정보에 영향을 받음(과거 정보 기억)

ex)

나 ~~~잤다

자동으로 '는'이라는 조사가 자동으로 생성

 

8.5 딥러닝의 미래

8.5.1 이미지 스타일(화풍) 변환

[그림 8-23] 「A Neural Algorithm of Artistic Style」 논문을 구현해 적용한 사례 : 왼쪽 위가 '스타일 이미지', 오른쪽 위가 '콘텐츠 이미지', 아래가 새로 생성한 이미지

 

  • 네트워크의 중간 데이터가 콘텐츠 이미지의 중간 데이터와 비슷해지도록 학습
  • '스타일 행렬' 개념 도입 : 스타일 이미지의 화풍 흡수
  • 스타일 행렬의 오차를 줄이도록 학습하여 입력 이미지를 고흐의 화풍과 비슷해지게 만듦

 

8.5.2 이미지 생성

아무런 입력 이미지 없이도 새로운 이미지를 그려내는 연구 진행 중

[그림 8-24] DCGAN으로 새롭게 생성한 침실 이미지들

  • 이미지를 생성하는 과정을 모델화
  • 모델을 대량의 이미지를 사용해 학습
  • 학습이 끝나면 그 모델을 이용하여 새로운 그림을 생성
  • DCGAN도 딥러닝 사용

 

GAN

  • 생성자(Generator)와 식별자(Discriminator)로 불리는 2개의 신경망 이용
  • 생성자가 진짜와 똑같은 이미지 생성, 식별자는 진실 판별
  • 둘을 겨루도록 학습시켜, 생성자는 정교한 가짜 이미지 생성, 식별자는 더 정확한 감정사로 성장

* NOTE

지도학습(supervised learning)

  • 이미지 데이터와 정답 레이블을 짝지은 데이터셋 이용

비지도 학습(unsupervised learning)

  • 지도용 데이터는 주어지지 않고, 단지 대량의 이미지(이미지의 집합)만 주어짐
    ex) GAN

 

8.5.3 자율 주행

  • 자율주행은 다양한 기술을 모아 구현
  • 그중에서도 주위 환경 올바르게 인식하는 기술이 가장 중요한 문제

 

ex) SegNet

  • CNN 기반 신경망

[그림 8-25] 딥러닝을 활용한 이미지 분할의 예 : 도로, 차, 건물, 인도 등을 정확하게 인식

 

8.5.4 Deep Q-Network(강화학습)

강화학습(reinforcement learning)

  • 에이전트라는 것이 환경에 맞게 행동을 선택, 그 행동에 의해서 환경이 변함
  • 환경이 변화하면 에이전트는 보상을 얻음
  • 보상을 받는 쪽으로 에이전트의 행동 지침을 바로잡는 것

[그림 8-26] 강화학습의 기본 틀 : 에이전트는 더 좋은 보상을 받기 위해 스스로 학습한다.

Deep Q-Network(DQN)

  • Q학습이라는 강화학습 알고리즘을 기초로 함
  • Q학습에서는 최적 행동 가치 함수로 최저인 행동을 정함
  • 함수를 딥러닝(CNN)으로 비슷하게 흉내 내어 사용하는 것

 

[그림 8-27] Deep Q-Network로 비디오 게임 조작을 학습. 비디오 게임 영상을 입력받아 시행착오를 거쳐 프로게이머 뺨치는 게임 컨트롤을 학습

 

8.6 정리