JD의 블로그

[The Idiomatic Programmer - Learning Kears ] - (1) 본문

머신러닝-딥러닝

[The Idiomatic Programmer - Learning Kears ] - (1)

GDong 2019. 8. 23. 23:23

 

"이 글은 Google AI Developer Relations에서 제작된
'The Idiomatic Programmer'의 내용을 정리한 것입니다."

 

첫 번째 시리즈에서는 Keras framework, 그리고 OpenCV와 더불어 computer vision에 대한 내용에 집중하고 있습니다.

 

목차

Part 1 - Deep Neural Networks
Part 2 - Convolutional and ResNet Neural Networks
Part 3 - Wide Convolutional Networks - ResNeXt, Inception
Part 4 - Advanced Computer Vision Models - DenseNet, Xception
Part 5 - Mobile Convolutional Networks - MobileNet, SqueezeNet  

 

이 글은 AI framework에 능숙하지 않은 Angular, React, Django 등과 같은 소프트웨어 엔지니어를 대상으로 쓰였으며, 기본적인 파이썬은 이해한다는 것을 가정하고 있습니다. 참고 바랍니다. 

 

머신러닝 엔지니어는 응용된 분야를 다루는 엔지니어이기 때문에 통계학(statistics)이나 계산이론(computational theory), 미적분 내용 등을 자세히 몰라도 상관없습니다. 중요한 것은 framework에 대해 배우고 이에 대한 기술과 경험을 실제 문제 해결을 위한 솔루션을 만드는 데 사용하는 것입니다. 이 책은 이를 도와주는 것을 목표로 합니다.

 


Part 1 - Deep Neural Networks 

 

훌륭한 머신러닝 엔지니어는 머신러닝 솔루션을 위한 과정을 다음의 5가지 단계로 나눌 필요가 있습니다. 

 

머신러닝의 단계

1. 문제에 대한 모델의 타입을 정하자

2. 모델을 설계하자

3. 모델에 대한 데이터를 준비하자

4. 모델을 훈련시키자

5. 모델을 이용하자

 

 

 

[1. 문제에 대한 모델의 타입을 정하자]

 케라스 프레임워크는 다수의 노드가 서로서로 연결된 신경망으로 이루어진 네트워크 모델을 만드는 것에 관한 툴입니다. 네트워크 내에서 데이터는 방향 그래프의 형태처럼 노드에서 노드로 특정한 방향을 가지고 흘러가게 됩니다. 그래프는 시작점(input layer)과 출구점(output layer)을 가지고 있으며, 그 사이에 hidden, pooling, convolution, dropout, activation, normalization 등등과 같은 내부 레이어들이 존재하게 됩니다. (지금은 이런 복잡한 층에 얽매이지 않고 넘어가도록 하겠습니다.) 이것들도 노드 종류의 일종입니다. 케라스 내에서, 이런 노드들은 클래스 객체로 표현되게 됩니다. 이때, 클래스 객체는 시작을 위한 버튼과 같은 존재이고 파라미터는 이를 조절하기 위한 장치라고 보시면 됩니다. 

 

신경망 네트워트의 레이아웃은 다음과 같이 4가지 카테고리로 분류합니다.

1. DNN (Deep Neural Networks) - 수치형 문제에 대한 해결책(numeric solution)으로 좋은 방법입니다.

2. CNN (Convolutional Neural Networks) - 컴퓨터 비전과 음성 신호 처리에 대한 해결책으로 좋은 방법입니다.

3. RNN (Recurrent Neural Networks) - 텍스트와 음성 인식과 시계열 특성을 가진 문제에 대한 해결책으로 좋은 방법입니다.

4. GAN (Generative Adversarial Networks) - 창의적인 작품을 합성하거나 복원하는 일에 관해 좋은 방법입니다. 

 

Input Layer

신경망 네트워크의 input layer은 숫자형이기 때문에 모든 input data는 숫자형으로 변환되어야 합니다. 숫자형은 벡터, 행렬, 또는 텐서의 형태가 될 수도 있습니다. 이것들을 구분 짓는 기준은 배열의 차원 수입니다. 벡터는 1차원 배열이며, 행렬은 2차원 배열이고, 텐서는 3차원 이상의 배열이라고 보시면 됩니다. 

 

 정규화(normalization) 또는 표준화(standarlization)이라는 것을 들어보셨는지 모르겠네요. 표준화라는 것은 숫자형을 평균이 0이고 표준편차가 1로 만드는 것을 뜻하며 이런 정규화 및 표준화는 Python이 가지고 있는 scikit-learn 또는 numpy 패키지 내의 라이브러리로 쉽게 구현 가능합니다.

 

numpy 패키지는 매우 큰 배열을 높은 수준의 성능으로 다루기 위해 고안되었으며, 모든 Python 머신러닝 프레임워크(케라스, 텐서플로, 파이토치 등등..)는 numpy type의 다차원 배열을 input layer에 대한 입력으로 받습니다. 

 

모든 과정은 Python 3 환경 내에서 구동되었습니다. 

케라스(Keras)는 다수의 클래스를 가진 객체지향 프로그래밍과 이와 연관된 방법과 특징에 기반한 프레임워크입니다. 

 

예제를 통해 케라스에 대해 살펴보도록 하겠습니다. 

이번 예제에서는 집 값 데이터를 이용해보고자 합니다. 각각의 열은 14개의 행(컬럼)을 가진 데이터로, 한 가지 예로 한 행은 집의 판매 가격을 나타냅니다. 우리는 이 집의 판매 가격이 궁금하기 때문에 우리가 관심을 가지는 행을 레이블(label)이라는 특별한 이름으로 부르도록 하겠습니다. 다른 13개의 행은 집의 다양한 특징들(집의 크기, 토지세 등등 - 모두 숫자들입니다!)을 나타내는 정보들이며 레이블이 아닌 나머지 것들을 피처(features)라고 부르도록 하겠습니다. 

 

결국 우리가 하고자 하는 것은 이러한 피처들로부터 모델을 학습시켜 레이블의 값을 예측하는 것입니다!

 

그럼 먼저, 케라스 프레임워크를 import하여 입력의 형태를 정의하겠습니다. 입력은 각각의 행의 값을 가지는 1차원 배열입니다. 

이는 'input_1:0'이라는 이름의 텐서 객체를 생성하며 이 이름은 나중에 모델을 디버깅할 때 유용하게 쓰일 겁니다. shape=(?,13)에서 '?'은 13개의 행으로 이루어진 열의 개수가 정해져 있지 않은 상태이기 때문에 나타나며 런타임 때 실제 샘플 데이터의 열의 개수에 맞게 변환됩니다.  'dtype'은 데이터 요소의 기본 데이터 타입을 표시합니다. 

 

Deep Neural Networks (DNN)

딥러닝에서 딥(Deep)이라는 것은 신경망 네트워크가 input layer과 output layer 사이에 한 개 이상의 층을 가지고 있다는 것을 나타냅니다. 4개의 층을 가진 DNN 구조를 방향 그래프의 형태로 시각화하면 아래와 같은 형태가 될 겁니다.

출력 층을 제외한 모든 층의 모든 노드는 같은 종류이며, 각각의 층의 모든 노드는 다음 층의 다른 노드들과 연결되어 있습니다. 이러한 형태는 fully connected neural network(FCNN)이라고 합니다. 예를 들어, 입력층에 3개의 노드가 있고 다음 은닉층에 4개의 노드가 있다면 12개(3x4)의 연결이 생기게 됩니다. 

 

Feed Forward

DNN(그리고 CNN)은 순방향 신경망(feed forward neural networks)이라고 알려져있습니다. 이것은 데이터가 네트워크를 따라 순차적으로 한 방향으로만 움직인다는 뜻입니다. 이는 절차 지향 프로그래밍에서의 함수와 같습니다. 입력은 파라미터로써 전달되고, 함수는 입력에 따른 다양한 기능들을 수행하여 결과를 출력하게 됩니다. 

 

케라스에서 순방향 신경망을 프로그래밍할 때 2가지 구별되는 스타일이 존재합니다. 

 

 

1. The Sequential Method Approach

Sequential 방법은 입문자들이 읽고 따라하기 쉬운 방법이지만 유연성이 떨어지다는 단점이 있습니다. 

Sequential 방법을 정의하는 다른 방식은 리스트 형식의 파라미터로 전달하는 방식입니다. 

2. The Functional Method (layers) Approach

layers 방법은 좀 더 진보된 방식인데 각각의 층을 분리하여 만든 후 그것을 함께 합칠 수 있게 해 주며, 이것이 나중에 층들을 창의적인 방식으로 결합할 수 있게 해 줍니다. 

Input Shape vs Input Layer

input shape과 input layer이 혼란스러울 수 있는데, 이는 같은 것이 아닙니다. 좀 더 정확하게 얘기하자면, input layer의 노드 수는 input vector의 shape과 같을 필요가 없습니다. 왜냐하면 input vector의 모든 요소가 input layer와 연결되어 input layer의 모든 노드를 통과할 것이기 때문이죠. 그리고 input vector와 input layer 노드 사이의 각각의 연결은 가중치(weight)와 bias(편향)이라는 것을 가지는데 이것이 훈련 과정 중에 신경망이 '학습'하는 요소입니다. 

 

The Dense() Layer

케라스에서, fully connected nerual network(FCNN)은 Dense layer로 알려져있습니다. Dense layer은 이전의 층과 완전히 연결되어 있으며 "n"개의 노드를 가집니다.

 

Sequential method를 사용하여 세 개의 층을 가진 신경망을 정의해보겠습니다. input layer은 10개의 노드를 가지며, 다음 hidden layer도 10개의 노드를 가지고, 마지막 output layer만 1개의 노드를 가지게 만들었습니다. output layer이 1개의 노드만을 가지게 만든 이유는 우리가 얻고자 하는 결과 값이 "예측된 집의 가격"으로 1개의 실수 값이기 때문입니다. (즉, 이 예제에서 우리가 회귀 분석을 위해 신경망을 사용하고자 함을 의미합니다.) 여기서 input과 hidden layer의 노드 수를 어떤 값으로 정하든 상관없습니다. 그렇지만 더 많은 노드를 가질수록, 신경망은 더 많이 학습할 것이며 이는 좀 더 복잡한 모델을 만들 것이고 학습과 예측에 더 많은 시간이 소요된다는 것을 의미합니다. 

 

 

 

The Sequential Method Approach(좌)                                 The Functional Method (layers) Approach(우)

 

활성화 함수(Activation functions)

 

훈련이나 예측을 할 때, 각각의 노드들은 다음 층의 노드로 값을 보냅니다. 이러한 값을 그냥 보내게 된다면 층을 쌓는 의미가 없기 때문에 때때로 이러한 값들을 특정한 방식으로 변경시켜줘야 합니다. 이러한 과정을 activation function이라고 합니다. 

Activation function의 작동원리

Activation function은 신경망이 학습을 더 빠르고 잘하도록 도와줍니다. 기본적으로, activation function이 정해지지 않았다면 어떤 층의 값은 다음 층으로 갈 때 변화 없이 같은 값을 가지게 됩니다. 가장 기본적인 activation function은 step function으로 입력 값이 0보다 크다면 1로 반환하고, 아니라면 0으로 반환합니다. 이는 굉장히 오~래 동안 사용되지 않았습니다. 

 

현재 가장 많이 쓰이는 세 가지 activation function은 ReLU(the rectified linear unit), sigmoid, 그리고 softmax입니다. 

 

ReLU : 입력 값이 0보다 크다면 입력 값을 그대로 반환하고, 아니라면 0을 반환합니다. ReLU는 일반적으로 층과 층 사이에서 많이 사용됩니다. 

우리가 생각한대로 제대로 만들었는지 확인해볼까요? 이는 model.summary() 명령어를 통해 확인할 수 있습니다. 

model.summary()의 결과

결과를 보시면 첫 번째 Dense층의 파라미터 개수가 140개인 것을 확인할 수 있습니다. 입력층의 개수가 13개고 10개의 노드가 있으면 130개가 되어야하는거 아닌가요? 라고 생각할 수도 있습니다. 140개인 이유는 각각의 입력값과 연결된 노드 사이에 가중치(weight)가 있으며 각각의 노드는 편향(bias)을 가지기 때문입니다. 그래서 가중치(130개) + 편향(10개) = 140개가 되는 것입니다. 마찬가지로 다음 은닉층의 파라미터가 110개인 이유는 10개의 입력값이 10개의 노드와 연결되어 있어 가중치(100개) + 편향(10개)이기 때문입니다. 

 

Shorthand Syntax

Keras는 층을 정의할 때 간편한 문법을 지원해줍니다, 그래서 층을 정의할 때 activation function을 분리해서 정해줄 필요가 없습니다. 그저 층을 정의할 때 파라미터로 activation function을 정해주면 됩니다. 

Shorthand Syntax 형태로 층을 정의하는 방법

Shorthand Syntax 형태로 층을 정의하면 summary()를 했을 때 activation function이 나타나지 않지만, 실제로 activation function이 층과 층 사이에 정의된 상태입니다. 

Shorthand Syntax로 정의했을 때 summary()

Optimizer (Compile)

 

순방향 신경망을 구성하는 것이 어느 정도 완료 되었다면, 이제 모델을 학습시키기 위해 몇 가지만 더 추가하면 됩니다. 

이것은 compile() 메서드를 통해서 하게 되는데, 이때 중요한 것으로 이 단계에서 훈련 과정 중에 역전파(backward propagation)을 추가하기 때문입니다. 우리가 신경망을 통해 순방향으로 데이터(또는 배치 단위의 데이터)를 보내면, 신경망은 레이블과 예측한 결과값을 비교하여 손실값(loss)을 계산합니다. 그리고 이러한 손실값은 점진적으로 노드의 가중치와 편항을 조절하기 위해 사용하게 됩니다. 손실값을 계산하는 다양한 방식이 존재하는데, 여기서는 회귀 분석을 하고자 하기 때문에 "Mean Square Error"이라는 손실 함수를 사용하여 손실값을 계산할 것입니다. compile() 메서드의 파라미터 loss를 통해 어떤 손실 함수를 사용할 것인지 설정할 수 있습니다. (loss = 'mse'라고 명시하면 Mean Square Error 손실 함수를 사용하겠다는 것을 의미합니다.)

 

다음 차례는 역전파 과정 중에서 일어나는 optimizer을 설정하는 것입니다. optimizer는 경사하강법(gradient descent)을 기반으로 하여 다양한 경사하강법들의 변형 중에서 어떤 방식의 경사하강법으로 손실값을 최적화할 것인지 선택하는 것입니다. 이것이 어려운 개념일 수도 있는데, optimizer가 필요한 이유는 앞서 설명한 계산된 손실값으로 얼마만큼 가중치와 편향을 변화시킬 것인지 결정해야하기 때문입니다. 우리의 목표는 각각의 샘플 데이터로 학습된 모델을 통해 점차적으로 좀 더 정확한 가중치와 편향을 얻어 정확하게 레이블 값을 예측하는 것입니다. 이러한 과정의 결과로 가까워지는 과정을 수렴(convergence)라고 하며, 손실값이 작아져 가장 작은 값에 수렴했을 때 그 결과가 신경망의 정확도가 될 것입니다. 

경사하강법으로 손실값을 줄이는 과정

우리의 신경망을 최적화하기 위해서는 rmsprop(root mean square property)라는 optimizer을 사용할 것입니다.

 

자! 이제 처음으로 '훈련 가능한' 신경망을 다 만들었습니다! 그러나 데이터 준비와 모델을 학습하기 전에, 신경망 구축에 관해 몇가지를 더 다루도록 하겠습니다.

 

DNN Binary Classifier

DNN의 다른 형태는 출력 값이 yer/no, true/false, 그리고 0/1 등등의 두 가지 종류인 신경망입니다. 예를 들어, 신용카드 거래에 대한 데이터 셋을 가지고 있으며 각각의 거래는 이 거래가 사기(fraudulent)인지 아닌지 라벨링되어 있을 때 이를 예측하는 것입니다. 이 또한 이전에 모델을 설계할 때의 방식이 크게 다르지 않으며, 단지 activation function과 loss/optimizer 방식에서의 차이만 존재합니다. 

 

이 경우에는 sigmoid activation function을 사용할 것입니다. sigmoid는 입력 값을 0과 1사이의 범위로 조절해주며 이 과정에서 중심에서 먼 값일수록 출력값으로 0과 1에 가깝게 만듭니다. 그리고 binary classification문제에 주로 쓰이는 loss로 'binary_crossentropy'를 사용할 것입니다.

 

sigmoid activation function(좌), DNN Binary classifier 신경망 (우)

아래에는 같은 코드를 layers approach로 작성한 것이며 각 층을 x에 할당해서 신경망을 구축하는 것을 보여주고 있는데 이것이 일반적으로 쓰이고 있는 방식입니다. 이러한 두 가지 스타일/방식에 익숙해지는 것이 개발 포럼, 책, 스택 오버플로우 등을 읽을 때 도움이 될 것입니다.

같은 코드를 layers approach로 작성한 것

DNN Multi-Class Classifier

또 다른 형태의 DNN은 레이블의 카테고리 수가 여러 가지 경우인 Multi-class classification문제를 해결하기 위한 모델입니다. 예를 들어 키와 몸무게 같은 신체적 특성과 성별을 가지고, 그 사람이 baby, toddler, preteen, teenage 그리고 adult의 5가지 클래스에서 어느 클래스로 분류되는지 예측하는 것입니다.