이전 챕터까지는 데이터의 매개변수, 즉 weight와 bias를 직접 제공하였다.
머신러닝에서는 data vector에서 사람이 feature를 추출하고(SIFT, HOG 등) feature들의 규칙을 학습으로 찾아낸다.
딥러닝의 경우 이미지의 feature 또한 스스로 학습한다.
이런 의미에서 딥러닝을 end-to-end machine learning이라 하기도 한다.
4.2 Loss Function
손실 함수는 딥러닝에서 최적의 매개변수를 찾기 위해 사용하는 함수이다.
일반적으로 사용하는 두 가지 함수를 알아보자!
4.2.1 SSE(오차 제곱합, Sum of Squares for Error)

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

이 때 책에서는 원-핫 인코딩을 사용했다고 가정한다.
이 경우, t에서 정답에 해당하는 인덱스만 1값을 가지므로,
실질적으로 정답에 대한 추정 값(확률)의 로그 값을 계산하는 식이 된다.
def sum_squares_error(y, t):
return 0.5 * np.sum((y-t)**2)
def cross_entropy_error(y, t):
delta = 1e-7 #log 0이 음의 무한대로 발산하므로 넣어주는 더미값
return -np.sum(t * np.log(y + delta))
4.2.3 미니 배치 학습 (mini-batch)
데이터의 양이 많아질수록, 손실 함수의 합을 계산하는 시간이 증가하기 때문에
일부를 무작위로 추려 전체의 근사치로 사용한다. 이를 미니 배치라고 한다.
미니 배치에서의 cross-entropy error 함수 구현은 아래와 같다.
(x_train, t_train) , (x_test, t_test) = \
load_mnist(normalize=True, one_hot_label = True)
# normalize - 픽셀 값을 0~1로 정규화 (0~255)
# flatten - false인 경우 3차원 배열(1x28x28) 아닐경우 784개 원소 1차원 배열
# one_hot_label - 원핫인코딩 여부
print(x_train.shape) # 60000, 784 - 훈련 이미지
print(t_train.shape) # 60000, 10 훈련 레이블
print(x_test.shape) # 10000, 784
print(t_test.shape) # 10000, 10
# 원-핫 인코딩
def cross_entropy_batch(y, t):
if y.dim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(t * np.log(y + 1e-7))/batch_size
# 레이블 (정답 레이블에 해당하는 출력 값만 뽑아낸다.)
def cross_entropy_label(y, t):
if y.dim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
batch_size = y.shape[0]
return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7))/batch_size
손실함수를 사용하는 이유는 무엇일까?
우리는 보통 미분값을 이용해 특정 모델의 성능을 발전시켜 나가는데,
정확도를 지표로 삼으면 매개변수의 미분이 대부분의 장소에서 0이 된다.
따라서 우리는 정확도 대신 loss function을 지표로 삼아 parameter의 값을 갱신시켜 나간다.
4.3 Numerical differentiation(수치 미분)

아주 작은 값이 주어졌을 때 차분으로 미분하는 것을 수치 미분법이라고 한다.
(수식을 전개해 미분하는 것은 해석적analytic 미분법 이라고 한다.)
이 경우, 실제 미분 값과 오차가 생기기 때문에 오차를 줄이기 위해 중앙 차분을 사용하기도 한다.
4.4 Gradient

수치 미분을 이용해 파이썬으로 그라디언트를 구현해본다.
def numerical_gradient(f, x):
h= 1e-4 # 0.0001
grad = np.zeros_like(x) #x와 shape가 같은 배열 생성
for idx in range(x.size):
tmp_val = x[idx]
x[idx] = tmp_val + h
fxh1 = f(x)
x[idx] = tmp_val - h
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val
return grad

4.4.1 Gradient Descent Method(경사 하강법)
일반적으로 손실함수의 수식은 매우 복잡하다.
따라서 우리는 그라디언트를 이용해, 손실함수를 줄이는 방향으로 매개변수를 업데이트 하는데 이 방법을 경사하강법이라 한다.
* 다만, 항상 그라디언트의 방향이 최솟값의 방향이라고 할 수는 없다.
극솟값(local minimum) 혹은 saddle point일 가능성이 있고,
또한 복잡한 형태의 함수는 평평한 지점에 들어가면서 학습이 진전되기 않는 고원(plateau)에 빠질 수 있다.

위 수식은 2차원일 때 업데이트하는 과정을 나타낸다.
learning rate의 경우 hyperparameter이므로 이전에 미리 특정 값으로 설정해야 함!
learning rate가 너무 크면 값이 발산하고,
너무 작으면 값의 갱신이 이루어지지 않는다.
위에 구현했던 gradient 함수를 이용해 이 과정을 코드로 구현해보자.
def gradient_desc(f, init_x, lr=0.01, step_num=100):
x = init_x
for i in range(step_num):
grad = numerical_gradient(f, x)
x -= lr * grad
return x
4.4.2 신경망에서의 gradient

간단한 신경망 구현
class simpleNet:
def __init__(self):
self.W = np.random.randn(2,3) # 정규분포로 초기화
def predict(self, x):
return np.dot(x, self.W)
def loss(self, x, t):
z = self.predict(x)
y = softmax(z)
loss = cross_entropy_error(y, t)
return loss
x = np.array([0.3, 0.3])
t = np.array([0, 0, 1])
net = simpleNet()
f = lambda w: net.loss(x, t)
dW = numerical_gradient(f, net.W)
이 때,

이 값이 0.4라고 한다면, w11을 h만큼 증가시켰을 때 손실함수가 0.4h만큼 증가한다는 의미이다.
따라서, 이 경우 loss를 줄이려면 w11을 음의 방향으로 갱신해야 한다.
4.5 학습 알고리즘 구현하기
Neural Network 학습의 절차
1. mini batch
훈련 데이터 중 무작위로 선별한 mini-batch를 이용한다.
mini-batch의 loss function을 최소화하는 것이 목표이다.
2. 기울기 산출
weight parameter의 gradient를 구한다.
3. 매개변수 갱신
loss function의 값을 줄이는 방향으로 parameter를 갱신한다!
4. 1-3 반복
위 단계를 계속 반복한당
이렇게 random하게 선정한 미니 배치에 대해 parameter를 update하는 방식을 stochastic gradient discent, SGD라 부른다.
엄밀히 말하면 MSGD(Mini-Batch Stochastic Gradient Descent)라 불러야 하지만, 혼용해서 사용하는 것 같다.
간단한 two layer net 클래스를 구현해 위 과정을 한 번 코드로 작성해본다.
다만, 이 클래스는 효율이 정말 좋지 않으므로 정말 구현에 목적이 있다고 생각해야 할 것 같다..
import sys, os
sys.path.append(os.pardir)
from common.functions import *
from common.gradient import numerical_gradient
import numpy as np
import pickle5 as pickle
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)
def predict(self, x):
W1, W2 = self.params['W1'], self.params['W2']
b1, b2 = self.params['b1'], self.params['b2']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = softmax(a2)
return y
def loss(self, x, t):
y= self.predict(x)
return cross_entropy_error(y, t)
def accuracy(self, x, t):
y= self.predict(x)
pred = np.argmax(y, axis = 1)
label = np.argmax(t, axis = 1)
accuracy = np.sum(pred == label) / float(x.shape[0])
return accuracy
def numerical_grad(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
# net = TwoLayerNet(input_size = 784, hidden_size = 100, output_size=10)
# x = np.random.rand(100, 784) #더미 데이터
# t = np.random.rand(100, 10) #더미 레이블
# grads = net.numerical_grad(x, t)
# grads
from dataset.mnist import load_mnist
(x_train, t_train), (x_test, t_test) = \
load_mnist(normalize=True, one_hot_label=True)
train_loss_list = []
train_acc_list = []
test_acc_list = []
# hyper parameter
iters = 10000
train_size = x_train.shape[0]
batch_size = 100
learing_rate = 0.1
iter_per_epoch = max(train_size/batch_size, 1)
network = TwoLayerNet(input_size = 784, hidden_size=50, output_size=10)
for i in range(iters):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
#그라디언트 계산하기
grad = network.numerical_grad(x_batch, t_batch)
#파라미터 업데이트
for key in ('W1','b1','W2','b2'):
network.params[key] -= learing_rate*grad[key]
#loss 기록하기
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)
#epoch의 정확도 계산
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 :" + str(train_acc) + " test_acc: " + str(test_acc))
'Deep Learning' 카테고리의 다른 글
| CH7 - CNN(Convolutional Neural Network) (0) | 2022.08.12 |
|---|---|
| CH6 - 학습 관련 기술들 (2) | 2022.07.20 |
| CH5 - Back Propagation (0) | 2022.07.13 |
| CH3 - 신경망(Neural Network) (0) | 2022.06.30 |
| CH2 - 퍼셉트론(Perceptron) (0) | 2022.06.30 |