일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Python
- SVD
- word2vec
- 밑바닥부터 시작하는 딥러닝2 2장
- Transformer
- sigmoid
- word embedding
- DataFrame
- 판다스
- 신경망 학습
- 역행렬
- 동시발생 행렬
- ReLU
- 자연어처리
- PPMI
- 밑바닥부터 시작하는 딥러닝
- 딥러닝
- NMT
- pandas
- 정칙행렬
- Linear Algebra
- RNN
- 벡터간 유사도
- 선형대수학
- 연립일차방정식
- NLP
- one-hot vector
- 데이터프레임
- 프로그래머를 위한 선형대수
- machine translation
- Today
- Total
생각하는감자
4장-신경망 학습 본문
학습
학습(train)이라는 것은 훈련용 데이터로부터 가중치 매개변수의 최적값을 자동으로 획득하는 과정이다. 신경망이 학습할 수 있도록 해주는 지표를 손실함수라고 한다. 결과적으로 손실함수를 가장 작게 만드는 가중치 매개변수를 찾는 것이 학습과정의 목표라고 할 수 있다.
신경망의 특징은 데이터에서 학습할 수 있다는 것이다. 이것은 수만~수억 개의 매개변수를 수작업으로 직접 사람이 정한다는 것은 불가능하다고 할 수 있는데, 데이터를 보고 가중치 매개변수의 값을 자동으로 결정한다는 것은 매우 큰 장점이다.
훈련데이터 & 시험데이터
신경망을 학습시키기에 앞서 전체 데이터셋을 훈련용과 시험용으로 구분짓는 것은 매우 중요하고 당연히 해야 할 일 중 하나이다. 먼저, 훈련데이터(training data)를 사용하여 학습하면서 최적의 매개변수를 찾는다. 그 후에 시험데이터를 활용해 신경망이 한 번도 본 적 없는 데이터에 대해서도 올바른 결괏값을 내는지 확인함으로써 신경망의 성능을 측정한다.
아직 보지 못한 데이터에 대해서도 올바르게 풀어내도록 만드는 것이 목표이기 때문에 하나의 데이터 셋으로 학습과 평가를 모두 진행하게 된다면 올바르지 못한 평가가 된다. 또한, 특정 데이터셋에만 지나치게 최적화된 상태를 의미하는 오버피팅(overfitting)을 피하는 것 또한 중요한 과제이다.
손실함수(Loss Function)
신경망 학습에서는 현재의 상태를 하나의 지표로 표현하게 되며 해당 지표를 가장 좋게 만들어주는 가중치 매개변수의 값을 찾아내는 것이다. 이 지표를 손실함수라고 하며, 일반적으로 오차제곱합과 교차 엔트로피 오차를 사용한다.
손실함수는 따지고 보면 신경망의 성능이 얼마나 나쁜지를 나타내는 지표이다. 현재의 신경망이 훈련데이터를 얼마나 잘 처리하지 못하고 있는지를 나타내고 있다고 보면 된다. 손실함수의 결과값에 -만 붙이면 얼마나 좋은지에 대한 지표로 변할 수 있기 때문에 얼마나 좋은지를 측정하는 것과 얼마나 나쁜지를 측정하는 것은 본질적으로 큰 차이는 없다고 할 수 있다.
*신경망 학습에서 높은 정확도를 가지는 매개변수값을 찾는 것이 목적인데 정확도를 사용하면 안 되는 이유가 있다. 가중치 매개변수의 손실함수의 미분이라는 것은 "가중치 매개변수의 값을 조금 변화시켰을 때, 손실함수가 어떻게 변하는지"를 의미한다. 해당 미분값이 음수이면 가중치 매개변수를 양의 방향으로 변화시키고, 미분값이 양수이면 가중치 매개변수를 음의 방향으로 변화시켜 손실 함수의 값을 줄여나가는 것이다. 하지만, 정확도는 미분값이 대부분의 장소에서 0이 되어 매개변수를 갱신할 수 없기 때문이다. 정확도는 매개변수의 미미한 변화에는 거의 반응하지 않고 반응이 있더라도 그 값이 불연속적으로 갑자기 변화하게 된다.
오차제곱합(sum of squares for error, SSE)
가장 많이 사용되는 손실함수이고 수식은 위와 같고, 파이썬 코드로 구현해보자면 아래와 같다.
import numpy as np
def sum_squares_error(y,t):
return 0.5 * np.sum((y-t)**2)
교차 엔트로피 오차(cross entropy error, CEE)
*one-hot encoding : 정답에 해당하는 인덱스의 원소만 1이고 나머지는 0
def cross_entropy_error(y, t):
delta = 1e-7
return -np.sum(t * np.log(y + delta))
delta를 사용한 이유는, np.log()에 0을 입력하게 되면 마이너스 무한대가 되어 더 이상 계산을 진행할 수 없게 된다. 그래서 아주 작은 값을 더해서 절대 0이 되지 않도록(마이너스 무한대가 발생하지 않도록) 한 것이다.
미니배치 학습
위의 수식들은 데이터 하나에 대한 손실함수를 생각한 것이고, 모든 훈련데이터에 대한 손실함수의 합을 구하는 방법을 생각해 보면 단순하게 N개의 데이터로 확장하면 된다. 마지막에 N으로 나누어 "평균 손실 함수"를 구하는 것이다.
신경망 학습을 진행할 때에 데이터의 일부만 골라 학습하는 것을 미니배치 학습이라고 하고, 이 때 전체데이터 중 일부를 미니배치라고 한다.
기울기
모든 변수의 편미분을 벡터로 정리한 것 = 기울기
기울기가 가리키는 쪽은 각 장소에서 함수의 출력을 가장 크게 줄이는 방향이다.
경사법
최적의 매개변수 값을 찾는 것은 손실함수가 최솟값을 가질 때이다. 하지만 매개변수 공간이 매우 넓기 때문에 어디가 최솟값이 되는 곳인지를 짐작할 수 없다. 이럴 때에 기울기를 활용하여 함수의 최솟값을 잘 찾으려는 것이 경사법이다. 기울기의 방향이 꼭 최솟값을 가리키는 것은 아니지만 그 방향으로 가야 최솟값을 줄일 수 있는 경우가 있을 수도 있고, 기울기가 가리키는 방향에 최솟값이 없는 경우가 있을 수도 있다. 특히, 찌그러진 함수의 경우 평평한 곳으로 파고들면서 고원이라 하는 학습이 진행되지 않는 정체기에 빠질 수도 있다.
경사법은 기계학습을 최적화하는 데 흔히 쓰는 방법인데, 현 위치에서 기울어진 방향으로 일정 거리만큼 이동하고, 이동한 곳에서 기울기를 구하며, 그 기울어진 방향으로 나아가기를 반복하여 함수의 값을 점차 줄이는 방법이다. 최솟값을 찾는 것을 경사 하강법이라고 하고, 최댓값을 찾는 것을 경사 상승법이라고 한다. (사실 함수의 부호만 바꾸면 상승과 하강은 동일하기 때문에 본질적으로 중요하진 않다. 주로 경사하강법이라고 한다.)
위 수식의 "갱신하는 양"을 학습률(learning rate)이라고 한다. 이 값은 미리 특정 값으로 정해두어야 하는데, 너무 커도 좋은 장소를 찾아갈 수 없고 너무 작아도 마찬가지이다. 따라서 신경망을 학습할 때에 보통 이 학습률을 변경해가면서 올바르게 학습하고 있는지를 확인한다.
def numerical_gradient(f, x):
h = 1e-4 #0.0001
grad = np.zeros_like(x) #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
def gradient_descent(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
경사하강법을 파이썬으로 구현한 것이다. f는 최적화하려는 함수, init_x는 초깃값, lr 은 learnin rate, step_num은 경사법에 따른 반복횟수를 의미한다. 함수의 기울기는 numerical_gradient 함수로 구하고 그 기울기에 학습률을 곱한 값으로 갱신하는 처리를 step_num 번 반복한다.
학습 알고리즘 구현하기
신경망 학습은 총 4단계로 이루어지는데, 데이터를 미니배치로 무작위 선정하며, 경사 하강법으로 배개변수를 갱신하기 때문에 확률적 경사하강법이라고 부른다.
- 미니배치
- 훈련 데이터 중 일부를 무작위로 가져온다
- 기울기 산출
- 미니배치의 손실 함수 값을 줄이기 위해 각 가중치 매개변수의 기울기를 구한다.
- 이때 기울기는 손실 함수의 값을 가장 작게 하는 방향을 제시한다
- 매개변수 갱신
- 가중치 매개변수를 기울기 방향으로 아주 조금 갱신한다.
- 반복
- 1~3단계를 반복한다
2층 신경망 클래스 구현
2층 신경망을 하나의 클래스로 구현하는 것부터 시작하자면...
import sys, os
sys.path.append(os.pardir)
from common.functions import *
from common.gradient import numerical_gradient
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
#x : 입력 데이터, t : 정답레이블
def loss(self, x, t):
y = self.predict(x)
return cross_entropy_error(y, t)
def accuracy(self, x, t):
y = self.predict(x)
y = np.argmax(y, axis=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
*sigmoid와 softmax와 같은 함수는 이전 게시물에 정리되어 있다.*
3장-신경망
신경망 퍼셉트론을 사용할 때의 단점은 여전히 원하는 결과를 출력하기 위해서는 사람이 수동적으로 가중치 값을 적절히 정해야 한다는 것이다. 신경망이 이 점을 해결해 주는데, 신경망은 가
thinking-potato.tistory.com
TwoLayerNet 클래스를 사용하여 미니배치 학습을 구현해 보자면..
import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet
(x_train, t_train), (x_test, t_test) = \ load_mnist(normalize = True, one_hot_label = True)
train_loss_list = []
#하이퍼파라미터
iters_num = 10000 # 반복횟수
train_size = x_train.shape[0]
batch_size = 100 #미니배치 크기
learning_rate = 0.1
network = TwoLayerNet(input_size = 784, hidden_size = 0, output_size = 10)
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)
#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)
시험데이터로 평가
신경망 학습의 원래 목적은 주어지는 학습데이터로 학습시켰을 때의 성능이 높은 것이 아니라 범용적인 능력을 익히는 것이라고 언급했었다. 처음 보는 데이터로도 높은 성능을 내는지 확인해 보긴 위해서는 training data에 포함되지 않았던 test data를 사용하여 평가해봐야 한다.
에폭(epoch)이라는 것은 훈련의 단위이다. 1 에폭은 학습에서 훈련 데이터를 모두 소진했을 때의 횟수를 의미하는 것이다. 위의 구현을 조금 수정하면 된다.
import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet
(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 = []
#1에폭 당 반복 수
iter_per_epoch = max(train_size / batch_size, 1)
#하이퍼파라미터
iters_num = 10000 # 반복횟수
train_size = x_train.shape[0]
batch_size = 100 #미니배치 크기
learning_rate = 0.1
network = TwoLayerNet(input_size = 784, hidden_size = 0, output_size = 10)
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)
#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)
#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)))
'딥러닝' 카테고리의 다른 글
6장-학습 관련 기술들 (0) | 2023.04.05 |
---|---|
5장-오차역전파법 (0) | 2023.03.30 |
3장-신경망 (0) | 2023.03.28 |
2장 - 퍼셉트론 (0) | 2023.03.28 |
Introduction (0) | 2023.03.28 |