* 이 글은 <밑바닥부터 시작하는 딥러닝2 (저자: 사이토 고키)> 책을 읽으며 정리한 글입니다.
* 나중에라도 제가 참고하기 위해 정리해 두었으며, 모든 내용을 적은 것이 아닌,
필요하다고 생각되는 부분만 추려서 정리한 것임을 미리 밝힙니다.
목차
- 딥러닝(deep learning)에서의 러닝(learning)
- 2층 신경망 클래스의 학습(learning) 구현
이제까지 배운 모든 것을 종합하여 드디어 딥러닝의 러닝을 구현할 수 있게 되었다!
앞의 내용을 놓친 사람은 필요한 내용을 리마인드하고 오도록 하자.
2020/03/11 - 인공신경망 구현을 위해 알아야할 것들 (활성화 함수와 소프트맥스)
2020/03/17 - 손글씨 숫자 인식으로 해보는 간단한 인공신경망 예측(feat. MNIST 데이터셋)
2020/03/17 - 인공신경망의 학습 지표, 손실함수(loss function)
2020/03/28 - 수치 미분, 딥러닝에서 경사하강법을 위한 기울기 계산
1. 딥러닝(deep learning)에서의 러닝(learning)
딥러닝(deep learning)에서의 러닝(learning)이란, 인공신경망에 존재하는 가중치와 편향을 훈련 데이터에 잘 들어맞도록 조정하는 과정을 뜻한다. 학습의 과정은 다음과 같다.
- 미니배치
- 기울기 벡터 산출
- 매개변수 갱신
- 반복
일단, (1) 미니배치 과정을 통해 훈련 데이터 중 일부를 가져온다. 다음, 미니배치에 있는 데이터들을 이용하여 각 가중치 매개변수의 (2) 기울기 벡터를 산출한다. 이렇게 산출한 기울기 벡터를 이용해, 손실 함수의 값을 가장 작게 하는 방향을 향해 (3) 매개변수를 갱신한다. 마지막으로 1단계~3단계를 (4) 반복한다.
2. 2층 신경망 클래스의 학습(learning) 구현
앞선 포스팅을 통해 간단한 3층 신경망을 구현하였고, 이어서 적절히 학습된 가중치들을 통해 새로운 데이터의 예측값을 산출하는 과정을 배웠다. 그러면 이 중간에 있는, '가중치를 적절히 학습시키는 법' 에 대해 지금부터 논해보도록 하자.
2-(1). 2층 신경망 클래스 구성
일단 2층짜리 인공신경망을 생성해보자.
import numpy as np
class TwoLayerNet:
# 인스턴스 생성
def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
# 가중치 초기화
self.params = {}
self.params['W1'] = np.random.normal(0, weight_init_std, (input_size, hidden_size))
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = np.random.normal(0, weight_init_std, (hidden_size, output_size))
self.params['b2'] = np.zeros(output_size)
# ----------------------------------------------------------------------------------------------
# 활성화 함수 정의
def sigmoid(self, x):
return 1/(1 + np.exp(-x))
def softmax(x):
x = x.T
c = np.max(x, axis = 0)
exp_x = np.exp(x - c)
y = exp_x / np.sum(exp_x, axis = 0)
return y.T
# 비용 함수 정의
def cee(self, y, t): # cross_entropy_error
if y.ndim == 1:
t = t.reshape(1, t.size)
y = y.reshape(1, y.size)
# 훈련 데이터가 원-핫 벡터라면 정답 레이블의 인덱스로 반환
if t.size == y.size:
t = t.argmax(axis=1)
batch_size = y.shape[0]
return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
# 다중 차원의 배열에서 gradient 계산 함수 정의
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
# ----------------------------------------------------------------------------------------------
# 에측 함수 정의
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 = self.sigmoid(a1)
a2 = np.dot(z1, W2) + b2
y = self.softmax(a2)
return y
# 손실 함수 정의
def loss(self, x, t):
y = self.predict(x) # 예측값 산출
return self.cee(y, t)
# 정확도 산출 함수 정의
def accuracy(self, x, t):
y = self.predict(x)
y = np.argmax(y, axis = 1) # 예측값에서 최대 확률을 가지는 위치 뽑아내기
t = np.argmax(t, axis = 1) # 정답에서 최대 확률을 가지는 위치 뽑아내기
return np.sum(y == t) / float(x.shape[0]) # 테스트 케이스의 총 개수 중에 정답을 맞춘 비율 구하기
# 수치 미분으로 gradient 계산
def numerical_gradient(self, x, t):
loss_W = lambda W: self.loss(x, t)
grads = {}
grads['W1'] = self.numerical_gradient(loss_W, self.params['W1'])
grads['b1'] = self.numerical_gradient(loss_W, self.params['b1'])
grads['W2'] = self.numerical_gradient(loss_W, self.params['W2'])
grads['b2'] = self.numerical_gradient(loss_W, self.params['b2'])
return grads
2-(2). 미니배치 학습 구현
랜덤하게 학습 데이터 중 일부를 뽑아내어 학습에 넣는 과정을 구현해보자.
# 하이퍼파라미터 정의
iters_num = 1000 # 반복 횟수를 적절히 설정한다.
train_size = x_train.shape[0]
batch_size = 100 # 미니배치 크기
learning_rate = 0.1
# 인공신경망 인스턴스 생성
network = TwoLayerNet(input_size = 784, hidden_size = 50, output_size = 10)
# loss와 accuracy의 변화를 추적하기 위한 리스트 생성
train_loss_list = []
train_acc_list = []
test_acc_list = []
# 1에폭당 반복 수
iter_per_epoch = max(train_size / batch_size, 1)
# iter_num만큼 가중치 갱신
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.numerical_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)
# 시험 데이터로 모델 평가
# 1에폭당 정확도 계산
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 | " + str(train_acc) + ", " + str(test_acc))
위의 코드를 실행하면 다음과 같은 결과가 나온다.
거듭 학습할수록 loss값이 떨어지는 결과를 보여준다. 학습이 제대로 이루어졌다는 뜻이다.
어쩐지 책의 그래프와는 조금 달라 보이는데, 책의 코드를 작성하는 과정에서 내 나름대로 조금씩 변형했기 때문이라고 추정된다. 1000회 째에 loss값이 0.5까지 떨어지며, 책의 결과(~1.0)보다 더 좋은 결과를 보여주고 있다.
정확도로 성능을 측정하면 다음과 같다. 첫번째 줄은 첫번째 학습 단계에서의 정확도이고, 두번째 줄은 마지막 학습 단계에서 산출한 정확도이다.
현저히 높아진 정답률을 볼 수 있다.
정리
지금까지, 머신러닝의 러닝 과정에 대해 알아보고 구현해보았다. 우리가 어렸을 때 학습지를 풀며 오답의 비율을 보고 나의 학습 성과를 측저했던 것처럼, 인공신경망 또한 '손실 함수'를 기준으로 오답이 적어지는 방향으로 학습한다는 사실을 숙지했다.
하지만, (위의 코드를 돌려 본 사람이면 눈치를 챘겠지만) 학습에 너무 오랜 시간이 걸린다. 다음 포스트에서는 이러한 학습의 시간을 효과적으로 단축시키는 오차역전파법에 대해 알아보도록 하겠다.
'IT Anthology > encyclopedia' 카테고리의 다른 글
[밑러닝] 파이썬으로 간단한 계층(덧셈 노드/곱셈 노드)에서의 역전파 구현하기 (0) | 2020.03.29 |
---|---|
[밑러닝] 이보다 더 쉬울 수는 없다! 오차역전파(back-propagation)법 완벽 파헤치기 (0) | 2020.03.29 |
[밑러닝] 수치 미분, 딥러닝에서 경사하강법을 위한 기울기 계산 (0) | 2020.03.28 |
[밑러닝] 인공신경망의 학습 지표, 손실함수(loss function) (0) | 2020.03.17 |
[밑러닝] 손글씨 숫자 인식으로 해보는 간단한 인공신경망 예측(feat. MNIST 데이터셋) (0) | 2020.03.17 |