11.Neural Network
지난 포스팅이 알파고 제로의 학습 방법인 강화학습에 대한 것이었기에, 이번 포스팅은 인공신경망(Artificial Neural Network, ANN)에 대한 것이 되는 것이 (수업을 연결하기) 자연스럽다. 왜냐하면 알파고는 강화학습의 MDP(Markov Decision Process)를 ANN의 하나인 Convolutional Neural Networks로 바꾸어 학습시킨 결과물이기 때문. 편의상 이 포스팅에서는 인공신경망을 신경망 또는 Neural Nets으로 지칭하겠다.
1. Intro
신경망은 요즘 계속 deep learning이라고 부르는 기술의 핵심이다. 무엇이 deep한가? 딥러닝이 뭐지? 이 질문에 대답하려면 신경망이 뭔지를 먼저 알아보아야 한다.

위 그림1은 신경망의 구조를 간단히 보여준다. 신경망은 이러한 구조가 뉴런으로 연결된 인간의 신경 구조와 닮았다고 해서 붙여진 이름이다. (인간 신경 기능의 발 끝에도 못 미치는 주제에 구조가 비슷하다고 신경망이라니..) 이 신경망은 크게 input layer, output layer와 그 사이에 배치된 hidden layer들로 이루어진다. 비교적 최근들어 이 hidden layer의 개수가 점점 많아지면서(깊이가 deep해지면서) 사람들은 내부의 상호작용을 더 잘 이해하지 못하게 되었고(?!), 그래서 딥러닝이라고 부르기 시작했다. 지금은 개수가 많건 적건 관계없이 여러 개의 hidden layer를 사용하기만 하면 Deep Neural Networks, 심층신경망 혹은 딥러닝이라고 부른다.
뭔가 약간은 스튜핏해 보이는 위의 개요와 다르게, 딥러닝은 무한한 가능성을 가지고 있다. 특히 최근의 문제들은 dimension, 즉 다뤄야할 파라미터가 아주 많은 (수천 ~ 수십만개) 입력들을 가지는데, 기존 머신러닝 방법론들로는 수많은 파라미터의 제어가 어렵다. 따라서 현실의 복잡한 문제를 모델링하기 좋은 신경망이 더 많이 사용되고 있는 것이다. 대표적으로 신경망이 많이 사용되고 있는 분야들은 다음과 같다.
- image recognition (SNS 얼굴 인식)
- speech recognition (Cortana?)
- machine translation (Google Translate)
- playing games (AlphaGo)
- 등등
참고로 구글 번역의 (라이브) 사진 번역은 서비스가 시작된지 시간이 꽤 지났음에도 새로 만난 사람들을 보여주면 상당히 놀라워 하는 경우가 많다. 여튼 인공신경망에 대해 간단히 알아보자.
2. The perceptron
신경망은 1957년 Rosenblatt의 perceptron에 관한 논문2으로부터 시작되었다. Perceptron은 간단한 binary classifier 알고리즘 정도로 볼 수 있는데, 예전 포스팅(추가 예정)에서 다루었지만 신경망의 중요한 개념인만큼 다시 한 번 다루어본다. Single perceptron의 구조를 보자.

여러 인풋(x)과 각 인풋의 가중치(w), 아웃풋(f(x))이 끝이다. 이것은 그림의 오른쪽 아래에서 보듯 step function, 즉 일정 값 이내에서는 값의 변화가 없다가 특정 값을 돌파하면 출력값이 바뀌는 형태를 가진다. 값을 넣어 실제로 분류를 한 번 해보자.

위처럼 가중치값을 정하여 hyperplane(직선)을 정의한다면, 저 직선 아래쪽에 있는 두 인풋 $x_1$과 $x_2$에 대해서는 항상 0의 출력이 나오고, 저 선부터는 1의 출력을 갖게 된다(이해를 돕기 위해 0.99라는 값을 사용하였다 과제를 12시 전까지가 아니라 11시 59분 59초까지 제출하라는 것과 같다). 이 perceptron을 사용한다면 linearly separable인 모든 데이터를 분류할 수 있다. 뿐만 아니라 변수를 아래와 같이 넣는다면, 이걸로 다양한 논리함수들도 구현할 수 있다.
- AND: $w_0=-0.99$, $w_1=0.5$, $w_2=0.5$
- OR: $w_0=-0.49$, $w_1=0.5$, $w_2=0.5$
- NOT: $w_0=0$, $w_1=0$, $w_2=-1$
이뿐만 아니라 NOR, NAND도 가능하다. 이제 모델을 잘 선택했으니, 이 모델을 평가하여 적절한 파라미터를 구할 방법을 찾아야 한다.
Perceptron의 한계와 Gradient Descent
perceptron은 발표된 당시에 큰 화제가 되었는데, 스스로 생각하는 로봇이 이것을 통해서 완성될 수 있다는 주장 때문이었다 (이를 기반으로 지금까지 발전해온 신경망 알고리즘들을 보니 아예 틀린 말은 아닌 것 같다.) 그런데 50년 가까이 지난 시점에서야 신경망이 폭발적으로 성장하기 시작한 이유는 perceptron의 장점 / 한계 때문이다. 다시 말해, perceptron은 linearly separable하지 않다면 데이터를 분류할 수 없기 때문에 그 이상의 기능을 발휘할 수가 없다. 이 점을 좀 더 자세히 알아보기 위해 컴퓨터의 기본 회로 중 하나인 XOR회로의 분포를 보자.

분포의 모양새만 봐도 선형 분류기로는 분류가 불가능함을 바로 알 수 있다. 1969년이 되어서야 Marvin Minsky와 Seymour Papert가 그들의 책 Perceptrons에서 single layer perceptron이 XOR회로를 표현할 수 없음을 보였고, 그 이후로도 신경망 연구가 계속되긴 했지만 예전보다는 한풀 꺾이게 되었다. 여기서 잠깐! layer를 하나만 추가하면 perceptron으로 XOR을 풀 수 있긴 하다. 이상적으로는. 하지만 현실적으로는 파라미터의 오류값을 측정하기 어렵다는 문제에 봉착한다.
파라미터의 오류값을 측정하기 전에 Gradient Descent를 이용해서 perceptron의 파라미터부터 구해보자. 일반적으로 잘 알려진 최적 파라미터 추정법 중 하나인 Gradient Descent(경사하강법)는 이름 그대로 함수의 경사(기울기)를 구하여 그 값이 낮은 방향으로 계속 이동시켜서 함수의 최댓값에 이를 때까지 반복하는 알고리즘이다. 이 역시 perceptron 포스팅(링크 추가 예정)에서 다루었으니 자세히 다루지는 않지만, 실제로 perceptron에 Grdient Descent를 적용할 때 발생하는 문제를 살펴볼 필요는 있다.

위는 perceptron을 이용하여 세모와 원으로 라벨링된 분포를 분류하려고 하는 그림이다. 이 때 Grdient Descent를 적용해서 최적 파라미터를 구해보자. 일반적으로 Gradient Descent를 적용하면 local optima에 봉착할 염려는 있지만, 기울기 값에 따른 오류값을 비교하면서 더 작은 오류값을 향해 직선을 조금씩 움직여 가며 최적값을 구할 수 있다.
perceptron이 파란 영역 내에 직선을 형성했을 대의 오류(sum of squared error)는 항상 2다 (잘못 분류된 것이 2개다). 그런데 Gradient Descent 알고리즘이 이 영역 안에서 움직이기 시작한다면 (분류의 오류값이 계속 일정하다면), 파란 영역을 벗어나 새로운 분류값을 마주치기 전까지는 계속 오류값이 똑같으니 Gradient Descent로 perceptron의 오류값을 측정하기가 힘들어진다. 물론 하나의 layer에서만 Gradient Descent를 적용한다면 그럭저럭 어떻게든 적절한 파라미터를 구해볼 수도 있을 것이다 (노가다). 하지만 두 개 이상의 layer(XOR을 구하기 위해서는 2개 laeyr가 필요하다는 것을 기억하자)에 이것을 적용한다면? 값의 변화를 예측하기도 어렵고, 어느 함수가 출력값에 어떻게 기여하는지 알기도 어려울 것이다.
Linear Unit
이 모든 문제는 Perceptron이 step function, 즉 출력이 입력의 변화에 계속 영향을 받는 것이 아니라 어느 한 포인트부터 다른 값을 갖는 함수를 이용하기에 발생하는 문제다. 이 문제를 극복하기 위해 step function을 대신하여 1차 함수를 이용해본다.

위와 같은 1차 함수를 이용한다면 입력의 변화에 따른 오류값의 변화를 아래와 같이 쉽게 추적할 수 있다 (식을 캡쳐해서 갖다 쓴 것은 레이블이 잘 붙어있어서다. 귀찮아서가 아니다).

오류값이 변화하기 때문에 Gradient Descent를 적용하기가 더 쉬워진 것은 두말하면 잔소리. 이 덕분에 여러 Layer를 적용하기도 쉬워졌다. 하지만 새로운 문제가 발생한다.
Linear 함수를 가지고 아무리 여러 layer를 두고, 가중치를 잘 주고, 열심히 지지고 볶아도, 출력은 결국 하나의 linear 함수라는 것이 바로 그 문제. 이 때문에 XOR은 표현할 수 없다.
Sigmoid Unit
그렇다면 다음은 곡선을 써 볼 차례. 여러 분야에서 다양하게 사용되고 있는 Sigmoid (Logistic) function을 도입3한다.

이 함수는 입력의 변화가 출력에도 잘 반영되고, 그래서 Gradient Descent를 적용하기도 용이하며, 여러 layer를 적용하기 쉬우면서도 이를 통해 임의의 비선형 함수를 만들어낼 수도 있기 때문에 linear unit처럼 항상 같은 형태의 출력이 발생하지도 않는다. 또 한 가지 중요한 부분은, sigmoid function은 continous derivative를 가지기 때문에, 다시 말해 미분가능한 도함수를 갖기 때문에, 여러 layer들 사이의 (잠시 후 간단히 다룰) back-propagation을 가능하게 한다는 점이다. 이러한 장점들 덕에 sigmoid function은 실제로 신경망을 활용한 여러 분야에 적용되고 있다. (참고로 하나의 layer에서 sigmoid function을 사용하면 logistic regression(링크 추가 예정)과 동일하다.)
sigmoid function처럼 입력값에 따라 출력값을 변화시키는 Neural Networks 내부의 함수를 activation function이라고 하는데, sigmoid뿐만 아니라 아래와 같은 함수들이 다양하게 사용된다.

그래프의 모양을 보면 어떤 함수인지는 직관적으로 감이 올테고, 이 함수의 활용은 추후 검색을.. 이 중 특히 Rectified Linear Unit (ReLU)은 자주 사용되므로 알아두면 좋다. (과제에서도 가장 높은 정확도를 보였다)
Design Choices
머신러닝 알고리즘은 항상 디자인이 관건이다. 거두절미하고 주어진 이미지에서 숫자를 추출하기 위한 샘플 디자인을 보자. 아래의 hidden layer에서 하나의 네모는 하나의 perceptron을 의미하고, 각 layer의 모든/많은 perceptron은 바로 이전 layer의 모든/많은 perceptron과 연결되어 있다 (모두 연결되어 있지 않을 수 도 있다는 이야기다).

또는 이러한 디자인도 가능하다.

쉽게 말해서, perceptron을 잘 이해하고 있다고 해도, 실제로 내가 풀고자 하는 문제에 이 perceptron을 어떻게 적용할 것인가 하는 문제가 남는다는 것. 이것이 이슈인 이유는 모델을 디자인하기 위해서 다음과 같은 많은 것들을 결정해야 하기 때문이다.
- 입력 데이터의 형태: 데이터들이 어떤식으로 신경망 안에 들어가야 잘 처리가 될 것인가
- 신경망 구조: 어떤 구조의 신경망을 구성할 것인가, 가중치는 어떻게?
- Layer의 개수: hidden layer를 몇개나 이용할 것인가. 각 layer의 형태는?
- Activation function: Tanh, ReLU, linear…
- layer 간의 연결: 한 레이어의 모든 perceptron unit이 모든 다음 레이어에 연결되도록 할 것인가
- Gradient Descent: 최적값을 찾는 방법은 어떻게 할 것인가
- 결과 값의 정규화
… 갈 길이 멀다. 그래서 가장 좋은 접근법은 내가 풀고자 하는 문제와 유사한 문제를 다뤄본 경험(논문)을 찾아 거기에 적용된 방법을 사용해보고, 바꾸어보고 하는 것이다. 머신러닝의 길은 노가다의 길이었어.
※ 몇 가지 디자인 팁
- 2017년 11월 기준으로 Gradient descent를 신경망에 적용할 때 처음 적용해 볼 것은 ADAM이라고 한다. 3년 전 논문이 citation이 5000이 넘었으니 말 다했지 뭐.
- 적은 수의 layer부터 시작하라. 안되면 추가하는 것으로.
- Too hard too learn? It is hard to run.
- sampling을 할 때 window effect를 조심하라. 신호처리를 배워봐도 괜찮겠다.
3. Convolutional Neural Networks
이제 perceptron을 이용해서 실제로 적용되고 있는 신경망을 알아볼 차례다. 꽤 오래되었지만 지금도 줄기차게 많이 쓰이고 있는 Convolutional Neural Network4 (합성곱 신경망, CNN)을 대표로 공부한다. 시각적으로 이해가 쉬운 이미지 프로세싱을 샘플로 공부하되, 슬라이드가 잘 되어 있기 때문에 이 부분의 모든 그림 자료는 슬라이드에서 가져온다.

Pardo 교수님께서 사랑해 마지 않으시는 이 동물은 Capybara이다. 영상 참조 (정말 귀엽다) 이 capybara 이미지의 사이즈는 500 * 1000. 그렇게 높은 해상도는 아니다. 이 이미지 하나를 인식하기 위해 perceptron을 이용해 보자.

많지 않은 수의 노드를 갖는 2개의 히든 레이어만 사용했음에도 처리해야할 가중치가 5천만개가 넘는다 (최근 수십~수백개의 레이어를 갖는 모델이 넘쳐난다). 이 많은 가중치를 반복 계산하가며 러닝하는 것도 문제지만, 러닝한 모델을 사용하는 것도 문제가 된다. 러닝은 좋은 머신을 사용해서 어떻게든 넘긴다고 해도, 막상 모델을 사용하는 사용자 입장에서는 그런 머신을 손에 들고 다닐리 없기 때문에 제대로된 성능을 내기가 어렵다 따라서 이렇게 많은 가중치는 좋지 못한 디자인 초이스.
이렇게 데이터를 날로 처리하는 것이 (이론적으로는 문제가 아니지만) 현실적인 문제이기 때문에(그리고 이 때문에 컴퓨터들의 퍼포먼스가 이만큼 올라올 때까지 신경망 연구가 지지부진하기도 하였다), 머신러닝을 하는 사람들은 데이터를 사전에 처리(preprocessing)하여 입력 데이터를 만들어내는 feature engineering에도 많은 관심을 기울인다. 그러는 와중에 탄생한 것이 바로 CNN이다. CNN은 신호처리의 기술 중 하나인 Convolution을 이용하여 raw data를 필터링하여 입력 데이터를 만들어내기 때문에 이러한 이름이 붙었는데, 우린 신호처리를 공부하는 것이 아니므로 자세한 것은 생략. 궁금 시 검색 요망. CNN은 이렇게 Convolution을 이용하는 Convolution layer와, 데이터의 사이즈를 더 줄여주는 Pool layer 두 가지를 뼈대로 삼는 알고리즘이다.
Convolution layer
데이터를 사전에 처리하겠다는 것은 알겠는데, 그럼 어떻게 데이터를 처리한다는 것인가? 생각해보자. 우리가 사용하고자 하는 신경망은 수많은 perceptron들을 내부에 가지고 있다. 각각의 perceptron을 어떻게 쓰겠는가? 하나의 perceptron에 (가령) 우리가 식별하고자 하는 이미지 중 하나의 특징만 매칭시키는 것이다. 그렇게 전체 이미지를 특징을 가진 부분으로 나누어가며 계산하면, 한번에 계산해야 하는 양이 크게 줄어든다. 이 각 부분들을 patch 혹은 window라고 하고, 이걸 이동해가면서 이미지를 분석하게 된다. 이미지를 분석하면서, 하나의 perceptron은 각 patch에서 하나의 feature(수평선, 수직선, 곡선 등)를 찾게 된다. 이미지의 여러 부분에서 비슷한 분석을 해야된다면 그 필터의 가중치를 공유하게 되는데, 이를 shared weights라고 하고, 같은 shared weights를 갖는 여러 노드(perceptron)의 집합을 feature map이라고 한다. CNN에서 하나의 layer가 곧 하나 혹은 그 이상의 feature map에 대응되고(이것이 바로 convolution layer이다), 여러 layer를 걸치며 점차 머신은 대상의 특징에 대해서 배워나가게 된다.

그렇게 convolution 작업을 통해 축소시킨다면 가중치를 위위 그림에서 대략 위 그림과 같이 줄일 수 있다. 3만여개의 가중치 역시 많아보이지만, 5천만개에 비한다면 새발의 피. 한결 수월한 작업이 될 것임을 예측할 수 있다.
Pooling layer
그런데 이정도로 줄이는 것이 충분한가? 위의 예시 사진은 500K 픽셀인데, 요즘 사진은 12M 픽셀. 24배다. 그럼 다시 가중치가 60만이 되는데, 5천만보다야 여전히 적지만 부담이 커지는 건 여전히 싫다. 그래서 사이즈를 좀 줄이는 작업을 추가하게 되는데, 이것이 pooling layer이다. 유명한 Max Pool layer의 예를 보자.


위 그림처럼 정해진 범위에서 가장 큰 값을 선택하여 데이터를 축소시키는 간단한 방법이다. 이 방법을 반복하면 위처럼 25픽셀짜리를 9픽셀짜로 크게 줄일 수 있다. 이 작업을 해주는 레이어가 Pooling layer이다.
Back-propagation
CNN의 기본뼈대를 알아보았다. 모델을 만들었니 역시 파라미터를 선택해야 하는데, 이 과정이 또 고역이다. 많은 layer의 많은 파라미터들이 출력에 어떻게 영향을 미쳤는지 어떻게 알고 어떻게 값을 수정해야 하는가? Gradient Descent 알고리즘을 이용하여 찾기는 어려울 것 같다. 이것을 위한 알고리즘이 back-propagation이다.
이에 따라 output layer에서 hidden layer로 거꾸로 전파가 되어가면서, 각 parameter가 loss에 얼마나 영향을 미쳤는지 계산하는 알고리즘이 바로 back-propagation이다. 중요한 부분이지만 시간 부족 과 피로 누적으로 간단히만 다룬다. 자세한 것은 링크 참조.
CNN 정리
CNN은 feature maps로 구성된, 하나 혹은 그 이상의 layer를 갖는 신경망이다. 각각의 layer를 convolution layer라고 부르고, 데이터의 크기를 더 줄일 필요가 있을 경우에는 pooling layer를 추가하기도 한다. 이 알고리즘은 shared weights를 통해 줄어든 conncection을 갖는 만큼 훈련을 비교적 빠른 시간 내에 시킬 수 있고, 트레이닝 샘플이 적게 필요한 것이 장점이다.
단점도 있다. 데이터 필터링을 해서 크기를 줄이는 것은 좋은데, 데이터를 제대로 이해하지 못한 채로 크기를 너무 줄여버린다거나 주요 특징을 구분해낼 수 있는 데이터들을 제외한다거나 하면 쉽사리 overfitting의 문제에 봉착한다. 특히 layer가 많아질수록 이 문제를 식별해내기란 쉽지 않다. 교수님 왈, 최근 제안된 대부분의 딥러닝 알고리즘에는 어느정도 overfitting 문제가 섞여 있을 거라고 확신하신다고 하는데, 눈으로 식별할 수 없는 수많은 차원에서 그런 문제를 완벽히 방지하는 것이 더 이상할 거라는 생각도 든다.
4. 정리
수업 시간 중 들은 기억에 남는 보안에 관한 이야기가 있다. 구글 무인차가 이미지 프로세싱을 이용하여 운행된다는 것은 잘 알려져있다. 그런데 구글의 무인차가 인식하는 이미지에 잘 고안된 잡음을 쏘아 원래의 이미지와 합성 시킨다면, 행인이 있는 도로를 아무도 없는 도로로 인식하게 만들 수도 있다. 해킹 기술이 발달하면 충분한 이야기. 어느 기술이든 마찬가지겠지만, 모든 기술의 발전에 있어 보안을 반드시 신경써야 한다. 하물며 그것이 사람 목숨에 관한 것이라면…
핫한 기술인 만큼 다룰 것들이 밑도 끝도 없이 많지만, 핵심적인 것 두가지만 간단히 소개하고 나머지는 아래와 같이 리스트업만 하기로 한다. 시간적 한계로 더 다루지 못함이 아쉽다. 배워볼 기회가 되면 추가 포스팅을..
- Hebbian Learning
- Hopfield Networks
- Restricted Boltzman Machine (RBM)
- Deep Belief Machine (DBN)
Recurrent Neural Networks
CNN은 feed forward networks, 즉, 데이터가 앞으로만 전달되면서 파라미터를 업데이트 되어가는 방식의 신경망인 반면, Recurrent Neural Networks (RNN)5은 각 unit 간의 연결이 순환적 (cycle)으로 연결된다. 이 특성 덕분에 시간에 따라 바뀌는 특징들을 모델링하는데 많이 쓰인다. 자세한 것은 링크를 참조.. 시간이 없다ㅠㅠ
Long-Short Term Memories
Long-Short Term Memories(LSTMs)6는 러닝 과정에서 도중에 데이터를 저장할 수 있도록 하는 알고리즘이다. 기존에는 멀리 떨어진 layer일수록 이전 layer의 출력값이 추상화되면서 영향을 거의 끼치지 못하였는데, LSTMs에서는 필요하다면 출력값을 저장해 두었다가 멀리 떨어진 layer에서도 사용할 수 있도록 한다. 굉장히 복잡하지만 최근의 핫한 인지기술에 많이 사용되고 있다고 한다. 설명은 링크 참조 (위키피디아에도 헷갈릴 거라고 친절하게 써있다.)
I acknowledge that contents of this posting is based on 2017 Fall EECS349 course by Prof. Bryan Pardo at Northwestern Universtiy. I post this for the learning purpose of myself.
-
All of images are taken from the slide in 2017 Fall EECS349 class at NU. ↩
-
Rosenblatt, F. (1958). The perceptron: A probabilistic model for information storage and organization in the brain. Psychological Review, 65(6), 386-408 ↩
-
Rumelhart, David E., James L. McClelland, and PDP Research Group. Parallel distributed processing. Vol. 1. Cambridge, MA, USA:: MIT press, 1987. ↩
-
LeCun, Yann, and Yoshua Bengio. “Convolutional networks for images, speech, and time series.” The handbook of brain theory and neural networks3361.10 (1995): 1995. ↩
-
Werbos, Paul J. “Backpropagation through time: what it does and how to do it.” Proceedings of the IEEE 78.10 (1990): 1550-1560. ↩
-
Hochreiter, Sepp, and Jürgen Schmidhuber. “Long short-term memory.” Neural computation 9.8 (1997): 1735-1780. ↩