바로 이전 포스트에서 경사하강법에 대해서 다루었다.
경사하강법의 경우 손실함수의 최솟값을 잘 찾지만, 한 번 가중치를 변경할 때마다 미분을 해야하므로 계산량이 많다는 단점이 있다(=사용하는 자원이 많다).
따라서 주어진 데이터를 모두 이용하지 않고, 그 중에서 무작위로 데이터를 골라잡아 경사하강법을 실시하는 것을 확률적 경사하강법이라고 한다.
💡 데이터 중 무작위로 선정된 데이터만으로 경사하강법을 실시하는 방법을 확률적 경사하강법이라고 한다.
공식은 경사하강법과 동일하다.
배치 (Batch)는 컴퓨터가 한 번에 처리할 데이터 묶음을 뜻한다.
한 배치를 모두 학습하는 것을 에포크(Epoch)라고 한다.
데이터셋에서 딱 1개의 데이터로 경사하강법을 실시함
1개는 아니고, 설정한 수만큼의 데이터로 경사하강법을 실시함
모든 데이터를 이용해서 경사하강법을 실시함
실습은 [혼자 공부하는 머신러닝+딥러닝]에 수록된 예제이다.
import pandas as pd
fish = pd.read_csv("https://bit.ly/fish_csv")
fish.isnull().sum()

결측치를 확인하고 넘어가자.
fish_input = fish[['Weight', 'Length', 'Diagonal', 'Height', 'Width']]
fish_target = fish['Species'].to_numpy()
# 데이터 나누기
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)
input과 target 데이터를 만들고, sklearn의 train_test_split 메서드를 통해 훈련셋과 테스트셋을 나눠준다.
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)
sklearn의 StandardScaler를 통해 표준화시켜준다.
# 확률적 경사하강법 모듈
from sklearn.linear_model import SGDClassifier
#max_iter는 최대 수행 epoch 횟수.
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))
🤓 이전 포스트에서 손실함수는 여러 개가 있다고 했는데, 위 예제의 경우 loss='log'가 손실 함수로서 로지스틱 손실함수를 이용한다는 뜻이다.
💡 로지스틱 손실함수는 후에 포스팅하겠지만, 다중 분류를 위한 크로스 엔트로피
오차를 통해 만들어진 이진 분류 손실함수라고.
💡 각 클래스에 대해 그렇다, 아니다를 분류하기에 로지스틱을 활용할 수 있는 것 같다.

점수의 차이가 극소하므로 과소적합이나 과대적합은 발생하지 않았다.
지금까지의 예제에서는 fit을 새로 하면 객체의 학습이 초기화되었다.
확률적 경사하강법 모듈은 그럴 필요 없이 partial_fit 메서드를 통해서 학습을 점진적으로 진행할 수 있다.
# 모델을 이어서 훈련할 땐 partial_fit 함수 이용
sc.partial_fit(train_scaled, train_target)
print(sc.score(test_scaled, test_target))
print(sc.score(test_scaled, test_target))
# epoch 하나 더 실행하니 점수가 올랐음. 너무 적으면 과소적합, 많으면 과대적합.

partial_fit을 통해 한번 더 학습하니 점수가 오른 것을 볼 수 있다.
# partial_fit만 써서 지도학습
import numpy as np
sc = SGDClassifier(loss='log', random_state=42)
train_score = []
test_score = []
classes = np.unique(train_target)
# partial_fit만 쓰려면 classes 매개변수에 전체 레이블을 줘야 됨
for _ in range(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))
partial_fit만 사용할 때는 classes를 직접 지정해줘야 한다.
위와 같이 코드를 돌리면, 300회 각각의 학습 점수가 train_score와 test_score 배열에 저장된다.
위의 배열을 가지고, epoch에 대한 점수의 그래프를 그려보자.
import matplotlib.pyplot as plt
plt.plot(train_score, label='train score')
plt.plot(test_score, label='test score')
plt.legend()
plt.show()

그래프를 보면, 약 100 구간 이상에서는 train score만 상승하고 test score는 그대로인 것을 볼 수 있다. 따라서 100회 이상에서는 과대적합이 되므로 적정 epoch 횟수는 100정도가 될 것 같다.
SGDClassifier는 일정 epoch동안 성능이 향상되지 않으면 멈춘다. 이 성능증가 최소를 객체 생성 시 tol 파라미터에 넘겨주면 된다.
sc = SGDClassifier(loss='log', 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))
위 경우 성능증가 최소를 None으로 줬으므로, 성능증가가 없더라도 무조건 에포크 100회를 수행하라는 코드이다.