4.1 데이터에서 학습하다!
학습 - 훈련 데이터로부터 가중치 매개변수의 최적값을 자동으로 획득하는 것
신경망이 학습할 수 있게 해주는 지표 → 손실 함수
실제 신경망의 매개변수는 무수히 많음 → 수작업으로 결정 X
4.1.1 데이터 주도 학습
기계학습의 중심에는 데이터 존재
주어진 데이터를 활용해서 해결) 이미지에서 특징 추출, 특징의 패턴을 기계학습 기술로 학습
두번째 접근 방식- 특징을 사람이 설계
세번째 접근 방식- 특징도 '기계'가 스스로 학습
※ 종단간(처음부터 끝까지) 기계학습 - 데이터에서 목표한 결과를 사람의 개입없이 얻음
4.1.2 훈련데이터와 시험데이터
훈련 데이터 / 시험 데이터
훈련 데이터만 사용하여 학습하며 최적의 매개변수를 찾음
시험 데이터를 사용하여 훈련한 모델의 실력을 평가
WHY?
범용 능력(아직 보지 못한 데이터로도 문제를 올바르게 풀어내는 능력)을 제대로 평가하기 위해
오버피팅- 한 데이터셋에만 지나치게 최적화된 상태
4.2 손실 함수
'하나의 지표'를 기준으로 최적의 매개변수 값을 탐색
신경망에서 사용하는 지표 → 손실 함수 (신경망 성능의 '나쁨'을 나타내는 지표)
4.2.1 오차제곱합
오차제곱합
y_k - 신경망의 출력(신경망이 추정한 값)
t_k - 정답 레이블
k - 데이터의 차원 수
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.1, 0.0, 0.0]
t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
원-핫 인코딩 방식
정답을 가리키는 원소 1 / 그 외에는 0
def sum_squares_error(y, t):
return 0.5 * np.sum((y-t)**2)
첫번째 예의 손실 함수 쪽 출력이 작음 = 정답 레이블과의 오차도 작음
4.2.2 교차 엔트로피 오차
교차 엔트로피 오차
y_k - 신경망의 출력(신경망이 추정한 값)
t_k - 정답 레이블 (정답에 해당하는 인덱스의 원소만 1, 나머지는 0)
실질적으로 정답일 때(t_k가 1일때)의 추정의 자연로그를 계산하는 식
x가 0에 가까워질수록 y의 값은 점점 작아짐
정답에 해당하는 출력이 커질수록 0에 다가감 / 출력이 1일 때 0 → 정답일 때의 출력이 작을 수록 오차는 커짐
def cross_entropy_error(y, t):
delta = 1e-7
return -np.sum(t * np.log(y + delta))
np.log() 에 0 입력을 막기 위해 아주 작은 값 delta 더함
오차제곱합의 판단과 일치
4.2.3 미니배치 학습
교차 엔트로피 오차 (훈련 데이터 모두에 대한 손실함수의 합을 구함)
t_nk - n번째 데이터의 k번째 값
y_nk - 신경망의 출력
t_nk - 정답 레이블
1/n - N으로 나눔으로써 '평균 손실 함수'를 구함
신경망 학습에서도 훈련 데이터로부터 일부만 골라 학습을 수행 - 미니배치 학습
일부 = 미니배치
train_size = x_train.shape[0] # 60000
batch-size = 10
batch_mask = np.random.choice(train_size, batch_size) # randomly select indecies from 0-59999
x_batch = x_train[batch_mask]
t_train = t_train[batch_mask]
'''
>>> np.random.choice(60000, 10)
array([8013, 14666, 58210, 10, 33333 ...])
'''
np.random.choice() - 지정한 범위의 수 중에서 무작위로 원하는 개수만 꺼냄
4.2.4 (배치용) 교차 엔트로피 오차 구현하기
def cross_entropy_error(y, t):
if y.ndim == 1: # if dimension of y is 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))
y- 신경망의 출력
t- 정답 레이블
y가 1차원 - 데이터 하나당 교차 엔트로피 오차를 구함 → reshape()로 데이터 형상 바꿔줌
원-핫 인코딩이 아닐 때
def cross_entropy_error(y, t):
if y.ndim == 1: # if dimension of y is 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[np.arange(batch-size), t] + 1e - 7))
※ t가 0인 원소는 교차 엔트로피 오차도 0이므로 계산 무시해도 좋음
4.2.5 왜 손실 함수를 설정하는가?
'정확도'라는 지표 대신 '손실 함수의 값' 사용.
WHY?
신경망 학습에서는 최적의 매개변수 탐색할 때 손실 함수의 값을 가능한 작게하는 매개변수 값 찾음
→ 매개 변수의 미분을 계산 → 미분 값을 단서로 매개변수의 값을 서서히 갱신하는 과정 반복
가중치 매개변수의 손실 함수의 미분 = '가중치 매개변수의 값을 아주 조금 변화 시켰을 때, 손실 함수가 어떻게 변하나'
정확도는 미분 값이 대부분의 장소에서 0이 되어 매개변수를 갱신할 수 없음
계단함수 - 값이 불연속적으로 변화(대부분의 장소에서 기울기 0)
시그모이드함수 - 값이 연속적으로 변하고 곡선의 기울기도 연속적으로 변함 (기울기가 0이 되지 않음)
4.3 수치 미분
기울기 값을 기준으로 나아갈 방향을 정함
미분 - 한순간의 변화량을 표시
def numerical_diff(f, x):
h = 1e-50
return (f(x+h) - f(x)) / h
※ 개선점
1. 반올림 오차 - 1e-50을 float32형으로 나타내면 0.0이 됨
2. f의 차분 - (x+h)와 x 사이의 기울기는 진정한 미분과 엄밀히 일치 X
(x+h) (x-h)
x를 중심으로 그 전후의 차분을 계산 - 중심 차분 or 중앙 차분
(x+h) x - 전방 차분
def numerical\_diff(f, x):
h = 1e-4 # 0.0001
return (f(x+h) - f(x-h)) / (2\*h)
4.3.2 수치 미분의 예
def function\_1(x):
return 0.01\*x\*\*2 + 0.1\*x
>>> 0.1999999999990898
>>> 0.2999999999986347
진정한 미분 - 0.2, 0.3
4.3.3 편미분
def function_2(x):
return x[0]**2 + x[1]**2
# 또는 return np.sum(x**2)
x- 넘파이 배열
편미분 - 변수가 여럿인 함수에 대한 미분
# x0=3, x1=4일때, x0에 대한 편미분 af/ax0
def function_tmp1(x0):
return x0*x0 + 4.0**2.0
print(numerical_diff(function_tmp1, 3.0))
>>> 6.00000000000378
# x0 = 3, x1 = 4일때, x1에 대한 편미분 af/ax1
def function_tmp2(x1):
return 3.0**2.0 + x1*x1
print(numerical_diff(function_tmp2, 4.0))
>>> 7.999999999999119
4.4 기울기
def numerical_gradient(f, x):
h = 1e-4
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
동작방식 - 변수가 하나일 때의 수치 미분과 거의 같음
np.zero_like(x) - x와 형상이 같고 원소가 모두 0인 배열을 만듦
f - 함수 / x - 넘파이 배열
- 방향을 가진 벡터로 그려짐
- 기울기는 함수의 '가장 낮은 장소(최솟값'를 가르킴
- '가장 낮은 곳'에서 멀어질수록 화살표의 크기가 커짐
기울기가 가리키는 쪽은 각 장소에서 함수의 출력 값을 가장 크게 줄이는 방향
4.4.1 경사법(경사 하강법)
매개 변수 공간이 광대하여 어디가 최솟값인지 짐작 X
→ 이런 상황에서 기울기를 이용해 함수의 최솟값을 찾으려는 것 = 경사법
각 지점에서 함수의 값을 낮추는 방안을 제시하는 지표 - 기울기 (보장 x)
경사법 - 현 위치에서 기울어진 방향으로 일정 거리만큼 이동
이동한 곳에서 기울기를 구하고, 기울어진 방향으로 나아가기를 반복 → 함수의 값을 점차 줄여나감
최솟값 찾음- 경사 하강법
최댓값 찾음- 경사 상승법
에타 - 갱신하는 양
def gradient_descent(f, init_x, lr = 0.01, step_num = 100):
x = init_x
for i in range(step_num):
grad = numerial_gradient(f, x)
x -= lr * grad
return x
init_x - 초깃값
lr - learning reate를 의미하는 학습률
step_num - 경사법에 따른 반복 횟수
값이 낮은 장소인 원점에 점차 가까워짐
- 학습률이 너무 크면 큰 값으로 발산
- 학습률이 너무 작으면 거의 갱신되지 않은 채 끝남
※하이퍼파라미터 - 학습률 같은 매개변수, 사람이 직접 설정해야하는 매개 변수
여러 후보 값 중에서 시험을 통해 잘 학습하는 값을 찾는 과정을 거침
4.4.2 신경망에서의 기울기
신경망에서의 기울기 = 가중치 매개 변수에 대한 손실 함수의 기울기
가중치가 W, 손실 함수가 L
1행 1번째 원소 - W11을 조금 변경했을 때 손실함수 L 이 얼마나 변하느냐를 나타냄
형상 W와 같음 2x3
simpleNet 클래스
class simpleNet:
def __init__(self):
self.W = np.random.randn(2, 3) # init with normal distribution
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
형상 2x3인 가중치 매개변수 하나를 인스터스 변수로 가짐
메서드 2개
predict(x) - 예측 수행
loss(x,t) - 손실 함수의 값을 구함
x- 입력 데이터
t - 정답 레이블
f= lambda w: net.loss(x,t)
dW= numerical_gradient(f, net.W)
신경망의 기울기 구함 → 경사법에 따라 가중치 매개변수를 갱신
4.5 학습 알고리즘 구현하기
전제
신경망에 적응 가능한 가중치, 편향이 있음
<경사 하강법>
1단계 - 미니배치
훈련 데이터 일부를 무작위로 가져옴. 선별 데이터 = 미니배치, 미니배치의 손실 함수 값을 줄이는 겂이 목표
2단계 - 기울기 산출
손실함수 값을 줄이기 위해 각 가중치 매개변수의 기울기를 구함. 손실 함수의 값을 가장 작게 하는 방향을 제시
3단계 - 매개변수 갱신
가중치 매개변수를 기울기 방향으로 아주 조금 갱신
4단계 - 반복
1~3 단계 반복
데이터를 미니 배치로 무작위 선정 → 확률적 경사 하강법 (SGD)
4.5.1 2층 신경망 클래스 구현하기
import sys, os
sys.path.append(os.pardir)
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'] =
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(input_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)
y = np.argmax(y, axis = 1)
t = np.argmax(t, axis = 1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
def numerical_gradient(self, x, t):
loss_W = lambda W: self.loss(x, t)
grads = {}
grads['W1'] = numerical_gradient(loss_W, self.parmas['W1'])
grads['b1'] = numerical_gradient(loss_W, self.parmas['b1'])
grads['W2'] = numerical_gradient(loss_W, self.parmas['W2'])
grads['b2'] = numerical_gradient(loss_W, self.parmas['b2'])
return grads
<변수>
params - 신경망의 매개변수를 보관하는 딕셔너리 변수(인스턴스 변수)
params['W1] - 1번째 층의 가중치, params['b1'] - 1번째 층의 편향
grads - 기울기 보관하는 딕셔너리 변수 (numerical_gradient() 메서드의 반환값)
grads['W1] - 1번째 층의 가중치의 기울기, grads['b1'] - 1번째 층의 편향의 기울기
<메서드>
__init__(self, input_size, hidden_size, output_size) - 초기화를 수행
(입력층이 뉴런수, 은닉층의 뉴런수, 출력층의 뉴런수)
predict(self,x) - 예측(추론)을 수행
x - 이미지 데이터
loss(self,x,t) - 손실 함수의 값 구함
x - 이미지 데이터 / t - 정답 레이블
accuracy(self,x,t) - 정확도 구함
numerical_gradient(self,x,t) - 가중치 매개변수의 기울기를 구함
gradient(self,x,t) - 가중치 매개변수의 기울기를 구함
4.5.2 미니배치 학습 구현하기
훈련 데이터 중 일부를 무작위로 꺼냄
import numpy as np
from mnist import load_mnist
(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.sahpe[0]
batch-size = 100
learning_rate = 0.1
network = TwoLayerNetwork(input_size = 784, hidden_size = 50, 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)
#매개변수 갱신
for key in ('W1', 'b1', 'W2', 'b2'):
network.parmas[key] -= learning_rate * grad[key]
#학습 경과 기록
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)
미니 배치 크기 100
왼쪽: 10,000회 반복까지의 추이
4.5.3 시험 데이터로 평가하기
에폭 - 훈련데이터 10,000개를 100개의 미니 배치로 학습할 경우 확률적 경사 하강법 100회 반복
100회 = 1 에폭
train_loss_list = []
train_acc_list = []
test_acc_list = []
# 1epoch당 정확도 계산
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))
훈련 데이터에 대한 정확도 = 실선
시험 데이터에 대한 정확도 = 점선
에폭이 진행될 수록 훈련 데이터와 시험 데이터를 사용한 정확도가 좋아짐 (차이 X)
'AI > 딥러닝' 카테고리의 다른 글
[밑바닥부터 시작하는 딥러닝] 7장 합성곱 신경망(CNN) (0) | 2023.04.15 |
---|---|
[밑바닥부터 시작하는 딥러닝] 6장 학습 관련 기술들 (1) | 2023.04.08 |
[밑바닥부터 시작하는 딥러닝] 5장 오차역전파법 (0) | 2023.04.01 |
[밑바닥부터 시작하는 딥러닝] 3장 신경망 (0) | 2023.03.18 |
[밑바닥부터 시작하는 딥러닝] 2장 퍼셉트론 (0) | 2023.03.18 |