하나의 모델만 사용할 때보다 더 좋은 결과를 낼 수 있도록 여러 가지 모델을 조합하는 방법
규칙에 따라 데이터를 분할하는 분기를 만들고 분기에 따라 데이터를 세분화해 최종적으로 의사 결정을 내릴 수 있도록 구조화하는 방법이다. 학습을 통해 데이터 분기를 구축하고 데이터를 분류해 결과값(레이블)을 예측한다. 학습 알고리즘은 학습 데이터의 입력 데이터와 타깃 레이블 사이의 관계에 따라 규칙을 생성하고 이러한 규칙은 트리의 노드로 표현된다.
데이터를 이용해 최적의 트리를 구축하기 위해서는 엔트로피라는 개념을 알아야 한다. 여기서 엔트로피란 정보의 불확실성 척도를 의미한다. 의사 결정 트리는 각 층에서 불확실성을 줄일 수 있도록 구축되어야 한다. 즉, 트리 아래로 이동할수록 엔트로피가 줄어들어야 한다는 것이다.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import classification_report
from sklearn import cross_validation
from sklearn.tree import DecisionTreeClassifier
from utilities import visualize_classifier
# Load input data
input_file = 'data_decision_trees.txt'
data = np.loadtxt(input_file, delimiter=',')
X, y = data[:, :-1], data[:, -1]
# Separate input data into two classes based on labels
class_0 = np.array(X[y==0])
class_1 = np.array(X[y==1])
# Visualize input data
plt.figure()
plt.scatter(class_0[:, 0], class_0[:, 1], s=75, facecolors='black',
edgecolors='black', linewidth=1, marker='x')
plt.scatter(class_1[:, 0], class_1[:, 1], s=75, facecolors='white',
edgecolors='black', linewidth=1, marker='o')
plt.title('Input data')
# Split data into training and testing datasets
X_train, X_test, y_train, y_test = cross_validation.train_test_split(
X, y, test_size=0.25, random_state=5)
# Decision Trees classifier
params = {'random_state': 0, 'max_depth': 4}
classifier = DecisionTreeClassifier(**params)
‘classifier.fit(X_train, y_train)
visualize_classifier(classifier, X_train, y_train, 'Training dataset')
y_test_pred = classifier.predict(X_test)
visualize_classifier(classifier, X_test, y_test, 'Test dataset')
# Evaluate classifier performance
class_names = ['Class-0', 'Class-1']
print("\n" + "#"*40)
print("\nClassifier performance on training dataset\n")
print(classification_report(y_train, classifier.predict(X_train), target_names=class_names))
print("#"*40 + "\n")
print("#"*40)
print("\nClassifier performance on test dataset\n")
print(classification_report(y_test, y_test_pred, target_names=class_names))
print("#"*40 + "\n")
plt.show()
+) 정확률은 분류의 정확도를, 재현율은 관련 데이터 중에서 올바로 분류된 데이터의 비율을 나타낸다. 좋은 분류기는 높은 정확률과 재현율을 보이지만 일반적으로 둘은 trade-off 관계다. 따라서 정확률과 재현율의 조화 평균인 F1 점수를 사용한다.
앙상블 학습에서 사용되는 방법 중 하나로, 다양한 의사 결정 트리 모델을 구축하고 사용하는 방법이다. 전체 학습 데이터를 임의로 여러 개의 부분 데이터로 나눈 뒤, 각 부분 데이터별로 의사 결정 트리 모델을 학습시켜 다양한 모델을 생성한다. 랜덤 포레스트는 학습 데이터를 임의로 분할해 다양성을 보장한다.
랜덤 포레스트의 최대 장점 중 하나는 오버피팅을 피할 수 있다는 것이다. 트리를 구축하는 과정에서 노드들은 클래스에 따라 성공적으로 데이터를 나누고, 각각의 계층별로 엔트로피를 줄이도록 최적의 임계 값을 선택된다. 이와 같은 방식으로 데이터를 나눌 때 모든 특징을 고려하지는 않는다. 대신 임의의 몇 개의 특징만을 선별하고 사용해 데이터를 나눈다. 임의성을 추가함으로써 데이터 편향성은 증가할 수 있지만 평균화를 통해 분산은 감소하고 결과적으로 견고한 모델을 얻을 수 있다.
import argparse
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import classification_report
from sklearn import cross_validation
from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier
from sklearn import cross_validation
from sklearn.metrics import classification_report
from utilities import visualize_classifier
# Argument parser
def build_arg_parser():
parser = argparse.ArgumentParser(description='Classify data using \
Ensemble Learning techniques')
parser.add_argument('--classifier-type', dest='classifier_type',
required=True, choices=['rf', 'erf'], help="Type of classifier
\to use; can be either 'rf' or 'erf'")
return parser
if __name__=='__main__':
# Parse the input arguments
args = build_arg_parser().parse_args()
classifier_type = args.classifier_type
# Load input data
input_file = 'data_random_forests.txt'
data = np.loadtxt(input_file, delimiter=',')
X, y = data[:, :-1], data[:, -1]
# Separate input data into three classes based on labels
class_0 = np.array(X[y==0])
class_1 = np.array(X[y==1])
class_2 = np.array(X[y==2])
# Visualize input data
plt.figure()
plt.scatter(class_0[:, 0], class_0[:, 1], s=75, facecolors='white',
edgecolors='black', linewidth=1, marker='s')
plt.scatter(class_1[:, 0], class_1[:, 1], s=75, facecolors='white',
edgecolors='black', linewidth=1, marker='o')
plt.scatter(class_2[:, 0], class_2[:, 1], s=75, facecolors='white',
edgecolors='black', linewidth=1, marker='^')
plt.title('Input data')
# Split data into training and testing datasets
X_train, X_test, y_train, y_test = cross_validation.train_test_split(
X, y, test_size=0.25, random_state=5)
# Ensemble Learning classifier
params = {'n_estimators': 100, 'max_depth': 4, 'random_state': 0}
if classifier_type == 'rf':
classifier = RandomForestClassifier(**params)
else:
classifier = ExtraTreesClassifier(**params)
classifier.fit(X_train, y_train)
visualize_classifier(classifier, X_train, y_train, 'Training dataset')
y_test_pred = classifier.predict(X_test)
visualize_classifier(classifier, X_test, y_test, 'Test dataset')
# Evaluate classifier performance
class_names = ['Class-0', 'Class-1', 'Class-2']
print("\n" + "#"*40)
print("\nClassifier performance on training dataset\n")
print(classification_report(y_train, classifier.predict(X_train), target_names=class_names))
print("#"*40 + "\n")
print("#"*40)
print("\nClassifier performance on test dataset\n")
print(classification_report(y_test, y_test_pred, target_names=class_names))
print("#"*40 + "\n")
$ python3 random_forests.py --classifier-type rf
극단 랜덤 포레스트는 임의성을 다음 계층으로 계속 전달한다. 입력 데이터셋의 특징 중 임이의로 선택된 몇 개의 특징을 이용하고 이에 더해 임계 값도 임의로 선택한다. 이렇게 생성돈 임계 값은 분할 규칙으로 이용돼 모델의 분산을 더욱 줄일 수 있게 된다. 따라서 극단 랜덤 포레스트는 랜덤 포레스트보다 더 매끈한 형태를 구축한다.
$ python3 random_forests.py --classifier-type erf
신뢰도를 추정하는 것은 머신 러닝에서 중요한 작업이다.
# Compute confidence
test_datapoints = np.array([[5, 5], [3, 6], [6, 4], [7, 2], [4, 4], [5, 2]])
print("\nConfidence measure:")
for datapoint in test_datapoints:
probabilities = classifier.predict_proba([datapoint])[0]
predicted_class = 'Class-' + str(np.argmax(probabilities))
print('\nDatapoint:', datapoint)
print('Predicted class:', predicted_class)
# Visualize the datapoints
visualize_classifier(classifier, test_datapoints,
[0]*len(test_datapoints),
'Test datapoints')
plt.show()
분류기의 성능은 사용된 데이터에 의해 크게 좌우된다. 분류기의 성능을 높이기 위해서는 각 클래스별로 동일한 수의 데이터가 있는 것이 좋으나 이는 쉽지 않다. 이러한 불균형은 알고리즘을 통해 해결할 수 있다.
$ python3 class_imbalance.py balance
분류기를 사용할 때 최적의 매개변수를 찾기 위해서 사용하는 것이 그리드 검색이다. 그리드 검색은 매개변수 값의 범위를 지정하면 자동으로 다양한 매개변수를 조합해 최적의 값을 찾아준다.
# Define the parameter grid
parameter_grid = [ {'n_estimators': [100], 'max_depth': [2, 4, 7, 12, 16]},
{'max_depth': [4], 'n_estimators': [25, 50, 100, 250]}
]
metrics = ['precision_weighted', 'recall_weighted']
for metric in metrics:
print("\n##### Searching optimal parameters for", metric)
classifier = grid_search.GridSearchCV(
ExtraTreesClassifier(random_state=0),
parameter_grid, cv=5, scoring=metric)
classifier.fit(X_train, y_train)
print("\nGrid scores for the parameter grid:")
for params, avg_score, _ in classifier.grid_scores_:
print(params, '-->', round(avg_score, 3))
print("\nBest parameters:", classifier.best_params_)
y_pred = classifier.predict(X_test)
print("\nPerformance report:\n")
print(classification_report(y_test, y_pred))
N차원 데이터가 포함된 데이터셋으로 작업할 때 모든 특징이 똑같이 중요하지는 않다. 아다부스트 회귀 분석기는 특징의 중요성을 계산해 성능을 향상시키는데 도움을 주는 알고리즘이다. 아다부스트는 여러 단계에 걸쳐 여러 개의 분류기를 생성하고 데이터 가중치를 업데이트해 성능을 개선하고 최종 레이블을 결정한다.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import AdaBoostRegressor
from sklearn import datasets
from sklearn.metrics import mean_squared_error, explained_variance_score
from sklearn import cross_validation
from sklearn.utils import shuffle
# Load housing data
housing_data = datasets.load_boston()
# Shuffle the data
X, y = shuffle(housing_data.data, housing_data.target, random_state=7)
# Split data into training and testing datasets
X_train, X_test, y_train, y_test = cross_validation.train_test_split(
X, y, test_size=0.2, random_state=7)
# AdaBoost Regressor model
regressor = AdaBoostRegressor(DecisionTreeRegressor(max_depth=4),
n_estimators=400, random_state=7)
regressor.fit(X_train, y_train)
# Evaluate performance of AdaBoost regressor
y_pred = regressor.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
evs = explained_variance_score(y_test, y_pred )
print("\nADABOOST REGRESSOR")
print("Mean squared error =", round(mse, 2))
print("Explained variance score =", round(evs, 2))
# Extract feature importances
feature_importances = regressor.feature_importances_
feature_names = housing_data.feature_names
# Normalize the importance values
feature_importances = 100.0 * (feature_importances / max(feature_importances))
# Sort the values and flip them
index_sorted = np.flipud(np.argsort(feature_importances))
# Arrange the X ticks
pos = np.arange(index_sorted.shape[0]) + 0.5
# Plot the bar graph
plt.figure()
plt.bar(pos, feature_importances[index_sorted], align='center')
plt.xticks(pos, feature_names[index_sorted])
plt.ylabel('Relative Importance')
plt.title('Feature importance using AdaBoost regressor')
plt.show()