Neural network 기초개념

2019-06-24

.

Deep_Learning_Studynotes_(20190622)

study program : https://www.fastcampus.co.kr/data_camp_deeplearning

[학습목표]

  • Neural network 대한 전반적인 개념

  • Neural network 최적화 방법 기초

[학습기록]

1

  • 1차 연립방정식이고, 1차식이고 미지수가 두개인데 식이 4개이기 때문에 아주 특별한 경우가 아니라면 미지수를 무난하게 구할 수 있다. 이 미지수를 찾는 것이 결국은 뉴런을 이용해서 하고자 하는 것이다. 고양이 사진이 들어오면 고양이라고 인식하는거는 이런 수학적 공식이 베이스가 되는 것이다.

  • 딥러닝은 아주 복잡하지만 가장 기본적인 연산으로 들어가면 그렇게 어려운것도 아니다. 어떤 데이터가 들어오면 어떤 가중치를 곱하고 더해주는 원리가 깔려있는 것이다.

  • 가중치로 무엇을 곱하는지 찾는 과정이 바로 training(학습)인 것이다. 고양이 이미지에 각각의 숫자를 곱한다음에 다 더해서 어떤 함수를 통과시키면 어떤 숫자 하나가 아웃풋으로 나오는데 이게 1이면 고양이 0이면 개로 만들고 싶은 것이다.

  • 그렇다면 어떻게 가중치를 찾을것이냐. 처음에는 아무런 정보가 없기 때문에 가중치를 랜덤값으로 채워넣는다. 그 다음에 최초로 연산된 값이랑 정답이랑 차이를 줄이는 방식으로 가중치를 조금씩 조정하고 결국에는 정답과 가장 가깝게 되도록 하게 된다.

2

  • 생물의 뉴런과 인공신경망을 비교해보자. 생물의 신경망은 이전뉴런으로부터 신호를 받아서 축삭돌기로 신호를 뱉는 형식이다. 생물의 신경망이 학습을 한다는 것은 파블로프의 개의 실험으로부터 알 수 있다. 개한테 먹이를 주려고 하면 개가 침을 흘리고, 개한테 먹이를 주면서 종을 치면 나중에는 먹이를 주지 않아도 종만 치게되어도 개가 침을 흘리는 것을 관찰 할 수 있었던 실험이다.

  • 위에 그림에서 볼 수 있듯이 먹이를 보는 신경과 침을 흘리는 신경을 애초에 강하게 연결되어 있었음을 알 수 있다. 그리고 종을 치는 것을 듣는 청각신경은 기존에는 침을 흘리는 신경과는 약하게 연결되어 있었다. 그런데 저렇게 먹이를 줄때마다 종을 울려서 학습을 시키면 나중에는 청각신경과 침샘분비 신경도 강하게 연결이 된다는 것이다.

  • 그래서 뉴런사이의 연결강도가 변하는 현상을 학습이라고 생물학적으로 정의한다. 이 개념을 그대로 인공신경망으로 가져온것이 퍼셉트론이다.

3

  • 신경간의 연결강도를 표현한 것이 바로 가중치이다. 최초에는 이 연결강도에 대한 정보가 없으니까 랜덤값으로 두고 정답과 비교하면서 올바른 강도를 찾아가는 것이다.

  • 그리고 활성함수로는 여러가지 함수를 쓰는데 대부분은 non-linear한 함수를 쓰고, 대표적으로 시그모이드 함수가 있다.

4

  • 뉴럴네트워크는 기본적으로 위와 같은 그래프로 구현할 수 있다.

5

  • 컴퓨터는 0 아니면 1의 데이터를 활용하는 기계이기 때문에 0과 1을 받는 다음과 같은 논리회로를 뉴럴네트워크로 구현할 수 있다.

6

  • 뉴럴네트워크가 하나의 레이어가 있으면 직선의 형태는 전부 표현이 가능하나 곡선의 형태의 표현은 레이어 하나만으로는 불가능하다. 대표적인 것이 exclusive nor 같은 것이다.

  • 결론적으로 두개이상의 레이어를 쌓아서 non-linear한 활성화 함수를 적용하면 어떠한 형태의 곡선도 전푸 풀 수 있다. 이런 결론을 Universal Function Approximation이라고 한다.

7

  • 머신러닝을 사용할 때는 자동차 사진을 넣어서 자동차인지 아닌지를 판별할때 중간에 사람이 바퀴, 차체 등 자동차의 특징을 잘 뽑아내는 필터같은 것을 디자인해서 classification하는 머신에 잘 넣어줘야 했다. 딥러닝 시대에서는 컴퓨팅 자원들이 비약적으로 좋아지면서 저런 특징을 뽑아내는 것도 기계한테 시키게 되었다. 그냥 데이터를 던져주면 기계가 알아서 전부 뽑아주게 되었다.

  • 이렇게 되니까 도메인 지식에 대한 경계가 무너지게 되었다. 머신러닝 시대에서는 자동차의 특징을 잘 뽑는 엔지니어의 도메인 지식이 중요했는데 딥러닝 시대에서는 그럴 필요가 없어졌기 때문이다.

딥러닝을 어렵게 하는 것들

1) vanishing gradient problem

시그모이드 함수를 사용했을때 시그모이드의 수는 0 ~ 1사이의 값이 되는데 이놈들을 계속해서 미분해주게 되면 0으로 점점 작아지게 되는 것이다. 다시말해서 그레디언트 값이 뉴럴네트워크의 뒤로 전달 될수록 점점 작아지는 현상이다. 이는 뒤쪽 레이어(input에 가까운 레이어)에서는 학습이 제대로 이루어지지 않는다는 말이다.

솔루션 : 시그모이드 함수말고 ReLU를 쓰자.

구현도 쉽고 그레디언트 베니싱 현상을 잡아 줄 수 있다.

아래 그림에서 파란색 선이 랠루 함수이다. 오른쪽에는 랠루 함수를 적용한 것을 도식화 한 것으로 하얀색 박스가 음수일 경우라 없어질 것이다.

랠루함수를 미분했을때 음수 쪽은 그냥 날아가는 것이고, 차피 포워드 방향에서도 0으로 바뀌기 때문에 날아갈 것이다.

9

2) overfitting problem

솔루션 : dropout 기법같은 정규화 방법들을 적용하자.

3) get stuck in local minima

8

멀티레이어 퍼셉트론에서 멀티 클래스 처리

  • 결론적으로 얘기하면 softmax를 쓰면 된다. 예를 들어 클래스가 개, 고양이, 사자, 호랑이 4개가 있다고 치자. 그러면 아래 왼쪽그림처럼 output을 낼때 각각의 노드에 시그모이드를 쓸 수 있다. 개일 확률, 고양이일 확률, 호랑이일확률 이런식으로 할 수 있다. 하지만 이론적으로 전부 1이 될 수도 있고, 전부 0이 될수도 있다는 문제가 발생한다. 보통 그래서 이런 경우에는 멀티 레이블 문제를 풀때 정도 사용하고, 보통은 소프트맥스를 쓰게 된다.

  • 그래서 소프트맥스 함수는 각각의 z에다가 exp를 취한 다음에 전부 이를 더한 수를 각각의 z의 exp 취한 것으로 나누어주면 된다. 그러면 아래 그림의 빨간색 박스처럼 된다.

  • 그러면 ajL을 다 더한게 1이 되기 때문에 확률처럼 보이게 된다.

1

  • 멀티클래스의 분류를 할때는 소프트맥스를 마지막 레이어의 활성화 함수로 쓰게 된다. regression 할때는 보통 활성화 함수를 쓰지는 않는다. 가끔 시그모이드난 하이퍼볼릭 탄젠트를 쓰는 경우도 있는데 이 경우는 회귀를 하는데 회귀의 목표치가 0 ~ 1 인경우이다. 그리고 classification할때는 binary classification 할때는 보통 시그모이드를 쓰고, 그외에 멀티클래스면 소프트맥스를 쓰게 된다.

  • 그러면 소프트맥스의 loss는 어떻게 계산하는가. 이때 loss function은 크로스엔트로피를 쓴다. 보통 분류문제에서는 크로스엔트로피를 쓴다고 보면된다. 크로스엔트로피는 바이너리 크로스엔트로피만 받는거 같은데 어떻게 멀티클래스를 처리하냐. 정답인 클래스와 정답이 아닌 나머지 클래스들 두덩어리로 나누어서 처리하면 된다.

2

  • 그러면 예를 들어서 계산을 해보자. 2가지 모델이 있다고 치자 이 모델의 classification error는 둘다 33%다. 이때 크로스엔트로피와, squared loss에 따른 각각의 loss를 구해보자

3

  • 딱봐도 model2가 더 좋은 모델이다. model1은 아리까리한 확률로 class를 맞췄지만 model2는 상대적으로 model1보다 확실한 확률로 class를 맞췄기 때문이다. loss 값을 실제로 계산해도 model2가 더 loss가 적게 나온다.

  • MSE는 참고로 정답에서 멀어질 수록 학습속도가 떨어지는 문제가 있다.

활성화 함수

  • 아래 그림과 같이 여러 활성화 함수가 있고, 이 외에도 더 있지만 통상 아래 있는 함수들이 가장 많이 쓰인다. 그리고 이중에서도 relu가 가장 많이 쓰이는 편이다. 내가 활성화 함수에 대해 잘 모르겠다 싶으면 랠루를 쓰는 것이 가장 무난하다.

4

[시그모이드 함수]

  • 특징 : 모든 입력값에 대해 0 ~ 1 사이의 값의 출력으로 변환해준다.

  • 문제점 :

1) Saturated neurons “kill” the gradients

그레디언트 소멸문제. 그레디언트가 0에 가까워지는 영역이 있는 문제

2) Sigmoid outputs are not zero centered

시그모이드 아웃풋이 음수가 안나오는 문제

시그모이드로 나오는 출력은 모두 양수이기 때문에 이 출력값이 다음 노드로 들어간다면 입력이 전부 역시 양수가 들어가게 된다. 특히 back propogation할때 input이 모두 positive일때 w에 대한 그레디언트가 모두 다 음수이거나 모두 다 양수이게 된다. 다시말해서 모든 w에 대해서 미분값이 전부다 양수이거나 전부다 음수이거나해서 최적화 시 아래 그림처럼 1사분면과 3사분면 방향으로 밖에 못가는 문제가 발생한다. 그래서 아래 그림처럼 zig zag path로 최적값을 찾게 되는데 아예 최적값을 못찾는것은 아니지만 속도가 느리다. 결론은 학습속도가 느리다는 것이다.

5

3) Exp () is a bit compute expensive

exp 연산이 있기 때문에 컴퓨터가 연산하는데 부담이 있다.(이런 문제점 때문에 지니불순도를 대신하여 사용하기도 한다.)

[하이퍼볼릭 탄젠트 함수]

6

  • 기울기가 가장클때는 1이다. 그래서 그레디언트의 크기가 항상 1보다 작거나 같게 된다. 그래서 saturated 영역이 있어서 시그모이드 함수와 마찬가지로 그레디언트 소멸문제가 발생할 수 있다.

[랠루 함수]

7

  • 그레디언트 계산하면 0 아니면 1이다. 그래서 0은 차피 그냥 날아가버릴것이고 forward 방향으로 갈때 연산이 안되고 사라져버리기 때문에 backword로 올때 그레디언트가 0이어도 문제가 되지 않는다.

  • 문제점

1) Not zero centered output

2) An annoyance dead ReLU Slide

모든 레이어는 f(wx+b)로 쓸 수 있는데 예를들어 w를 초기값을 잘못줘서 아래 그림과 같이 빨간색 선으로 그어버리게 되면 dead 랠루에 빠져서 가중치 업데이트가 불가능하게 된다. 또는 learning rate를 잘못줘서 선을 빨간색 선처럼 그어버리게 되면 또 데드랠루에 빠져서 가중치 업데이트가 아예 불가능해진다. 초기값을 부여할때 그래서 트릭으로 바이어스값을 살짝 양수로 줘서 바이어스 만큼 약간 양수쪽으로 쉬프트되기 때문에 완전히 다 데드랠루에 빠지지 않는다고 한다.

8

그래서 사람들이 랠루처럼 음수쪽을 전부 버리는 것이 올바른것이냐 생각하게 되었고 음수쪽에도 약간의 기울기를 주자는 아이디어가 나왔다.

그래서 아래 그림과 같이 랠루의 단점을 보완한 함수들이 등장했다.

9

  • ELU에서 음수쪽에 살짝 꺽은 이유는 아웃라이어에 둔감하게 만들기 위해서다.

[maxout]

랠루와 위키랠루 이런 얘들의 정규화 버전이다.

10

데이터 전처리

  • 이미지 데이터의 전처리

variance를 노멀라이징을 잘 안하는 편이다. 왜냐하면 픽셀값이 0 ~ 255로 보통 정해져 있기 때문이다.

그러면 min max 스케일링 같은거 할때는 어떻게 하냐. alexnet에서는 예를 들어서 이미지가 3만장이 있으면 3만장의 이미지에 대해 각각의 사진에 대한 픽셀값의 평균을 계산한다. 반면에 vgcnet에서는 채널별로 평균을 구해서 적용했다.

가중치 초기화

모든 w에 대해 같은 값으로 초기화를 해주면 어떻게 될까. 예를 들어 모든 w값을 constant로 주게되면 얘네들이 똑같이 업데이트가 되어 학습이 거의 안된다고 보면 된다. 그래서 가중치를 줄때 w를 모두 랜덤하게해서 다르게 줘야한다.

그러면 네트워크를 딥하게 해도 학습이 잘 될 수 있도록 초기값을 어떻게 줘야 할까. 가장 기초적인 아이디어는 아래 그림과 같이 가우시안 노멀분포에서 랜덤하게 뽑아서 w에 할당해주는 것이다.

아래 그림의 예시는 가중치값에 대해서 하이퍼볼릭 탄젠트 함수를 통과한 레이어의 출력값(output값) 대한 분산을 보여주는 것이다.

** 아래 그림에서 참고사항 = fan_in : 입력개수, fan_in : 출력개수

11

  • 실제 가우시안 분포로 w값을 뽑는 것을 시뮬레이션하면 레이어가 깊어질 수록 평균이 0이고 표준편차도 0이 되버리는 문제가 발생한다. 입력으로 들어오는 값이 0의 근처의 값이 들어오고 거기에 w도 0에 가까운 값이 들어오게 되면 하이퍼볼릭탄젠트 함수는 0 근처에서 그냥 linear하게 통과시키기 때문에 점점 0으로 수렴하게 된다.

  • 이런문제를 막아보고자 가중치를 키우는데 가우시안 노멀분포에서 0.01이 아니라 1을 곱해봤다. 그런데 분포를 보니까 양쪽 극단으로 쫙 퍼진 현상을 볼 수 있다. 왜냐하면 하이퍼볼릭 탄젠트 함수가 모든 입력에 대해서 -1 ~ 1 사이로 출력을 주니까 다 극단으로 가게 된다. 그래서 세츄에이션 영역으로 빠져버렸다 그리고 그레디언트가 0이 되어버리는 문제가 발생한다.

12

  • 이런 문제 때문에 나온것이 하비에르 초기화 방법이다. 결과적으로 분포 모양을 보면 w값이 가우시안 정규분포를 이쁘게 이루고 있다. 이 가중치를 표준편차를 정해놓고 무작정 초기화 하는 것은 좋지 않은 방법이라고 판단하고, 퍼셉트론의 가지가 몇개 달려있는지 보고 그 표준편차를 정해야한다는 아이디어다. 그래서 in/out 의 variance 를 같게 해보자

  • 그런데 하비에르 초기화 방법은 activation 함수를 linear한 함수라고 가정한 것이 문제다. 그러나 활성화 함수중에 non-linear한 함수가 대부분이다. 만약에 아래 그림처럼 랠루함수를 활성화 함수로 쓰는 경우에 하비에르 초기화 방법을 쓰게 되면 분산이 0으로 점점 줄어드는 문제가 발생한다.

13

  • 그래서 Activation function 을 ReLU 나 PReLU를 쓰고도 in/out의 variance를 동일하게 해보자 해서 나온 것이 위의 그림과 같은 He 초기화 방법이다.

가중치 초기화 결론

사실 가중치 초기화를 대충 막 해도 batch nomalization 때문에 학습이 잘 된다. 분포가 0이 되거나 쪼그라드는 문제가 있는데 매 레이어마다 정규화를 하면 이런 문제가 사라진다. 그래도 초기의 학습방향을 잘 잡기 위해 가중치 초기화를 잘 해야하는 것이다. 캐라스에서는 initializer API의 디폴트값으로 하비에르 initializer를 쓴다.

** batch nomalization : 매 레이어마다 정규화를 시켜주는 개념

Learning Rate

  • 러닝레이트도 하이퍼파라미터로 상당히 중요한 요소이다. 러닝레이트가 너무 크면 데드렐루에 빠질 수도 있고, 반면에 너무 작으면 학습이 잘 안될수도 있다.

14

  • 이런 러닝레이트의 고민으로 나온 방법이 Leanring Rate Decay다.

쉽게 말해서 학습되는 것을 모니터링 하다가 잘 학습이 안된다고 하면 러닝레이트를 탄력적으로 적용하는 것이다.

15

  • cyclic learning rate 방법도 있다.

16

정규화방법

17

가로 축이 모델사이즈라고 보면 되는데 결론적으로 적절한 모델사이즈를 찾는 것이 중요하다.

내가 풀고자 하는 문제를 풀 수 있는 가장 작은 모델을 쓰는 것을 만드는 것이 가장좋다고 하는데 이는 참 애매한 개념이다. 하이퍼 파라미터 튜닝할것도 많은데 현실적으로 찾을 수 없다.

그래서 딥러닝에서는 모델을 일단 키우고 대신에 generalization gap을 최대한 줄이는 방법을 찾아보자는 방법을 일반적으로 쓴다.

그래서 많이 쓰는 정규화 방법중 하나가 early stopping이다. 아래 오른쪽 그림처럼 gap이 커지기 전에 멈추는 방법인데 어디서 멈춰야 할지 아는 것이 쉽지는 않다.

18

또 다른 방법으로 generalization gap을 직접적으로 줄여주는 것이 정규화방법이다.

일반적인 loss function에 추가로 penalty term을 추가해서 이 penalty term이 커질수록 loss가 커지는 것처럼 효과를 주는 것을 말한다.

L2 방법은 모든 가중치값들을 제곱해서 더한다. L1 방법은 반면에 모든 가중치값들의 절대값을 취해서 더한다. elastic net 방법은 L1방법과 L2방법을 적절하게 조화시킨 방법이다.

그래서 핵심은 W값들이 너무 튀게 하지 않게 줄여줌으로써 함수의 일반화를 유도하는 것이다.

19

정규화를 할 수 있는 방법으로 Dropout 이라는 방법도 있다. 특정 노드에 대해서 동전을 던져서 이 노드를 살릴지 죽일지 결정하여 노드를 날리는 방법이다. 동전 던지기 확률은 사용자가 파라미터로 지정해줄 수 있다. 이렇게 임의의 노드를 날려서 과적합을 막을 수 있다.

트레인할때 노드를 드랍아웃으로 날리고 학습을 하고, 테스트 할때는 다시 전부 노드를 살려서 테스트를 하게 된다. 아래 그림과 같이 만약에 드랍아웃 확률이 0.5인 네트워크가 있다고 하자. training time에는 둘다 살아있는 경우, 둘다죽은경우, 하나만 사는 경우로 하면 1/4씩 해서 각각의 경우에 대한 기대값을 구한다. 그리고 test time에는 dropout 확률을 곱해서 원래 크기를 맞춰준다.

20

정규화를 위한 또 다른 방법으로 Batch normalization이 있다. 이 방법이 가장 대중적으로 많이 쓰이는 방법이다.

간단하게 말하면 배치 정규화는 학습 시의 미니배치를 한 단위로 정규화를 하는 것으로 분포의 평균이 0, 분산이 1이 되도록 정규화(nomalize)하는 것을 말한다.

먼저 Input으로 사용된 미니배치의 평균과 분산을 계산을 한다. 그 다음 hidden layer의 활성화값 및 출력값에 대해서 평균이 0, 분산이 1이 되도록 정규화를 한다(=변환). 그래서 데이터 분포가 덜 치우치게 되고 배치 정규화 단계마다 확대scale와 이동shift 변환을 수행한다.

데이터가 있으면 아래 그림과 같이 N이 batch size다. 이 랜덤으로 뽑힌 특정 batch size의 데이터에 대해서 첫번째 레이어에 들어가는 것을 mean zero라고 하고, 그 다음부터 들어가는 것은 batch가 한꺼번에 통과해서 출력이 batch 단위로 나올건데 그 batch 단위로 나오는 출력에 대해서 노멀라이즈를 하는 것이다. 그런데 이렇게 무작정 노멀라이즈를 하면 바이어스를 더해주는 의미가 없고, variance도 이렇게 정규화해서 1로 맞추는것도 가장 바람직한 방법은 아니다. 그래서 이 노멀라이즈 한 배치 출력값에 대해 감마를 곱하고 배타를 더해준다. 즉 w를 곱해주고 b를 다시 더해주는 셈이 된다. 이 감마와 베타도 학습을 하면서 최적의 값을 찾아나간다.

이런 배치 노멀라이제이션을 해주면 그레디언트 플로우가 잘 흘러가는 장점이 있다. w값의 초기화를 대충해도 잘 학습이 된다. 또한 learning rate를 키워도 잘 학습이 된다고 알려져 있다. 왜냐하면 distribution이 잘 유지되기 때문이다. 또한 정규화 효과도 있다.

21

이 배치노멀라이제이션도 트레인과 테스트를 다르게 해주는 것이 특징이다. 트레이닝할때 배치 단위로 평균과 분산을 구했는데, 테스트 타임에 배치단위의 데이터가 들어올거라는 보장이 없기 때문이다. 그러면 얘를들어서 나는 이미지 데이터를 가지고 뉴럴네트워크를 트레이닝할때 100장의 배치단위로 배치노멀라이제이션을 해줬는데 그러면 모델 서비스를 제공할때 사람들이 100장 이미지를 올릴때까지 기다려야 하냐. 이런 문제를 해결하기 위해서 트레이닝 할때 배치의 평균들의 평균을 계산해놨다가 이걸 테스트 타임에 한장이 들어와도 이 평균값을 이용해서 테스트를 할 수 있게 해준다. 다시말해서 트레이닝 타임에 학습할때 썼던 평균과 분산의 평균과 분산을 기록해두었다가 테스트 할때 써먹게 된다.

이 배치 노멀라이즈도 단점이 있는데 배치사이즈가 작으면 문제가 생길 수 있다. 왜그러냐면 아무래도 평균을 이용하기 때문에 노이즈에 취약할 수 있다. 샘플링의 양이 적을수록 variance가 엄청커지기 때문이다.

그 밖에 데이터를 어떻게 쪼개는지에 따라서 노멀라이즈 방법이 달라지게 된다.

또한 오버피팅을 잡는 가장 확실한 방법은 데이터의 양을 늘려주는 것인데 내가 갖고 있는 데이터를 살짝 변형해서 데이터의 수를 강제로 늘려주는 방법도 있을것이다. 이를 data augmentation이라고 한다.

22

렐루부터 리마인딩하자면

  • 렐루의 단점은 모든 입력에 대해서 output이 음수가 나오면 렐루함수를 통과하는 순간 0이 되어서 전부다 kill되는 데드렐루 현상이 발생해서 학습이 안될수도 있다. 이런 문제를 보완하기 위해 나온것이 위키렐루와 피렐루이다.

  • 위키렐루는 음수쪽에 기울기를 약간 주는 것이고 피렐루는 기울기까지 학습시키는 것이다. 일루라는 것도 있는데 값이 마이너스 무한대로 갔을때 출력도 마이너스 무한대로 표현함으로써 아웃라이어(노이즈)에 대해서도 대응할 수 있는 함수이다. 그리고 이것을 일반화 한것을 maxout이라고 한다.

  • 데이터 전처리에 대해서도 언급했었다. 평균 0에 표준편차도 1로 데이터들을 변환해주는것이 보통이다. 이미지에 대해서는 이런 노멀라이징은 잘 안한다. 다만 평균을 맞춰주는 것을 보통으로 한다. 그렇다고 노멀라이징을 아예 안하는건 아니다. 평균을 구할때 어떤것을 기준으로 평균을 구하는게 중요한데 모든 트레이닝 데이터셋에 대해서 평균값을 구할건데 R,G,B 채널별로 구한다. R채널의 평균, G채널의 평균, B채널의 평균. 이미지가 100만장 있으면 100만장의 R채널들만 전부 모아서 거기에 있는 모든 픽셀을 더한다음 전체 픽셀로 나누어서 평균을 구하고, B채널, G채널도 마찬가지로 이런식으로 해준다.

  • 예를들어서 이미지넷으로 트레이닝 시켜서 사람들이 학습된 모델을 사람들이 공개를 한다. 그거를 다운받아서 나도 돌려보고 싶은데 돌릴때 테스트 데이터에 대해서 채널별로 트레이닝 했을때 평균을 맞춰주지 않고 돌리게되면 이상한 결과가 나올 수도 있다. 그래서 모델을 만든사람들이 가중치를 공개할때 내가 쓴 평균값이 뭐인지도 공개한다. 학습할때 사용한 이미지의 R채널의 평균값, G채널의 평균값, B채널의 평균값이 뭐인지 공개한다.

  • 그렇게 안하고 mnist에서 하듯이 0 ~ 1사이로 그냥 255로 나누어서 하는 케이스도 있다. 이런경우는 채널별로 평균 안구해도 되고 255로 그냥 나누어주면 된다.

  • 그리고 가중치 초기화도 언급을 했었다. 가중치를 너무 크게하면 안되고, 결국에는 평균 0에 표준편차도 얼마를 할지를 보통 정하는 편인데 표준편차를 너무크게하면 Saturation 영역으로 빠지게 되고, 작게하면 전부 0이되버리기 때문에 적당히 작게해야 한다. 적당히 작게 한다는게 퍼셉트론의 입력으로 들어오는게 몇개인지 개수를 보고 정하는것이 좋다. 이런 아이디어로 나온것이 하비에르 초기화 방법이고, he 초기화 방법은 렐루를 쓸때는 음수 절반이 날라가기 때문에 나누어주는 것을 작게 나누어줘야 한다는 아이디어에서 나온것이다. 하비에르 초기화방법에는 들어오는 입력이 10개이기 때문에 루트10으로 나누어주는데 he 초기화 방법에서는 그렇게 하지말고 10개중에 절반은 음수이기 때문에 0으로 날라가니까 루트 10으로 나누지말고 루트 5로 나누어야 한다는 아이디어다.

  • 그리고 cyclic learning rate 처럼 learning rate를 키웠다 줄였다 하는 아이디어를 쓰는 경우도 있는데 learning rate를 순간적으로 훅 키우는 이유는 세들포인트에 빠지지 않기 위함이다.

  • 또한 딥러닝에서 오버피팅에 대한 우려도 있기 때문에 이를 막기 위한 아이디어를 알아봤다. 정규화를 언급을 했었는데 L1은 어떤얘들이 먼저 0으로 바뀌는 것이다. 예를 들어서 가중치가 100개가 있는데 100중에 하나씩 하나씩 0으로 바뀌어 가면서 네트워크가 sparse해지게 된다. L2를 쓰게 되면 가중치가 0이 되는 얘들은 없지만 전체적인 가중치가 작아지게 된다.

  • 그리고 dropout을 봤었다. 트레이닝할때 일정확률로 노드일부를 날리는 것이다. 그리고 테스트할때는 다시 노드전체로 테스트하게 된다. 대신에 트레이닝할때 랜덤하게 켜지고 꺼지고 하는것을 테스트할때는 그렇게 안할 것이기 때문에 그거에 대한 보상을 해줘야 하는데 가중치를 평균내서 보상을 해준다.

  • 배치놈도 공부했었다. 배치놈은 wx+b와 같이 가중치를 곱해서 바이어스까지 더한다음에 배치놈을 한번적용하고 그 다음에 활성화 함수를 쓰게 된다. 이때 배치놈을 한다는게 들어오는 배치단위로 평균, 표준편차를 구해서 평균 0, 표준편차 1로 맞춰주는 것이다. 대신에 너무 그렇게 다 하면 자유도가 없어지니까 감마랑 배타랑 스케일 시프트 팩터로 들어간다. 얘도 마찬가지로 트레이닝할때 배치를 어떻게 하느냐에 따라서 랜덤성이 들어가기 때문에 트레이닝할때 배치단위마다 매레이어마다 평균, 표준편차를 구한다. 그런다음에 이 평균과 표준편차들의 평균을 저장해놨다가 테스트 타임에 어떤 데이터가 들어오든 그 저장해놓은 평균과 표준편차를 사용해서 테스트 데이터를 노멀라이징 한다음에 테스트를 해야한다. 그래서 단점이 트레이닝할때 배치사이즈가 작으면 좋지 않다. 왜냐하면 배치로 뽑는 샘플이 전체 데이터를 잘 대변하고 있다고 가정하기 때문이다. 일반적으로 배치노멀라이제이션을 많이 한다.

1

  • 그레디언트 디센트는 산과 골짜기들이 어떻게 되어있는지 우리는 모르는 상황에서 어쨌든 눈을 가리고 제일 낮은곳으로 가야한다. 지금 서있는 곳의 경사를 보고 경사가 내려가는 방향으로 계속 가다보면 언젠가는 제일낮은곳으로 갈 수 있겠다는 개념이다. 일반적인 그레디언트 디센트는 배치 그레디언트 디센트를 말한다.

  • 위의 그림에서 세트가 가중치라고 생각하면 된다. 이전 세타에다가 러닝레이트에다가 J가 로스값이라고 생각하면 되는데 이 J를 세타로 미분한다. 이게 그레디언트인데 이 그레디언트에 러닝레이트를 곱한값을 이전 세타에서 빼주면된다.

  • 스토케스틱 그레디언트 디센트는 샘플하나를 뽑아서 그 하나가 전체를 대표할것이라 가정하는 방법이다. 그래서 그 특정샘플 하나를 뽑아서 그거에 대해서 로스를 게산하고 미분한 다음에 업데이트 하는 것이다.

  • 스토케스틱 그레디언트 디센트 방법은 너무 극단적으로 소수의 샘플링만하기 때문에 나온 방법이 미니배치 그레디언트 디센트 방법이다. N개의 샘플을 가져와서 그것의 로스를 계산하고 미분을 하는 방법이다.

  • 그런데 그레디언트 디센트 방법의 근본적인 문제가 있다. local minima나 세들포인트 같은 그레디언트가 0에 근접한 지역에 도착하면 더이상 학습이 안되는 문제가 있다. 또한 최적점을 찾아가는 모먼트의 효율성이 떨어지는 편이다. 우리는 최적점으로 다이렉트 화살표로 가고 싶은데 그레디언트 디센트는 아래 그림처럼 지그제그를 그리면서 천천히 찾아간다. 그리고 최적점으로 갈수록 더욱더 느리게 수렴하게 되는 문제도 있다.

2

  • 그래서 우리는 아래 그림과 같이 최적화 방법에 대해 알아볼 것이다. 그래 그림은 카카오의 하용호님이 만든 자료로 최적화 방법들에 대한 개념을 잘 도식화 하였다.

3

그레디언트는 백터니까 방향과 크기가 있다. 그래서 위의 그림에서 위쪽 방향 즉, 파란색 화살표들은 방향을 잘 조절해보겠다는 최적화 방법이고, 아래에 빨간색 화살표들은 크기(러닝레이트)를 상황에 따라서 컨트롤을 해보겠다는 방법이다.

4

  • 그래서 모멘텀 방법은 이전의 그레디언트 방향에 감마라는 어떤값을 곱하는 것이다. 우리가 모멘텀이라는 방법을 쓰기위해 코딩을 한다면 감마를 사용자가 정해줘야 한다. 보통 1에 가까운 값이 디폴트로 들어가 있다. 결론적으로 이전의 움직였던 방향에다가 백터의 합을한 그 방향으로 움직이겠다라는 것이다. 좌측 그렘에 그레디언트 디센트는 최적점을 찾아가는 방향이 비교적 심한 지그제그 형태로 움직이면서 가는데 모멘텀을 적용하면 지그제그 형태가 조금더 직선에 가까운 형태로 상쇄되는 양상이다. 즉 진폭은 작아지고 최적점으로 다가가는 방향은 좀더 다이렉트해지게 된다. 그래서 더 빨리 최적점으로 수렴하게 하겠다는 의도가 담긴 방법이다. 모멘텀 방법은 그레디어트 디센트와 비교했을때 단점도 있다. 관성을 가지고 최적점에 다가가기 때문에 옵티멀 포인트를 지나쳐버리는 경우도 있다.

5

  • 그 다음에 NAG라는 것도 있다. 이 방법은 모멘텀 방법을 반영한 것이다. 모멘텀 방법은 J()안에 세타만 있었는데 세타에서 무언가를 뺐다. 그게 뭐냐면 감마Vt-1이다. 그림을 보면 모멘텀방법에서는 그레디언트가 빨간색 선이고, 이전에 이동했던 방향이 초록색이라면 그 둘의 합인 방향 파란색 화살표 방향으로 움직이는 반면에 네스트로프 방법은 일단 이전에 움직였던 방향을 한번 더간다.그 다음에 그자리에서 그레디언트를 게산해서 거기서 그레디언트 방향으로 가는 방법이다.

  • 이렇게 하면 뭐가 좋아질까? 결론적으로 파란색 라인의 기울기가 모멘텀 방법보다 작아지게 되어서 최적점에 다가갈때 너무 다이렉트로 가지않게해서 최적점을 지나쳐버리는 여지를 조금 더 줄여준다.

  • 위의 두가지 방법이 그레디언트의 방향을 컨트롤하는 최적화 방법이다.

6

  • 세타t+1을 세타t에서 뭔가를 빼준것이다. 그레디언트 디센트의 러닝레이트 자리에 무언가 루트가 씌워진게 분모에 들어가있다. 그 루트씌워진거 안에 Gt가 붙어있는 그레디언트를 Adaptive 그레디언트라는 것이다. 이 아다그라드 방법의 컨셉은 그레디언트의 크기의 제곱을 그레디언트 이전에 움직였던 그레디언트의 크기의 제곱을 모든방향에 대해서 따로따로 계속 누적하는 것이다. 그레디언트의 크기를 다 더해서 제곱한 다음에 그거를 루트로 나누는 방법이라고 할 수 있다.

  • 그러면 이 방법을 통해 무엇을 하겠다라는 거냐면 내가 움직였던 방향으로 계속가면 점점 다가가는 보폭을 줄여주겠다(러닝레이트를 점점 줄이겠다)는 의도다. 그런데 이게 element-wise product이기 때문에 모든 방향(x축,y축…)에 대해 따로따로 계산한다. 그래서 특정방향이 최적점으로 많이 다가가면 그 특정방향의 보폭을 줄여주고, 아닌 방향은 천천히 가는 방법이다.

  • 이 아다그라드는 뭐가 문제냐면 계속 누적해서 더하기 때문에 무조건 학습을 진행하면 진행할수록 이 루트안에 있는 Gt가 커질수밖에 없다. 그래서 언젠가는 러닝레이트가 너무작아져서 학습이 멈추게되는 현상이 발생할수도 있다. 그래서 실전에서는 잘 안쓰는 방법이다.

  • 그래서 이런 단점을 극복하고자 나온방법이 RMSprop이다.

Use exponentially decaying average of squared gradient

\[\ G_{t} = \gamma G_{t-1} + (1-\gamma)(\nabla_\theta j(\theta_t))^{2}\] \[\ \theta_{t+1} = \theta_{t} - \frac{\eta}{\sqrt{G_{t}+\epsilon}} \centerdot \nabla_\theta j(\theta_t)\]
  • (1-감마)를 추가로 앞에 붙여줘서 특정방향으로 계속갔다가 그 특정방향이 아닌 다른 방향으로 계속가지 않았으면 거기에 (1-감마)가 계속곱해져서 나누어주는 텀을 작게 만들어주는 방법이다. 그래서 특정방향으로 계속가다가 오랜만에 다른 방향으로 가면 그 방향으로 가면 스텝이 커질 수 있게 해주는 방법이다. 실전에서 굉장히 많이쓰는 방법이다. 일반적으로는 regression할때 더 많이 쓴다.

7

  • 아담이라는 방법은 방향은 모멘텀 방향으로가고 그때 러닝레이트는 RMSprop으로 하겠다는 방법이다. 즉 방향은 모멘텀, 크기는 RMSprop을 합친방법이다. 굉장히 유명한 방법이고, 잘 모르면 최적화방법으로 아담을 쓰는 것이 가장 무난하다.

전체적인 트레이닝 프로세스

1) 데이터 준비 및 전처리

2) 뉴럴네트워크의 아키텍처 선정(예를 들어서 레이어는 얼마나할지, 뉴런수는 얼마나할지, 드랍아웃 방법을 쓸건지 쓸거면 얼마나 가중치를 부여해서 줄건지 등등)

3) 내가 코딩을 다 한다음에 이 네트워크가 제대로 돌아가는지 확인하기 위해서 로스를 체크해본다. 어떤 정규화 방법을 체택했다면 한번 빼보고 드랍아웃을 채택했다면 드랍아웃을 0으로 한번해보는 등 정규화나 드랍아웃 코드는 그대로 두는데 적용하는 값을 0으로 하는 것이다. 그런다음에 트레이닝을 아주약간 해보고 로스를 출력해본다. 그러면 로스가 어떻게 나와야 되면 클래스의 수가 N개라고 할때 로그 N으로 나눈다. 그 -로그N 분에 1이 나오게 된다.

위에서 언급한 내용이 아래 두문장과 같다.

Initial loss must be about –log(1/n) w/o regularization loss

w/ regularization loss, loss must increase

만약에 우리가 가정한 -log(1/n)이 안나오고 엄청 크거나 작게 나오면 뭔가 내가 코딩을 잘못한거라고 할 수 있다.

4) 내가 만든 네트워크가 주어진 데이터를 오버피팅 할 수 있는지 확인해야 한다. 내가 데이터가 100만개 있어도 100만개를 다쓰지말고 배치사이즈의 3 ~ 4배정도만 집어넣고 게속학습을 시켜본다. 예를들어서 100만개의 데이터가 있으면 64개의 데이터만 샘플링한 다음에 배치사이즈 16으로해서 배치 3 ~ 4로해서 계속학습을 시킨다. 그래서 얘가 64개의 데이터에 대해서 100프로 맞출 수 있는지 확인해본다. 오버피팅해서 암기하는 식으로 해도 이 네트워크가 주어진 데이터를 풀 수 있는지를 확인해본다.

5) 이런 검증을 해본다음에 하이퍼 파라미터 튜닝을 해본다. 그래서 전체 데이터를 사용해서 학습을 할텐데 네트워크에 들어가는 하이퍼파리미터를 제외하고 러닝레이트, 정규화 지수등을 조절해본다. 그런다음에 에포크를 많이주지 말고 약간의 에포크로 학습을 진행해본다. 그 결과로 하이퍼파라미어 튜닝을 해본다. 그리드서치나 랜덤서치등을 이용해서 해본다.

그러면 하이퍼파라미터 튜닝을 할때 어떻게 대응해야하냐. 문제가 발생했을때 어디서 어떤것을 고쳐야 하는가. 결론적으로 가장 큰 힌트를 주는 지표가 로스커브이다.

8

  • 위의 그림에서 가장 좌측에 있는 그림은 학습이 아예 안되고 있는 상황이다. 그레디언트가 웨이트에 적용이 안되는 케이스이다. 우측에 두개 그림은 비슷한 상황이다. 이런경우들에는 트레인셋은 로스가 0에 가까워지는데 벨리데이션 테스트는 겝이 생겨버렸다. 이런 경우는 거의 오버피팅이라고 할 수 있다. 모델이 데이터의 수에 비해 너무 크거나 혹은 데이터가 너무 적은 경우다. 그래서 이럴때는 역시 데이터를 늘리는 것을 가장 큰 해결방안으로 생각해야 한다. 차선책으로 정규화를 적용하는 방안을 생각해봐야 한다. 데이터를 늘리지 않고 정규화를 걸어버리는 방법도 사실 바람직한 방법은 아니다. 그래서 데이터를 어떻게든 늘리는 방법을 강구해야 한다.

9

  • 가장 좌측에 있는 그림은 어떤 상황이냐면 학습이 더디게 진행되는 것이라고 할 수 있다. 따라서 러닝레이틀 좀더 크게 잡을 필요가 있다는 말이다. 아니면 시간을 좀 더 길게 잡고 트레이닝을 한다던지 해야한다. 가운데 그림은 학습 처음할때 슬로우 스타트가 발생한 경우다. 인내가 없는 사람들이 학습을 했다면 이거 무슨 문제가 아니야 라는 의심을 할수도 있다. 이런경우에는 가중치 초기화를 너무 작게 잡거나, 잘못 부여한 경우일수도 있는데 인내심이 있다면 그냥 기다려도 상관없기는 하다. 가장 오른쪽 그림은 학습이 안되고 있는 상황이고 오히려 로스가 커지고 있다. 이 경우는 그레디언트의 부호를 거꾸로 적용한 경우이다.

10

  • 여기서 위에 두개의 그림은 어떻게 보면 크리티컬한 문제는 아닐수도 있는데 이런 경우는 한 에포크 돌고 데이터를 셔플링하지 않은 경우에 저런현상이 발생할 수 있다. 좌측하단의 그림은 컴퓨터가 표현할 수 있는 숫자의 범위를 넘어버려서 학습이 중단되버린 상황이다. 이런 상황에서는 입실론 같은 숫자들을 활용해볼 필요가 있다. 우측하단의 그림은 벨리데이션 세트가 너무 작으면 발생할 수 있는 상황이다. 그래서 벨리데이션 세트의 수도 너무 아끼면 안된다.

11

  • 좌측 그림은 무엇을 나타내냐면 저 색갈 다섯개의 라인이 모두 같은 성능의 네트워크다 한치의 수정이 없는 네트워크를 다섯번 테스트를 해본것인데 결과는 각각 다 다르게 나왔다. 마지막 레이어에 활성화함수를 이중으로 쓰게되면 이런현상이 발생한다. 과거에 텐서플로우버전에서 소프트맥스와 크로스 엔트로피를 동시에 수행해주는 API를 사람들이 일반적으로 많이 썼다. 원래는 소프트맥스 집어넣고 그 다음에 크로스엔트로피를 집어넣어줘야 하는데 이 API를 사용함으로써 마지막 레이어에는 활성화함수를 아예 안쓰고 그 동시에 수행해주는 API를 넣게된다. 그런데 코딩을 하는 과정에서 실수로 소프트맥스를 넣고, 소프트맥스와 크로스엔트로피를 동시에 수행하는 api를 또 넣어버리는 실수들이 많이 발생하게 되었는데 이런 경우 위의 그림과 같은 현상이 발생할 수 있다.

[신경망 필기노트로 정리]

그림, 실습코드 등 학습자료 출처 : https://datascienceschool.net

1) 신경망 개요

1

  • 신경망은 크게봐서 basis function의 형태를 모수값으로 변화시킬 수 있는 함수모형이다. 그 이유는 비선형적인 데이터들을 처리하기 위함이다.

  • 기존에 커널이나 basis function을 사용할때는 사람이 기저함수을 골라놓고 이것을 고정해서 쓰는데 신경망은 이 기저함수를 기계가 자동으로 바꿔주게 된다. 따라서 신경망을 adaptive basis function model이라고 부르기도 한다.

  • 형태로는 퍼셉트론을 여러층으로 쌓아놓기 때문에 multi-layer perceptron이라고도 부른다.

  • 우리가 알고 있는 퍼셉트론은 classification을 0 아니면 1 두개의 바이너리로 할 수 있는 판별함수 모형이다.

예를들어 x라는 이미지를 입력하게 되면 x라는 이미지와 w라는 가중치와 inner product해서 아래와 같이 들어오게 된다. inner product시 이미지에 1이라는 상수항을 미리 augmentation을 시켰으면 x와 w의 innerproduct로 끝났을텐데 신경망에서는 이 1을 나중에 계산상의 편의성을 위해 미리 augmentation을 시키지 않고 바깥쪽으로 빼버린다.

[신경망에서 쓰는 퍼셉트론의 형태]

x1 -> w1 ->

x2 -> w2 -> a = w.T+b -> z = h(a) -> y헷

x3 -> w3 ->

\[\ a = \sum_{i=1}^3 w_i x_i + b = w^T x + b\]

h는 활성화 함수(activation function)를 말한다.

2) 시그모이드 활성화 함수

2

  • 여기까지는 선형모형이고 위의 이 a라는 값을 어떤 비선형 함수에 통과시켜서 z값을 추출하게 된다. 핵심은 비선형 함수를 써야한다는 것이다. 비선형함수를 사용하지 않으면 신경망을 여러겹으로 쌓는 의미가 없다.

  • 먼저 활성화 함수를 알아봐야 하는데 신경망은 보통 로지스틱함수를 많이 쓴다. 그 이유는 우리가 최적화를 하려면 미분값을 계산해야 하는데 로지스틱함수는 미분값을 계산할때 다음과 같이 쉽게 계산할 수 있기 때문이다. \(\ \dfrac{d\sigma(a)}{da} = \sigma(a)(1-\sigma(a)) = \sigma(a)\sigma(-a)\)

  • 그리고 classification을 해야하기 때문에 \(\ \hat{y} = \text{sign}\left(z - \dfrac{1}{2}\right) = \text{round}(z)\) 처럼 0.5를 기준으로 classification을 할 수 있다.

  • 기존의 퍼셉트론은 헤비사이드 스텝함수를 이용해서 classification을 하였으나 미분이 안된다는 단점때문에 미분이 되는 로지스틱 함수를 이용하는 형태로 x 백터가 들어갔을때 y헷이 나오는 것을 신경망에서는 채택했다

3) 비선형 기저함수

3

  • 하지만 이렇게 하면 y헷은 어디까지나 직선형태가 된다는 문제가 있다. 이 직선형태가 되면 실제로 classification이 안되는 데이터들이 많다. 예를 들어 XOR문제 같은거..

  • 그래서 우리는 basis function 방식을 쓸 수 있다. 이 방식이 뭐냐 원래는 x를 쓰는게 맞는데 비선형적인 함수형태를 통과시키자는 것이다. 전체적으로 \(\ a = w^T x + b\) 형태를 쓰되 x대신에 x를 basis function \(\ \phi(x)\)에 통과시킨 것을 사용하자는 것이다.

  • 따라서 아래와 같이 활성화 함수를 쓸 수 있다. \(\ z = h \left( \sum_{j=1}^J w_j \phi_j(x) + b \right) = h \left( w^T \phi(x) + b \right)\) 여기서 J는 J개의 많은 기저함수를 사용한다는 의미에서 J인 것이다.

  • 파이(x)는 다차원 함수이다. x백터가 들어가면 백터가 나오는 형태인데 들어갈때 차원과 나올때 차원은 다를 수 있다. 예를 들어 x백터가 3차원이었는데 100개의 파이가 있었다면 100차원 백터가 나온다는 것이다. 파이를 많이 생각해내면 생각해 낼 수록 좋다 왜냐하면 이 비선형 함수중에 하나는 데이터를 설명할 수 있기 떄문이다. 문제는 파이가 또 너무 많아지면 x간에 상관관계가 심해져서 오버피팅이 발생하거나 컨디션넘버가 높아지는 안좋은 현상이 발생한다.

  • 그래서 가장 좋은것은 데이터의 비선형성에 딱맞는 적절한 개수의 파이만 있으면 좋은데 사람은 그것을 시각적으로 확인해서 조율할 수 있는 부분에서 한계가 있기 때문에 현실적으로는 그냥 무작정 많이 파이를 만들어보도록 기계한테 의존하는 것이다.

4) 하이퍼 파라미터에 의해 모양이 바뀌는 비선형 기저 함수

4

  • 그래서 뭔가 이 파이자체도 모양을 바꿔서 이런저런 파이를 골라내고 싶다. 다시말해 기저함수를 여러개 써본다음에 그중에 좋은게 뭔지를 내가 찾고 싶다. 1차 2차 3차… 등의 함수를 써보고 싶다. 그렇게 하려면 모형자체를 바꾸는 하이퍼파라미터를 조절하면 된다. 좋은 파이를 찾는다는 것은 하이퍼파라미터 최적화를 한다는 의미이다.

  • 이 파이에 모양을 바꿀 수 있는 하이퍼파라미터를 하나 생각을 한다. \(\ z = h \left( w^T \phi(x ; \theta) + b \right)\) 그게 이 식에서 세타가 그것을 의미한다. 예를 들어서 x의 세타승이라고 하면 세타가 1일때는 1차함수, 세타가 2일때는 2차함수 이런방식이 되는 것이다. 그래서 이 세타를 할 수 있는 것을 다써보고 가장 좋은 것을 고르는 것이 하이퍼파라미터 최적화이다.

  • 이 파이를 찾는데 어떤 방법이 좋을까 생각하다가 사람들이 발견한 것이 이 파이를 갖다가 뭔가 비선형 함수를 써야하는데 사람이 생각하기에 한계가 있으니까 퍼셉트론 자체에 활성화함수를 파이로 바로 활용해보자는 것이다.

  • \(\ z = h \left( \sum_{j=1}^M w_j h \left(w_{j}^{(1)} x + b_j^{(1)} \right) + b \right)\) 이런식으로..

  • 위 식에서 w와 b를 바꿔주게 되면 원래 파이의 모양이 달라지게 되는 효과와 같다는 것이다. 다시말해 이 w와 b를 조절해주면 우리가 쓰는 파이라고 하는 basis function이 변한다는 것이다. 이런식으로 만들면 신경망을 구현할 수 있다.

  • 정말로 이런식으로 하면 비선형 문제를 풀수 있는가에 대해 수학적으로 증명한 정리 Universal Approximation Theorem에서는 이 basis function(파이)을 무한히 많이 사용하면 어떠한 형태의 함수와도 유사한 형태의 함수 z(x) 를 만들 수 있다고 한다.파이를 몇개를 만들어야 한다고 단정하지는 않았다.

  • 퍼셉트론은 하나만 썼을경우 linear한 문제밖에 못풀지만 그걸 여러개로 연결시켜서 네트워크를 만들게되면 XOR 같은 비선형문제도 풀 수 있다.

  • 신경망에서는 Multi-classification을 할때 OvR 방식을 사용하기 위해 출력계층을 여러개 두는 경우도 있다. 통상 저렇게 여러개의 출력계층을 둘때는 출력계층에 소프트맥스 함수를 붙인다. 소프트맥스 함수는 들어가는 입력의 순서는 바뀌지 않으면서 모두 더했을때 합이 1이되게 하는 함수이다. 소프트맥스 함수를 쓰게되면 마치 확률인것처럼 보이게 된다. 실제 확률은 아니지만.. 어떤 카테고리 확률값으로 보이는 것이다.

5) Multi-Layer Perceptrons

5

6) 신경망 가중치 표기법

6

  • 신경망에서 가중치를 표기할때 주의해야할 점이 있다. \(\ w^{(l)}_{j,i}\) 와 같이 표기해야 한다. l-1 번째 계층의 i번째 뉴런과 l번째 계층에서 j번째 뉴련을 연결하는 가중치라는 의미이다.

  • 왜 이렇게 하느냐. 이렇게 뒤집어서 하게 되면 우리가 알고 있는 행렬의 원소기호 쓰는 방식으로 w를 묶어서 메트릭스를 만들 수 있다. 그리고 여기서 뒤쪽으로 넘어와서 y값 계산하는 과정을 메트릭스하고 벡터의 곱으로 표현할 수 있다. 아직까지는 활성화 함수를 통과한 상태는 아닌것이다.

7) feedforward propagation(순방향 전파)

7

  • l−1 번째 계층의 출력과 l 번째 계층의 출력은 다음과 같은 관계가 있다. \(\ a^{(l)} = {W^{(l)}} z^{(l-1)} + b^{(l)}\), \(\ z^{(l)} = h\left({W^{(l)}} z^{(l-1)} + b^{(l)}\right)\), \(\ z^{(0)} = x\), \(\ \hat{y} = z^{(L)}\)

  • 그래서 앞단에서 출력한 z값과 그 중간을 연결하는 네트워크들(w) 곱한다음에 b값을 더해주면 신경망 내부에 있는 활성화 함수 값이 된다. 근데 얘는 출력은 아니다. 출력이 되려면 h 활성화 함수를 통과해야 한다.

  • 다시정리하면 앞단에서 z가 나오게 되면 걔를 w와 곱하고 b를 더해서 활성화 함수값 구하고 그값을 활성화 함수를 통과시키고 나면 이 단의 출력이 된다. 앞단에서 뒷단까지 순차적으로 계층마다 이 과정을 반복하면 된다.

  • 위와 같은 과정을 feedforward propagation(순방향 전파)라고 한다.

8) 오차함수와 가중치 최적화

8

  • 퍼셉트론에서는 w의 값을 잘 조절하면 classification을 잘 할수 있다는 것이 핵심이었다. 신경망에서도 마찬가지로 w와 b를 잘 찾으면 classification을 잘 할 수 있다.

  • 그런데 신경망에서는 이 w와 b를 찾는데에도 성능을 높이기 위해 파이라는 기저함수로 쓰게되었다.

  • 과거에는 w값을 좋은것을 찾아서 고정을 시키고, 파이는 그리드서치를 통해 최적화를 시켰었으나 너무 번거로우니 그냥 w와 파이를 한방에 찾을 수 있는 방법이 없을까 수학자들이 고민하였다.

  • 다시말해 하이퍼파라미터 튜닝, 기저함수 찾는 과정, 모델 fitting과정 이 세개를 한방에 처리하자는 것이다.

  • 목적함수는 예를 들어서 사진을 넣었을때 개면 1, 개가 아니면 0으로 출력되도록 말이다.

  • 그렇게 하려면 트레이닝용 데이터는 있어야 한다. 그래서 목적함수를 어떻게 잡을 것인가. 다음과 같은 오차함수 수식을 이용한다. \(\ \begin{eqnarray} C(w,b) = \sum_{i=1}^N C_i(w,b) = \sum_{i=1}^N \| y_i - z_i^{(L)}(w,b) \|^2 \end{eqnarray}\)

  • 이 때 N 은 트레이닝 데이터의 갯수이다. 로지스틱 활성 함수를 이용한 분류 문제를 풀 때는 정답 y 가 클래스 k 에 속하는 데이터에 대해 k 번째 값만 1이고 나머지는 0인 원핫인코딩(one-hot-encoding) 벡터이다.

  • 예를들어서 이미지가 여러장 있으면 이 이미지를 하나씩 넣게 된다. 사진을 넣으면 y헷이 나오게 되면 이게 개사진이 맞는 정답이면 1이어야 하고 아니면 0으로 나와야 한다.

  • 위의 식처럼 유클리디안 거리를 이용해서 모든 이미지에 대해 실제 정답과 y헷값이 가능하면 가장 최소화하는 w와 b값을 찾으면 된다. 여기서 말하는 w와 b는 전처리 부분에 들어가는 w와 실제퍼셉드론에 들어가는 b를 원샷에 찾겠다는 것이다.

  • 우리가 알고 있듯이 중간에 활성화 함수들이 막 끼워져 있다. 그래서 상당히 찾는 과정이 복잡해진다. 결론적으로 그레디언트 백터를 찾아서 조금씩 더 좋은 값으로 전진하는 방법을 채택해야한다. 신경망에서도 이 방법을 쓰면 된다.

  • 수식으로 표현하면 다음과 같다. \(\ \begin{eqnarray} w_{k+1} &=& w_{k} - \mu \frac{\partial C}{\partial w} \\ b_{k+1} &=& b_{k} - \mu \frac{\partial C}{\partial b} \end{eqnarray}\) 이고 여기서 뮤는 최적화 스텝사이즈를 말한다.

9) back propagation

9

  • 만약에 입력계층이 784개이고 은닉계층이 15개 출력계층이 10개인 경우 w와 b는 몇개를 찾아야 하나. w는 앞단에 11760개 + 뒷단에 150개를 찾으면 된다. b는 앞단에 15개 + 뒷단에 10개 이다. 우리가 찾아야할 파라미터의 수는 11935개가 되는 것이다.

  • 이말은 약 12000차원에서 최적화를 실시한다는 의미이고 그레디언트 백터는 약 12000개를 찾아야 한다는 것이다. 또한 미분을 12000번하면 된다는 의미이다.

  • 문제는 C를 구하려면 사진을 한장씩 집어넣으면서 y헷이 얼마나 나오는지 보고 걔와 y를 빼서 애러값을 구해서 더하고 이러는 과정을 사진갯수만큼 해줘야한다.

  • 데이터수가 커지면 감당이 안된다.. 너무 계산양이 커지게 되므로 그레디언트 백터를 구할때 효율적으로 하기 위해서 back propagation(역전파)라는 방법을 수학자들이 생각했다.

  • 역전파 방법은 위의 수치적 미분을 하는것 보다는 훨씬 계산양이 줄어들게 된다.

  • 먼저 델타를 뒤에서 앞으로 전파한다. 델타는 c를 a로 미분한 것이며 다음과 같다. \(\ \delta_j^{(l)} = \dfrac{\partial C}{\partial a_j^{(l)}}\)

  • 그런데 a라는 것은 노드마다 하나씩 존재하게 된다. a의 갯수는 즉 노드의 개수가 된다. 아까는 a가 아니라 네트워크 엣지 w를 구해야하는 것인데 그것에 비해서는 상당히 줄어든 개수기 때문에 일단 a를 먼저 구하겠다는 것이다.

  • 사실상 그래서 델타는 노드 하나마다 붙어있게 된다.

  • 그리고 델타를 계산할 수 있는 수식을 수학자들이 찾았는데 그 수식은 다음과 같다. \(\ \begin{eqnarray} \delta^{(l-1)}_j = h'(a^{(l-1)}_j) \sum_{i=1}^{N_{(l)}} w^{(l)}_{ij} \delta^{(l)}_i \end{eqnarray}\)

위식에서 N(l)은 l번째 레이어의 노드의 갯수를 말한다.

그리고 여기서 h프라임은 미분에서 만든 도함수를 의미한다. 활성화함수가 아니다. 그니까 aj l-1을 activation 함수를 미분해서 만든 도함수에 집어넣으면 무슨 숫자가 하나 나올것이다(h프라임함수의 출력은 기울기 상수가 나옴.스칼라다).

  • 델타값l-1을 구하는데 델타 l을 이용하게 된다. 이게 무슨말이냐면 뒷단에 있는 델타를 이용해 앞단에 있는 델타를 구한다는 것이다.

  • 그래서 해당 단에서 모든 j에 대해 기호로 쓸 경우에는 다음과 같은 식을 쓴다. 다시말해 위의 식을 벡터와 행렬의 식으로 쓰면 다음과 같다. \(\ \delta^{(l-1)} = h'(a^{(l-1)}) \odot ({W^T}^{(l)} \delta^{(l)})\) 여기서 \(\ \odot\) 기호는 Hadamard Product, Schur product, 혹은 element-wise product 라고 하는데 예를들어 다음과 같다.

  • \[\ x \odot y = \left(\begin{array}{c} x_1 \\ x_2 \\ x_3 \end{array}\right) \odot \left(\begin{array}{c} y_1 \\ y_2 \\ y_3 \end{array}\right) = \left(\begin{array}{c} x_1 y_1 \\ x_2 y_2 \\ x_3 y_3 \end{array}\right)\]
  • 이런식으로 back propogation을 하면 되는데 시작하는 지점은 y와 y헷의 차이, 즉 잔차를 쓰면 된다. 잔차가 마지막 부분의 델타다. 수식으로는 \(\ \delta^{(L)}_j = y_j - z_j\)와 같다.

  • 여기까지하면 델타를 구하게 되는 것이고 우리의 원래 목적은 w와 b에 대한 미분값을 구해야하는 것이므로 그것은 다음과 같이 해주면 된다.

  • 오차값에서 가중치에 대한 미분은 다음과 같이 구한다. 앞단에 있는 z와 뒷단에 있는 델타를 곱하면 된다. \(\ \frac{\partial C}{\partial w^{(l)}_{ji}} = \delta^{(l)}_j z^{(l-1)}_i\) 요고를 앞단으로 계속 전파해주면 된다.

  • 또한 바이어스에 대한 미분값은 다음과 같은데 공교롭게도 그냥 델타 자체다.

b라는건 노드하나마다 b라는 값이 있고, b라는 얘의 미분값도 노드마다 달려있는데 그게 그냥 델타 자체라는 것이다.

\[\ \frac{\partial C}{\partial b^{(l)}_{j}} = \delta^{(l)}_j\]

10

11

10) back propagation 의 증명

참고로 l번째 layer의 a값은 l+1번째 layer에 대한 어떤 함수라는 것이 성립한다.

l+1 단의 벡터를 집어넣으면 l번째 단의 a가 나오는 z의 역함수가 존재한다.

왜냐하면 a하고 z를 연결해주는 것이 시그모이드 함수인데 이 시그모이드 함수의 역함수가 존재하기 때문이다.

12

11) Stochastic Gradient Descent

13

  • 이렇게해서 w값을 피팅할 수는 있는데 여전히 계산량은 많다. 왜냐하면 먼저 C에 대한 미분값을 찾아야 하는데 C라는게 이미지 한장한장 넣었을때 오차의 합이고 이걸 w로 미분한다는 것은 한장한장에 대한 오차의 미분의 합이라는 것이다.

  • 이는 전체 오차를 구한것이 아니고 사진한장에 대한 오차를 구하는 것인데 이것을 이미지 갯수만큼 실시하고 델타를 모두 더하는게 전체 오차에 대한 델타가 되는 것이다.

  • back propagation을 10만장의 이미지라면 10만번 100만장이면 100만번 해야하는데 꼭 이렇게 무식하게 반복을 해야하냐라는 것에서 나온 아이디어가 Stochastic Gradient Descent 방법이다.

  • 데이터의 특성이 10만개가 갖고 있는 특성이나 이중에 100개를 뽑았을때 데이터 특성이나 크게 다르지 않다는 것이다. 그렇기 때문에 굳이 10만개를 다 안써도 된다는 것이다.

  • 다시말해 일부데이터만 이용해서 그레디언트 백터를 계산하는 것이다.

  • 데이터를 10만개 전부 다쓰게 되면 목적함수의 값이 반드시 줄어드는 형태로 나오지만 SGD는 줄어들기도하고 늘어나기도 하는 형태로 왔다갔다하게 된다. 그러나 특이한건 수렴속도는 두개가 비슷하다는 점이다.

  • 따라서 계산량이 너무 많다보니까 줄여서 해보자는 방법이 현실적으로 많이 쓰인다.

  • 그렇다면 또 문제가 되는 것이 전체 데이터중에 n개만 뽑아서 써야 하는데 어떤것을 빼서 써야하냐 주사위를 써서 랜덤 초이스를 해야하냐. 이 랜덤 초이스 자체도 연산이기 때문에 부담스럽다.

  • 현실적으로는 그래서 데이터가 10만개 있으면 맨앞에 데이터 n개만써서 그레디언트를 업데이트 한다.

  • 그 다음에는 n개 이후에 데이터를 쓰게 된데 새로운 데이터를 써보기 위해서. 그래서 또 그레디언트를 업데이트 해준다.

  • 이렇게 데이터를 부분적으로 잘라서 그레디언트를 업데이트를 계속해주게 된다.

\(\ \dfrac{\partial C}{\partial w_k} = \sum_{i=1}^N \dfrac{\partial C_i}{\partial w_k}\), \(\ w_{k+1} = w_k - \mu \dfrac{\partial C}{\partial w_k}\)

  • 실제로 w는 미니배치사이즈 만큼 사용해서 업데이트해주고 미니배치를 데이터개수만큼 다 쓸 경우를 1 에포크라고 한다.

  • 정리하면 신경망의 계수는 다음과 같이 찾으면 된다.(신경망 fitting하기)

맨첨에 w와 b값을 임의로 집어넣어넣는다. 그다음에 이미지 한장을 입력하게되면 임의로 집어넣은 w와 b값을 이용해서 feedforward propagation을 하게되고 a와 z값을 계산하게 된다. 최종출력계층의 z와 실제 y간의 오차를 계산해서 다시 back propagation을 해주게 된다. 그러면 델타가 나오게 되는데 델타와 z값을 곱해서 그레디언트 값을 계산을 한다. 그러면 이게 그레디언트 한번 계산된것이다. 이것을 이미지 개수만큼 반복해야 하지만 계산량이 워낙 많기 때문에 minibatch-size 만큼만 이미지를 뽑아서 iteration을 해주면 된다. 그래서 생긴 그레디언트들를 모두 평균낸다. 그 만큼 또 w와 b를 갱신해준다. 그 다음에 미니배치 사이즈만큼 또 이터레이션해주어 그레디언트 값을 업데이트해주고 그 그레디언트를 이용해서 w와 b를 반복한다.

# [요약정리]

  • 신경망모형은 기저함수도 모수값에 의해 변화할 수 있는 적응형 기저함수모형이며 구조적으로는 여러개의 퍼셉트론 레이어를 쌓아놓은 형태이므로 MLP(Multi-layer perceptron)으로도 불린다.

  • 신경망에 속한 퍼셉트론은 neuron, node라고 불린다. 각 계층은 다음 계층에 대해 적응형 기저함수의 역할을 한다. 최초의 계층은 입력계층, 마지막 계층은 출력계층, 중간은 은닉계층이라고 한다.

# 신경망 계수탐색 프로세스

1) initialize

모든 w,b 값을 임의의 값으로 초기화

2) input

하나의 표본데이터 \(\ x_i\)로 입력 계층 설정

3) feedforward propagation

모든 뉴런에 대해 a, z값 계산

4) output and error calculation

최종출력계층의 값 \(\ z^{(L)} $ 및 오차 $\ \delta^{(L)}\)계산

5) back propagation

반대방향으로 오차 \(\ \delta\) 전파

6) gradient calculation

표본데이터 \(\ x_i\)에 의한 오차함수의 미분값(그레디언트) \(\ \frac{\partial C_i}{\partial w}=z \, \delta $, $\ \frac{\partial C_i}{\partial b}=\delta\) 계산

7) minibatch-size iteration

표본데이터를 \(\ x_{i+1}\)로 바꾸어 미니배치크기 만큼 2) ~ 6) 단계를 반복

8) weight update

미니배치크기 만큼의 데이터를 사용한 후 그레디언트 \(\ \frac{\partial C}{\partial w} $, $\ \frac{\partial C}{\partial b}\) 계산

이 그레디언트 값으로 w, b값을 업데이트

  • 단순하게 수치적으로 미분을 계산한다면 모든 가중치에 대해서 개별적으로 미분을 계산해야 한다. 그러나 Back propagation방법을 사용하면 모든 가중치에 대한 미분값을 한번에 계산할 수 있다.

# 신경망 성능 개선방법

  • 크로스 엔트로피 형태의 오차함수를 사용하면 출력 레이어에서 활성화 함수의 도함수에 의한 영향을 제거 할 수 있다.

크로스 엔트로피 형태의 오차함수 : \(\ \begin{eqnarray} C = y \log z^{(L)} + (1-y) \log (1-z^{(L)}) \end{eqnarray}\)

  • 활성화 함수로 로지스틱 함수 대신 하이퍼탄젠트를 사용하면 도함수의 최댓값이 로지스틱 함수의 4배인 1이 되므로 그레디언트 감소현상이 줄어든다.

하이퍼탄젠트 : \(\ \begin{eqnarray} \tanh(a) \equiv \frac{e^a-e^{-a}}{e^a+e^{-a}} = 2\sigma(2a) - 1 \end{eqnarray}\)

** 아래 두개는 가장많이 쓰는 방법으로 알려져 있다.

  • ReLu 활성화함수를 사용하는 것이다. 렐루는 가중치 총합 a가 큰 경우에도 기울기(그레디언트)가 1로 유지되므로 a가 커도 그레디언트 감소현상이 발생하지 않는다. CNN과 같이 레이어의 수가 많은 경우에 유용하다.

  • dropout 정규화 방법은 이러한 문제를 해결하기 위해 에포크 마다 임의의 은닉계층 뉴런의 p%(보통 절반) dropout하여 최적화 과정에 포함하지 않는 방법이다.

# 자주 사용하는 신경망 최적화 방법(그레디언트에 변형을 주는 방법)

** 기본 그레디언트 방법 : \(\ w_{k+1} = w_k - \mu_k g(w_k) = w_k - v_k\)

1. decay 방법 : step size를 줄여 oscillation 현상을 회피

\[\ \mu_{k+1} = \mu_{k} \dfrac{1}{1 + \text{decay}}\]

2. momentum 방법 : 진행하던 방향으로 계속 진행하게하여 최적화를 빠르게 수행

\[\ v_{k+1} = \text{momentum} \cdot v_k - \mu_k g(w_k)\]