[DL] #4. 신경망 학습

wonnie1224·2022년 10월 24일
0

ML / DL

목록 보기
3/5

1. 데이터 학습

1.1. 학습이란?

학습 : 훈련 데이터로부터 가중치 매개변수의 최적값을 자동으로 획득하는 것
신경망이 학습할 수 있도록 해주는 지표엔 손실함수가 있음

학습의 목표 : 손실 함수의 결괏값을 가장 작게 만드는 가중치 매개변수를 찾는 것

1.2. 데이터로부터의 학습

신경망의 특징 - 데이터를 보고 학습할 수 있다는 점!
즉, 가중치 매개변수의 값을 데이터를 보고 자동으로 결정한다는 의미


이는 기존 기계학습에서 사용하던 방법보다 사람의 개입을 더욱 배제할 수 있도록 해주는 중요한 특성

위의 그림에서 볼 수 있듯이

  • 기계학습 : '사람이 생각하는 특징' + '기계학습'으로 표현
  • 신경망(딥러닝) : 사람 개입 x, 기계학습과 달리 이미지에 포함된 중요한 특징도 기계가 스스로 학습

딥러닝 = 종단간 기계학습 (end-to-end maching learning)
: 데이터(입력)에서 목표한 결과(출력)를 사람의 개입 없이 얻는다는 뜻

신경망의 장점 : 모든 문제를 같은 맥락에서 풀 수 있다는 점
ex) 뭘(개, 사람 얼굴...) 인식하던 주어진 문제의 패턴 발견해서 매개변수 결정함

1.3. 훈련 데이터 & 시험 데이터

기계학습의 경우 데이터를 훈련 데이터(training data) - 전체의 70~80% & 시험 데이터(test data)- 전체의 20~30% 로 나눠 학습과 실험을 수행


1) 훈련 데이터만 사용해 학습하면서 최적의 매개변수를 찾음
2) 그다음 시험 데이터를 사용해 앞서 훈련한 모델의 실력을 평가함

💡 나눠야 하는 이유?

  • 범용적으로 사용할 수 있는 모델을 만들기 위해서
  • 일부 데이터셋에만 지나치게 최적화된 상태인 오버피팅을 방지

2. 손실 함수

신경망 학습에서 최적의 매개변수(w,b) 값을 찾을 수 있도록 하는 지표 (신경망 성능의 '나쁨'을 나타냄)
일반적으로 오차제곱합 & 교차 엔트로피오차 사용

2.1. 오차제곱합 (sum squares error, SSE)

y_k : 신경망의 출력(신경망의 추정한 값)
t_k : 정답 레이블
k : 데이터의 차원 수

  • y_k : softmax 함수의 출력, 각 클래스에 대한 확률 값 가짐
  • t_k : 정답 클래스에 대해서만 1, 나머진 0을 갖는 원-핫 인코딩된 정답 레이블

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번째 추정 결과가 정답에 더 가깝다고 판단

2.2. 교차 엔트로피 오차 (cross entroy error, CCE)

y_k : 신경망의 출력(신경망의 추정한 값)
t_k : 정답 레이블
k : 데이터의 차원 수

  • t_k : 정답에 해당하는 인덱스의 원소만 1, 나머진 0인 원-핫 인코딩된 값이기 땜에 실질적으로 정답일 때의 추정(= t_k가 1일 때의 y_k)의 자연로그 값을 계산하는 것과 같은 식이 됨

=> 교차 엔트로피 오차 - 정답일 때의 출력이 전체 값을 결정함

  • 자연로그 그래프 - x의 값이 1에 가까우면 0에 가가워짐 / x값이 0에 가까우면 값이 커짐
    -> 교차 엔트로피 오차 식의 값은 정답에 해당하는 출력(정답 레이블의 확률값)이 커질수록 0에 가까워지다가, 그 출력값이 1일 때 오차가 0이 됨

def cross_entropy_error(y, t):
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))	# log 0을 정의할 수 없기 떔에 아주 작은 값인 delta를 더해줌

-> 교차 엔트로피 오차 결과값이 더 작은 1번째 추정이 정답에 더 가깝다고 판단

2.3. 미니 배치 학습

많은 데이터를 처리하기 위해 모든 훈련 데이터를 대상으로 손실 함수 값을 구하고 그것들의 합을 구하는 방법이 필요함

  • 훈련데이터 모두에 대한 평균손실함수

모든 데이터 값을 훑으며 손실 함수 합 구하는 건 비효율적임

미니배치 : 훈련데이터가 큰 경우에 데이터의 일부만 이용해서 근사치를 계산하는 방식 씀, 이 일부를 미니배치라고 함

2.4. 손실 함수의 필요성

'정확도'라는 지표 대신 '손실 함수 값' 사용하는 이유?
-> 미분의 역할에 주목!

신경망 학습에선 최적의 매개변수 탐색 시 매개변수의 미분값을 단서로 서서히 갱신함
정확도의 경우 미분값이 대부분의 장소에서 0이 돼서 매개변수를 갱신할 수 없기 땜에 사용 불가능

정확도는 손실함수에 비해 비교적 불연속적 -> 미분으로의 매개변수 갱신이 어려움


3. 수치 미분


4. 기울기

기울기 : 모든 변수의 편미분을 벡터로 정리한 것

기울기의 방향 (기울기가 가리키는 방향) = 각 장소에서의 함수 출력 값을 가장 크게 줄이는 방향

경사법(경사 하강법)

: 손실 함수가 최솟값이 될 때의 매개변수를 찾기 위해서 기울기를 이용해 함수의 최솟값을 찾는 방법

  • 현 위치에서 기울어진 방향으로 일정 거리 이동
  • 다음 이동한 곳에서도 기울기를 구하고 기울어진 방향으로 나아감
    => 위의 과정 반복해서 함수의 값을 점점 줄여 나감

+) 경사 하강법 : 함수의 최솟값 찾음 / 경사 상승법 : 함수의 최댓값 찾음

η 기호(에타)는 갱신하는 크기를 나타냄 = 학습율(learning rate)
-> 매개변수 값을 얼마나 갱신할지 결정

학습률 너무 크면 발산할 수 있고, 너무 작으면 거의 갱신 안 된 채로 학습이 끝나므로
학습률을 적절히 설정하는 것이 중요함

학습률 같은 매개변수 = 하이퍼 파라미터
가중치 / 편향 같은 매개변수 - 학습 알고리즘에 의해 '자동'으로 획득
but 하이퍼 파라미터 - 사람이 직접 설정해야 함
-> 여러 후보 값들 중 시험을 통해 최적값 선정하는 과정 필요함

신경망에서의 기울기

신경망에선 가중치 매개변수(W)에 대한 손실 함수(L)의 기울기를 계산함


5. 학습 알고리즘 구현

신경망 학습의 절차

- 전제
- 신경망엔 적응 가능한 가중치(w), 편향(b)이 있음
- 학습 : 이 가중치 & 편향을 훈련 데이터에 적응하도록 조정하는 과정
- 1단계 : 미니배치
- 훈련 데이터 중 일부를 무작위로 선별 (= 미니 배치)
- 이제 미니배치의 손실 함수 값을 줄이는것이 목표임
- 2단계 : 기울기 산출
- 각 가중치 매개변수(W)의 기울기를 구함; L을 W에 대해 편미분한 값
- 기울기는 손실함수 값을 최소화하는 방향을 제시
- 3단계 : 매개변수 갱신
- 가중치 매개변수(W)를 기울기 방향으로 조금 갱신함
- 4단계 : 반복
- 1~3단계 반복

=> 미니배치를 무작위로 선정하기 땜에 "확률적 경사 하강법(SGD)"라고 부름

< 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):
    	# 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

TwoLayerNet 클래스의 변수

params : 신경망의 매개변수를 보관하는 딕셔너리 변수(인스턴스 변수; 클래스를 실체화한 것)

  • params['W1'] : 1번째 층의 가중치 / params['b1'] : 1번째 층의 편향
  • params['W2'] : 2번째 층의 가중치 / params['b2'] : 2번째 층의 편향

grads : 기울기를 보관하는 딕셔너리 변수 (numerical_gradient()메소드의 리턴값)

  • grads['W1'] : 1번째 층의 가중치의 기울기 / params['b1'] : 1번째 층의 편향의 기울기

=> 그냥 쉽게 생각하면 params ; 매개변수 보관 딕셔너리 변수
grads ; params 값들(가중치, 편향)의 기울기값 보관 딕셔너리 변수

__init__에 대해 간단히 정리하자면,

  • 컨스트럭터라고 불리는 초기화를 위한 함수(메소드)
  • 인스턴스화를 실시할 때 반드시 처음에 호출되는 특수한 함수
  • 객체 생성(인스턴스를 생성)과 관련하여 데이터의 초기를 실시하는 함수
    __init__()은 반드시 첫 번째 인수로 self를 지정해야한다. self에는 인스턴스 자체가 전달되어 있다. 이로 인해, 최과 메소드 내에 인스턴스 변수를 작성하거나, 참고하는 것이 가능해진다.

TwoLayerNet 클래스의 메소드

init(self, input_size, hidden_size, output_size) : 초기화를 수행 (TwoLayerNet타입의 객체를 생성할 때 호출되는 메서드임)

  • 인수; 입력층의 뉴런 수, 은닉층의 뉴런 수, 출력층의 뉴런수

predict(self, x) : 예측(추론) 수행함

  • 인수; x - 이미지 데이터

loss(self,x,t) : 손실 함수의 값을 구함

  • 인수; x - 이미지 데이터, t - 정답 레이블

accuracy(self,x,t) : 정확도를 구함

numerical_gradient(self,x,t) : 가중치 매개변수(W,b)의 기울기를 구함 (수치 미분 방식)

학습 반복 -> 훈련 데이터의 미니배치에 대한 손실함수 값이 작아짐
오버피팅 방지를 위해 학습 도중 정기적으로 훈련 데이터, 시험 데이터를 대상으로 정확도를 기록함
1 epoch(= 학습에서 훈련 데이터를 모두 소진했을 때의 횟수) 마다 기록
1 에폭 당 계산 반복수 = 훈련 데이터 개수 / 배치수

profile
안녕하세요😊 컴퓨터비전을 공부하고 있습니다 🙌

0개의 댓글