학습 : 훈련 데이터로부터 가중치 매개변수의 최적값을 자동으로 획득하는 것
신경망이 학습할 수 있도록 해주는 지표엔 손실함수가 있음
학습의 목표 : 손실 함수의 결괏값을 가장 작게 만드는 가중치 매개변수를 찾는 것
신경망의 특징 - 데이터를 보고 학습할 수 있다는 점!
즉, 가중치 매개변수의 값을 데이터를 보고 자동으로 결정한다는 의미
이는 기존 기계학습에서 사용하던 방법보다 사람의 개입을 더욱 배제할 수 있도록 해주는 중요한 특성
위의 그림에서 볼 수 있듯이
딥러닝 = 종단간 기계학습 (end-to-end maching learning)
: 데이터(입력)에서 목표한 결과(출력)를 사람의 개입 없이 얻는다는 뜻
신경망의 장점 : 모든 문제를 같은 맥락에서 풀 수 있다는 점
ex) 뭘(개, 사람 얼굴...) 인식하던 주어진 문제의 패턴 발견해서 매개변수 결정함
기계학습의 경우 데이터를 훈련 데이터(training data) - 전체의 70~80% & 시험 데이터(test data)- 전체의 20~30% 로 나눠 학습과 실험을 수행
1) 훈련 데이터만 사용해 학습하면서 최적의 매개변수를 찾음
2) 그다음 시험 데이터를 사용해 앞서 훈련한 모델의 실력을 평가함
💡 나눠야 하는 이유?
- 범용적으로 사용할 수 있는 모델을 만들기 위해서
- 일부 데이터셋에만 지나치게 최적화된 상태인 오버피팅을 방지
신경망 학습에서 최적의 매개변수(w,b) 값을 찾을 수 있도록 하는 지표 (신경망 성능의 '나쁨'을 나타냄)
일반적으로 오차제곱합 & 교차 엔트로피오차 사용
y_k : 신경망의 출력(신경망의 추정한 값)
t_k : 정답 레이블
k : 데이터의 차원 수
ex)
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]
오차제곱합 구현
def sum_squares_error(y, t):
return 0.5 * np.sum((y-t)**2)
오차제곱합이 더 작은 1번째 추정 결과가 정답에 더 가깝다고 판단
y_k : 신경망의 출력(신경망의 추정한 값)
t_k : 정답 레이블
k : 데이터의 차원 수
=> 교차 엔트로피 오차 - 정답일 때의 출력이 전체 값을 결정함
def cross_entropy_error(y, t):
delta = 1e-7
return -np.sum(t * np.log(y + delta)) # log 0을 정의할 수 없기 떔에 아주 작은 값인 delta를 더해줌
-> 교차 엔트로피 오차 결과값이 더 작은 1번째 추정이 정답에 더 가깝다고 판단
많은 데이터를 처리하기 위해 모든 훈련 데이터를 대상으로 손실 함수 값을 구하고 그것들의 합을 구하는 방법이 필요함
모든 데이터 값을 훑으며 손실 함수 합 구하는 건 비효율적임
미니배치 : 훈련데이터가 큰 경우에 데이터의 일부만 이용해서 근사치를 계산하는 방식 씀, 이 일부를 미니배치라고 함
'정확도'라는 지표 대신 '손실 함수 값' 사용하는 이유?
-> 미분의 역할에 주목!
신경망 학습에선 최적의 매개변수 탐색 시 매개변수의 미분값을 단서로 서서히 갱신함
정확도의 경우 미분값이 대부분의 장소에서 0이 돼서 매개변수를 갱신할 수 없기 땜에 사용 불가능
정확도는 손실함수에 비해 비교적 불연속적 -> 미분으로의 매개변수 갱신이 어려움
기울기 : 모든 변수의 편미분을 벡터로 정리한 것
기울기의 방향 (기울기가 가리키는 방향) = 각 장소에서의 함수 출력 값을 가장 크게 줄이는 방향
: 손실 함수가 최솟값이 될 때의 매개변수를 찾기 위해서 기울기를 이용해 함수의 최솟값을 찾는 방법
+) 경사 하강법 : 함수의 최솟값 찾음 / 경사 상승법 : 함수의 최댓값 찾음
η 기호(에타)는 갱신하는 크기를 나타냄 = 학습율(learning rate)
-> 매개변수 값을 얼마나 갱신할지 결정
학습률 너무 크면 발산할 수 있고, 너무 작으면 거의 갱신 안 된 채로 학습이 끝나므로
학습률을 적절히 설정하는 것이 중요함
학습률 같은 매개변수 = 하이퍼 파라미터
가중치 / 편향 같은 매개변수 - 학습 알고리즘에 의해 '자동'으로 획득됨
but 하이퍼 파라미터 - 사람이 직접 설정해야 함
-> 여러 후보 값들 중 시험을 통해 최적값 선정하는 과정 필요함
신경망에선 가중치 매개변수(W)에 대한 손실 함수(L)의 기울기를 계산함
- 전제
- 신경망엔 적응 가능한 가중치(w), 편향(b)이 있음
- 학습 : 이 가중치 & 편향을 훈련 데이터에 적응하도록 조정하는 과정
- 1단계 : 미니배치
- 훈련 데이터 중 일부를 무작위로 선별 (= 미니 배치)
- 이제 미니배치의 손실 함수 값을 줄이는것이 목표임
- 2단계 : 기울기 산출
- 각 가중치 매개변수(W)의 기울기를 구함; L을 W에 대해 편미분한 값
- 기울기는 손실함수 값을 최소화하는 방향을 제시
- 3단계 : 매개변수 갱신
- 가중치 매개변수(W)를 기울기 방향으로 조금 갱신함
- 4단계 : 반복
- 1~3단계 반복
=> 미니배치를 무작위로 선정하기 땜에 "확률적 경사 하강법(SGD)"라고 부름
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):
# ex) MNIST 데이터 분석; 입력 : 784개, 출력 : 10개
# 가중치 매개변수 초기화
self.params = {} # 신경망의 매개변수를 보관하는 딕셔너리 변수
self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size) # 가중치 매개변수 초기화; 정규분포를 따르는 난수로 초기화함(행렬 크기는 앞층 x 다음층)
self.params['b1'] = np.zeros(hidden_size) # 편향은 모두 0으로 초기화
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'] # 각 층별 가중치 W
b1, b2 = self.params['b1'], self.params['b2'] # 각 층별 편향 b
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1) # 1번째 층의 활성화함수 : 시그모이드 함수
a2 = np.dot(x, W2) + b2
y = softmax(a2) # 분류니까 출력층의 활성화함수로 softmax 함수 사용
return y
## 손실 함수 값 계산 메소드
# x : 입력 데이터, t : 정답 레이블
def loss(self, x, t):
y = self.predict(x) # 신경망 추론 결과를 y에 저장
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
=> 그냥 쉽게 생각하면 params ; 매개변수 보관 딕셔너리 변수
grads ; params 값들(가중치, 편향)의 기울기값 보관 딕셔너리 변수
__init__
에 대해 간단히 정리하자면,
- 컨스트럭터라고 불리는 초기화를 위한 함수(메소드)
- 인스턴스화를 실시할 때 반드시 처음에 호출되는 특수한 함수
- 객체 생성(인스턴스를 생성)과 관련하여 데이터의 초기를 실시하는 함수
__init__()
은 반드시 첫 번째 인수로 self를 지정해야한다. self에는 인스턴스 자체가 전달되어 있다. 이로 인해, 최과 메소드 내에 인스턴스 변수를 작성하거나, 참고하는 것이 가능해진다.
학습 반복 -> 훈련 데이터의 미니배치에 대한 손실함수 값이 작아짐
오버피팅 방지를 위해 학습 도중 정기적으로 훈련 데이터, 시험 데이터를 대상으로 정확도를 기록함
1 epoch(= 학습에서 훈련 데이터를 모두 소진했을 때의 횟수) 마다 기록
1 에폭 당 계산 반복수 = 훈련 데이터 개수 / 배치수