1. 나의 첫 머신러닝
머신러닝 vs 딥러닝
머신러닝은 규칙을 일일이 프로그래밍하지 않아도 자동으로 규칙을 학습하는 알고리즘을 연구하는 분야이다.
딥러닝이란 많은 머신러닝 알고리즘 중에 인공 신경망을 기반으로 한 방법들을 통칭하여 딥러닝이라고 부른다.
마켓과 머신러닝
머신러닝에서 여러 개의 종류(혹은 클래스) 중 하나를 구별해 내는 문제를 분류(classification)라고 부른다. 2개의 클래스 중 하나를 고르는 문제를 이진 분류(binary classification)라고 한다.
데이터의 특징, column을 특성(feature)이라고 부른다.
K-Nearest Neighbors
from sklearn.neighbors import KNeighborsClassifier
k-최근접 이웃 알고리즘은 데이터에 대한 답을 구할 때 주위의 다른 데이터를 보고 다수를 차지하는 것을 정답으로 사용한다. neighbors의 기본값은 5이나, 홀수인 정수로 변경가능하다. 단점은 알고리즘이 가장 가까운 직선거리에 있는 데이터를 찾기에 데이터가 아주 많은 경우 사용하기 어렵다.
p 파라미터로 거리를 재는 방법을 지정할 수 있다. 1의 경우 맨해튼 거리를 사용하고, 2의 경우 유클리디안 거리를 사용한다. 기본값은 2이다.
2. 데이터 다루기
훈련 세트와 테스트 세트
머신러닝 알고리즘은 크게 지도 학습(supervised learning>과 비지도 학습(unsupervised learning)으로 나눌 수 있다.
지도 학습은 훈련하기 위한 데이터와 정답이 필요하다. 지도 학습에서 데이터와 정답을 입력(input)과 타깃(target)이라고 하고, 이 둘을 합쳐 훈련 데이터(training data)라고 부른다.
머신러닝 알고리즘의 성능을 제대로 평가하려면 훈련 데이터와 평가에 사용할 데이터가 각각 달라야 한다. 이렇게 하는 가장 간단한 방법은 평가를 위해 또 다른 데이터를 준비하거나 이미 준비된 데이터 중에서 일부를 떼어 내어 활용하는 것이다. 평가에 사용하는 데이터를 테스트 세트(test set), 훈련에 사용되는 데이터를 훈련 세트(train set)라고 부른다. 테스트 세트는 전체 데이터에서 20~30%를 사용하는 경우가 많다. 전체 데이터가 아주 크다면 1%만 덜어내도 충분할 수 있다.
훈련하는 데이터와 테스트하는 데이터에 샘플이 골고루 섞여 있어야 하는데 샘플링이 한쪽으로 치우쳤다면 샘플링 편향(sampling bias)이라고 부른다.
데이터 전처리
from sklearn.model_selection import train_test_split
훈련 세트와 테스트 세트를 섞어서 나눠준다.
그러나, K-nearest neighbors 알고리즘 같은 경우, 직선 거리를 계산할 때, 각 변수별 거리들을 이용해 계산하기 때문에, 변수의 정규화 혹은 스케일이 필요하다. 이러한 작업을 데이터 전처리라고 부른다. 가장 널리 사용하는 전처리 방법 중 하나는 표준점수(standard score)이다. 여기서 주의할 점은, 테스트 세트로 평가하거나 혹은 실제 feature들을 모델에 입력할 때, 동일한 전처리를 진행해야 한다. 훈련 세트의 평균과 표준편차로 변환을 하는 등의 스케일이 같아지도록 해주어야 한다.
mean = np.mean(train_input, axis = 0)
std = np.std(train_input, axis = 0)
3. 회귀 알고리즘과 모델 규제
회귀
회귀는 클래스 중 하나로 분류하는 것이 아니라 임의의 어떤 숫자를 예측하는 문제이다. 일반적으로는 두 변수 사이의 상관관계를 분석하는 방법을 회귀라 부른다.
K-nearest neighbors regression
예측하려는 샘플에 가장 가까운 샘플 k개를 선택하고, 이웃 샘플의 수치를 사용해 평균을 구해 샘플의 타깃을 예측한다.
결정계수($R^2$)
분류의 경우에는 테스트 세트에 있는 샘플을 정확하게 분류한 개수의 비율이다. 정확도라고 불렀다. 회귀에서는 정확한 숫자를 맞힌다는 것은 거의 불가능하다. 예측하는 값이나 타깃 모두 임의의 수치이기 때문이다.
회귀의 경우에는 조금 다른 값으로 평가하는데 이 점수를 결정계수(coefficient of determination)라고 부른다. 또는 간단히 $R^2$라고도 부른다.
$$ R^2 = 1 - \frac{\sum{(target - prediction)^2}}{\sum{(target - mean)^2}} $$
각 샘플의 타깃과 예측한 값의 차이를 제곱하여 더한다. 그다음 타깃과 타깃 평균의 차이를 제곱하여 더한 값으로 나눈다. 만약 타깃의 평균 정도를 예측하는 수준이라면 $R^2$은 0에 가까워지고, 예측이 타깃에 아주 가까워지면 1에 가까운 값이 된다.
과대적합 vs 과소적합
테스트 점수와 훈련 점수의 차이가 유의미하게 발생하는 경우이다. 모델을 훈련 세트에 훈련하면 훈련 세트에 잘 맞는 모델이 만들어진다. 이 모델을 훈련 세트와 테스트 세트에서 평가하면 보통 훈련 세트의 점수가 조금 더 높게 나온다.
훈련 세트에서 점수가 굉장히 좋았는데 테스트 세트에서는 점수가 굉장히 나쁘다면 모델이 훈련 세트에 과대적합(overfitting)되었다고 말한다. 훈련 세트에만 잘 맞는 모델이라 실전에서 새로운 샘플에 대한 예측을 만들 때 잘 동작하지 않을 것이다.
반대로 훈련 세트보다 테스트 세트의 점수가 높거나 두 점수가 모두 낮은 경우는 과소적합(underfitting)되었다고 말한다.
K-nearest neighbors regression에서 과소적합이 발생한 경우, 모델을 조금 더 복잡하게 만들면 된다. 이웃의 개수인 k를 줄이면 국지적인 패턴에 민감해지고, 개수를 늘리면 데이터 전반에 있는 일반적인 패턴을 따를 것이다.
선형 회귀(linear regression)
k-최근접 이웃의 한계점이 있다. 가장 가까운 샘플을 찾아 타깃을 예측하기에 새로운 샘플이 훈련 세트의 범위를 벗어나면 엉뚱한 값을 에측할 수 있다. k-최근접 이웃을 사용해 이 문제를 해결하려면 새로운 샘플을 포함시킨 훈련 세트를 다시 만들어야 한다. 머신러닝 모델은 주기적으로 훈련해야 한다는 것이 숙명이기는 하지만, 이러한 문제점은 치명적이다.
선형 회귀는 널리 사용되는 대표적인 회귀 알고리즘이다. 특성을 잘 나타낼 수 있는 직선을 찾는 알고리즘이다.
coef_, intercept_와 같은 머신러닝 알고리즘이 찾은 값인 모델 파라미터가 잇다. 많은 머신러닝 알고리즘의 훈련 과정이 최적의 모델 파라미터를 찾는 것과 같다. 이를 모델 기반 학습이라고 부른다. k-최근접 이웃은 모델 파라미터가 없다. 훈련 세트를 저자아하는 것이 훈련의 전부이다. 이를 사례 기반 학습이라고 부른다.
다항 회귀
특성과 타깃의 관계가 선형적이지 않다면? 예를 들어, 2차식의 관계를 가진다면 feature의 제곱값을 가지는 새로운 feature로 선형 회귀를 수행할 수 있다.
다중 회귀(multiple regression)
하나의 특성을 사용하는 것이 아닌, 여러 개의 특성을 사용한 선형 회귀를 다중 회귀라고 부른다. 기존의 특성을 사용해서 새로운 특성을 뽑아내는 작업을 특성 공학(feature engineering)이라고 부른다.
변환기(transformer)
from sklearn.preprocessing import PolynomialFeature
앞서 나온 LinearRegression 같은 사이킷런 모델 클래스는 추정기(estimator)라고 부른다.
poly = PolynomialFeatures()
poly.fit([[2, 3]])
print(poly.transform([[2, 3]]))
>> [[1. 2. 3. 4. 6. 9.]]
transformer도 훈련을 해야 변환이 가능하다. fit() 메서드는 새롭게 만들 특성 조합을 찾고 transform() 메서드는 실제로 데이터를 변환한다. PolynomialFeatures 클래스는 각 특성을 제곱한 항과 특성끼리 서로 곱한 항을 추가한다. 1은 절편의 계수이기 때문에 추가된다.
poly.get_feature_names_out()
>> ['x0', 'x1', 'x2', 'x0^2', 'x1^2', 'x2^2', 'x0 x1', 'x0 x2', 'x1 x2']
get_feature_names_out() 메서드는 특성이 각각 어떤 입력의 조합으로 만들어졌는지 알려 준다. degree 파라미터를 사용하여 필요한 고차항의 최대 차수를 지정할 수 있다. 그러나, 특성의 개수를 크게 늘리면 선형 모델을 아주 강력해진다. 즉, 훈련 세트에 편향적이게 된다. 하지만 너무 과대적합된 것이므로 실용적이지 못하다.
규제(regularization)
모델이 훈련 세트를 너무 과도하게 학습하지 못하도록 훼방하는 것이다. 선형 회귀 모델의 경우 특성에 곱해지는 계수의 크기를 작게 만드는 일이다. 특성의 스케일에 대해 생각해보면 특성의 스케일이 정규화되지 않으면 곱해지는 계수에서도 차이가 나게 된다. 일반적으로 회귀 모델에 규제를 적용할 때 계수 값의 크기가 서로 많이 다르면 공정하게 제어되지 않을 것이다. 따라서 규제를 적용하기 전에 먼저 정규화를 해야한다.
from sklearn.preprocessing import StandardScaler
ss = StandardScaler()
ss.fit(train_poly)
train_scaled = ss.transform(train_poly)
test_scaled= ss.transform(test_poly)
선형 회귀 모델에 규제를 추가한 모델을 릿지(ridge)와 라쏘(lasso)라고 부른다. 두 모델은 규제를 가하는 방법이 다르다. 릿지는 계수를 제곱한 값을 기준으로 규제를 적용하고, 라쏘는 계수의 절댓값을 기준으로 규제를 적용한다. 일반적으로 릿지를 조금 더 선호한다. 두 알고리즘 모두 계수의 크기를 줄이지만 라쏘는 아예 0으로 만들 수도 있다.
릿지와 라쏘 모델을 사용할 때 규제의 양을 임의의 조절할 수 있다. 모델 객체를 만들 때 alpha 파라미터로 규제의 강도를 조절한다. alpha값은 릿지 모델이 학습하는 값이 아니라 사전에 우리가 지정해야 하는 값이다. 이렇게 머신러닝 모델이 학습할 수 없고 사람이 알려줘야 하는 파라미터를 하이퍼파라미터라고 부른다. 적절한 alpha 값을 찾는 한 가지 방법은 alpha 값에 대한 $R^2$값의 그래프를 그려 보는 것이다. 훈련 세트와 테스트 세트의 점수가 가장 가까운 지점이 최적의 alpha 값이 된다. alpha값의 범위가 0.001부터 100까지 등 범위가 크기에 log그래프로 플롯을 그리는 식이다.
4. 다양한 분류 알고리즘
로지스틱 회귀
타깃 데이터에 2개 이상의 클래스가 포함된 문제를 다중 분류(multiclass classification)라고 부른다.
proba = kn.predict_proba(test_scaled) # 사이킷런의 분류 모델을 predict_proba() 메서드롤 클래스별 확률값을 반환
로지스틱 회귀는 이름은 회귀이지만 분류 모델이다. 기본적으로 릿지 회귀와 같이 계수의 제곱을 규제한다. 이런 규제를 L2 규제라고 부른다. 릿지 회귀에선 alpha 파라미터로 규제의 양을 조절했으나, 로지스틱 회귀에서는 C 파라미터이다. 하지만 C는 alpha와 반대로 작을수록 규제가 커진다.
$$ z = a × (Weight) + b × (Length) + c × (Diagonal) + d × (Height) + e × (Width) + f $$
이진 분류에서는 시그모이드(sigmoid)함수를 사용해 z를 0과 1 사이의 값으로 변환하지만, 다중 분류는 소프트맥스(softmax)함수를 사용하여 각 클래스에 대한 z 값을 확률로 변환한다.
확률적 경사 하강법
훈련 데이터가 점점 추가된다고 생각해보자. 계속해서 새로운 훈련 세트를 가지고 모델을 학습시키는 것은 장기적으로 봤을 때 지속 가능한 방법이 아니다. 따라서, 앞서 훈련한 모델을 버리지 않고 새로운 데이터에 대해서만 조금씩 더 훈련하는 방법이 있는데 이를 점진적 학습 또는 온라인 학습이라고 부른다. 대표적인 점진적 학습 알고리즘은 확률적 경사 하강법(stochastic gradient descent)이다. 산에서 내려온다고 가정해볼때, 등산로 입구까지 내려가야 한다. 어떤 산길도 척척 내려갈 수 있는 능력이 있다면 가장 빠른 길을 선택하는 것이 좋다. 가장 빠른 길은 경사가 가장 가파른 길이다. 하지만, 한번에 걸음이 너무 크면 경사를 따라 내려가지 못하고 오히려 올라갈 수 있다. 따라서 천천히 조금씩 내려오는 것이 중요하다. 이 때, 훈련 세트 전체 샘플을 사용하지 않고 딱 하나의 샘플을 랜덤하게 골라 가장 가파른 길을 찾기 때문에 확률적 경사 하강법이다.
이 때, 모든 샘플을 다 사용했는데도 산을 다 내려오지 못했다면 다시 훈련 세트에 모든 샘플을 채워 넣고 이어서 경사를 내려가는 것이다. 훈련 세트를 한 번 모두 사용하는 과정을 epoch라고 부른다. 랜덤한 하나의 샘플이 아닌 몇 개의 샘플을 사용해 경사 하강법을 수행하는 방식을 미니배치 경사 하강법(minibatch gradient descent)이라고 한다. 극단적으로 전체 샘플을 사용할 수도 있는데 이를 배치 경사 하강법이라고 부른다. 가장 안정적인 방법이나 연산량이 매우 커진다.
손실 함수(loss function)
손실함수는 문제에서 머신러닝 알고리즘이 얼마나 엉터리인지를 측정하는 기준이다.
분류에서 손실은 아주 확실하다. 정답을 못 맞히는 경우다. 하지만, 분류의 경우에 정확도가 이산적으로 나타난다. 경사 하강법은 조금씩 내려오는 방법이기에, 이산적이라면 사용할 수가 없다. 따라서, 로지스틱 회귀 모델에서 확률을 출력했듯, 확률을 이용해 손실 함수를 만들 수 있다.
로지스틱 손실 함수
-(샘플의 예측값 * 1(양성 클래스 타깃)). 예측이 1에 가까울수록 예측과 타깃의 곱의 음수는 점점 작아진다. 타깃이 음성 클래스라면 (1-샘플의 예측값)으로 양성 클래스 예측값을 계산할 수 있다. 여기에 로그 함수를 적용하면 더 좋다. 예측 확률의 범위는 0~1 사이인데 로그 함수는 이 사이에서 음수가 되므로 최종 손실 값은 양수가 된다. 또 로그 함수는 0에 가까울수록 아주 큰 음수가 되기 때문에 손실을 아주 크게 만들어 모델에 큰 영향을 미칠 수 있다.
타깃 = 1일때, $-log(예측 확률)$. 타깃 = 0일 때, $-log(1-예측확률)$
이를 로지스틱 손실 함수 또는 이진 크로스엔트로피 손실 함수(binary cross-entropy loss function)라고도 부른다.
SGDClassifier
사이킷런에서 확률적 경사 하강법을 제공하는 대표적인 분류용 클래스이다. 객체를 만들 때 2개의 파라미터를 지정한다. 손실함수와 에포크 횟수이다.
from sklearn.linear_model import SGDClassifier
sc = SGDClassifier(loss = 'log', max_iter = 10, random_state=42)
sc.fit(train_scaled, train_target)
sc.partial_fit(train_scaled, train_target)
모델을 이어서 훈련할 때는 partial_fit() 메서드를 사용한다. 호출할 때마다 1 에포크씩 이어서 훈련할 수 있다. 그러나 무작정 많이 반복할 수는 없다.
적은 에포크 횟수는 과소적합된 모델일 가능성이 높다. 많은 에포크 횟수는 훈련 세트에 편향된 과대적합된 모델일 가능성이 높다. 에포크가 진행될수록 훈련 세트 점수는 꾸준히 증가하지만 테스트 세트 점수는 어느 순간 감소하기 시작한다. 바로 이 지점이 과대적합되기 시작하는 곳이다. 과대적합이 시작하기 전에 훈련을 멈추는 것을 조기 종료(early stopping)라고 한다.
SGDClassifier의 loss 파라미터의 기본값은 hinge이다. 힌지 손실은은 서포트 벡터머신(support vector machine)이라 부릴는 또 다른 머신러닝 알고리즘을 위한 손실 함수이다.
5. 결정 트리(Decision Tree)
결정 트리는 스무고개와 같다. 질문을 하나씩 던져서 정답과 맞춰가는 것이다.
from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier()
dt.fit(train, target)
# 트리 그리기
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(10, 7))
plot_tree(dt, max_depth=1, filled=True, feature_names=['alcohol', 'sugar', 'pH'])
plt.show()
max_depth 파라미터는 루트 노드를 제외하고 그릴 깊이를 지정하며, filled 파라미터는 클래스에 맞게 노드의 색을 칠한다. feature_name 파라미터는 특성의 이름을 전달할 수 있다.
결정 트리에서 예측하는 방법은 리프 노드에서 가장 많은 클래스가 예측 클래스가 된다. 노드 상자 안에 gini라는 것이 있다.
gini는 지니 불순도(Gini impurity)를 의미한다. DecisionTreeClassifier 클래스의 criterion 매겨변수의 기본값이 'gini'이다. 노드에서 데이터를 분할할 기준을 정하는 척도이다.
$$ 지니 불순도 = 1 - ({음성 클래스 비율}^2 + {양성 클래스 비율}^2) $$
이게 끝이다. 다중 클래스 문제라면 클래스가 더 많겠지만 계산하는 방법은 동일하다. 만약 두 클래스의 비율이 1/2씩이라면 지니 불순도는 0.5가 되어 최악이 된다. 노드에 하나의 클래스만 있다면 지니 불순도는 0이 되고, 이런 노드를 순수 노드라고 한다. 결정 트리 모델은 부모 노드와 자식 노드의 불순도 차이가 가능한 크도록 트리를 성장시킨다. 부모와 자식 노드 사이의 불손도 차이를 정보 이득(information gain)이라고 부른다.
사이킷런에는 또 다른 불순도 기준인 엔트로피 불순도가 있다. 지니 불순도처럼 제곱이 아니라 밑이 2인 로그를 사용하여 곱한다. 보통 지니 불순도와 엔트로피 불순도가 만든 결과의 차이가 크지 않다.
가지치기
결정 트리에서 가지치기를 하지 않으면 무작정 끝까지 자라나는 트리가 만들어진다. 이는 오버피팅을 유발한다. 가장 간단한 방법은 자라날 수 있는 트리의 최대 깊이를 지정하는 것이다. 결정 트리 알고리즘에서는 특성값의 스케일이 아무런 영향을 미치지 않는다. 따라서 표준화 전처리를 할 필요가 없고 이것이 결정 트리 알고리즘의 장점 중 하나이다. 또 다른 장점은 특성 중요도를 활용하여 특성 선택에 활용할 수 있다라는 것이다.
교차 검증(cross validation)
교차 검증이란 훈련 세트를 여러 부분으로 나눠서 교차적으로 검증에 사용하는 방법이다. k-fold cross validation은 훈련 세트를 k부분으로 나누는 것이다.
from sklearn.model_selection import cross_validate
scores = cross_validate(dt, train_input, train_target)
이 함수는 fit_time, score_time, test_score 키를 가진 딕셔너리를 반환한다. 기본적으로 5-fold cross validation을 수행한다. cv 파라미터를 수정하여 폴드 수를 바꿀 수도 있다. test_score에 담긴 점수들을 평균하여 최종 점수를 얻을 수 있다.
하이퍼 파라미터 튜닝
모델마다 적게는 1~2개에서, 많게는 5~6개의 매개변수를 제공한다. 이 매개변수를 바꿔가며 모델을 훈련하고 교차 검증을 수행해서 최적의 값을 찾는다. 만약, 한 매개변수의 최적값을 찾고 그 값을 고정하여 다른 매개변수의 최적값을 찾는다고 가정하면, 기존에 찾은 최적값이 그대로 최적값일 수가 없다. 이러한 과정을 반복문으로 직접 구현해도 되겠지만, 사이킷런에서 그리드 서치(Grid Search)를 제공한다.
from sklearn.model_selection import GridSearchCV
params = {'min_impurity_decrease' : [0.0001, 0.0002, 0.0003, 0.0004, 0.0005]}
gs = GridSearchCV(DecisionTreeClassifier(), params, n_jobs=1)
gs.fit(train_input, train_target)
fit() 메서드를 호출하면 그리드 서치 객체는 params값을 바꿔가며 실행한다. GridSearchCV의 cv 파라미터 기본값은 5이다. 따라서, params 1개의 값마다 5-fold cross validation을 수행하기에 이러한 경우에는 5 × 5 = 25개의 모델을 훈련한다. n_jobs 파라미터를 지정해 병렬 실행에 사용할 CPU 코어 수를 지정한다. 기본값은 1이며, -1을 지정하면 모든 코어를 사용한다. 또한, 그리드 서치는 훈련이 끝나면 25개의 모델 중에서 검증 점수가 가장 높은 모델의 매개변수 조합으로 전체 훈련 세트에서 자동으로 다시 모델을 훈련한다. 그러나, 여기서 매개변수의 값이나 간격이 임의적이지, 근거가 없다. 좀 더 좁거나 넓은 간격으로 시도해 볼 수 있지 않을까?
랜덤 서치
매개변수 값의 목록을 전달하는 것이 아닌 매개변수를 샘플링할 수 있는 확률 분포 객체를 전달한다. 싸이파이의 sats 서브 패키지에 있는 uniform과 randint 클래스는 모두 주어진 범위에서 고르게 값을 뽑는다. randint는 정숫값을 뽑고, uniform은 실숫값을 뽑습니다.
from scipy.stats import uniform, randint
rgen = randint(0, 10)
rgen.rvs(10)
>> array([6, 4, 2, 2, 7, 7, 0, 0, 5, 4])
난수 발생기랑 유사하게 생각하면 된다. params를 이렇게 설정하고, RandomizedSearchCV의 n_iter 파라미터를 통해 매개변수 조합에 대한 시행 횟수를 설정하여 최적의 파라미터 조합을 찾을 수 있다.
트리의 앙상블
정형 데이터와 비정형 데이터
특정한 구조로 되어 있는 데이터를 정형 데이터라고하고, 이와 반대되는 데이터를 비정형 데이터라고 부른다. 비정형 데이터의 예는 책의 글과 같은 텍스트 데이터, 사진, 음악 등이 있다.
정형 데이터를 다루는 데 가장 뛰어난 성과를 내는 알고리즘이 앙상블 학습이다. 이 알고리즘은 대부분 결정 트리를 기반으로 만들어져 있다. 비정형 데이터의 경우 신경망 알고리즘을 통해 학습을 한다.
랜덤 포레스트(Random Forest)
nbsp;
앙상블 학습의 대표 주자 중 하나로 안정적인 성능 덕분에 널리 사용되고 있다. 결정 트리를 랜덤하게 만들어 결정 트리의 숲을 만든다. 각 결정 트리의 예측을 사용해 최종 예측을 만든다.
랜덤 포레스트는 각 트리를 훈련하기 위한 데이터를 랜덤하게 만드는데, 훈련 데이터에서 랜덤하게 샘플을 추출하여 훈련 데이터를 만든다. 이때 한 샘플이 중복되어 추출될 수도 있다. 이렇게 만들어진 샘플을 부트스트램 샘플(bootstrap sample)이라고 부른다. 기본적으로 부트스트랩 샘플은 훈련 세트의 크기와 같게 만든다. 또한 각 노드를 분할할 때 전체 특성 중에서 일부 특성을 무작위로 고른 다음 이 중에서 최선의 분할을 찾는다. 분류 모델인 RandomForestClassifier는 기본적으로 전체 특성 개수의 제곱근만큼의 특성을 선택한다. 다만 회귀 모델인 RandomForestRegressor는 전체 특성을 사용한다. 그다음 분류일 때는 각 트리의 클래스별 확률을 평균하여 가장 높은 확률을 가진 클래스를 예측으로 삼는다. 랜덤 포레스트는 랜덤하게 선택한 샘플과 특성을 사용하기 때문에 훈련 세트에 과대적합되는 것을 막아주고 검증 세트와 테스트 세트에서 안정적인 성능을 얻을 수 있다.
cross_validate() 함수를 사용해 교차 검증을 수행해보자. 기본적으로 100개의 결정 트리를 사용하므로 n_jobs 파라미터를 -1로 지정하여 모든 CPU 코어를 사용하고, cross_validate() 메서드의 n_jobs도 -1로 지정하여 최대한 병렬로 교차 검증을 수행해보자. 또 return_train_score 파라미터를 True로 지정하면 검증 점수뿐만 아니라 훈련 세트에 대한 점수도 같이 반환하여 과대적합을 파악하는 데 용이하다.
from sklearn.model_selection import cross_validate
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(n_jobs = -1)
scores = cross_validate(rf, train_input, train_target,return_train_score=True, n_jobs = -1)
>> 0.9973 0.8905
결과를 보면 다소 과대적합된 것 같다. 랜덤 포레스트는 결정 트리의 앙상블이기 때문에 DecisionTreeClassifier가 제공하는 파라미터를 모두 제공한다. criterion, max_depth, max_features, min_samples_split, min_impurity_decrease, min_samples_leaf 등이다. 또한 결정 트리의 큰 장점 중 하나인 특성 중요도를 계산한다. 같은 케이스를 결정 트리에서 만든 특성 중요도와 랜덤 포레스트에서 만든 특성 중요도를 비교해보면 랜덤 포레스트는 특성의 일부를 랜덤하게 선택하여 결정 트리를 훈련하기에 하나의 특성에 과도하게 집중하지 않고 좀 더 많은 특성이 훈련에 기여할 기회를 얻는다. 이는 과대적합을 줄이고 일반화 성능을 높이는 데 도움이 된다. 또한, 랜덤 포레스트는 자체적으로 모델을 평가하는 점수를 얻을 수 있다. 부트스트랩 샘플을 만들어 결정 트리를 훈련하는데 이때 부트스트랩 샘플에 포함되지 않고 남는 샘플이 있다. 이런 샘플을 OOB(Out Of Bag)샘플이라고 한다. 이 샘플을 검증 세트처럼 사용할 수 있다. 이 점수를 얻으려면 oob_score 파라미터를 True로 지정하면 된다.
엑스트라 트리
랜덤 포레스트와 매우 비슷하게 동작한다. 기본적으로 100개의 결정 트리를 훈련한다. 랜덤 포레스트와 차이점은 부트스트랩 샘플을 사용하지 않는다는 점이다. 즉 각 결정 트리를 만들 때 전체 훈련 세트를 사용한다. 대신 노드를 분할할 때 가장 좋은 분할을 찾는 것이 아니라 부작위로 분할한다. 보통 엑스트라 트리가 무작위성이 좀 더 크기 때문에 랜덤 포레스트보다 더 많은 결정 트리를 훈련히야 한다. 하지만 랜덤하게 노드를 분할하기 때문에 빠른 계산 속도가 엑스트라 트리의 장점이다.
그래디언트 부스팅(gradient boosting)
깊이가 얕은 결정 트리를 사용하여 이진 트리의 오차를 보완하는 방식으로 앙상블 하는 방법이다. 사이킷런의 GradientBoostingClassifier는 기본적으로 깊이가 3인 결정 트리를 100개 사용한다. 깊이가 얕은 결정 트리를 사용하기에 과대적합에 강하고 일반적으로 높은 일반화 성능을 기대할 수 있다. 그래디언트에서 알 수 있듯 경사 하강법을 사용해 트리를 앙상블에 추가한다. 분류에서는 로지스틱 손실 함수를 사용하고 회귀에서는 평균 제곱 오차 함수를 사용한다. n_estimators파라미터로 결정 트리의 개수를 설정할 수 있고, learning_rate 파라미터로 학습률을 조정할 수 있다. subsample파라미터의 기본값은 1.0으로 훈련 세트 전체를 사용한다는 뜻이다. 이 파라미터를 조정하여 확률적 경사 하강법이나 미니배치 경사 하강법과 유사하게 작동할 수 있다. 일반적으로 랜덤 포레스트보다 조금 더 높은 성능을 얻을 수 있다. 하지만 순서대로 트리를 추가하기 때문에 훈련 속도가 느리다.
히스토그램 기반 그레디언트 부스팅(Histogram-based Gradient Boosting)
정형 데이터를 다루는 머신러닝 알고리즘 중에 가장 인기가 높은 알고리즘이다. 입력 특성을 256개의 구간으로 나누어 최적의 분할을 매우 빠르게 찾을 수 있다. 256개의 구간 중에서 하나를 떼어 놓고 누락된 값을 위해서 사용한다. 따라서 입력에 누락된 특성이 있더라도 이를 따로 전처리할 필요가 없다. 트리의 개수를 지정하는데 앞서 사용한 n_estimators 파라미터가 아닌 max_iter를 통해 부스팅 반복 횟수를 지정한다.
사이킷런 말고도 그레디언트 부스팅 알고리즘을 구현한 라이브러리가 여럿 있는데 대표적인 라이브러리는 XGBoost이다. 이 라이브러리는 cross_validate() 메서드도 함께 사용할 수 있으며 다양한 부스팅 알고리즘을 지원한다. 또, MS에서 만든 LightGBM이 있다. LightGBM은 빠르고 최신 기술을 많이 적용하고 있어 인기가 점점 높아지고 있다.