반응형


안녕하세요~



오늘은 신경망의 정확도를 높이기위한 여러가지 방법을 소개하겠습니다.


Ⅰ. 매개변수 갱신


Ⅱ. 가중치 초기값 설정


Ⅲ. 배치 정규화


Ⅳ. Dropout / weight decay


Ⅴ. 적절한 하이퍼 파라미터 값 찾기



이제 각각의 방법에 대해 자세히 다뤄보도록 하겠습니다.



Ⅰ. 매개변수 갱신 ( 고급 경사하강법 )


underfitting을 해결하기위한 방법 중 하나입니다.



GD(GradientDescent)에는 큰 단점 2가지가 있습니다.


1) 계산량이 많아서 속도가 느리다.


2) local minima에 빠질 수 있다.



GD는 가장 기본적인 뉴럴넷 학습 방법으로


전체 데이터에 대한 비용함수의 weight 과 미분하고


각 W(가중치) 파라미터에 대한 기울기를 이용하여


W를 업데이트하는 방법입니다.



그런데 이러한 GD는 층이 많아질수록 parameter의 복잡도가 늘어남에 따라


비용함수가 더욱 복잡해져서


local minima에 빠지는 현상이 더욱 잘 발생하게 됩니다.



이러한 문제를 해결하기 위해 나온 것이


SGD(Stochastic Gradient Descent) 확률적 경사하강법입니다.



1. SGD


전체 data 중 랜덤하게 추출한 1/N의 데이터만을 사용해서 빠르게 가중치를 업데이트 하는 방법입니다.


[SGD 식]



GD의 단점을 해결한 SGD에도 문제가 있는데,

단순하고 구현도 쉽지만 문제에 따라 비효율 적일 때가 있다는 것입니다.


또한 아래와 같은 그래프의 경우 global minima 쪽으로 기울기 방향이 있는게 아니기 때문에

local minima에 빠지고 global minima 쪽으로 도달하지 못하는 단점이 있습니다.




2. Momentum


Momentum은 운동량을 뜻하는 단어로 기울어진 방향으로 물체가 가속되는 물리 법칙을 나타냅니다.


[Momentum 식]




(α: 마찰계수, v: 속도, η : 학습률, W: 가중치, ∂L / ∂W : W에 대한 손실함수 미분)



원리:


내리막: 기울기가 음수이면 속도가 증가


오르막: 기울기 양수이면 속도 감소



3. AdaGrad


learning rate 가 학습되면서 자체적으로 조정되는 경사하강법


보통 처음에는 크게 학습하다가 조금씩 작게 학습을 진행하는 방법입니다.


[AdaGrad 식]



(h: 기울기에 값을 제곱하여 더해줌, ◎: 행렬의 원소별 곱셈)



원리:


learning rate가 클 경우: 발산될 위험은 있지만 수렴의 속도가 빨라진다.


learning rate가 작을 경우 : 발산될 위험은 작지만 local minima에 빠지거나 학습이 안될 수 있다.



각각의 매개변수 원소가 갱신되는 값이 다르다는 것이 특징입니다.



4. Adam


Momentum의 장점 (가속도) + RMSProp의 장점 (각 매개변수마다 학습률 조절) == Adam


최근 딥러닝에서 가장 많이 사용하는 최적화 방법이라고 합니다! : )


* 참고: RMSProp는 가장 최근 반복에서 비롯된 그래디언트만 누적함으로

AdaGrad의 단점(local minima에 수렴함)을 보안한 옵티마이저 입니다.


[각각의 optimizer를 파이썬으로 구현하는 코드]


# gradient descent 종류 class SGD: def __init__(self, lr=0.01): self.lr = lr def update(self, params, grads): for key in params.keys(): params[key] -= self.lr * grads[key] class Momentum: def __init__(self, lr=0.01, momentum=0.9): self.lr = lr self.momentum = momentum self.v = None def update(self, params, grads): if self.v is None: self.v = {} for key, val in params.items(): self.v[key] = np.zeros_like(val) for key in params.keys(): self.v[key] = self.momentum * self.v[key] - self.lr * grads[key] params[key] += self.v[key] class AdaGrad: def __init__(self, lr=0.01): self.lr = lr self.h = None def update(self, parmas, grads): if self.h is None: self.h = {} for key, val in params.items(): self.h[key] = np.zeros_like(val) for key in parmas.keys(): self.h[key] += grads[key] * grads[key] params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7) class Adam: def __init__(self, lr=0.001, beta1=0.9, beta2=0.999): self.lr = lr self.beta1 = beta1 self.beta2 = beta2 self.iter = 0 self.m = None self.v = None def update(self, params, grads): if self.m is None: self.m, self.v = {}, {} for key, val in params.items(): self.m[key] = np.zeros_like(val) self.v[key] = np.zeros_like(val) self.iter += 1 lr_t = self.lr * np.sqrt(1.0 - self.beta2 ** self.iter) / (1.0 - self.beta1 ** self.iter) for key in params.keys(): self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key]) self.v[key] += (1 - self.beta2) * (grads[key] ** 2 - self.v[key]) params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)



[각 optimizer 가 최소값에 수렴하는 방법]





Ⅱ. 가중치 초기값 설정


underfitting을 해결하기위한 방법 중 하나입니다.


가중치 초기값을 적절히 설정하면 각 층의 활성화 값의 분포가 적당히 퍼지는 효과가 발생합니다.


활성화 값이 적당히 퍼지게 되면 학습이 잘되고 정확도가 높아집니다.



1. 초기값을 0으로 설정


초깃값을 모두 0으로 설정하면

오차역전파법에서 모든 가중치의 값이 똑같이 갱신되기 때문에

학습이 올바르게 이뤄지지 않습니다.


2. 표준편차가 1인 정규분포를 사용해 초기값 설정


- 표준편차가 작을수록 데이터가 평균에 가깝게 분포


- 표준편차가 클 수록 데이터가 많이 흩어져 있다.


즉 데이터가 0과 1에 치우쳐 분포하게 되면 역전파의 기울기 값이 점점 작아지다 사라집니다.


층을 깊게 하는 딥러닝에서는 기울기 소실은 더 심각한 문제가 될 수 있습니다.


3. Xavier 초기값 설정


- 표준편차가 √1/n 인 정규분포로 초기화한다.


- sigmoid 와 짝꿍


각 층의 활성화 값들을 광범위하게 분포 시킬 목적으로 적절한 분포를 찾고자 했습니다.


그리고 앞 계층의 노드가 n개라면 표준편차가 √1/n 인 분포를 사용하면 된다는 결론을 이끌었습니다.



4. He 초깃값


- 표준편차가 √2/n 인 정규분포로 초기화한다.


- ReLU 와 짝꿍


ReLU는 음의 영역이 0이라서 더 넓게 분포시키기 위해


Xavier함수보다 2배의 계수가 필요하다고 해석할 수 있습니다.



[초기화 그래프]





Ⅲ. 배치 정규화


마찬가지로 underfitting을 해결하기 위한 방법 중 하나 입니다.


앞에서 가중치 초기값을 적절히 설정하면 각 층의 활성화 값의 분포가 적당히 퍼지는 효과를 보았습니다.


그런데,

배치 정규화는 바로 각 층에서의 활성화 값이 적당히 분포되도록 강제로 조정하는 것을 말합니다.



딥러닝의 경우는 hidden layer가 많아서


가중치의 조금한 변화가 가중되어서 쌓이면


hidden layer가 많아질수록 출력되는 값의 변화가 크기 때문에


학습이 어렵습니다.



배치 정규화는 값이 활성화 함수를 통과하기 전에 가중의 변화를 줄이는 것이 목표


가중의 합이 배치 정규화에 들어오게 되면 기존의 값의 스케일을 줄여버리게 됩니다.



[ 배치초기화 식]



[파이썬으로 구현하기]


class BatchNormalization: """ http://arxiv.org/abs/1502.03167 """ def __init__(self, gamma, beta, momentum=0.9, running_mean=None, running_var=None): self.gamma = gamma self.beta = beta self.momentum = momentum self.input_shape = None # 합성곱 계층은 4차원, 완전연결 계층은 2차원 # 시험할 때 사용할 평균과 분산 self.running_mean = running_mean self.running_var = running_var # backward 시에 사용할 중간 데이터 self.batch_size = None self.xc = None self.std = None self.dgamma = None self.dbeta = None def forward(self, x, train_flg=True): self.input_shape = x.shape if x.ndim != 2: N, C, H, W = x.shape x = x.reshape(N, -1) out = self.__forward(x, train_flg) return out.reshape(*self.input_shape) def __forward(self, x, train_flg): if self.running_mean is None: N, D = x.shape self.running_mean = np.zeros(D) self.running_var = np.zeros(D) if train_flg: mu = x.mean(axis=0) xc = x - mu var = np.mean(xc**2, axis=0) std = np.sqrt(var + 10e-7) xn = xc / std self.batch_size = x.shape[0] self.xc = xc self.xn = xn self.std = std self.running_mean = self.momentum * self.running_mean + (1-self.momentum) * mu self.running_var = self.momentum * self.running_var + (1-self.momentum) * var else: xc = x - self.running_mean xn = xc / ((np.sqrt(self.running_var + 10e-7))) out = self.gamma * xn + self.beta return out def backward(self, dout): if dout.ndim != 2: N, C, H, W = dout.shape dout = dout.reshape(N, -1) dx = self.__backward(dout) dx = dx.reshape(*self.input_shape)

return dx def __backward(self, dout): dbeta = dout.sum(axis=0) dgamma = np.sum(self.xn * dout, axis=0) dxn = self.gamma * dout dxc = dxn / self.std dstd = -np.sum((dxn * self.xc) / (self.std * self.std), axis=0) dvar = 0.5 * dstd / self.std dxc += (2.0 / self.batch_size) * self.xc * dvar dmu = np.sum(dxc, axis=0) dx = dxc - dmu / self.batch_size self.dgamma = dgamma self.dbeta = dbeta return dx



[구현 예]

# 계층생성 layers = OrderedDict() # 순전파 순서의 반대로 역전파가 된다. layers['Affine1'] = Affine(params['W1'], params['b1']) layers['BatchNorm1'] = BatchNormalization(gamma=1.0, beta=0.) layers['Relu1'] = Relu() layers['Affine2'] = Affine(params['W2'], params['b2']) layers['BatchNorm2'] = BatchNormalization(gamma=1.0, beta=0.) layers['Relu2'] = Relu() layers['Affine3'] = Affine(params['W3'], params['b3']) lastLayer = SoftmaxWithLoss()




Ⅳ. Dropout / weight decay


두 방법 모두 overfitting을 규제하기 위한 방법입니다.



1. Dropout(드롭아웃)


오버피팅을 억제하기 위해서 뉴런을 임의로 삭제하면서 학습시키는 방법


사공이 많으면 배가 산으로 간다는 속담이 있습니다.


따라서 몇 명의 전문가만 선별해서 반복적으로 같은 결과가 나오면 그것을 답으로 보는 방법입니다.



일반적으로 입력층에서는 20% ~ 50% 정도,


은닉층에서는 50% 정도의 노드를 생략한다고 합니다.



[파이썬 코드]

class Dropout: """ http://arxiv.org/abs/1207.0580 """ def __init__(self, dropout_ratio=0.15): self.dropout_ratio = dropout_ratio self.mask = None def forward(self, x, train_flg=True): if train_flg: self.mask = np.random.rand(*x.shape) > self.dropout_ratio return x * self.mask else: return x * (1.0 - self.dropout_ratio) def backward(self, dout): return dout * self.mask


[구현 예]

# 계층생성 layers = OrderedDict() # 순전파 순서의 반대로 역전파가 된다. layers['Affine1'] = Affine(params['W1'], params['b1']) layers['BatchNorm1'] = BatchNormalization(gamma=1.0, beta=0.) layers['Relu1'] = Relu() layers['Dropout1'] = Dropout() layers['Affine2'] = Affine(params['W2'], params['b2']) layers['BatchNorm2'] = BatchNormalization(gamma=1.0, beta=0.) layers['Relu2'] = Relu() layers['Dropout2'] = Dropout() layers['Affine3'] = Affine(params['W3'], params['b3']) lastLayer = SoftmaxWithLoss()




2. Weight Decay(가중치 감소)


학습과정에서 큰 가중치에 대해서는 그에 상응하는 큰 패널티를 부여하여 오버피팅을 억제하는 방법


가중치 감소는 모든 가중치 각각의 손실함수에 (1/2) * λW **2를 더합니다.



[구현 예]

weight_decay_lambda = 0.001


# 1. 비용함수 수정

weight_decay = 0 for idx in range(1, 4): # 3계층 하드코딩 W = self.params['W' + str(idx)] weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W ** 2)


# 2. 기울기 계산하는 코드 변경

for idx in range(1, 4): grads['W' + str(idx)] = self.layers['Affine' + str(idx)].dW + \ self.weight_decay_lambda * self.layers['Affine' + str(idx)].W grads['b' + str(idx)] = self.layers['Affine' + str(idx)].db



Ⅴ. 적절한 하이퍼파라미터 값 찾기


신경망에서 말하는 하이퍼파라미터는 각 층의 뉴런 수, 배치 크기, 매개변수 생신 시의 학습률과 가중치 감소 등입니다.



1. 검증데이터


우선 하이퍼파라미터를 조정할 때는 검증데이터로 하이퍼파라미터의 적절성을 평가해야합니다.


test_data에서 하이퍼파라미터를 조정하면 값이 test_data에 오버피팅 되기 때문에

범용 성능이 떨어지는 모델이 될 수도 있기 때문입니다.



즉, 아래와 같이 데이터를 나눠서 사용할 수 있습니다.


- 훈련데이터: 매개변수 학습


- 검증데이터: 하이퍼파라미터 성능 평가


- test 데이터: 신경망의 범용 성능 평가



※ Tip! 만약 적은 양의 데이터라면 교차검증(cross validation)방법을 사용할 수 있습니다.



2. 하이퍼 파라미터 최적화


하이퍼파라미터를 최적화할 때의 핵심은


무작위로 샘플링 후 정확도를 살피면서 하이퍼파라미터의 '최적 값'이 존재하는 범위를 조금씩 줄여간다는 것입니다.


0단계: 하이퍼파라미터 값의 범위를 설정합니다.


1단계: 설정된 범위에서 하이퍼파라미터의 값을 무작위로 추출합니다.


2단계: 1단계에서 샘플링한 하이퍼 파라미터 값을 사용하여 학습하고, 검증 데이터로 정확도를 평가합니다.


3단계: 1단계와 2단계를 반복하며, 그 정확도의 결과를 보고 하이퍼파라미터의 범위를 좁힙니다.



※ Tip!


위에서 설명한 하이퍼파라미터 최적화 방법은

과학이라기보다는 직관에 의존한다는 느낌이 들지만 많이 사용되는 실용적인 방법입니다.


더 세련된 기법을 원한다면 베이즈 최적화(수학이론을 구사)를 사용할 수 있습니다 : )




오늘은 여기까지!!


유난히 긴 포스팅이었네요!


※ '밑바닥부터 시작하는 딥러닝' 을 참고하였습니다


도움이 되었다면 공감♡ 꾸욱 

반응형
반응형


" You are going be fine! "


이전 포스팅에서 만든 신경망을 "더 좋은 신경망으로 만드는 방법"에 대해 알아보도록 하겠습니다.




오차 역전파란?


신경망 학습 처리에서 오차를 최소화하는 함수의 경사를


효율적으로 계산하기 위한 방법입니다.



< 비용함수(cost function)의 경사(기울기)를 계산하는 방법>



1. 수치미분


: 구현하기 쉽고 정확성이 높으나, 매우 느리다.



2. 오차 역전파


: 출력층으로부터 차례대로 역방향으로 각 층에 있는 노드의 오차를 계산하여,

그 오차로 함수의 경사를 계산합니다.


즉, 전파된 오차를 이용하여 가중치를 조정하는 것이 '오차역전파' 입니다.



< 계산 그래프 >


계산 그래프란, 순전파와 역전파에 계산 과정을 그래프로 나타내는 방법입니다.



왜 계산 그래프로 문제를 해결할까요?


1. 전체가 아무리 복잡해도 각 노드에서 단순한 계산에 집중하여

문제를 단순화 할 수 있습니다. ( 국소적 계산 )



2. 역전파를 통해서 미분을 효율적으로 계산할 수 있습니다.




< 합성함수 미분 구하기 >



우선 기본적인 미분 공식은 다음과 같습니다.



원리: 접선은 할선의 극한입니다.



하지만 위와 같은 진정한 미분은 h 가 0이 될 경우, 컴퓨터로 구현하기 어렵기 때문에


근사한 접선인 할선을 구하는 "수치미분"을 사용해야합니다.


따라서 수치미분에는 약간의 오차가 포함됩니다.



[ 파이썬으로 수치미분 구현하기 ]

def numerical_diff(f,x): h = 1e-4 # 0.0001 # 컴퓨터로 극한 값을 구하기 어려우므로 도함수를 구현한다. # return (f(x+h) - f(x-h)) / ((x+h) - (x-h)) return (f(x+h) - f(x-h)) / 2*h



< 편미분 >


변수가 2개 이상인 함수를 미분할 때 미분 대상 변수 외에 나머지 변수를

상수처럼 고정시켜 미분하는 것을 편미분이라고 합니다.



[ 파이썬으로 편미분 구현하기 ]


def numerical_gradient(f,x): h = 1e-4 grad = np.zeros_like(x) for idx in range(x.size): tmp_val = x[idx] # f(x+h) 계산 x[idx] = tmp_val + h fxh1 = f(x) # f(x-h) 계산 x[idx] = tmp_val - h fxh2 = f(x) grad[idx] = (fxh1 - fxh2) / 2*h x[idx] = tmp_val # 값 복원 return grad



< 신경망 학습을 시키기 위한 방법 >



(W: Weight, 기울기: 오차함수 미분한 값)




< 합성함수 미분 >



추가설명:

f(x) = ax + b


f(x)를 X로 보고 미분하고 (ax + b)를 속 미분하여 더한다.


혹시 증명을 알고 싶으신 분들은 구글에..ㅎㅎ



< 연쇄법칙 >


합성 함수의 미분은 합성 함수를 구성하는 각 함수의 미분의 곱으로 나타낼 수 있습니다.


이것이 연쇄 법칙의 원리 입니다.




< 역전파 >


신경망의 역전파에서의 중요한 성질은

출력과 정답 레이블의 차이인 오차가 앞 계층에 전해지는 것입니다.


1. Affine / Relu 계층


Affine 계층이란,


신경망의 순전파 때 수행하는 행렬의 내적은 기하학에서는 어파인 변환이라고 합니다.


그래서 어파인 변환을 수행하는 처리를 'Affine 계층'이라는 이름으로 구현하도록 하겠습니다.



2. Softmax-with-Loss 계층


마지막으로 출력층에서 사용하는 함수로


신경망의 학습의 목적인 신경망의 출력(Softmax의 출력)이

정답 레이블과 가까워지도록 가중치 매개변수의 값을 조정하는 역할을 합니다.




< MNIST 신경망 구현하기 >


드디어 역전파를 Python으로 구현해보도록 하겠습니다.



[ 2층 신경망 구현하기 ]


import numpy as np from common.layers import * from common.gradient import numerical_gradient # 수치미분 함수 from collections import OrderedDict class TwoLayerNet: 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['b2'] = np.zeros(output_size) # 계층생성 self.layers = OrderedDict() # 순전파 순서의 반대로 역전파가 된다. self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1']) self.layers['Relu1'] = Relu() self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2']) self.lastLayer = SoftmaxWithLoss() # 예측 수행 def predict(self, x): for layer in self.layers.values(): x = layer.forward(x) return x # x: 입력데이터, t: 레이블 def loss(self, x, t): y = self.predict(x) return self.lastLayer.forward(y,t) def accuracy(self, x, t): y = self.predict(x) y = np.argmax(y, axis=1) if t.ndim != 1: t= np.argmax(t, axis=1) accuracy = np.sum(y == t) / float(x.shape[0]) return accuracy # x: 입력데이터, t: 레이블 def numerical_gradient(self, x, t): loss_W = lambda W: self.loss(x,t) grads = {} grads['W1'] = numerical_gradient(loss_W, self.params['W1']) grads['b1'] = numerical_gradient(loss_W, self.params['b1']) grads['W2'] = numerical_gradient(loss_W, self.params['W2']) grads['b2'] = numerical_gradient(loss_W, self.params['b2']) return grads # 오차 역전파 def gradient(self, x, t): # 순전파 self.loss(x,t) # 역전파 dout = 1 dout = self.lastLayer.backward(dout) layers = list(self.layers.values()) layers.reverse() for layer in layers: dout = layer.backward(dout) # 결과 저장 grads = {} grads['W1'] = self.layers['Affine1'].dW grads['b1'] = self.layers['Affine1'].db grads['W2'] = self.layers['Affine2'].dW grads['b2'] = self.layers['Affine2'].db return grads


※ 참고


수치미분과 오차역전파법의 결과를 비교하면 오차역전파법의 구현에 잘못이 없는지 확인할 수 있습니다.



[ MNIST 데이터 학습 구현하기 ]


import numpy as np from dataset.mnist import load_mnist # 데이터 읽기 (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True) network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10) iters_num = 10000 train_size = x_train.shape[0] batch_size = 100 learning_rate = 0.1 train_loss_list = [] train_acc_list = [] test_acc_list = [] iter_per_epoch = max(train_size / batch_size, 1) for i in range(iters_num): batch_mask = np.random.choice(train_size, batch_size) x_batch = x_train[batch_mask] t_batch = t_train[batch_mask] # 오차역전파법으로 기울기를 구한다. grad = network.gradient(x_batch, t_batch) # 갱신 for key in ('W1', 'b1','W2','b2'): network.params[key] -= learning_rate * grad[key] loss = network.loss(x_batch, t_batch) train_loss_list.append(loss) if i % iter_per_epoch == 0: train_acc = network.accuracy(x_train, t_train) test_acc = network.accuracy(x_test, t_test) train_acc_list.append(train_acc) test_acc_list.append(test_acc) print(train_acc, test_acc)


※ 추가


문제. 위의 2차 신경망으로 3차 신경망을 구하고 정확도를 확인하시오



코드 출처: Deep Learning from Scratch

데이터: 한빛 미디어 깃 허브 저장소




오늘은 여기까지!


다음 포스팅은 학습 관련 기술들에 대해 더 자세히 알아보도록 합시다!!

반응형
반응형


안녕하세요!


오늘은 손실함수에 대해 알아보도록 하겠습니다!!



손실함수란?


신경망 성능의 '나쁨'을 나타내는 지표로,

현재의 신경망이 훈련데이터를 얼마나 잘 처리하지 '못'하느냐 나타냅니다.



굳이 손실함수를 사용하는 이유?


우리의 궁극적인 목표는 높은 '정확도'를 끌어내는 매개변수 값을 찾는 것입니다.



그렇다면 '정확도'라는 지표를 놔두고

'손실함수의 값' 이라는 우회적인 방법을 택하는 이유는 무엇일까요?



신경망 학습에서는

최적의 매개변수(가중치와 편향)을 탐색할 때

손실함수의 값을 가능한 작게하는 매개변수 값을 찾습니다.



손실함수에서 미분이란

'가중치 매개변수의 값을 아주 조금 변화시켰을 때,

손실 함수가 어떻게 변하나' 라는 의미입니다.



'정확도'를 지표로 삼으면 안되는 이유는

미분값이 대부분의 장소에서 0이 되어 매개변수를 갱신할 수 없기때문입니다.




평균 제곱 오차(Mean Squared Error, MSE)


가장 많이 쓰이는 손실함수는 '평균 제곱 오차' 입니다.



(y: 신경망 출력, t: 정답레이블, k: 데이터의 차원 수)


평균 제곱 오차 함수는 회귀에서 많이 쓰이는 손실함수입니다.



파이썬으로는 아래와 같이 구현합니다.


def MSE(y,t):
    return (1/2)* np.sum((y-t)**2)



교차 엔트로피 오차(Cross Entropy Error, CEE)


또 다른 손실함수로서 교차엔트로피 오차도 자주 사용합니다.



(여기서 log는 밑이 e 인 자연로그 입니다.

y: 신경망 출력, t: 정답레이블, k: 데이터의 차원 수)


교차 엔트로피 오차 함수는 분류에서 많이 쓰이는 손실함수입니다.



파이썬으로는 아래와 같이 구현합니다.


def cross_entrpy_error(y,t): delta = 1e-7 return -np.sum(t*np.log(y+delta))



delta를 더해주는 이유?


밑수가 자연상수이고 진수가 0인 로그의 값은 -inf가 출력되어

수치연산이 불가능해집니다.


따라서 연산이 가능하도록 아주 작은 값인 delta를 더합니다.




미니 배치 학습


훈련 데이터 중 일부만 골라서 학습하는 방법으로


통계적인 관점으로 봤을 때, 표본을 뽑아서 학습을 시키는 학습방법입니다.



즉,

훈련데이터가 100개 있으면,

그로부터 계산한 100개의 손실함수들의 합을 지표로 삼는 것입니다.



[교차 엔트로피 오차_미니배치]



(N: 데이터 개수, t: n번째 데이터의 k번째 값)


좀 복잡해보이지만, N으로 나눔으로써 '평균 손실 함수'를 구하는 것입니다.



배치용 교차 엔트로피 오차함수를 파이썬으로 구현해보도록 하겠습니다.


def crossEntropyError(y, t): delta = 1e-7 return -np.sum(t*np.log(y+delta)) / y.shape[0] # y.shape[0] : batch_size




신경망 학습 알고리즘 구현하는 절차


전제

: 신경망에는 적응 가능한 가중치와 편향이 있고,

이 가중치와 편향을 훈련 데이터에 적응하도록 조정하는 과정을 '학습'이라고 합니다.



1단계

: 미니배치


선별된 데이터인 미니 배치의 손실함수 값을 줄이는 것이 목표합니다.



2단계

: 기울기 산출


미니배치의 손실 함수 값을 줄이기 위해 각 가중치 매개변수의 기울기를 구합니다.

기울기는 손실함수의 값을 가장 작게 하는 방향을 제시합니다.


추가:

경사하강법은 이전 포스팅에서 다뤘습니다!



3단계

: 매개변수 갱신


가중치 매개변수를 기울기 방향으로 갱신합니다.



4단계

: 1 - 3 단계를 반복합니다.




자! 오늘은 여기까지!


다음은 오차를 효율적으로 갱신하는 방법


오차 역전파에 대해 알아보도록 하겠습니다.



내용 출처: Deep Learning from Scratch | 사이토 고키

반응형
반응형



안녕하세요~ 오늘은 드디어! 신경망에 대해 살짝 맛보도록 하겠습니다!



우선, 퍼셉트론과 신경망의 차이부터 알아봅시다!


퍼셉트론을 모른다면 "퍼셉트론(Perceptron)이란?" 을 참고하세요!



1. 퍼셉트론은 원하는 결과를 출력하도록 사람이 직접 가중치를 정해줘야 합니다!


AND, OR, NAND 게이트를 만들때 가중치를 채워 넣는 문제를 냈습니다!


어떻게 찾으셨나요? 저는 직접 숫자를 대입해보며 찾았답니다!


 혹시 더 쉽게 찾는 방법을 아시나요? 아신다면 댓글로 공유해주세요!



2. 신경망은 가중치 매개변수의 적절한 값을 기계가 데이터로부터 학습해서 자동으로 알아냅니다!





" 신경망 학습하기 "




이전 포스팅에서 신경망에 들어가는 함수


계단함수, 시그모이드 함수, 렐루 함수에 대해 알아보았습니다!



좀더 자세히 말하자면 위의 3가지 함수는


은닉층에 들어가는 함수 중 3가지를 의미합니다.




그렇다면 마지막 출력층에 들어가는 함수는 어떻게 될까요?


출력층 함수란,


그동안 흘러온 확률의 숫자를 취합해 결론을 내는 함수입니다.



신경망으로 구현하고자 하는 문제에 따라 사용하는 출력층 함수가 다릅니다.



1. 회귀(Regression)의 경우, 항등함수를 사용합니다.


항등함수란, 어떤 변수도 자기 자신을 함수값으로 하는 함수입니다.


[파이썬코드]

def identity(x): return x

간단하죠?




2. 분류(Classfication) 의 경우, 두 가지로 나뉘는데요.


2-1) 시그모이드(Sigmoid) 함수 : 2클래스 분류에서 사용합니다. ( ex, 개 vs 고양이 분류)


2-2) 소프트맥스(Softmax) 함수 : 다중 클래스 분류 (ex, 정상 폐사진 vs 폐결절, 폐혈증... 등 분류)



시그모이드는 은닉층의 활성화 함수에서 알아봤으니,



새로운 함수인, 소프트맥스 함수에 대해 더 자세히 알아보겠습니다.


소프트 맥스(Softmax) 함수란?


0과 1사이의 숫자를 출력하는 함수로, 출력하는 값은 확률입니다.


[파이썬 코드]


def softmax(a): C = np.max(a) exp_a = np.exp(a-C) sum_a = np.sum(exp_a) return exp_a / sum_a


[소프트 맥스 함수식]


e%203.10.png



소프트 맥스 함수식과 위의 파이썬 코드가 조금 다르죠?


그 이유는, overflow때문입니다!



우선, 소프트 맥스 함수는 지수함수를 사용하는데 이 지수함수라는 것은 아주 쉽게 아주 큰 값을 내뱉습니다.



예를 들어


exp == e (무리수: 자연상수)


e ^10 > 20000: 자연상수의 10승은 20000보다 크고


e ^1000 == inf : 자연상수의 1000승은 무한대(inf)를 나타냅니다.



따라서 표현할 수 없는 큰 값을 해결하기위해 아래와 같은 식으로 변형을 해서 사용합니다.




지수함수와 로그함수를 잘 모르겠다면 "여기"를 참고하세요!






" 이제, 아래의 신경망을 직접 구현해보도록 하겠습니다!"





import numpy as np # 신경망 함수 def sigmoid(x): return 1 / (1 + np.exp(-x)) def identity(x): return x def init_network(): network = {} network['W1'] = np.array([[1,3,5],[2, 4, 6]]) # weight 은 관습적으로 W라고 합니다. network['W2'] = np.array([[1,2],[3,4],[5,6]]) network['W3'] = np.array([[1,2],[3,4]]) return network

# 신경망 구현 def forward(network, x): W1, W2, W3 = network['W1'], network['W2'], network['W3'] y = np.dot(x, W1) y_hat = sigmoid(y) k = np.dot(y_hat, W2) k_hat = sigmoid(k) j = np.dot(k_hat, W3) j_hat = identity(j) # identity 대신 softmax함수를 사용할 수 있습니다. return j_hat network = init_network() x = np.array([1,2]) # 입력 y = forward(network, x) # 출력 print(y)





오늘은 여기까지!


신경망 학습 방법은 다음 포스팅에서 더 자세히 다루겠습니다!


좋은 하루 보내세요! 

반응형

+ Recent posts