본문 바로가기
DATA Science/DeepLearing from scratch

딥러닝

by Rainbound-IT 2021. 5. 23.
반응형
딥러닝의 특징
과제

가능성
최근 딥러닝 기술들


심층신경망

손글씨 숫자를 인식하는 심층 CNN

위와 같이 구성된 CNN을 만들어보자

여기에서 사용되는 합성곱 계층은 3X3 크기의 필터고 층이 깊어지면서 채널 수가 늘어나는 것이 특징이다.

(합성곱 계층 채널 수는 16, 16, 32, 32, 64, 64로 늘어난다)

풀링계층을 추가하여 중간 데이터의 공간 크기를 줄여 간다.

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

 

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=(12828),
                 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*316*3*316*3*332*3*332*3*364*3*364*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((025710121518)):
            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((025710121518)):
            self.layers[layer_idx].W = self.params['W' + str(i+1)]
            self.layers[layer_idx].b = self.params['b' + str(i+1)]
cs

 

위 그림을 구현한 코드이다. (deep_conv_net_parap.pkl은 학습된 가중치 매개변수이다.)

학습 시켜보려고 햇는데 반나절 이상 걸린다고해서 하다가 그만 뒀다.

 

정확도를 높이는 방법은?

 

대규모 일반 사물 인식에서는 문제가 복잡하여 층을 깊게하면 정확도를 크게 끌어올릴 수 있다.

 

데이터확장(data augmentation)

입력 이미지(훈련 이미지)를 알고리즘을 동원해 '인위적'으로 확장한다. 

입력이미지를 회전, 세로이동등 미세한 변화를 주어 이미지 개수를 증가시켰다

이외에도 이미지 일부를 잘라내는 crop, 좌우를 뒤집는 flip등이 있다.

 

층을 깊게 하는 것의 중요성

층을 깊게하는것이 중요한가에 대한 이론적 근거가 많이 부족하다고 한다.

하지만 실험결과로 몇가지 증명할수 있는 것이 있다.(이미지 인식 대회의 결과등)

층을 깊게하면 적은 매개 변수로 층을 깊게하여 많은 매개변수 수준의 표현력을 달성할 수 있다.

 

위에서 보면 5X5 합성곱 연산 1회는 3X3 연산 2회 수행하여 대처 할수 있다.

전자의 매개변수가 25개(5X5), 후자는 18개(2X3X3)이며, 매개변수 수는 층을 반복할수록 적어진다.

작은 필터를 겹쳐 신경망을 깊게 할때 장점은 매개변수 수를 줄여 넓은 수용영역(receptive field)을 소화 할 수 있다는데 있다. 활성화 함수가 신경망에 '비선형'힘을 가하고, 비성형 함수가 겹치면서 더 복잡한 것도 표현할 수 있게 되기 때문이다.

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

얕은 신경망인 경우 변화가 풍부하고 많은 학습데이터가 필요하여 학습 시간이 오래 걸린다.

그러나 신경망을 깊게 하면 학습해야 할 문제를 계층적으로 분해하여 단순한 문제가 되어 훨씬 간단해 진다.

 

딥러닝의 초기 역사

대규모 데이터셋 ImageNet의 데이터들

ILSVRC 대회에서 구분하는 데이터 셋이다. 이것을 분류하는 딥러닝 기법을 보면

최우수 팀의 성적 추이

2015년 이후로 인간의 인식능력을 능가 했다고 본다.

 

VCG

합성곱 계층과 풀링 계층으로 구성되는 기본적인 CNN이다.

다른점은 비중있는층을(합성곱 계층, 완전연결 계층)을 모두 16층(or 19층)으로 심화한 것이다.

VCG

여기서 특이한 점은 3X3의 작은 필터를 사용한 합성곱 계층을 연속으로 거친다는 점이다.

VCG는 구성이 간단하여 응용하기 좋다는 점이 있다.

 

GoogLeNet

GoogLeNet(사각형은 합성곱계층과 풀링 계층등을 나타낸다)

CNN과 다르게 세로도 깊지만 가로도 깊다.

GoogLeNet에서 가로 방향에 폭이 있는 것이 인셉션 구조라고 한다.

인셉션 구조

인셉션 구조는 다른 필터(와 풀링)를 여러 개 적용하여 그 결과를 결합한다.

이 구조를 하나의 구성요소로 사용하는 것이 GoogLeNet의 특징이다.

또 GoogLeNet에서는 1X1 크기의 필터를 사용한 합성곱 계층을 많은 곳에서 사용한다.

 

ResNet(Residual Network)

지금까지보다 층을 더깊게 할수 있는 특별한 기능이 더해지는게 특징이다.

여태까지 딥러닝의 학습에서는 층이 지나치게 깊으면 학습이 잘 되지 않고, 오히려 성능이 떨어지는 경우가 있다.

그래서 ResNet에서는 스킵연결을 도입한다. 이 구조가 층의 깊이에 비례해 성능을 향상시킬 수 있게 한 핵심이다.

 

ResNet(weight layer는 합성곱 계층)

입력데이터를 합성곱 계층을 건너뛰어 출력에 바로 더하는 구조이다.

이 과정 때문에 기울기가 지나치게 작아지는것을 방지 할 수 있다.

ResNet : 블록이 3X3인 합성곱 계층에 대응, 여러개 붙인것

이미지넷이 제공하는 거대한 데이터셋으로 학습한 가중치 값들은 실제 제품에 활용해도 효과적이고, 또 많이들 그렇게 활용하고 있다. 이를 전이 학습(transfer learning) 라고해서 학습된 가중치를 다른 신경망에 복사한 다음, 그 상태로 재학습을 수행한다. (보유한 데이터셋이 적을때 효율적이다.)

 

 

딥러닝 고속화

과거에는 주로 cpu로 계산을 담당햇으나, cpu만으로 딥러닝 연산을 처리하기에는 부족한 현실이다.

그래서 실제로 딥러닝 프레임워크 대부분은 GPU를 활용해 대량연산을 고속으로 처리하고 있다.

그 고속화에 관해 알아보자!

 

딥러닝에서 문제점

딥러닝에서 어떠한 처리에 시간이 소요되는지 알아보자

예로 AlexNet의 forward처리(순전파)에서 각 층이 소비하는 시간을 원그래프로 알아보자

왼쪽이 gpu, 오른쪽이 cpu. / conv:합성곱 계층, pool:풀링계층, fc: 완전연결계층, norm: 정규화계층

위 그림을 보면 AlexNet에서는 오랜시간 합성곱 계층(conv)에서 소요한다.

실제로 합성곱 계층의 시간을 더하면 GPU에서는 95%, CPU 에서는 89% 를 차지하고 있다.

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

위는 추론 때의 결과이나 학습시에도 많은 시간을 합성곱 계층에서 소비하게 된다.

 

GPU를 활용한 고속화

최근 GPU를 그래픽 전용이 아닌 범용 수치 연산에도 이용한다. 

GPU는 병렬 수치 연산을 고속으로 처리 할 수 있어서, 다양한 용도로 활용 하자는것이 GPU 컴퓨팅의 목적이다.

그래서 이 GPU를 범용 수치연산을 수행하는것을 GPU 컴퓨팅이라고 한다.

 

GPU는 대량 병렬 연산(CPU는 연속적인 복잡한 연산)을 처리하는데 좋다.

딥러닝에서 대량의 단일 곱셈-누산(or 큰 행렬의곱)을 수행해야한다.

그래서 딥러닝 연산에서 GPU를 이용하면 CPU만 쓸때 보다 훨씬 빠르게 결과를 얻을 수있다.

그것을 직접 비교해보면 

AlexNet의 학습 시간을 '16코어xeonCPU'와 '엔비디아타이탄GPU'를 비교한 결과

cpu는 40여일이 걸리나 GPU는 6일까지 단축할수가 있다.

cuDNN이라는 딥러닝 최적화 라이브러리를 이요하면 더 빨라지는것을 볼수 있다.

딥러닝 프레임워크는 엔비디아 GPU에서만 혜택을 받을수 있다. 

GPU컴퓨팅용 개발 환경인 CUDA를 사용하기 때문이다.

 

분산학습

학습하는데 시간이 너무 오래 걸려서 최대한 단축하자는 아이디어, 분산학습이 중요해졌다.

그래서 딥러닝 계산을 더욱 고속화하고자 다수의 GPU와 기기로 계산을 분산한다.

그래서 다수의 GPU와 컴퓨터를 이용한 딥러닝 프레임 워크들이 나오고 있다.

그 중 구글의 Tensorflow와 마이크로소프트의 CNTK(Computational Network Toolkit)는 분산학습에 역점을 두고 개발하고 있다.

텐서플로의 분산 학습 성능

GPU개수에 따라 성능이 엄청나게 상승한 것을 볼수 있다.

계산을 어떻게 분산시키느냐는 프레임워크를 자세히 살펴보는게 좋다.

https://www.tensorflow.org/?hl=ko

 

TensorFlow

모두를 위한 엔드 투 엔드 오픈소스 머신러닝 플랫폼입니다. 도구, 라이브러리, 커뮤니티 리소스로 구성된 TensorFlow의 유연한 환경입니다.

www.tensorflow.org

홈페이지 가면 여러가지 잘 나와있다. 진짜로

 

 

연산 정밀도와 비트 줄이기

계산 능력 외에도 메모리 용량과 버스 대역폭 등이 딥러닝 고속화에 병목이 될 수 있다.

 

메모리 용량 면에서는 대량의 가중치 매개변수와 중간데이터를 메모리에 저장한다는것을 생각해야한다.

버스 대역폭 면에서는 GPU(or CPU)의 버스를 흐르는 데이터가 많아져 한계를 넘어서면 병목이 된다.

이러한 경우를 고려하면 네트워크로 주고받는 데이터의 비트 수는 최소로 만드는 것이 좋다.

 

많은 비트를 사용할수록 오차는 줄어들지만,

그만큼 계산에 드는 비용과 메모리 사용량이 늘고 버스 대역폭에 부담을 준다.

 

딥러닝은 높은 수치의 정밀도를 요구하지 않는다. 

이는 신경망의 중요한 성질 중 하나로 신경망의 견고성에 따른 특성이다.

ex. 입력이미지에 노이즈가 조금 섞여 있어도 출력결과가 달라지지 않음

 

컴퓨터에서 실수를 표현하는 방식은 32bit 단정밀도, 64bit 배정밀도 부동소수점등의 포맷이 있지만

지금까지의 실험으로는 딥러닝이 16bit 반정밀도만 사용해도 상관이 없다.(엔비디아 GPU Pascal 지원됨!)

 

파이썬은 64bit 배정밀도 부동소수점을 사용하지만

numpy는 16bit 반정밀도도 지원하는데 정확도가 떨어지지 않았다.

 

그래서 딥러닝의 피트 수를 줄이는 연구가 계속해서 이뤄지고 이싿고 한다.

 

 

딥러닝의 활용

여태까지 손글씨 숫자 인식을 했는데 이것은 사물인식의 분야였지만 이외에도 여러가지 응용할수가 있다.

그래서 다른 컴퓨터 비전(CV:Computer Vision)에 대해 알아보자!

 

사물검출

이미지 속에 사물 검출의 예

여태까지는 이미지 전체를 대상으로 했는데 이제는 이미지내에 사물의 위치를 알아내는 것을 해야한다.

이런 사물 검출에도 CNN을 적용하여 좋은 효과를 보았다.

그중 R-CNN이 유명하다

R-CNN 처리 흐름

과정은 먼저 사물이 위치한 영역을 찾아내고, 추출한 각영역에 CNN을 적용하여 클래스를 분류하는것이다.

 

분할

이미지를 픽셀 수준에서 분류하는 방법이다.

픽셀단위로 객체마다 채색된 지도데이터를 사용해 학습한다.

그리고 추론할때 입력이미지의 모든 픽셀을 분류한다.

왼쪽이 입력이미지, 오른쪽이 지도용 이미지

픽셀 수준으로 적용하기 위해 가장 단순한 방법은 모든 픽셀을 각각 추론하는 것이다.

예를들어 직사각형 영역의 중심 픽셀의 클래스를 분류하는 신경망을 만들어서, 모든 픽셀을 대상으로 추론작업 실행하는 것인데 이것은 픽셀 수만큼 forward 처리해야해서 시간이 오래걸린다.

그래서 낭비를 줄이기 위해 FCN(Fully Convolutional Network)가 고안되었다. 

이는 단한번의 forward 처리로 모든 픽셀의 클래스를 분류 해준다.

FCN의 전체 그림

이름처럼 합성곱 계층만으로 구성된 네트워크라 완전연결 계층을 같은 기능을 하는 합성곱 계층으로 바꾼다.

  • 신경망은 중간 데이터의 볼륨을 1차원으로 바꿔 한줄로 늘어선 노드로 처리햇으나, FCN은 공간볼륨 유지한채로 마지막 출력까지 처리할수있다.
  • 마지막에 공간 크기를 확대하는 처리를 도입했다.(이중 선형 보간-bilinear interpolation)

 

사진 캡션 생성

컴퓨터 비전과 자연어를 융합하여, 사진을 주면 그 사진을 설명하는 글을 자동으로 생성하는 연구가 있다.

캡션 자동생성 예

캡션 생성하는 대표적인 방법으론 NIC(Neural Image Caption)모델이 있다.

NIC의 전체 구성

CNN과 자연어를 다루는 순환신경망(RNN)으로 구성된다.

RNN은 순환적관계 갖는 신경망으로 자연어, 시계열데이터등의 연속 데이터를 다룰때 많이 사용된다.

동작 방식은 CNN으로 사진에서 사진 특징 추출하고, 그 특징을 RNN에 넘긴다. 그리고 RNN은 CNN이 추출한 특징을 초깃값으로 해서 텍스트를 '순환적'으로 생성한다.

멀티모달 처리(mutlimodal processing) : 사진이나 자연어 같은 여러 종류의 정보를 조합하고 처리하는것

 

딥러닝의 미래

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

 

왼쪽위 '스타일이미지'와 오른쪽위 '콘텐츠이미지'를 이용하여 생성한 아래이미지

 

이미지생성

앞에서는 이미지 두장으로 새 이미지를 만들었지만 학습후 아무런이미지 없이 새로운 이미지를 만드는 방식도 있다.

DCGAN기법으로 생성한 침실 이미지

위는 진짜 사진이 아닌 새로 생성한 이미지이다.

DCGAN 기술의 핵심은 생성자와 식별자로 불리는 2개의 신경망을 이용하는 것이다.

생성자가 이미지 생성하고 식별자로 진짜인지 판정하는 방식이다. 

이런식으로 계속 학습시키는 방식이 GAN(Generateive Adversarial Network) 기술이다.

 

자율주행

주위 환경 사물인식을 하여 자율주행 하는것인데 이것또한 딥러닝 기술이 들어간다. 

SegNet이라는 CNN 기반 신경망과 비슷한 예

Deep Q-Network(강화학습)

사람처럼 시행착오 과정을 거치며 스스로 학습하게 하려는 분야이다.

강화 학습은 에이전트라는 것이 환경에 맞게 행동을 선택하고, 그 행동에 의해서 환경이 변한다는 것이 특징이다.

에이전트는 더 좋은 보상을 받기위해 학습한다.

여기서 보상은 정해진 것이 아니라 '예상 보상' 이라는 것이다.(상황에 따라 다르다)

 

강화학습중 Deep Q-Network(DQN)이라는 방법을 살펴보자

Q학습이라는 강화학습 알고리즘을 기초로 한다.

Q학습에서는 최적 행동 가치 함수로 최적인 행동을 정한다.

이 함수를 딥러닝(CNN)으로 비슷하게 흉내 내어 사용하는 것이  DQN이다.

비디오 게임조작에서 DQN 학습

 

반응형

'DATA Science > DeepLearing from scratch' 카테고리의 다른 글

CNN --- 합성곱 신경망  (0) 2021.05.20
딥러닝 학습 관련 기술들  (0) 2021.05.17
오차역전파법  (0) 2021.05.14
신경망 학습 - 학습 알고리즘 구현  (0) 2021.05.13
신경망 학습  (2) 2021.05.12

댓글