0. 들어가며

개인적으로 해왔던 공부를 되돌아보는 시간을 가지기 위해서 PyTorch를 복습하기로 하였다. 단순이 PyTorch의 문법적인 복습이 아닌, 여지껏 구현하고 사용해왔던 기술들에 대한 복습을 하며 정리를 하는 시간을 가질까 한다. 저번 글에서는 언제 어디서나 많이 사용되고 좋은 성능을 보여주는 선형 회귀(Linear Regression)을 정리하였고 , 이번 시간에는 인공신경망(Artificial Neural Networks; ANN)에 대해서 간단한 정리와 함께 파이토치로 구현해볼까 한다.

1. 신경망이란 무엇인가

인공신경망

그림 1. 인공신경망

인공신경망은 McCulloch, W.S., Pitts, W (1943)에서 처음 소개 되었다. 생물학적 신경망은 여러 자극이 수상돌기(Dendrite)들을 통하여 신경세포로 넘어오고 어느정도 이상의 자극이 들어오면 이를 축색?삭?(Axon)을 통해 다른 세포로 전하는 구조이다. 이러한 구조를 프로그래밍적으로 모방하여 만든 것이 ANN이다. 여러 자극 즉, 입력이 들어오면 각각의 가중치를 곱하여 더해주고 추가적으로 편차도 더해주어서 활성화 함수(Activation Function)을 통해 변형하여 전달하는 단위를 인공 뉴런(Artificial Neuron)이라한다. 이러한 뉴런들이 모인 네트워크를 ANN이라 한다.

인공신경망의 발전사

그림 2. ANN의 발전사

ANN은 명제 논리(Propositional Logic)를 사용하여 생명체의 뇌의 생물학적 뉴런이 복잡한 계산을 위해 어떻게 상호작용하는지에 대해서 계산 모델을 제시하였다. 1960년대까지는 ANN을 통해서 지능을 가진 기계화 대화가 가능 할 것이라고 사람들은 생각하였다. 하지만 사람들의 기대와는 달리 XOR 문제를 해결할 수 없고, 1990년 대에는 서포트 벡터 머신(Support Vector Machine; SVM)과 같은 성능 좋은 기계학습 알고리즘이 개발되면서 ANN의 사용은 줄어 들게 된다. 하지만 2000년대에 들면서 ANN은 하드웨어의 성능의 비약적인 발전으로 납득할만한 시간에 대규모의 신경망을 학습 할 수 있게 되었으며 훈련 알고리즘의 지속적인 발달로 약진할 수 있게 되었다.

3. 인공 신경망의 요소

단층 신경망

그림 3. 단층 인공신경망

다층 인공신경망

그림 4. 심층 인공신경망

인공신경망은 여러 개의 입력 값과 출력 값을 가질 수 있다. 입력단과 출력단 사이의 은닉층(Hidden Layer)의 개수에 따라서 단층 신경망과 심층 신경망으로 나뉜다. 심층 신경망은 2개 이상의 은닉층을 가진 신경망을 의미한다.

가중치

그림 5. 가중치의 곱

입력값들의 가중치의 합을 활성화 함수에 통과시켜 변형시키고 이 과정을 모든 값에 적용시켜 최종 결과값을 만들어 낸다. _그림 4_의 네트워크를 수식으로 나타내면 다음과 같다.

\[y=w_4\left( \sigma\left( w_3\left( \sigma\left( w_2\left( \sigma\left( w_1\times x+b_1 \right) \right)+b_2 \right) \right)+b_3 \right) \right)+b_4\]

이렇게 계산된 값들은 시그모이드(Sigmoid), 하이퍼 볼릭 탄젠트(\(\tanh\)), 수정 선형 유닛(Rectified Linear Unit; ReLU)같은 활성화 함수를 거쳐서 비선형성(Nonlinearity)를 띄도록 만든다. 이런 비선형성으로의 변화를 거치지 않을 경우 은닉층이 몇 개라도 결국 선형변환이기 때문에 깊은 모델을 만든 의미가 사라진다. 활성화 함수에는 다양한 종류가 있는데 많이 사용되는 활성화 함수의 종류는 _그림 6_과 같다. 보통 인공 신경망에서 많이 사용되는 활성화 함수는 Sigmoid와 \(\tanh\)이다. 시그모이드는 소위 통계학에서 말하는 로짓 변환을 사용한 로지스틱(Logisitic) 함수 \(\frac{1}{1+e^{-x}}\), 하이퍼 볼릭 탄젠트는 \(\frac{e^{x} - e^{-x}}{e^{x}+e^{-x}}\) 의 형태를 가진다.

활성화 함수

그림 6. 다양한 활성화 함수

4. 전파와 역전파

인공신경망은 입력값이 들어오면 여러 개의 은닉층을 거쳐서 결과값을 만들어내는데, 이러한 과정을 순전파(Feed Forward)라고 한다.

\[\left[ \begin{array}{cccc} w_{00} & w_{01} & w_{02} & w_{02}\\ w_{10} & w_{11} & w_{12} & w_{12} \\ w_{20} & w_{21} & w_{22} & w_{22} \\ w_{30} & w_{31} & w_{32} & w_{32} \end{array} \right] \left[ \begin{array}{cccc} w_{00} & w_{01} & w_{02} & w_{02}\\ w_{10} & w_{11} & w_{12} & w_{12} \\ w_{20} & w_{21} & w_{22} & w_{22} \\ w_{30} & w_{31} & w_{32} & w_{32} \\ w_{40} & w_{41} & w_{42} & w_{42} \end{array} \right] \left[ \begin{array}{cc} w_{00} & w_{01}\\ w_{10} & w_{11}\\ w_{20} & w_{21}\\ w_{30} & w_{31}\\ w_{40} & w_{41} \end{array} \right]\]

간단한 행렬의 곱이다 \(3\times 4\) 와 \(4\times4\) 과 \(4\times2\) 의 결과는 \(3\times 2\) 이다. 우리는 [5, 3] 형태의 입력을 가정한다. 5는 데이터의 개수, 3은 데이터의 Feature의 개수이다. \(I\) 는 입력(Input), \(w\) 는 가중치(Weight) 그리고 \(o\) 는 출력(Output)이다.

\[\left[ \begin{array}{ccc} I_{00} & I_{01} & I_{02}\\ I_{10} & I_{11} & I_{12}\\ I_{20} & I_{21} & I_{22}\\ I_{30} & I_{31} & I_{32}\\ I_{40} & I_{41} & I_{42} \end{array} \right] \left[ \begin{array}{cccc} w_{00} & w_{01} & w_{02} & w_{02}\\ w_{10} & w_{11} & w_{12} & w_{12} \\ w_{20} & w_{21} & w_{22} & w_{22} \\ w_{30} & w_{31} & w_{32} & w_{32} \end{array} \right]= \left[ \begin{array}{cccc} o_{00} & o_{01} & o_{02} & o_{02}\\ o_{10} & o_{11} & o_{12} & o_{12} \\ o_{20} & o_{21} & o_{22} & o_{22} \\ o_{30} & o_{31} & o_{32} & o_{32} \\ o_{40} & o_{41} & o_{42} & o_{42} \end{array} \right]\]

예측값인 \(\hat{y}\) 를 구하는 과정이 바로 전파이다. 가중치를 w, 편차를 b 그리고 활성화 함수를 \(\sigma\) 로 두고 전파의 과정을 수식으로 표현하면 다음과 같다.

\[\hat{y} = w_{3}\times \sigma \left( w_{2} \times \sigma \left( w_{1} \times x + b_{1} \right)+b_{2} \right)+b_{3}\]

이제는 손실(Loss)를 계산 할 차례이다. 정답을 \(y\) 라 가정하였을 때 가중치과 편차에 대한 손실을 미분하여보면 다음식과 같다.

손실 그러면 \(\frac{\delta \mbox{loss}}{\delta w_{2}}\)의 미분은 미적분에서 사용되는 연쇄법칙(Chain Rule)을 사용한다.

수식01

의 식을 가진다.

우리가 가정한 식인 \(loss\)에서 \(w_{2}\times \sigma\left(w_{1} \times x + b_{1}\right)\) 은 은닉층의 입력(\(H_{2\ in}\))이며 활성화 함수를 거친 \(\sigma \left(w_{2}\times \sigma\left(w_{1}\times x + b_{1}\right)+b_{2}\right)\)는 은닉층의 출력(\(H_{2\ out}\))이다. 따라서 가중치 2(\(w_{2}\))로 미분한 결과는 다음식으로 구할 수 있다. \(\frac{\delta\mbox{loss}}{\delta w_{2}}= w_{3} \times \sigma’\left(H_{2\ in}\right)\times \sigma\left(w_{1} \times x + b_{1}\right)\)

이렇게 입력값이 은닉층얼 거쳐 결과로 나오는 과정이 순전파(Feed Forward)였다고 하면, 역전파(Back Propagation)은 정갑과 결과의 차이로 계산된 손실을 연쇄법측을 이용하여 입력단까지 절달하는 과정을 의미한다.

5. ANN 모델의 구현

지금까지 정리한 모든 것을 다 구현하지는 않고 PyTorch로 구현되있는 라이브러리를 활용하여 정리해본다. 혹시나 ANN의 기본적인 구현부터 역전파까지 구현을 해보고 싶으면 밑바닥부터 시작하는 딥러닝라는 책을 추천한다. Numpy 라이브러리로 한땀한땀 구현해나가는 쾌감을 경험할 수 있다.

앞에서는 순전파와 역전파를 미분을 운운하면서 어렵게 설명하였지만 PyTorch에서는 loss.backward()의 함수 하나로 해결할 수 있다.

import torch
import torch.nn as nn # neurun network
import torch.optim as optim # optimizer
import torch.nn.init as init # 데이터 초기화용 initializer

필요한 라이브러리를 import한다

num_data = 1000 # data 개수
num_epoch = 10000 # 반복 횟수

noise = init.normal_(torch.FloatTensor(num_data, 1), std=5)
# 정규분포로 noise를 생성. 표준편차는 5로 설정한다
x = init.uniform_(torch.Tensor(num_data, 1), -15, 15)
# x값은 유니폼 분포로 생성한다.
y = (x**2)+3
y_noise = y + noise

이렇게 생성한 데이터의 분포는 그림 7과 같다. ann 분포

그림 7. 생성 데이터
model = nn.Sequential(
    nn.Linear(1, 100),
    nn.ReLU(),
    nn.Linear(100, 10),
    nn.ReLU(),
    nn.Linear(10, 6),
    nn.ReLU(),
    nn.Linear(6, 1),
) # 모델 생성

loss_func = nn.L1Loss() # Loss function  설정
optimizer = optim.Adam(model.parameters(), lr=0.0002) # optimizer 설정

nn을 통하여 모델을 생성한다. Sequential 은 모델링을 할때 연속적인 구조를 한번에 묶어서 넣을수 있도록 해주는 편리한 함수이다. 우리가 만든 모델은 1개의 입력을 받아 100개의 은닉층으로 보낸 다음 활성화 함수로 ReLU 를 사용하였고 그 다음 은닉층은 100개의 입력을 받아 10개의 출력을 가지는 이런 식의 반복적인 ANN이다 결과적으로 1개의 입력을 받아 1개의 결과(예측) 출력을 가진다. 손실 함수(Loss Function)은 L1을 선택하였고 경사하강법의 최적화 방법으론 Adam optimizer를 선택하였다. 학습 그래프는 그림 8과 같다. 학습 그래프

그림 8. 학습 그래프

학습 그래프는 학습의 손실(Loss)를 시각화한 작업이다. 손실은 그림 8과 같이 감소하여 0으로 수렴하는 것을 볼 수 있다. 우리가 예측한 값의 시각화는 그림 9와 같다. 예측 결과

그림 9. 예측 결과

파란 색이 원래 값, 주황색이 우리가 예측한 값이다. 주황색선을 자세히 보면 중간 중간에 직선으로 가다 꺽인 부분들이 존재하는데, 이는 ReLU 함수의 영향이다. 은닉층은 입력값과 가중치를 곱하여 선형변환을 시켜주고, 활성화 함수인 ReLU는 입력값이 0보다 작은 모든 값을 0으로 만들어 주기에 은닉층을 여러번 통과하면 꺽인 모양이 발생한다(\(ReLU(x) = max\left(0, x\right)\)).