crossorigin="anonymous"> [확률적 경사 하강법] Chapter 04-2. 확률적 경사 하강법

카테고리 없음

[확률적 경사 하강법] Chapter 04-2. 확률적 경사 하강법

Writing coder 2024. 9. 13. 18:21
반응형

● 학습목표: 경사 하강법 알고리즘을 이해하고 대량의 데이터에서 분류 모델을 훈련하는 방법을 배웁니다.

 

● 키워드: 확률적 경사 하강법, 손실 함수, 에포크 (Epoch)

 

● 지난 시간에 배운 로지스틱 회귀 문제 풀이 scheme

1. Data 불러오기

2. Input / Target data 분리하기

3. Preprocessing - Scaling

4. Train / test set 분리하기

5. Logistic regression model 학습 시키기

6. 원하는 값의 확률 예측하기 (LinearRegression.predict_prob)

 

 확률적 경사 하강법 문제 풀이

1. Data 불러오기

2. Input / Target data 분리하기

3. Preprocessing - Scaling

4. Train / test set 분리하기

5. Stochastic Gradient Descending Classifier (SGDClassifier) model의 데이터 학습

6. Epoch를 증가시키면서 최적의 accuracy 찾기

7. 해당 Epoch로 훈련 시켜 test set accuracy 확인

 

[문제상황]

한빛 마켓은 7가지 생선 데이터를 모델에 지속적으로 학습 시키고 있습니다. 하지만, 생선 럭키백의 수요가 너무 많아 모든 종류의 데이터가 한번에 준비되지 못하고 조금씩 준비된다는것입니다.

 

[해결 방법]

1. 모든 데이터를 보관하고 새로운 데이터가 도착할 때 마다 모델을새로 학습한다. → 데이터가 많이 쌓이는 문제

2. 모델을 학습 시키고 이전 데이터는 지워서 훈련 데이터를 일정하게 유지하는 문제 → 다른 데이터에는 없는 중요한 데이터가 있으면, 문제가 될 수도 있음.

3. 훈련한 데이터를 버리지 않고 새로운 데이터 조금씩 학습하는 방법 → 점진적 학습 or 온라인 학습

    대표적인 점진적 학습 → 확률적 경사하강 (Stochastic Gradient Descent)

 

확률적 경사 하강법 (Stochastic Gradient Descent)

훈련 세트에서 랜덤하게 하나의 샘플을 선택하여 가파른 경사를 조금 내려갑니다. 그 다음 후련 세트에서 랜덤하게 도 다른 샘플을 하나 선택하여 가파른 경사를 조금 내려갑니다. 그다음 훈련 세트에서 랜덤하게 또 다른 샘플을 하나 선택하여 경사를 조금 내려갑니다. 이런 식으로 전체 샘플을 모두 사용 할 때 까지 계속합니다.

 

비유적 표현을 통해 먼저 설명하려고 합니다. "경사를 따라 내려간다." 라고 표현하며

확률적: Random하다

경사: 기울기

하강: 내려간다.

로 생각 하면 된다.

  • 경사 하강법 모델 훈련

산에서 내려올 때, 가장 빠르게 아래 있는 집에 도착하려면 가장 빠른 길을 통해 내려와야 합니다.

하지만, 가제트 형사처럼 너무 긴 다리를 통해 급격하게 내려온다면, 웅덩이가 있는지도 모르고 지나쳐버릴수도 있습니다.

따라서, 가장 가파른 길을 찾아 내려오돼, 조금씩 내려와야합니다.

  • 확률적의 의미

딱 하나의 샘플을 훈련 세트에서 랜덤하게 골라 가장 가파른 길을 찾는 방법

  • 에포크 (Epoch)

훈련 세트를 한 번 모두 사용하는 과정

일반적으로 수십, 수백 번 이상 에포크를 수행합니다.

훈련 세트를 모두 다 사용하게 되면, 다시 처음부터 훈련세트를 사용해 다시 랜덤하게 하나의 샘플을 선택해 경사 하강을 통해 만족하는 위치에 도달 시키는것.

 

# 중요: 확률적 경사 하강법과 신경망 알고리즘

신경망 알고리즘은 확률적 경사 하강법을 꼭 사용합니다. 신경망은 일반적으로 많은 데이터를 사용하기 때문에 한 번에 모든 데이터를 하송하기 어렵습니다. 또 모델이 매우 복잡하기 때문에 수학적인 방법으로 해답을 얻기 어렵습니다.

 

경사하강법의 종류

1. 미니배치 경사 하강법 (minibatch gradient descent): 무작위로 몇 개의 샘플을 선택해서 경사를 따라 내려감. 실전에서 아주 많이 사용.

2. 배치 경사 하강법 (Batch gradient descent) 전체 샘플을 사용하여 경사 하강법을 진행.

  • 전체 데이터를 사용하기 때문에 가장 안정적인 방법이 됨
  • 그만큼 컴퓨터 자원을 많이 사용.
  • 데이터가 너무 많은 경우 한번에 데이터를 불러 올 수 없음.

손실 함수(Loss function) 

머신러닝 알고리즘이 얼마나 엉터리인지 측정하는 기준. '오차'로 생각하면 편하다. 즉, 손실함수의 값이 작을수록 좋다. 대부분 많은 문제에서 필요한 손실 함수는 정의 되어 있다.

 

로지스틱 손실함수(Logistic loss function) or 이진 크로스엔트로피 손실함수 (Binary cross-entropy loss function): 이진 분류 문제에서 손실함수

크로스엔트로피 손실함수 (Cross-entropy loss function): 다중분류 문제에서 손실함수

평균 제곱 오차 (Mean squared error): 회귀 문제에서 손실함수

 

 

 

# 비용함수 (cost function) vs 손실 함수 (loss function)

  • 손실함수: 샘플 하나에 대한 손실
  • 비용함수: 훈련 세트에 있는 모든 샘플에 대한 손실 함수의 합

# 손실 함수의 특징: 미분 가능해야 한다. 분류 문제에서 손실함수가 정확도라면, 샘플개수에 한정되어, 그 값이 0, 0.25, 0.75, 1 등으로 제한될것이다. 따라서, 손실함수는 미분가능해야 한다.

 

이제 코드를 통해

확률적 경사하강법 분류 알고리즘을 이용해서

7가지 생선을 무게, 길이, 대각선 길이, 높이, 너비 특성을 통해 예측해볼것이다. 

#1. 데이터 불러오기

import pandas as pd

fish = pd.read_csv('https://bit.ly/fish_csv_data')
#2. 데이터 input/target으로 나누기

import numpy as np

fish_input = fish.iloc[:,1:].to_numpy()

fish_target = fish['Species'].to_numpy()
#3. Train/Test set 나누기

from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(fish_input, fish_target, random_state = 42)
#4. Scaling 하기

from sklearn.preprocessing import StandardScaler

ss = StandardScaler()

ss.fit(train_input)

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

SGDClassifier

확률적 경사하강법을 통해 분류 문제를 예측하는 알고리즘 라이브러리이다.

 

매개변수

  • loss: 손실 함수의 종류를 지정, loss = 'log_loss'로 지정하면 클래스마다 이진 분류 모델을 만듭니다. 즉, 도미는 양성, 나머지는 음성으로 합니다. 이를 OvR (One versus Rest)라고 합니다. 기본값은 hinge입니다.
  • max_iter: 수행할 에포크 횟수
  • tol: 일정 에포크 동안 성능이 향상 되지 않으면 훈련하지 않고 자동으로 멈추는 최솟값을 지정. None으로 지정시 지정한 epoch까지 계속 학습

# hinge loss는 support vector machine 이라고 불리는 또 다른 머신러닝 알고리즘을 위한 손실 함수이며, 널리 사용합니다.

 

메서드

  • partial_fit(): 이어서 훈련하는 매개변수
#5. 확률적 경사 하강법을 제공하는 model 훈련
## Maximum number of iteration reached before convergence. Consider increasing max_iter to improve the fit.
## 이 경고를 보면 학습 횟수를 늘려줘야 한다.

from sklearn.linear_model import SGDClassifier

sc = SGDClassifier(loss = 'log_loss', 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

 

Score가 낮은것으로 보아, 에포크수가 무복한것으로 보인다.

 

# Convergence Warning 경고

에포크가 끝났지만, 모델이 충분히 수렴하지 않았다는 경고입니다. 실제 상황에서는 max_iter 값을 증가 시켜주면 됩니다.

 

partial_fit() 함수를 통해 다시한번 train set를 통해 model을 학습 시킵니다.

#6. 점진석 학습을 하기 위해서 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.85

 

한번 더 학습을 진행하니 모델의 점수가 향상 되었습니다.

하지만, 무작정 많이 학습을 수행 할 수 없으니 기준을 정하는 방법이 필요하겠습니다.

에포크 수가 너무 높으면, Train set에 대해서 너무 많은 학습이 되어 과대적합이 될 수 있으며,

에포크 수가 너무 적으면, 훈련이 너무 되지 않아 과소적합이 될 수 있습니다.

 

에포크가 증가함에 따른 모델의 정확도

 

훈련세트 점수는 에포크가 진행될수록 꾸준히 증가하지만,

테스트세트 점수는 어느수간 감소하기 시작합니다. 이 지점이 과대적합이 되기 시작하는 점입니다.

과대적합이 시작되기 전에 훈련을 멈추는것을 조기 종료(Early stopping)라고 합니다.

 

이번 예제에서 점차적으로 에포크 수를 늘려가며 score가 어떻게 변하는지 그래프로 확인해보고 조기종료 시킬 지점을 확인하는 그래프를 그리기 위해 partial_fit() 함수를 사용할것입니다.

partial_fit()으로 처음부터 학습을 시작 할 때는 알고리즘이 처음에 어떤 class가 있는지 알 수 없으므로, classes 매개변수에 어떤 class를 target으로 할것인지 지정해주어야 합니다.

 

#7. 300번 epoch를 점진적으로 늘려가면서, 각각의 score를 기록하기

from sklearn.linear_model import SGDClassifier

sc = SGDClassifier(loss = 'log_loss', random_state = 42)

classes = np.unique(train_target)

train_score = []
test_score = []

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

# python에서 _는 특별한 변수입니다. 나중에 사용하지 않고 그냥 버리는 용도로 사용합니다. 여기서는 0에서 299까지 반복하기 위한 용도로 사용합니다.

#8. Epoch에 따른 Train/Test set의 Accuracy 확인하기

import matplotlib.pyplot as plt

plt.plot(train_score, label = 'Train set')
plt.plot(test_score, label = 'Test set')
plt.xlabel('epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show()

 

epoch가 100이상부터 Train set와 Test set의 값이 점점 벌어지고 있는것처럼 보입니다. 따라서 100회 학습이 적절한 값으로 보입니다.

epoch를 100으로 지정하고 학습시켜 점수를 확인해 보겠습니다.

#9. Epoch 100으로 설정하고 훈련해보기

sc = SGDClassifier(random_state = 42, loss = 'log_loss', max_iter = 100, tol = None)

sc.fit(train_scaled, train_target)

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

# 0.957983193277311
# 0.925

확실히 epoch 수를 100으로 지정하니 점수가 많이 향상 되었습니다.

 

# SGDRegressor는 확률적 경사하강법을 사용한 회귀 알고리즘입니다.

 

# hinge loss를 활용한 훈련

#10. Hiindge 손실 함수를 사용한 model 훈련

sc = SGDClassifier(loss = 'hinge', max_iter = 100, tol = None, random_state = 42)
sc.fit(train_scaled, train_target)
print(sc.score(train_scaled, train_target))
print(sc.score(test_scaled, test_target))

# 0.9495798319327731
# 0.925

 

# 복습

# 확률적 경사하강법을 사용한 7가지 생선 분류 


# 데이터 불러오기

import pandas as pd

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

fish.head(5)

fish_input = fish.iloc[:, 1:].to_numpy() ### 헤맴 : .iloc

fish_target = fish['Species'].to_numpy()

# Train/Test set 분류

from sklearn.model_selection import train_test_split

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

from sklearn.preprocessing import StandardScaler

ss = StandardScaler()

ss.fit(train_input)

train_scaled = ss.transform(train_input)

test_scaled = ss.transform(test_input)

# 확률적 경사하강 분류 모델 훈련하기

from sklearn.linear_model import SGDClassifier

sg = SGDClassifier(loss = 'log_loss', max_iter = 10, random_state = 42) # 손실함수, epcoh 지정

sg.fit(train_scaled, train_target)

print(sg.score(train_scaled, train_target), sg.score(test_scaled, test_target)) # 10회 학습 accuracy # 0.773109243697479 0.775


# 부분적으로 더 학습 시키기 (총 11회)

sg.partial_fit(train_scaled, train_target)

print(sg.score(train_scaled, train_target), sg.score(test_scaled, test_target)) # 0.8151260504201681 0.85

# epoch를 증가시키면서 accuracy가 어떻게 달라지는지 확인
# 최적의 epoch를 찾기

from sklearn.linear_model import SGDClassifier
import numpy as np

sg = SGDClassifier(loss = 'log_loss', random_state = 42)

sg_train_score = []
sg_test_score = []
classes = np.unique(train_target)

for _ in range(1,300) :
  sg.partial_fit(train_scaled, train_target, classes = classes) ### partial fit으로 점진적으로 학습 시키기 위해서는 classes로 무엇을 학습할지 제공해야 줘야 한다.
  sg_train_score.append(sg.score(train_scaled, train_target))
  sg_test_score.append(sg.score(test_scaled, test_target))

import matplotlib.pyplot as plt

plt.plot(sg_train_score, label = 'Train set')
plt.plot(sg_test_score, label = 'Test set')

plt.xlabel('Epoch')
plt.ylabel('Accuracy')

plt.legend()
plt.show()

# 최적의 epoch으로 훈련 다시 시키기

sg = SGDClassifier(loss = 'log_loss', max_iter = 100, tol = None , random_state = 42)

sg.fit(train_scaled, train_target)

print(sg.score(train_scaled, train_target), sg.score(test_scaled, test_target)) # 0.957983193277311 0.925


# 손실 함수를 hinge로 바꾸기

sg = SGDClassifier(loss = 'hinge', max_iter = 100, tol = None , random_state = 42)

sg.fit(train_scaled, train_target)

print(sg.score(train_scaled, train_target), sg.score(test_scaled, test_target))
반응형