💠 머신러닝 분류 모델 ( 1 ) 💠
1. 분류
분류는 머신러닝의 학습 방식 중 하나인 지도학습의 대표적인 유형이다.
지도 학습이란 레이블(명시적인 정답)이 있는 데이터가 주어진 상태에서 학습하는 머신러닝 방식으로
문제와 정답을 모두 알려주고 모델을 학습시키는 방법이다.
분류는 학습 데이터로 주어진 데이터의 피처와 레이블값으로 학습해 모델을 생성한 뒤
새로운 데이터 값이 주어졌을 때 레이블 값을 예측하는 것이다.
이 때 학습이란 ML 알고리즘을 통해 기존 데이터값의 패턴을 인지하는 것을 의미한다.
2. 결정 트리
분류는 결정 트리 알고리즘을 기반으로 구현하는 경우가 많다
결정 트리의 정의 및 특징
결정 트리는 데이터에 있는 규칙을 학습을 통해 자동으로 찾아내 트리 기반의 분류 규칙을 만드는 것이다


루트 노드 : 깊이가 0인 맨 위의 노드
규칙 노드 : 규칙 조건을 만드는 노드 / 자식 노드로 분할됨
리프 노드 : 최종 레이블 값이 결정되는 노드
서브 트리 : 새로운 규칙 조건마다 생성되는 트리
규칙이 많을수록 각각의 데이터에 너무 최적화된 규칙이라는 뜻이기 때문에 과적합으로 이어지기 쉽다
따라서 결정 트리의 깊이가 깊어질수록 예측 성능이 저하될 가능성이 높다
결정 트리의 규칙 조건 설정 방식
규칙 조건은 정보 균일도가 높은 데이터 세트를 먼저 선택할 수 있도록 설정하는 것이 유리하다
이 때 균일도란 혼잡도와는 반대되는 개념으로 같은 종류의 노드끼리 최대한 많이 묶여있는 상태가 균일도가 높은 상태라고 할 수 있다
균일도를 측정하는 대표적인 방법은 엔트로피(혼합도)를 이용한 정보 이득 지수와 지니 계수가 있다
정보 이득 지수 = ( 1 - 엔트로피 지수 )
-> 1에 가까울수록 균일도가 높은 속성
지니 계수는 0이 가장 균일하고 1로 갈수록 불평등하다
-> 0에 가까울수록 균일도가 높은 속성
균일도가 좋은 피처를 추출하는 방식
DecisionTreeClassifier의 feature_importances_ 이용
결정 트리 파라미터

결정 트리 과적합
앞서 말했듯이 결정 트리의 규칙 조건이 너무 많아지면 학습 데이터에만 최적화된 과적합이 일어나게 된다
따라서 위의 파라미터를 이용해 트리의 크기를 사전에 제한하는 튜닝을 통해 결정 트리의 과적합을 제어해줄 필요가 있다
사용자 행동 인식 데이터 세트
데이터 세팅
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
feature_name_df = pd.read_csv('human+activity+recognition+using+smartphones/UCI HAR Dataset/features.txt',sep='\s+',header=None, names=['column_index','column_name'])
feature_name = feature_name_df.iloc[:, 1].values.tolist()
print('전체 피처명에서 10개만 추출:',feature_name[:10])
겹치는 column 확인 및 정리
# 겹치는 column 확인
feature_dup_df = feature_name_df.groupby('column_name').count()
print(feature_dup_df[feature_dup_df['column_index']>1].count())
feature_dup_df[feature_dup_df['column_index']>1].head()
# 겹치는 column 정리
def get_new_feature_name_df(old_feature_name_df):
feature_dup_df = pd.DataFrame(data=old_feature_name_df.groupby('column_name').cumcount(), columns=['dup_cnt'])
feature_dup_df = feature_dup_df.reset_index()
new_feature_name_df = pd.merge(old_feature_name_df.reset_index(), feature_dup_df, how='outer')
new_feature_name_df['column_name'] = new_feature_name_df[['column_name', 'dup_cnt']].apply(lambda x : x[0]+'_'+str(x[1]) if x[1]>0 else x[0], axis = 1)
new_feature_name_df = new_feature_name_df.drop(['index'], axis = 1)
return new_feature_name_df
def get_human_dataset():
feature_name_df = pd.read_csv('human+activity+recognition+using+smartphones/UCI HAR Dataset/features.txt',sep='\s+',header=None, names=['column_index','column_name'])
new_feature_name_df = get_new_feature_name_df(feature_name_df)
feature_name = new_feature_name_df.iloc[:, 1].values.tolist()
X_train = pd.read_csv('human+activity+recognition+using+smartphones/UCI HAR Dataset/train/X_train.txt',sep='\s+', names=feature_name)
X_test = pd.read_csv('human+activity+recognition+using+smartphones/UCI HAR Dataset/test/X_test.txt',sep='\s+', names=feature_name)
y_train = pd.read_csv('human+activity+recognition+using+smartphones/UCI HAR Dataset/train/y_train.txt',sep='\s+', header = None, names=['action'])
y_test = pd.read_csv('human+activity+recognition+using+smartphones/UCI HAR Dataset/test/y_test.txt',sep='\s+', header = None, names=['action'])
return X_train, X_test, y_train, y_test
X_train, X_test, y_train, y_test = get_human_dataset()
데이터 세트 레이블 확인
print('## 학습 피처 데이터셋 info()')
print(X_train.info())
print(y_train['action'].value_counts())
결정 트리 알고리즘 적용
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
# 결정 트리 객체 생성
dt_clf = DecisionTreeClassifier(random_state = 156)
# 모델 학습
dt_clf.fit(X_train, y_train)
# 모델 예측
pred = dt_clf.predict(X_test)
# 모델 평가
accuracy = accuracy_score(y_test, pred)
print('결정 트리 예측 정확도: {0:.4f}'.format(accuracy))
# 결정 트리 객체 파라미터 확인
print('DecisionTreeClassifier 기본 하이퍼 파라미터:\n',dt_clf.get_params())
GridSearchCV를 통해 결정 트리의 최적 깊이(파라미터) 구하기
from sklearn.model_selection import GridSearchCV
params={
'max_depth' : [6, 8, 10, 12, 16, 20, 24],
'min_samples_split' : [16]
}
grid_cv = GridSearchCV(dt_clf, param_grid=params, scoring='accuracy', cv=5, verbose=1)
grid_cv.fit(X_train, y_train)
print('GridSearchCV 최고 평균 정확도 수치: {0:.4f}'.format(grid_cv.best_score_))
print('GridSearchCV 최적 하이퍼 파라미터:',grid_cv.best_params_)
cv_results_df = pd.DataFrame(grid_cv.cv_results_)
cv_results_df[['param_max_depth','mean_test_score']]


결정 트리 모델의 파라미터(트리의 깊이) 최적값 구하기 - 정확도 수치, GridSearchCV
max_depths = [6, 8, 10, 12, 16, 20, 24]
for depth in max_depths:
dt_clf = DecisionTreeClassifier(max_depth=depth, min_samples_split=16, random_state=156)
dt_clf.fit(X_train, y_train)
pred = dt_clf.predict(X_test)
accuracy = accuracy_score(y_test, pred)
print('max_depth = {0} 정확도 = {1}'.format(depth, accuracy))
params = {
'max_depth':[8, 12, 16, 20],
'min_samples_split':[16, 24],
}
grid_cv = GridSearchCV(dt_clf, param_grid=params, scoring='accuracy', cv=5, verbose=1)
grid_cv.fit(X_train, y_train)
print('GridSearchCV 최고 평균 정확도 수치: {0:.4f}'.format(grid_cv.best_score_))
print('GridSearchCV 최적 하이퍼 파라미터:', grid_cv.best_params_)


위에서 구한 트리 깊이의 최적값을 적용했을 때 정확도 구하기
best_df_clf = grid_cv.best_estimator_
pred1 = best_df_clf.predict(X_test)
accuracy = accuracy_score(y_test, pred1)
print('결정 트리 예측 정확도:{0:.4f}'.format(accuracy))

결정 트리 피처의 중요도 표현
import seaborn as sns
ftr_importances_values = best_df_clf.feature_importances_
ftr_importances = pd.Series(ftr_importances_values, index = X_train.columns)
ftr_top20 = ftr_importances.sort_values(ascending=False)[:20]
plt.figure(figsize=(8,6))
plt.title('Feature importances Top 20')
sns.barplot(x=ftr_top20, y=ftr_top20.index)
plt.show()

3. 앙상블
분류를 학습시키는 ML 알고리즘은 다양한데 그중에서 앙상블 방법을 통해 분류 모델을 생성하는 방법을 알아보겠다
앙상블의 정의와 유형
앙상블 방법은 여러 개의 분류기를 생성하고 그 예측을 결합함으로써 보다 정확한 최종 예측을 도출하는 기법을 말한다
앙상블 학습의 유형은 보팅, 배깅, 부스팅과 스태킹이 있다
보팅 방식 : 서로 다른 알고리즘을 가진 분류기 결합
배깅 방식 : 같은 유형의 알고리즘을 가진 분류기를 결합하되 각 분류기마다 서로 다른 데이터 세트를 학습시킨 뒤 결합

부스팅 방식 : 여러 개의 약한 학습기를 순차적으로 학습 - 예측하면서 잘못 예측한 데이터에 가중치를 부여해 오류를 개선해 나가면서 학습하는 방식
스택킹 방식 : 개별 알고리즘으로 예측한 결과 데이터 세트를 최종 메타 데이터 세트로 만들어 별도의 ML 알고리즘으로 최종 학습을 수행하고 테스트 데이터를 기반으로 다시 최종 예측을 수행하는 방식
3-1. 보팅(Voting) 방식
보팅 유형 - 하드 보팅 / 소프트 보팅
보팅은 각 분류기들의 예측값을 하나의 결괏값으로 결합하는 과정을 의미하기도 하는데
하드 보팅과 소프트 보팅으로 나누어 진다
하드 보팅 : 예측한 결괏값 중 다수의 분류기가 결정한 예측값을 최종 보팅 결과로 선정하는 것
소프트 보팅 : 분류기들의 각 레이블 값에 대한 결정 확률의 평균치를 구해서 가장 확률이 높은 레이블 값을 최종 보팅 결과로 선정하는 것
보팅 분류기 - Voting Classifier
보팅 방식의 앙상블은 VotingClassifier를 통해 구현한다
파라미터 - estimators : 결합할 분류기들의 종류 / voting : 하드 보팅, 소프트 보팅
앙상블 기법 응용과 정확도 변화 관찰
import pandas as pd
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
cancer = load_breast_cancer()
data_df = pd.DataFrame(cancer.data, columns=cancer.feature_names)
data_df.head(3)
# 로지스틱 회귀와 KNN 객체 생성
lr_clf = LogisticRegression(solver='liblinear')
knn_clf = KNeighborsClassifier(n_neighbors=8)
# 앙상블 기법 - 보팅 (로지스틱 회귀, KNN)
vo_clf = VotingClassifier(estimators=[('LR',lr_clf),('KNN',knn_clf)], voting='soft')
# 학습/테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, test_size=0.2, random_state=156)
# 앙상블한 모델 학습
vo_clf.fit(X_train, y_train)
# 모델 예측
pred = vo_clf.predict(X_test)
# 모델 평가
print('Voting 분류기 정확도: {0:.4f}'.format(accuracy_score(y_test, pred)))
# 앙상블하지 않은 각각의 모델(로지스틱 회귀, KNN) 평가
classifiers=[lr_clf, knn_clf]
for classifier in classifiers:
classifier.fit(X_train, y_train)
pred = classifier.predict(X_test)
class_name = classifier.__class__.__name__
print('{0} 정확도: {1:.4f}'.format(class_name, accuracy_score(y_test, pred)))

3-2. 배깅(Bagging) 방식 - 랜덤 포레스트
배깅의 대표적인 알고리즘은 랜덤 포레스트이다
랜덤 포레스트 : 여러 개의 결정 트리 분류기가 전체 데이터에서 배깅 방식으로 각자의 데이터를 샘플링해 개별적으로 학습한 뒤 최종적으로 모든 분류기가 소프트 보팅을 통해 예측을 결합하는 알고리즘
랜덤 포레스트는 배깅 방식으로 구현되기 때문에 주어진 데이터 세트를 분리해서 각각의 분류기에 적절히 배분해줘야 한다
이때 데이터 세트를 분리하는 것을 부트스트래핑이라고 하며
부트스트래핑은 데이터 세트를 일부분 중첩되게 분리하는 방식이다
랜덤 포레스트 구현
랜덤 포레스트는 사이킷런의 RandomForestClassifier를 통해 구현한다
RandomForestClassifier 파라미터
n_estimators : 결정 트리 분류기의 개수 / default : 10
결정 트리의 파라미터들 - max_features / max_depth / min_samples_leaf / min_samples_split
랜덤 포레스트의 단점은 하이퍼 파라미터가 너무 많아서 튜닝을 위한 시간이 많이 소모된다는 것이다
랜덤 포레스트 응용
데이터 세팅
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
def get_new_feature_name_df(old_feature_name_df):
feature_dup_df = pd.DataFrame(data=old_feature_name_df.groupby('column_name').cumcount(), columns=['dup_cnt'])
feature_dup_df = feature_dup_df.reset_index()
new_feature_name_df = pd.merge(old_feature_name_df.reset_index(), feature_dup_df, how='outer')
new_feature_name_df['column_name'] = new_feature_name_df[['column_name', 'dup_cnt']].apply(lambda x : x[0]+'_'+str(x[1]) if x[1]>0 else x[0], axis = 1)
new_feature_name_df = new_feature_name_df.drop(['index'], axis = 1)
return new_feature_name_df
def get_human_dataset():
feature_name_df = pd.read_csv('human+activity+recognition+using+smartphones/UCI HAR Dataset/features.txt',sep='\s+',header=None, names=['column_index','column_name'])
new_feature_name_df = get_new_feature_name_df(feature_name_df)
feature_name = new_feature_name_df.iloc[:, 1].values.tolist()
X_train = pd.read_csv('human+activity+recognition+using+smartphones/UCI HAR Dataset/train/X_train.txt',sep='\s+', names=feature_name)
X_test = pd.read_csv('human+activity+recognition+using+smartphones/UCI HAR Dataset/test/X_test.txt',sep='\s+', names=feature_name)
y_train = pd.read_csv('human+activity+recognition+using+smartphones/UCI HAR Dataset/train/y_train.txt',sep='\s+', header = None, names=['action'])
y_test = pd.read_csv('human+activity+recognition+using+smartphones/UCI HAR Dataset/test/y_test.txt',sep='\s+', header = None, names=['action'])
return X_train, X_test, y_train, y_test
X_train, X_test, y_train, y_test = get_human_dataset()
랜덤 포레스트 객체 생성 및 모델 학습/예측
rf_clf = RandomForestClassifier(random_state=0, max_depth=8)
rf_clf.fit(X_train, y_train)
pred = rf_clf.predict(X_test)
accuracy = accuracy_score(y_test, pred)
print('랜덤 포레스트 정확도: {0:.4f}'.format(accuracy))
GridSearchCV로 최적 하이퍼 파라미터 구한 뒤 파라미터 최적값 적용해서 정확도 구하기
from sklearn.model_selection import GridSearchCV
params = {
'max_depth':[8, 16, 24],
'min_samples_leaf':[1, 6, 12],
'min_samples_split':[2, 8, 16],
}
rf_clf = RandomForestClassifier(n_estimators=100, random_state=0, n_jobs=-1)
grid_cv = GridSearchCV(rf_clf, param_grid=params, cv=2, n_jobs=-1)
grid_cv.fit(X_train, y_train)
print('최적 하이퍼 파라미터:\n',grid_cv.best_params_)
print('최고 예측 정확도: {0:.4f}'.format(grid_cv.best_score_))
랜덤 포레스트 객체 생성 및 최적 하이퍼 파라미터 적용해서 모델 학습/예측/평가
rf_clf1 = RandomForestClassifier(n_estimators=100, min_samples_leaf=6, max_depth=16, min_samples_split=2, random_state=0)
rf_clf1.fit(X_train, y_train)
pred = rf_clf1.predict(X_test)
print('예측 정확도: {0:.4f}'.format(accuracy_score(y_test, pred)))
랜덤 포레스트 주요 파라미터 시각화
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
ftr_importances_values = rf_clf1.feature_importances_
ftr_importance = pd.Series(ftr_importances_values, index=X_train.columns)
ftr_top20 = ftr_importances.sort_values(ascending=False)[:20]
plt.figure(figsize=(8,6))
plt.title('Feature importances Top20')
sns.barplot(x=ftr_top20, y=ftr_top20.index)
plt.show()