[파이썬 머신러닝 완벽 가이드 (권철민)]
Chapter 04 분류
01. 분류(Classification)의 개요
지도학습이란 label과 같은 명시적인 정답이 있는 데이터가 주어진 상태에서 학습하는 머신러닝 학습 방법이다.
지도학습의 대표적인 유형인 분류(Cassification)는 학습 데이터로 주어진 데이터의 feature와 label값(결정 값, 클래스 값)을 ML 알고리즘으로 학습해 모델을 생성하고, 이렇게 생성된 모델에 새로운 데이터가 주어질 때 미지의 label 값을 예측하는 것이다.
분류의 다양한 ML 알고리즘으로 구현 할 수 있다.
- 베이즈(Bayes) 통계와 생성 모델에 기반한 나이브 베이즈(Naive Bayes)
- 독립변수와 종속변수의 선형 관계성에 기반한 로지스틱 회귀(Logistic Regression)
- 데이터 균일도에 따른 규칙 기반의 결정 트리(Decision Tree)
- 개별 class 간의 최대 분류 마진을 효과적으로 찾아주는 서포트 벡터 머신(Support Vector Machine)
- 근접 거리를 기준으로 하는 최소 근접(Nearest Neighbor) 알고리즘
- 심층 연결 기반의 신경망(Neural Network)
- 서로 다른(또는 같은) ML 알고리즘을 결합한 앙상블(Ensemble)
이번 장에서는 다양한 알고리즘 중에서 앙상블 방법(Ensemble Method)을 집중적으로 다룹니다.
앙상블은 분류에서 가장 각광을 받는 방법 중 하나이다.
물론 이미지, 영상, 음성, NLP 영역에서 신경망에 기반한 딥러닝이 머신러닝계를 선도하고 이지만. 이를 제외한 정형 데이터의 예측 분석 영역에서는 앙상블이 매우 높은 예측 성능으로 인해 많은 분석가와 데이터 과학자들에게 애용되고 있다.
앙상블은 서로 다른(또는 같은) 알고리즘을 단순히 결합한 형태도 있으나, 일반적으로 배깅(Bagging)과 부스팅(Boosting) 방식으로 나뉜다.
배깅 방식의 대표가 랜덤 포레스트(Random Forest)이며 뛰어난 예측 성능, 빠른 수행시간 등으로 많이 애용된다.
하지만 근래에는 부스팅 방식으로 지속 발전하고 있다.
가장 처음의 부스팅 방식인 그래디언트 부스팅(Gradient Boosting)은 뛰어난 예측 성능을 가졌지만 수행 시간이 너무 오래 걸려 최적화 튜닝이 어려웠다. 하지만 XgBoost(eXtra Gradient Boost)와 LightBGM 등 기존 그래디언트 부스팅의 예측 성능을 한 단계 발전시키면서도 수행 시간을 단축시킨 알고리즘이 계속 등장하면서 정형 데이터의 분류 영역에서 가장 활용도가 높은 알고리즘으로 자리잡았다.
이 자에서는 앙상블 방법의 개용와 랜덤 포레스트, 그래디언트 부스팅의 전통적인 앙상블 기법뿐만 아니라 부스팅 계열의 최신기법인 XGBoost와 LightGBM, 그리고 앙상블의 앙상블이라고 불리는 스태킹(Sackinh) 기법에 대해서도 상세히 알아보겠습니다.
앙상블은 서로 다른/또는 같은 알고리즘을 겨합한다고 했는데, 대부분은 동이란 알고리즘을 곃합합니다. 앙상블의 기본 알고리즘으로 일반적으로 사용하는 것은 결정 트리입니다.
02. 결정트리(Decision Tree)
- 데이터에 있는 규칙을 학습을 통해 자동으로 찾아내 분류 규칙을 만들고, 트리 형태로 분류 된 구조를 만들어 내는 것
- 규칙을 가장 쉽게 표현 하는 방법으로 if / else 를 사용하여 표현 하는 것 입니다.
- 즉, if / else 기준을 데이터로 부터 자동으로 찾아내, 예측을 위한 규측을 만드는 알고리즘
- 알고리즘 성능 : 규칙을 어떻게 만드냐가 결정
결정 트리(Decision Tree)는 ML 알고리즘 중 직관적으로 이애하기 쉬운 알고리즘이다. 데이터에 있는 규칙을 학습을 통해 자동으로 찾아내는 트리(Tree) 기반의 분류 규칙을 만드는 것이다. 일반적으로 규칙을 가장 쉽게 표현하는 방법은 if/else 기반으로 나타내는 것인데, 쉽게 생각하면 스무고개 게임과 유사하며 룰 기반의 프로그램에 적용되는 if/else를 자동으로 찾아내서 예측을 위한 규칙을 만드는 알고리즘으로 이해하면 쉽다. 따라서 데이터의 어떤 기준을 바탕으로 규칙을 만들어 내는지에 따라 알고리즘의 성능이 크게 좌우된다.
위 이미지는 결정 트리의 구조를 간략하게 나타낸 것이다.
규칙 노드(Decision Node)는 규칙 조건이 되고, 리프 노드(Leaf Node)는 결정 class 값이다. 그리고 새로운 구칙 조건마다 서브 트리(Sub Tree)가 생성된다. 데이터 세트에 피처가 있고 이러한 피처가 결합하여 규칙 조건을 만들 때마다 규칙 노드가 만들어지는데 많은 규칙이 있다는 것은 그만큼 결정 트리가 복잡해진다는 것이기 때문에 과적합이 되기 쉽다. 즉, 트리의 깊이(depth)가 깊어질수록 결정 트리의 예측 성능이 저하될 수 있다. 따라서 가능한 적은 결정 노드로 높은 정확도를 가져야 하기 때문에 데이터를 분류할 때 최대한 많은 데이터세트가 해당 분류에 속할 수 있도록 결정 노드의 규칙이 정해져야 한다. 이를 위해서 트리(Tree)를 어떻게 분할(Split)할 지가 중요한데 최대한 균일한 데이터세트를 구성할 수 있도록 분할하는 것이 필요하다.
데이터세트의 균일도는 데이터를 구분하는데 필요한 정보의 양에 영향을 미친다.
위 그림에서 무작위로 데이터셋트 C에서 데이터를 뽑는다면 데이터에 대한 정보가 없어도 검은 공일 것이라고 간단하게 예측이 가능하다. 하지만 A에서 뽑는다면 상대적으로 균일도가 낮아서 같은 조건으로 데이터를 판단할 경우 더 많은 정보가 필요하다.
즉 C > B > A 순으로 균일도가 높다.
결정 노드는 정보 균일도가 높은 데이터세트를 먼저 선택할 수 있도록 규칙 조건을 만듭니다.. 즉, 정보 균일도가 데이터세트가 쪼개질 수 있도록 조건을 찾아 서브 데이터세트를 만들고, 다시 서브 데이터세트에서 균일도가 높은 자식 데이터세트로 쪼개는 방식을 자식 트리로 내려가면서 반복하는 방식으로 데이터 값을 예측한다.
이러한 정보의 균일도를 측정하는 대표적인 방법은 엔트로피를 이용한 정보 이득(Information Gain) 지수와 지니 계수가 있다.
• 정보 이득은 엔트로피라는 개념을 기반으로 한다. 엔트로피는 주어진 데이터 집합의 혼잡도를 의미하는데, 서로 다른 값이 섞여 있으면 엔트로피가 높고, 같은 값이 섞여 있으면 엔트로피가 낮다. 정보 이득 지수는 1에서 엔트로피 지수를 뺀 값이다. 즉, 1 - 엔트로피 지수이다. 결정 트리는 이 정보 이득 지수로 분할 기준을 정한다.
즉, 정보 이득이 높은 속성을 기준으로 분할한다.
m개의 레코드가 속하는 박스에 대한 엔트로피는 아래와 같은 식으로 정의됩니다.
(Pk=박스에 속하는 레코드 가운데 k 범주에 속하는 레코드의 비율)
$$
E = -\sum_{k=1}^m p_k log_2(p_k)
$$
전체 16개(m=16) 가운데 빨간색 동그라미(범주=1)는 10개, 파란색(범주=2)은 6개일때,
$$
E = - {10 \over 16} {log_2 {10 \over 16} } - {6 \over 16} {log_2 {6 \ \over 16} } \approx 0.95
$$
• 지니 계수는 원래 경제학에서 불평등 지수를 나타낼 때 사용하는 계수이다. 경제학자인 코라도 지니(Corrado Gini)의 이름에서 딴 계수로서 0이 가장 평등하고 1로 갈수록 불평등하다.
머신러닝에 적용될 때는 지니 계수가 낮을수록 데이터 균일도가 높은 것으로 해석해 지니 계수가 낮은 속성을 기준으로 분할한다.
지니 계수 / <파이썬을 이용한 빅데이터 분석, 유성준 외, 21세기사>
$$
G = 1 - \sum_{j=1}^c {({n_j \over n})^2}
$$
여기서 C는 범주수, n은 노드에 속하는 개체수, nj 는 개체중 노드의 범주에 속하는 개체 수
예> 맑음, 습함, 구름의 인자로 테니스 게임이 열릴지 여부 판단하는 경우
맑음은 경우가 총 5일 인데, 이중 열린 날이 3, 안 열린 날이 2 이면,
$$
G = 1 - \left\{ \, \left( \frac{3}{5} \right)^2 + \left( \frac{2}{5} \right)^2\,\right\}
$$
사이킷런에서 구현한 결정 트리 알고리즘인 DecisionTreeClassifier는 기본으로 지니 계수를 이용해 데이터세트를 분할한다. 결정 트리의 일반적인 알고리즘은 데이터세트를 분할하는 데 가장 좋은 조건인 정보 이득이 높거나 지니 계수가 낮은 조건을 찾아서 자식 트리 노드에 걸쳐 반복적으로 분할한 뒤, 데이터가 모두 특정 분류에 속하게 되면 분할을 멈추고 분류를 결정한다.
결정 트리 모델의 특징
결정 트리 장점 | 결정 트리 단점 |
1. 쉽다. 직관적이다. 2. 피처의 스케일링이나 정규화 등의 사전 가공 영향도가 크지않다. |
1. 과적합으로 알고리즘 성능이 떨어진다. 이를 극복하기 위해 트리의 크기를 사전에 제한하는 튜닝이 필요하다. |
결정 트리 파라미터
- min_sample_split [default=2]
- 분할되기 위해 노드가 가져야 최소 샘플 수
- 과적합 제어에 사용
- 작게 설정할수록 분할되는 노드 많아져 과적합 증가
- min_smaples_leaf
- 말단 노드(leaf)가 되기 위한 가지고 있어야 할 최소 샘플 수
- 과적합 제어용
- 비대칭적 데이터의 경우 특정 클래스 데이터 매우 작을 수 있으므로 작게 설정해야 함
- max_features
- 각 노드에서 분할에 사용할 특성의 최대 수
- 디폴트는 None : 데이터셋의 모드 피처 대상 분할 수행
- inf, float, log 등 옵션 있음
- max_depth
- 트리의 최대 깊이 설정
- 디폴트는 None : 완벽하게 클래스 결정값이 될때까지 깊이를 계속 키우며 분할. min_samples_split 보다 작아질 때까지 깊이 증가시킴
- 깊이 깊어지면 과적합 가능
- max_leaf_nodes : 리프 노드의 최대수
- criterion='gini' : 정보의 균일도 측정 방법
결정 트리 모델의 시각화
- Graphviz 패키지 사용
- export_graphviz() API 제공 : 함수 인자로써 학습이 완료된 estimator, 피처 이름 리스트, 레이블 이름 리스트 입력
- 파이썬과 인터페이스할 수 있는 Wrapper 모듈을 별도 설치 해야함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
from sklearn.datasets import load_iris
from sklearn.tree import export_graphviz
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
import warnings
warnings.filterwarnings('ignore')
# DecisionTree Classifier 생성
dt_clf = DecisionTreeClassifier(random_state=156)
# 붓꽃 데이터를 로딩하고, 학습과 테스트 데이터 셋으로 분리
iris_data = load_iris()
X_train , X_test , y_train , y_test = train_test_split(iris_data.data, iris_data.target, test_size=0.2, random_state=11)
# DecisionTreeClassifer 학습.
dt_clf.fit(X_train , y_train)
# export_graphviz()의 호출 결과로 out_file로 지정된 tree.dot 파일을 생성함.
export_graphviz(dt_clf, out_file="tree.dot", class_names=iris_data.target_names, feature_names=iris_data.feature_names, impurity=True, filled=True)
import graphviz
# 위에서 생성된 tree.dot 파일을 Graphviz 읽어서 Jupyter Notebook상에서 시각화
with open("tree.dot") as f:
dot_graph = f.read()
graphviz.Source(dot_graph)
|
cs |
출력된 결과로 트리의 브랜치(branch) 노드와 말단 리프(leaf) 노드가 어떻게 구성되어 있는지 알 수 있다.
자식 노드가 있으면 브랜치 노드이고, 없으면 리프 노드이다.
• petal length(cm) <= 2.45 와 같이 조건이 있는 것은 자식노드를 만들기 위한 규칙 조건이다.
이 조건이 없으면 리프 노드이다.
• gini는 다음의 value안의 데이터 분포에서의 지니 계수이다.
• samples는 현재 노드의 데이터 수이다.
• value = [ ]는 class 값 기반의 데이터 수이다. 붓꽃 데이터세트는 class값으로 0, 1, 2 가지기 때문에 길이가 3이다.
그리고 트리 구조의 첫 줄 노드부터 깊이(depth)를 1로 새어 위의 이미지와 같은 트리 구조는 깊이가 6인 트리 구조이며,
4번 노드를 보면 value가 [0, 37, 1]로 되어 이를 구분하기 위해 자식노드를 다시 생성한다.
이처럼 결정 트리는 완벽한 규칙을 찾기 위해 트리 노드를 계속 만들기 때문에 매우 복잡한 트리 구조를 만들기 쉬워져서 과적합이 상당히 많이 일어나는 ML 알고리즘이다.
그래서 결정 트이 알고리즘을 제어하는 대부분의 hyper parameter는 복잡한 tree가 생성되는 것을 막기 위한 용도이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# max_dapth = 3
dt_clf = DecisionTreeClassifier(random_state=156,
max_depth=3)
# 붓꽃 데이터를 로딩하고, 학습과 테스트 데이터 셋으로 분리
iris_data = load_iris()
X_train , X_test , y_train , y_test = train_test_split(iris_data.data, iris_data.target, test_size=0.2, random_state=11)
# DecisionTreeClassifer 학습.
dt_clf.fit(X_train , y_train)
# export_graphviz()의 호출 결과로 out_file로 지정된 tree.dot 파일을 생성함.
export_graphviz(dt_clf, out_file="tree.dot", class_names=iris_data.target_names, feature_names=iris_data.feature_names, impurity=True, filled=True)
import graphviz
# 위에서 생성된 tree.dot 파일을 Graphviz 읽어서 Jupyter Notebook상에서 시각화
with open("tree.dot") as f:
dot_graph = f.read()
graphviz.Source(dot_graph)
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# min_samples_leaf = 4
dt_clf = DecisionTreeClassifier(random_state=156,
min_samples_leaf=4)
# 붓꽃 데이터를 로딩하고, 학습과 테스트 데이터 셋으로 분리
iris_data = load_iris()
X_train , X_test , y_train , y_test = train_test_split(iris_data.data, iris_data.target, test_size=0.2, random_state=11)
# DecisionTreeClassifer 학습.
dt_clf.fit(X_train , y_train)
# export_graphviz()의 호출 결과로 out_file로 지정된 tree.dot 파일을 생성함.
export_graphviz(dt_clf, out_file="tree.dot", class_names=iris_data.target_names, feature_names=iris_data.feature_names, impurity=True, filled=True)
import graphviz
# 위에서 생성된 tree.dot 파일을 Graphviz 읽어서 Jupyter Notebook상에서 시각화
with open("tree.dot") as f:
dot_graph = f.read()
graphviz.Source(dot_graph)
|
cs |
리프 노드 : 자식 노드가 없는 노드 // min_samples_split는 리프 노드가 될 수 있는 최소한의 samples의 수를 말한다. 따라서 위와 같은 경우엔 분할하여 자식 노드로 만들어 질 경우 해당 자식 노드들 안의 samples의 수가 최소 4 이상이어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# min_samples_split = 4
dt_clf = DecisionTreeClassifier(random_state=156,
min_samples_split=4)
# 붓꽃 데이터를 로딩하고, 학습과 테스트 데이터 셋으로 분리
iris_data = load_iris()
X_train , X_test , y_train , y_test = train_test_split(iris_data.data, iris_data.target, test_size=0.2, random_state=11)
# DecisionTreeClassifer 학습.
dt_clf.fit(X_train , y_train)
# export_graphviz()의 호출 결과로 out_file로 지정된 tree.dot 파일을 생성함.
export_graphviz(dt_clf, out_file="tree.dot", class_names=iris_data.target_names, feature_names=iris_data.feature_names, impurity=True, filled=True)
import graphviz
# 위에서 생성된 tree.dot 파일을 Graphviz 읽어서 Jupyter Notebook상에서 시각화
with open("tree.dot") as f:
dot_graph = f.read()
graphviz.Source(dot_graph)
|
cs |
min_samples_split는 자식 노드를 만들기 위한 최소한의 samples의 수이다.
min_samples_split = 4로 설정하면 최소 4개의 sample이 있어야 자식 노드를 만들 수 있다
노드가 분할될 경우 자식 노드들이 모든 샘플 데이터 건수가 4 이상을 만족할 수 이는지를 확인한 후에 분할을 수행하게 됩니다. 결정 트리는 해당 조건을 반영하여 트리 구조를 변경합니다.
* feature_importance_
결정 트리는 균일도에 기반하여 어떠한 속성을 규칙 조건으로 선택하느냐가 중요한 요건이다. 중요한 몇 개의 피처가 명확한 규칙 트리를 만드는 데 크게 기여하며, 모델을 간결하게 만들어 준다. 사이킷런은 이러한 규칙을 정하는데 있어 각 피처들의 중요도를 feature_importances_를 통해 제공한다.
결정 트리는 알고리즘 자체가 직관적이기 때문에 관련된 요소를 시각적으로 표혀날 수 있는 다양한 방안이 있습니다.
앞서 진행한 규칙 트리의 시각화와 feature_importance_속성을 통해 결정 트리 알고리즘이 어떻게 동작하는지 직관적으로 이해할 수 있습니다.
결정 트리 과적합 (Overfitting)
결정 트리가 어떻게 학습 데이터를 분할해 예측을 수행하고 이로 인한 과적합 문제를 시각화해 알아보겠습니다.
먼저 분류를 위한 데이터세트를 만들어야 하는데 사이킷런은 분류를 위한 테스트 용 데이터를 쉽게 만들 수 있도록 make_classification() 함수를 제공한다.
make_classification() : 테스트 데이터 발생기
분류를 위한 테스트용 데이터 생성 기능 제공
2개의 피처가 3가지 클래스를 갖는 데이터 세트 생성
1
2
3
4
5
6
7
8
9
10
11
12
|
from sklearn.datasets import make_classification
import matplotlib.pyplot as plt
%matplotlib inline
plt.title("3 Class values with 2 Features Sample data creation")
# 2차원 시각화를 위해서 feature는 2개, 결정값 클래스는 3가지 유형의 classification 샘플 데이터 생성.
X_features, y_labels = make_classification(n_features=2, n_redundant=0, n_informative=2,
n_classes=3, n_clusters_per_class=1,random_state=0)
# plot 형태로 2개의 feature로 2차원 좌표 시각화, 각 클래스값은 다른 색깔로 표시됨.
plt.scatter(X_features[:, 0], X_features[:, 1], marker='o', c=y_labels, s=25, cmap='rainbow', edgecolor='k')
|
cs |
- 각 피처가 X, Y축으로 나열된 2차원 그래프이며, 3개의 클래스 값 구분은 색으로 구분
- X_features와 y_labels 데이터 세트를 기반으로 결정 트리를 학습
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
def visualize_boundary(model, X, y):
fig,ax = plt.subplots()
# 학습 데이타 scatter plot으로 나타내기
ax.scatter(X[:, 0], X[:, 1], c=y, s=25, cmap='rainbow', edgecolor='k',
clim=(y.min(), y.max()), zorder=3)
ax.axis('tight')
ax.axis('off')
xlim_start , xlim_end = ax.get_xlim()
ylim_start , ylim_end = ax.get_ylim()
# 호출 파라미터로 들어온 training 데이타로 model 학습 .
model.fit(X, y)
# meshgrid 형태인 모든 좌표값으로 예측 수행.
xx, yy = np.meshgrid(np.linspace(xlim_start,xlim_end, num=200),np.linspace(ylim_start,ylim_end, num=200))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
# contourf() 를 이용하여 class boundary 를 visualization 수행.
n_classes = len(np.unique(y))
contours = ax.contourf(xx, yy, Z, alpha=0.3,
levels=np.arange(n_classes + 1) - 0.5,
cmap='rainbow', clim=(y.min(), y.max()),
zorder=1)
|
cs |
- visualize_boundary 함수 생성
1
2
3
4
5
|
from sklearn.tree import DecisionTreeClassifier
# 특정한 트리 생성 제약 없는 결정 트리의 학습과 결정 경계시각화.
dt_clf = DecisionTreeClassifier().fit(X_features, y_labels)
visualize_boundary (dt_clf, X_features, y_labels)
|
cs |
- 일부 이상치(Outlier) 데이터까지 분류하기 위해 분할이 많이 일어나 결정 기준 경계가 많아진 것을 볼 수 있다.
이렇게 복잡한 모델은 학습 데이터 세트의 특성과 약간만 다른 형태의 데잍 ㅓ세트를 예측하면 예측 정확도가 떨어지게 된다.
따라서 이번엔 min_samples_leaf를 6으로 설정하고 해보았다.
1
2
3
4
|
from sklearn.tree import DecisionTreeClassifier
dt_clf = DecisionTreeClassifier(min_samples_leaf=6).fit(X_features, y_labels)
visualize_boundary(dt_clf, X_features, y_labels)
|
cs |
min_samples_leaf = 6으로 늘려서 노드 생성 규칙을 단순화 처리
이상치에 크게 반응하지 않고, 일반화된 분류 규칙에 따라 분류됨
다양한 테스트 데이터 세트를 기반으로 한 결정 트리 모델의 예측 성능은 첫 번째 모델보다는 min_samples_leaf=6으로 트리 생성 조건을 제약한 모델이 더 뛰어날 가능성이 높다.
왜냐하면 테스트 데이터 세트는 학습 데이터 세트와는 다른 데이터 세트인데 학습 데이터에만 지나치게 최적화된 분류기준은 오히려 테스트 데이터 세트에서 정확도를 떨어뜨릴 수 있기 때문이다.
'데이터 공부 > 머신러닝 공부' 카테고리의 다른 글
파이썬 머신러닝 완벽 가이드(권철민) - Chapter 03 평가 -② (0) | 2024.04.19 |
---|---|
통계적 머신러닝 - Sklearn /데이터 인코딩 (0) | 2024.04.18 |
통계적 머신러닝 - Sklearn / 교차분석/ KFold / cross_val_score (0) | 2024.04.17 |
01. 머신러닝(marchine learning)이란? (1) | 2024.04.15 |