[혼공머신] 확률적 경사 하강법

강민우·2022년 1월 22일
0
post-thumbnail

[혼자 공부하는 머신러닝+딥러닝] 책에 기반한 정리글입니다.
전체 소스코드는 아래 Github 링크에서 확인할 수 있습니다.

Github 링크

0. 개요

지금까지는 모델을 훈련할 때 훈련 데이터를 가지고 매번 모델을 새로 만들었다. 이번에는 훈련한 모델을 버리지 않고 새로운 데이터에 대해서 조금씩 더 훈련하는 방법을 알아본다. 이를 점진적 학습이라고 하며, 대표적인 점진적 학습 알고리즘 중 하나가 확률적 경사 하강법이다.

1. 확률적 경사 하강법

확률적 경사 하강법

훈련 세트에서 샘플을 하나씩 꺼내 손실 함수의 경사를 따라 최적의 모델을 찾는 알고리즘이다. 샘플을 여러개씩 사용하면 미니배치 경사 하강법이고, 한번에 전체 샘플을 사용하면 배치 경사 하강법이다.

  • 확률적 경사 하강법은 훈련 세트에서 랜덤하게 하나의 샘플을 선택하여 가파른 경사를 조금 내려간다. 이후 훈련세트에서 랜덤하게 또 다른 샘플을 하나 선택하여 경사를 조금 내려간다. 전체 샘플을 모두 사용하면 이 과정을 반복하는데, 훈련세트를 한번 모두 수행하는 과정을 에포크라고 한다. 보통 수십, 수백번 이상의 에포크를 진행한다.
  • 미니배치 경사 하강법은 무작위로 몇 개의 샘플을 선택해서 경사를 내려간다.
  • 배치경사 하강법은 전체 샘플을 사용하여 경사를 내려간다. 가장 안정적인 방법이지만 컴퓨터 자원을 많이 사용한다.

이들을 꼭 사용하는 알고리즘이 바로 신경망 알고리즘이다. 신경망 모델이 확률적 경사하강법이나 미니배치 경사하강법을 주로 이용한다.

2. 손실 함수

손실 함수

확률적 경사 하강법이 최적화할 대상이다. 대부분의 문제에 잘 맞는 손실 함수가 정의되어 있는데, 이진 분류에는 로지스틱 회귀 손실함수, 다중분류에는 크로스엔트로피 손실함수를 사용한다. 회귀 문제에는 평균 제곱 오차 손실함수를 사용한다.

손실 함수는 어떤 문제에서 머신러닝 알고리즘이 얼마나 엉터리인지 측정하는 기준이며 작을수록 좋다.

로지스틱 손실 함수

손실 함수는 연속적이며 미분 가능해야 한다.

  • 양성클래스(타깃=1)일 때 : -log(예측확률)
    확률이 1에서 멀어질수록 손실은 아주 큰 양수가 된다.
  • 음성클래스(타깃=0)일 때 : -log(1-예측확률)
    확률이 0에서 멀어질수록 손실은 아주 큰 양수가 된다.

이 손실 함수를 로지스틱 손실 함수, 또는 이진 크로스엔트로피 손실함수 라고한다.

다중 분류에서는 크로스엔트로피 손실함수, 회귀에는 평균 제곱 오차를 사용한다.

3. 확률적 경사 하강법을 이용한 분류모델 만들기

3-1. 훈련 데이터 준비하기

먼저 훈련 데이터를 준비한다.

import pandas as pd
fish = pd.read_csv('https://bit.ly/fish_csv_data')

fish_input = fish[['Weight', 'Length', 'Diagonal', 'Height', 'Width']].to_numpy()
fish_target = fish['Species'].to_numpy()

훈련 세트와 테스트 세트를 나누고, 특성을 표준화 전처리한다.

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

train_input, test_input, train_target, test_target = train_test_split(fish_input, fish_target, random_state=42)

ss = StandardScaler()
ss.fit(train_input)

train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

3-2. 확률적 경사 하강법으로 모델 훈련하기

SGDClassifier 클래스는 확률적 경사 하강법을 제공하는 분류용 클래스이다.

# 확률적 경사 하강법을 제공하는 분류용 클래스
from sklearn.linear_model import SGDClassifier

# loss : 손실 함수의 종류 지정, max_iter : 수행할 에포크 횟수
sc = SGDClassifier(loss='log', max_iter=10, random_state=42)
sc.fit(train_scaled, train_target)
print(sc.score(train_scaled, train_target))
print(sc.score(test_scaled, test_target))

출력
0.773109243697479
0.775
정확도가 낮은걸로 보아, 10번의 에포크가 부족했다.

3-3. 에포크를 한번 더 실행하여 훈련하기

모델을 이어서 훈련할 때는 partial_fit() 메서드를 사용한다.

sc.partial_fit(train_scaled, train_target)
print(sc.score(train_scaled, train_target))
print(sc.score(test_scaled, test_target))

출력
0.8151260504201681
0.825
에포크를 한번 더 실행하니 정확도가 향상되었다.

4. 적절한 에포크 횟수 찾기

에포크 횟수가 적어 모델이 훈련세트를 덜 학습하면 과소적합, 너무 많은 에포크 횟수는 과대적합된 모델일 가능성이 있다.

4-1. partial_fit() 으로만 모델 훈련하기

fit() 메소드 대신 partial_fit() 만 사용하여 적절한 에포크 횟수를 알아본다.

import numpy as np
sc = SGDClassifier(loss='log', random_state=42)
train_score = []
test_score = []
classes = np.unique(train_target) # 7개의 생선의 종류
#partial_fit() 메서드만 사용하려면 훈련 세트의 전체클래스의 레이블을 전달해 주어야 함

for _ in range(0, 300) :
  sc.partial_fit(train_scaled, train_target, classes = classes)
  train_score.append(sc.score(train_scaled, train_target))
  test_score.append(sc.score(test_scaled, test_target))

각 횟수별로 훈련세트와 테스트세트의 점수를 train_score test_score 리스트에 넣는다.

4-2. 에포크 횟수에 따른 점수 그래프 그리기

import matplotlib.pyplot as plt
plt.plot(train_score)
plt.plot(test_score)
plt.xlabel("epoch")
plt.ylabel("accuracy")
plt.show()

그래프에 따라서 에포크 횟수 100번 정도가 적절한 반복 횟수임을 알 수 있다.

SGBClassifier 는 일정 에포크 동안 성능이 향상되지 않으면 더 훈련하지 않고 자동으로 멈춘다. tol 매개변수에서 향상될 최솟값을 지정할 수 있는데, None 이라면 자동으로 멈추지 않고 max_iter 만큼 무조건 반복한다.
sc = SGDClassifier(loss='log', max_iter=100, tol=None)

loss 매개변수의 기본값은 hinge 이며 , 힌지 손실은 서포트 벡터 머신 알고리즘을 위한 손실 함수이다.

profile
어제보다 성장한 오늘

0개의 댓글