8월, 2017의 게시물 표시

오버피팅 억제법

신경망 학습을 하다보면 오버피팅이 일어날 수 있다. 오버피팅이란 신경망이 훈련 데이터에만 지나치게 적응되어 그 외의 데이터에는 제대로 대응하지 못하는 상태를 말한다. 오버피팅은 주로 매개변수가 많고 표현력이 높은 모델 훈련 데이터가 적은 경우 위 경우에 일어난다. 이를 방지하기 위한 방법을 알아보자. 가중치 감소 가중치 매개변수의 값이 작아지도록 학습하는 방법이다. 오버피팅은 가중치 매개변수의 값이 클 때 발생하는 경우가 많기때문에, 학습 과정에서 큰 가중치에 대해서는 그에 상응하는 페널티를 적용해 오버피팅을 억제시키겠다는 것이다. 그럼 어떻게 작게 만들 수 있을까? 우선 초기값을 아예 작게 만드는 방법을 생각해 볼 수 있다. 그렇다고 아예 0으로 설정해버리면 학습이 되지 않는다.(정확히는 가중치들을 다 같은 값으로 설정하는 경우) 왜냐하면 오차역전파법 에서 가중치의 값들이 똑같이 갱신되기 때문이다.(*연산과 +연산에 있어서 역으로 미분값이 전파될 때를 생각하면 알 수 있다.) 따라서 초깃값을 무작위로 설정해 줘야 하고 이를 위해 다음과 같이 표준편차가 0.1, 0.01과 같은 정규분포를 사용해 초기화 한다. 간단한 가중치 초기화하는 코드를 보자. def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01): # 가중치 초기화 self.params = {} self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size) self.params['b1'] = np.zeros(hidden_size) self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) self.params[

RMSprop

이미지
AdaGrad 방법에서 h값이 계속커지는 것을 방지하기 위해, h를 계속 더하는 것이 아니라 지수이동평균 (EMA = Exponential Moving Average) 으로 바꾼다. 무슨뜻이냐 하면, 과거의 모든 기울기를 계속해서 균일하게 더해가는 것이 아닌 과거의 기울기는 서서히 잊고 새로운 기울기 정보를 크게 반영하겠다는 것이다. 식을 보며 이해해 보자. $$ h \gets \gamma h + (1-\gamma)\frac{\partial L}{\partial W} \bigodot \frac{\partial L}{\partial W} \\ W \gets W - \eta \frac{1}{\sqrt{h + \epsilon}}\frac{\partial L}{\partial W} $$ AdaGrad와 비교하면 크게 달라진것이 없어보인다. 차이점은 $\gamma$라는 변수가 새로 보이는 것이다. 이를 decay rate라고 부르며 보통 이 값은 0.9나 0.99등의 값으로 설정하는데, 연산되는 위치를 살펴보면 우선 기존의 h값에 곱해짐을 알 수 있다. 이 값이 1미만임을 생각해보면, 이를통해 과거의 기울기를 잊고자 함을 수식으로 구현했다고 볼 수 있다. 그리고 기울기값에 $1-\gamma$값을 곱하며 새로운 기울기 정보를 반영해나간다. 그러면 SGD 의 단점에서 다룬 함수로 비교해보자. 사실 AdaGrad방법과 큰 차이가 보이지 않는다. 애초에 AdaGrad가 갱신량이 0이 될때의 문제점을 개선하기 위한 알고리즘이니까, 차이를 느끼기 어렵긴 하다. AdaGrad와 비교해 갱신량이 줄어도 계속해서 진행됨을 시각적으로 더 보고 싶으면 이 블로그 의 애니메이션을 보면 좋을 것 같다.

Adam

이미지
AdaGrad 는 매개변수마다 적응정도 조정을, Momentum 은 바닥을 구르는 듯한 진행을 한다. Adam은 이 두 기법을 융합하면 어떻게 될까라는 생각에서 출발한 기법이다. 두 기법의 특징에다가 하이퍼파라미터의 편향보정이 진행되기도 한다. 자세한 내용은 원 논문 에서 확인하고, 실제 진행되는 모습을 SGD 의 단점에서 사용한 함수로 비교해보자. 그림에서 볼 수 있듯이, Momentum처럼 그릇 바닥을 구르듯 움직이지만 Momentum보다 공의 흔들림이 적음을 알 수 있다. 이는 학습의 갱신 정도를 적응적으로 조정하기 때문에 얻는 혜택이라 할 수 있다.

AdaGrad

이미지
신경망 학습에 있어서 학습률은 중요한 값이다. AdaGrad는 이 학습률을 학습진행에 따라 서서히 낮추어 가며 학습하는 방법이다. 각각의 매개변수에 적응적으로(adaptive) 학습률을 조정하면서 학습을 진행한다. 수식은 아래와 같다. $$ h \gets h + \frac{\partial L}{\partial W} \bigodot \frac{\partial L}{\partial W} \\ W \gets W - \eta \frac{1}{\sqrt{h + \epsilon}}\frac{\partial L}{\partial W} $$ $\epsilon$은 0으로 나눔을 방지하기 위한 변수며 h라는 변수는 학습률의 조정을 위해 생긴 변수다. h는 매 학습마다 기존 기울기값을 계속해서 곱해 더해준다.($\bigodot$ : 행렬의 원소별 곱셈) 그러면 h값의 크기는 기울기가 큰 만큼 커지고, 이 커진 h를 학습률에 곱해줘서 학습률의 크기를 줄여준다. 결론적으로 매개변수 중 많이 움직인(=크게 갱신된) 원소는 학습률이 낮아진다. 단, 이 방법을 통해 무한히 학습한다면 h값이 계속 증가해(기울기 제곱값은 항상 양수고, 양수를 계속해서 더해가므로) 갱신량이 0이되어 더이상 진행이 안 되는 경우가 생긴다. 이를 해결하기 위한 방법이 RMSprop 이다. 그럼 실제로 어떻게 진행되는지 알아보기위해 SGD 의 단점에서 사용한 함수와 비교해보자. 그림에서 알 수 있듯이, y축 방향은 기울기가 커서 처음에는 크게 움직이지만 이에 비례해 갱신 정도도 큰 폭으로 작아지게 조정된다. 따라서 y축 방향으로 갱신 정도가 빠르게 약해지고, SGD와는 달리 지그재그 움직임이 줄어든다. 실제로 기울기의 변화정도를 비교하면 다음과 같다. 왼쪽이 x축 방향의 기울기 값이고, 오른쪽이 y축 방향의 기울기 값이다. 기울기의 변화 정도가 y가 더 큼을 알 수 있다.

Momentum

이미지
운동량을 뜻하는 단어가 의미하는 바와 같이, 모멘텀 기법은 아래의 수식과 같이 공이 그릇의 바닥을 구르는 듯한 움직임을 보여준다. $$ v \gets \alpha v - \eta \frac{\partial L}{\partial W} \\ W \gets W + v $$ SGD와 마찬가지로 W는 가중치 매개변수, L은 손실함수, $\eta$는 학습률이다. 새로나오는 변수중 v는 속도(velocity)고, $\alpha$로 속도의 크기를 설정해줌으로써 물체가 아무런 힘을 받지 않을 때도 서서히 하강시키게 된다. 보통 $\alpha$값은 0.9등의 값으로 설정한다. SGD 의 단점에서 예를 든 함수와 비교하며 변수의 변화에 따라 Momentum이 어떤식으로 학습하는지 알아보자. $\eta : 0.1, \alpha : 0.9$, 반복횟수 30회 위와 같이 지면을 따라 구르듯이 진행함을 알 수 있다. 그런데 여기서 최솟값인 0.0을 지나서 계속 굴러지나가는(?) 모습이 보이는데, 반복 횟수를 높여 그냥 지나가버리는지 아니면 다시 최솟값을 향해 굴러오는지 확인해보자. 우선 현재 최솟값부근을 확대하면 아래와 같다. $\eta : 0.1, \alpha : 0.9$, 반복횟수 60회 최솟값을 향해 잘 굴러옴을 알 수 있다. 확대해서 보면 아래와 같다. 이번엔 속도의 크기를 낮춰보자. $\eta : 0.1, \alpha : 0.8$, 반복횟수 60회 속도크기가 0.9일때와 비교해보면, 진행방향이 아래-위가 바뀔때의 위치가 작아졌음을 볼 수 있다. 즉, 이 $\alpha$값이 작을수록 조금씩만 진행된다고 할 수 있다. 이번에는 크기를 키워보자. $\eta : 0.1, \alpha : 1$, 반복횟수 60회 1로 설정하니 학습이 거의 이루어지지 않아 보인다. 실제로 반복횟수를 훨씬 더 큰 값으로 하면 아래와 같이 확연히 알 수 있다. 그렇다면 1보다 커지면 어떻게 되는지 알아보자. $\eta : 0.1, \alpha : 1.1$ 위에서

Backpropagation

이미지
오차역전파법. 풀어쓰자면 Backward Propagation Of Errors 라고 할 수 있다. 즉, 오차를 역으로(반대방향으로) 전파하는 방법이다. 오차역전파법은 계산 그래프로 이해해 볼 수 있다. 계산 그래프 계산 과정을 그래프로 나타낸 것. 이곳 에서 볼 수 있듯이 계산 그래프의 흐름은 일반적으로 아래와 같다. 간단하게 나타내면 연산식($+,*$ 같은 간단한 연산부터 $\sqrt{x+\epsilon},**2$ 같은 연산까지)이 각각의 Node가 되고, Edge는 변수혹은 노드들의 국소적 계산 결과가 된다. 이렇게 국소적 계산을 하는 계산 그래프를 사용하면 역전파를 통해 미분을 효율적으로 계산할 수 있다. 위와 같이 연산식에 따른 역전파는 상류의 역전파값에 국소적 미분값을 곱해서 하류로 흘려보낸다. 이렇게 국소적 미분값을 곱해서 흘림으로써, 위에 간단하게 나타낸 그림에서 마지막 $x$로의 역전파 값을 보면 $\partial{z}$와 $\partial{t}$가 사라져 $$\frac{\partial z}{\partial x} \text{ : x에 대한 z의 미분} $$ 을 구할 수 있다. 같은 방식으로 $t$에 대한$z$의 미분값도 구할 수 있다. 즉, "중간 결과값들에 대한 최종결과값의 미분"을 구할 수 있다는 것이고, 이를 신경망에 적용해 생각해보면 각 층마다의 가중치와 편향값에 대한 손실 함수의 미분값이 구해진다는 것이다. 어파인 = Affine 계층 신경망의 순전파 때 수행하는 행렬의 내적을 어파인 변환 = Affine Transformation 이라고 한다. 어파인 계층은 이 어파인 변환을 수행하는 처리를 말한다. 순전파 : 입력x와 가중치 W의 내적 후 편향을 더해준다.(numpy 브로드캐스트) 역전파 : 가중치의 미분값에는 전치행렬을 사용하고, 편향의 미분값은 상류의 미분흐름의 총합으로 구해준다. 입력 데이터가 텐서(4차원 데이터)인 경우도 고려해서 파이썬으로 구현하면 다음과 같다. cl

Numerical Differentiation

$f(x) = x^2$ 을 미분해서 $f'(x) = 2x$로 구하는 것을 '해석적 해' 혹은 '해석적으로 미분한다'라고 함. 이와 달리 수치 미분은 실제 미분 공식($\Delta$ 사용해서 표현하는 것)에 맞게 '근사치'로 함수의 미분값을 구하는 것임. 수치미분에서는 오차를 최소화 하기 위해 미분 공식처럼 x+h와 x사이의 함수 f의 차분을 계산하지 않고, x-h와 x+h사이의 차분을 계산함. 파이썬으로 구현해 보면, def numerical_diff(f, x): h = 1e-4 # 0.0001 return (f(x+h) - f(x-h)) / (2*h) 위와 같음. 이를 경사하강법 에 응용하기 위해 전미분이 되는(넘파이 배열 x에 대해 각 x에 대한 기울기를 구하는) 함수를 구현하면, import numpy as np def numerical_gradient(f, x): h = 1e-4 # 0.0001 grad = np.zeros_like(x) it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite']) while not it.finished: idx = it.multi_index tmp_val = x[idx] x[idx] = float(tmp_val) + h fxh1 = f(x) # f(x+h) x[idx] = tmp_val - h fxh2 = f(x) # f(x-h) grad[idx] = (fxh1 - fxh2) / (2*h) x[idx] = tmp_val # 값 복원 it.iternext() return grad 위와 같이 구현 가능함.

SGD = Stochastic Gradient Descent

이미지
신경망 학습에서 '확률적으로 무작위로 골라낸 데이터'에 대해 수행하는 경사 하강법. 신경망 학습에서는 데이터를 미니배치로 무작위로 선정해서 학습하므로, SGD라 지칭함. 경사법 = Gradient Method 현 위치에서 기울어진 방향으로 일정 거리만큼 이동함. 그 뒤 이동한 부분에서 다시 기울기를 구하고, 또 그 기울어진 방향으로 이동을 반복함. 이런 방법으로 함수의 값을 줄여나가거나(경사 하강법) 늘려나가며(경사 상승법) 기울기가 0인 지점을 찾음. 이 방법의 경우, 복잡한 함수일 때 고원 = plateau 이라는 평평한 곳으로 파고들며 학습이 진행되지 않는 정체기에 빠질 수 있음. 또한 기울기가 0인 곳은 최솟값이나 최댓값 뿐만 아니라 극솟값/극댓값/안장점 이 될 수도 있음. 경사 하강법 = Gradient Descent Method 경사법 중 최솟값을 찾는 방법. 일반적으로 신경망 분야에서는 경사법이 경사 하강법으로 등장함(손실 함수의 최솟값을 찾아야 하므로) 매개변수의 갱신과정은 아래와 같음.($h$,$b$ : 가중치, 편향과 같은 매개변수, $\eta$ : 학습률 = learning rate, $f$ : 손실 함수) $$ w = w - \eta\frac{\partial f}{\partial w} \\ b = h - \eta\frac{\partial f}{\partial b} $$ 여기서 학습률이란 갱신하는 양으로, 한 번의 학습으로 얼마만큼 학습해야 하는지를 정함. 일반적으로 0.01, 0.001과 같이 미리 특정 값으로 정함. 이 학습률이 너무 크거나 작으면 목표하고자 하는 곳에 도달할 수 없음. 파이썬 구현 수치 미분 구현(numerical_gradient 함수)은 이곳 참조. def gradient_descent(f, init_x, lr=0.01, step_num=100): x = init_x x_history = [] # 학습에 따른 변화 정도를 보기위해 값 저장 f

Loss Function

손실 함수 = Loss Function 신경망 성능의 '나쁨'을 나타내는 지표. 즉, 현재의 신경망이 훈련 데이터를 얼마나 잘 처리하지 못하는지를 나타냄. 이러한 '나쁨'을 최소로 하는 것은 사실상 '좋음'을 최대로 하는 것과 같으므로, 신경망 학습에서는 손실 함수값이 작아지는 방향으로 학습하게 됨. 신경망 학습에서는 최적의 매개변수(가중치, 편향)을 탐색할 때 손실함수의 값을 가능한 한 작게 하는 매개변수 값을 찾음. 찾을 때는 매개변수의 미분(=기울기)을 계산하고, 이 계산값을 단서로 새로 매개변수 값을 갱신해나감. 자세히 말하면 '가중치 매개변수에 대한 손실 함수의 미분=가중치 매개변수의 값을 아주 조금 변화시켰을 때, 손실 함수의 변하는 정도'임. 따라서 이 값이 음수 : 가중치 매개변수를 양의 방향으로 변화(=값 증가)시켜 손실 함수의 값을 줄임. 양수 : 가중치 매개변수를 음의 방향으로 변화(=값 감소)시켜 손실 함수의 값을 줄임. 굳이 손실 함수를 쓰는 이유는, 정확도를 지표로 하면 매개변수의 미분이 대부분의 장소에서 0이 되기 때문임. 정확도라는 것은 전체 중 얼마나 정확히 인식했냐이므로, 예를 들어 100장이 전체일 때는 32%, 33%와 같은 값만 같게 됨. 따라서 32%상황에서 매개변수를 조금 변화시켜도 32.0234%같이 안 되고 여전히 32%이거나(이 경우 미분이 0), 33%같이 큰 값으로(=불연속적으로) 값이 변화하므로 최적의 매개변수 값을 찾기에 적합하지 않음. 딥러닝에 자주쓰이는 손실함수에는 다음과 같은 함수들이 있음. 평균 제곱 오차 = MSE(Mean Squared Error) $$ E = \frac{1}{n}\sum_{k=1}^n (y_k-t_k)^2 \qquad y_k\text{ : 신경망의 출력, } t_k\text{ : 정답 레이블, } n\text{ : 데이터의 차원 수} $$ 특징 수치예측에 많이 사용됨. 파이썬 구현

Neural Network

이미지
신경망 퍼셉트론은 가중치, 편향등의 매개변수 설정 작업을 사람이 수동으로 했음. 이를 개선하기위해, 매개변수의 적절한 값을 데이터로부터 자동으로 학습 하도록 신경망이 나옴. 구성은 보통 입력층 / 출력층 / 은닉층 으로 이루어짐. 활성화 함수 영어로는 Activation Function. 입력 신호의 총합을 출력 신호로 변환하는 함수를 지칭함. 활성화 함수를 통해 입력 신호의 총합이 활성화를 일으키는지 정함. 신경망에서는 활성화 함수로 비선형 함수를 사용해야 함. 만약 선형 함수를 사용한다면, 층을 깊게해도 의미가 없기때문. 예를 들어, $h(x) = cx$ 라는 활성화 함수가 있으면 이를 3층으로 겹쳐도 $h(x) = c*c*cx$가 되고 사실상 $h'(x) = c'x$와 같은 식이되어 의미가 없어짐. 따라서, 아래의 종류와 같은 활성화 함수를 사용함. 계단 함수 $$ h(x) = \begin{cases}0 & (x \leq 0) \\ 1 & (x > 0) \end{cases} $$ 파이썬으로 구현 def step_function(x): return np.array(x > 0, dtype=np.int) X = np.arange(-5.0, 5.0, 0.1) Y = step_function(X) plt.plot(X, Y) plt.ylim(-0.1, 1.1) plt.show() 결과 시그모이드 함수 $$ h(x) = \frac{1}{1+\exp(-x)} $$ 파이썬으로 구현 def sigmoid(x): return 1 / (1 + np.exp(-x)) X = np.arange(-5.0, 5.0, 0.1) Y = sigmoid(X) plt.plot(X, Y) plt.ylim(-0.1, 1.1) plt.show() 결과 ReLU 함수 Recified Linear Unit 의 약자임. $$ h(x) = \begin{cases}0 &

코드안에 html 코드 넣기

http://accessify.com/tools-and-wizards/developer-tools/quick-escape/default.php

Perceptron

퍼셉트론? 다수의 신호를 입력으로 받아 하나의 신호를 출력. 신호 => 강물, 전류, 바람 같이 "흐름"이 있는 것으로 생각! 가중치=weight(w), 편향=bias(b) 를 매개변수로 설정해 나타냄. 간단한 게이트들 : AND, NAND, OR, NOR def AND(x1, x2): x = np.array([x1,x2]) w = np.array([0.5, 0.5]) b = -0.7 tmp = np.sum(w*x) + b if tmp <= 0: return 0 else: return 1 def NAND(x1, x2): x = np.array([x1,x2]) w = np.array([-0.5, -0.5]) b = 0.7 tmp = np.sum(w*x) + b if tmp <= 0: return 0 else: return 1 def OR(x1, x2): x = np.array([x1,x2]) w = np.array([0.5, 0.5]) b = -0.2 tmp = np.sum(w*x) + b if tmp <= 0: return 0 else: return 1 def NOR(x1, x2): x = np.array([x1,x2]) w = np.array([-0.5, -0.5]) b = 0.2 tmp = np.sum(w*x) + b if tmp <= 0: return 0 else: return 1 퍼셉트론의 한계 퍼셉트론은 직선 하나로 나눈 영역만 표현할 수 있음.(선형 영역 가능, 비선형 영역 불가) ex) 단층으로는 XOR 게이트 표현 불가 따라서 "층을 쌓아" 다층 퍼셉트론을

Matplotlib Usage

이미지
기본적인 import및 sin 그래프 그려보기 import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 6, 0.1) y = np.sin(x) plt.plot(x,y) plt.show() 결과 : x축 범위가 0~6으로 설정된다.

Numpy Usage

배열 만들기 import numpy as np A = np.array([[1,2], [3,4]]) print(A) [[1 2] [3 4]] 브로드 캐스트 : 스칼라값이 행렬에 확산되어 연산되는 기능. B = np.array([10,20]) A*B array([[10, 40], [30, 80]]) 평탄화 : 1차원 배열로 변환 X = np.array([[51,55], [14,19], [0,4]]) print(X) [[51 55] [14 19] [ 0 4]] X = X.flatten() print(X) [51 55 14 19 0 4] bool 배열 사용해 특정 조건을 만족하는 원소 얻기 X > 15 array([ True, True, False, True, False, False], dtype=bool) X[X>15] array([51, 55, 19])

Useful Python Tips

# Show number as bit def binary(n): return "{:08b}".format(n) print(binary(7)) #00000111 print(binary(7<<7)) #1110000000