Search

SVM & 앙상블

결정 경계를 정의하는 데 필요한 데이터들이 서포트 벡터뿐이기 때문에 나머지 데이터들은 무시해도 된다. 따라서 매우 빠르게 처리 가능

앙상블

: 여러 개의 분류기를 합친다는 의미로써의 모델. 약한 분류기들을 합쳐 더 좋은 예측을 하기 위해 만들어짐.

학습 유형

보팅
배깅
부스팅

보팅?

서로 다른 알고리즘을 가진 모델을 결합한 모델

배깅?

같은 알고리즘을 가진 모델을 데이터 샘플링을 다르게 하여 학습하는 모델

하드보팅 VS 소프트보팅

하드 보팅 : 다수결원칙. 다수의 분류기가 예측한 값을 최종 결괏값으로 선정
소프트 보팅 : 분류기들의 예측확률의 평균으로 결괏값을 선정

배깅

학습데이터에서 일부가 중첩되게 샘플링하여 서로 다른 데이터 세트를 만드는 방식을 사용하는데 이를 부트스트래핑 분할 방식이라고 부른다.

배깅 - 랜덤포레스트

여러 개의 결정 트리 분류기를 결합해 보팅을 통해 최종 예측 결과를 결정함.

랜덤포레스트의 하이퍼 파라미터 및 튜닝

n_estimators : 결정 트리(즉, 약한 분류기)의 갯수를 지정, 디폴트는 10
max_features : 결정 트리의 max_features와 같음. 하지만 디폴트는 sqrt(=auto)
그외 다수는 결정 트리의 하이퍼 파라미터와 동일함. (생략)

부스팅

여러 개의 학습기를 순차적으로 학습/예측 해나가면서 잘못 예측한 데이터에 가중치를 부여해 오류를 개선해나가는 방식.
대표적인 방법은
adaboost
그래디언트부스트

부스팅 - GBM(Gradient Boosting Machine)

오차의 가중치 업데이트를 경사 하강법을 사용하는 모델
but 단점 : 수행시간이 오래 걸리고, 하이퍼 파라미터 튜닝의 수고가 필요함.
< GBM의 하이퍼 파라미터 >
loss(default=’deviance’) : 경사 하강법에서의 비용 함수
learning_rate(default=0.1) : 작을수록 예측 성능 좋아지지만 오래 걸린다.
n_estimators(default=100) : 하위 모델의 갯수
subsample(default=1) : 학습에 사용할 데이터 샘플링의 비율. 과적합이 염려될 경우 1보다 작은 값으로 지정

XGBoost

GBM을 개선시킨 모델. GBM의 느린 학습 수행 속도와 과적합 규제 부재등을 해결한 모델이다.
< 패키지 모듈 >
파이썬 래퍼 XGBoost 모듈
사이킷런 래퍼 XGBoost 모듈
—> 둘의 하이퍼 파라미터 차이가 존재하기 때문에 유의해야 한다. 하지만 일반적으로 사이킷런 래퍼를 많이 사용!

LightGBM

XGBoost를 또다시 개선시킨 모델. 시간과 용량을 모두 단축시켰다. 예측 성능에는 큰 차이가 없다.
but 단점 : 적은 데이터 세트(10,000건 이하)를 적용하면 과적합이 발생하기 쉽다.
특징 : 다른 결정 트리 분할 방식과 달리 리프 중심 분할 방식
장점 :
< LightGBM의 하이퍼 파라미터 >
주요 파라미터
n_estimators(default=100)
learning_rate(default=0.1)
max_depth(default=-1)
min_child_samples(default=20)
num_leaves(default=31)
boosting(default=gbdt)
subsample(default=1)
colsample_bytree(default=1)
reg_lambda(default=0) : l2 regulation
reg_alpha(default=0) : l1 regulation
학습 태스크 파리미터
objective : 손실함수

SVM(서포트 벡터 머신)

: 분류모델 중 하나.
서포트 벡터 머신은 결정 경계(Decision Boundary) , 즉 분류를 위한 기준 선을 정하는 모델이다.
속성이 2개인 데이터 : 결정 경계는 아래처럼 2차원의 선으로 표현됨.
속성이 3개인 데이터 : 3차원으로 표시되고 결정 경계는 평면으로 표현됨.
속성이 4개 이상인 데이터 : 고차원으로 넘어가기 때문에 형성되는 결정 경계를 초평면이라고 부름.

최적의 결정 경계

최적의 결정 경계를 찾는 것이 목표가 된다.
HOW?
결정 경계는 데이터 군으로부터 최대한 멀리 떨어지는 것이 좋다.
support vectors : 결정 경계와 가장 가까이 있는 데이터들
결정 경계를 정의하는 가장 결정적인 역할

마진

: 결정경계와 서포트 벡터 사이의 거리
따라서 마진을 최대화하는 결정 경계가 최적의 결정 경계가 된다.
BUT 유의할 점 : n개의 속성을 가진 데이터에는 최소 n+1개의 서포트 벡터가 존재해야 한다
SVM장점 :
결정 경계를 정의하는 데 필요한 데이터들이 서포트 벡터뿐이기 때문에 나머지 데이터들은 무시해도 된다. 따라서 매우 빠르게 처리 가능
회귀와 분류 문제에 적용 모두 가능
예측력이 좋다.
SVM단점:
해석이 어렵다.

이상치의 허용

하드 마진 : 아웃라이어를 허용하지 않는 기준으로 빡세게 결정 경계를 정의
마진이 매우 좁아지고 과적합의 위험이 있음.
소프트 마진 : 아웃라이어를 마진 안에 허용함으로써 기준을 넉넉히 정의하는 것
마진이 커지게 되지만 언더피팅의 위험이 있음.

Scikit-learn 에서의 SVM

from sklearn.svm import SVC classifier = SVC(kernel='linear') training_points = [[1, 2], [1, 5], [2, 2], [7, 5], [9, 4], [8, 2]] labels = [1, 1, 1, 0, 0, 0] classifier.fit(training_points, labels) # 학습 classifier.predict([[3,2]]) # 예측 classifier.support_vectors_ #결정 경계를 정의하는 서포트 벡터
SQL
복사

하이퍼 파라미터 C

C(default=1) : 이상치 허용 파라미터.
C값이 클수록 하드마진, 작을수록 소프트 마진이다.
sol) C의 최적 값은 GridSearchCV를 통해서 찾아나간다.
classifier = SVC(C=0.01)
SQL
복사

kernel

결정 경계를 선, 곡선, rbf 등으로 구체적으로 지정
classifier = SVC(kernel='poly')
SQL
복사
BUT 주의해야 할 점 : 단순히 이상치때문에 선형으로 분리할 수 없다고 판단해서 선형 아닌 커널을 사용하면 안된다. —> 과적합의 위험
poly(다항식) :
rbf(방사 기저 함수) : RBF커널 혹은 가우시안 커널, kernel의 기본값
→ 2차원의 점을 무한한 차원의 점으로 변환
classifier = SVC() # default kernel = 'rbf'
SQL
복사

하이퍼 파라미터 감마

학습 데이터에 얼마나 민감하게 반응할 것인지를 조정해주는 것으로 C와 비슷
classifier = SVC(C=2, gamma=0.5)
SQL
복사
gamma↑ : 과적합 위험↑
gamma↓ : 언더피팅의 위험↑
과적합의 예시
언더피팅의 예시

scikit-learn 붓꽃 문제 적용

from sklearn.datasets import load_iris iris = load_iris() x1 = iris.data[:100,:2] # 100개의 데이터와, 2개의 변수만 활용 y1 = iris.target[:100] # 종속변수 from sklearn.svm import SVC model1 = SVC(kernel='linear', C=1e10).fit(x1,y1) # C값이 10의 10승이므로 매우 큰 값. 하드마진 서포트벡터 # 예측 결과의 평가 from sklearn.metrics import classification_report print(classification_report(y1,model1.predict(x1))) # 결과 precision recall f1-score support 0 1.00 1.00 1.00 50 1 1.00 1.00 1.00 50 accuracy 1.00 100 macro avg 1.00 1.00 1.00 100 weighted avg 1.00 1.00 1.00 100 # 이번에는 C값을 0.1로 지정하여 다시 구해보자. model2 = SVC(kernel='linear', C=0.1).fit(x1,y1) print(classification_report(y1,model2.predict(x1)))
SQL
복사

stroke 데이터 문제 적용

import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns sns.set_style('whitegrid') # load data df = pd.read_csv('D:me/jupyterNotebook/study/strokedata.csv') df.drop('id', axis=1,inplace=True) df.head() df.info() df.describe() ###### data cleansing ###### # dealing with na # 'bmi' 열의 null값을 mean으로 대체 df['bmi'] = df['bmi'].fillna(df['bmi'].mean()) # 'gender'내의 Other값을 가지고 있는 행 제거 other_index = df[df['gender']=='Other'].index df = df.drop(other_index) ###### visualizing features ###### # 흡연여부, 직장, 거주지역이 stroke와의 연관성이 있을까? # 확인해보기 위해 시각화를 진행한다. import warnings warnings.filterwarnings('ignore') plt.figure(figsize=(21,5)) # 흡연여부 그래프 (stroke에 대한) plt.subplot(1,3,1) # subplot 중 첫번째 sns.countplot(x=df['smoking_status'], alpha=0.8, palette='Paired', hue=df['stroke']); sns.despine(fig=None, ax=None, top=True, right=True, left=False, bottom=True, offset=None, trim=False); plt.xlabel(''); plt.title('Smoking Status'); # 직장 타입 plt.subplot(1,3,2) sns.countplot(x=df['work_type'], alpha=0.8, palette='Paired', hue=df['stroke']); sns.despine(fig=None, ax=None, top=True, right=True, left=False, bottom=True, offset=None, trim=False); plt.xlabel=(''); plt.title('Work Type'); # 거주 지역 plt.subplot(1,3,3) sns.countplot(x=df['Residence_type'], alpha=0.8, palette='Paired', hue=df['stroke']); sns.despine(fig=None, ax=None, top=True, right=True, left=False, bottom=True, offset=None, trim=False); plt.xlabel=(''); plt.title('Residence Type'); --> 그림으로써는 큰 영향이 있는 것으로 판단되지 않는다. #성별, 고혈압의여부, 심장병의 여부 가 stroke와의 연관성이 있을까? plt.figure(figsize=(21,5)) plt.subplot(1,3,1) sns.countplot(x=df['gender'], alpha=0.8, palette='Paired',hue=df['stroke']); plt.tick_params(axis='both', which='both',bottom=False, left=True, right=False, top=False, labelbottom=True, labelleft=True); # 틱과 경계선을 한꺼번에 설정 sns.despine(fig=None, ax=None, top=True, right=True, left=False, bottom=True, offset=None, trim=False); plt.xlabel=('') plt.title('Gender, F/M : 59/41 %'); plt.subplot(1,3,2) sns.countplot(x=df['hypertension'], alpha=0.8, palette='Paired',hue=df['stroke']); sns.despine(fig=None, ax=None, top=True, right=True, left=False, bottom=True, offset=None, trim=False); plt.xlabel=('') plt.title('Hypertension'); plt.subplot(1,3,3) sns.countplot(x=df['heart_disease'], alpha=0.8, palette='Paired',hue=df['stroke']); sns.despine(fig=None, ax=None, top=True, right=True, left=False, bottom=True, offset=None, trim=False); plt.xlabel=(''); plt.title('Heart Disease'); # 나이, 평균 Glucose level, bmi 가 stroke와의 연관성이 있을까? sns.set_style('white') plt.figure(figsize=(21,5)) plt.subplot(1,3,1) sns.kdeplot(x=df['age'], alpha=0.2, palette='Set1', label='Smoker', fill=True, linewidth=1.5, hue=df['stroke']); sns.despine(fig=None, ax=None, top=True, right=True, left=True, bottom=True, offset=None, trim=False); plt.xlabel(''); plt.title('Age Distribution'); plt.subplot(1,3,2) sns.kdeplot(x=df['avg_glucose_level'], alpha=0.2, palette='Set1', label='avg_glucose_level', linewidth=1.5, fill=True, hue=df['stroke']); sns.despine(fig=None, ax=None, top=True, right=True, left=True, bottom=True, offset=None, trim=False); plt.xlabel(''); plt.title('Average Glucose Level'); plt.subplot(1,3,3) sns.kdeplot(x=df['bmi'], alpha=0.2, palette='Set1', label='BMI', shade=True, linewidth=1.5, hue=df['stroke']); sns.despine(fig=None, ax=None, top=True, right=True, left=False, bottom=True, offset=None, trim=False); plt.xlabel('') plt.title('BMI');
SQL
복사
→ age가 확실히 영향력있는 변수인 것으로 확인된다.
# stroke 종속변수의 불균형적인 분포를 확인 --> 후에 오버샘플링이 필요 df['stroke'].value_counts()
SQL
복사
###### preprocessing data ###### # 범주형 데이터 --> 수치화 df['gender'] = df['gender'].replace({'Male':0, 'Female':1, 'Other':-1}).astype(np.uint8) df['Residence_type'] = df['Residence_type'].replace({'Rural':0, 'Urban':1}).astype(np.uint8) df['work_type'] = df['work_type'].replace({'Private':0, 'Self-employed':1, 'Govt_job':2, 'children':-1, 'Never_worked':-2}).astype(np.uint8) df['ever_married'] = df['ever_married'].replace({'Yes':1, 'No':0}).astype(np.uint8) df['smoking_status'] = df['smoking_status'].replace({'never smoked':0, 'Unknown':1, 'formerly smoked':2, 'smokes':-1}).astype(np.uint8)
SQL
복사
###### Model building ####### X = df.drop('stroke', axis=1) y = df.pop('stroke') # using SVM Classifier # using pipeline from sklearn.svm import SVC from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler, LabelEncoder svm_pipeline = Pipeline(steps=[('scale'.StandardScaler()), ('SVM',SVC(random_state=0. probability = True))])
SQL
복사
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X,y) # default y_train.shape # using SMOTE for oversampling from imblearn.over_sampling import SMOTE oversample = SMOTE() X_train_resh, y_train_resh = oversample.fit_resample(X_train,y_train.ravel())
SQL
복사
교차 검증을 수행한다. cv=10 으로 우선 적용한다. 평가 지표는 f1 score로 진행한다.
from sklearn.model_selection import cross_val_score svm_cv = cross_val_score(svm_pipeline, X_train_resh, y_train_resh, cv=10, scoring='f1') svm_cv.mean() ###### model evaluation ###### from sklearn.metrics import confusion_matrix svm_pipeline.fit(X_train_resh, y_train_resh); svm_train_predict = svm_pipeline.predict(X_train) svm_pred = svm_pipeline.predict(X_test) svm_cm = confusion_matrix(y_train,svm_train_predict) # train데이터에 대한 오차행렬 from sklearn.metrics import roc_curve, auc fpr_lr, tpr_lr = roc_curve(y_test, svm_pipeline.predict_proba(X_test)[:,1]) # test데이터에 대한 roc_curve plt.figure(figsize=(12,8)); plt.plot(fpr_lr, tpr_lr); plt.xlabel('False Positive Raet', fontsize=16); plt.ylabel('True Positive Rate', fontsize=16); plt.title('ROC curve', fontsize=16); plt.plot([0,1],[0,1], color='navy', lw=3, linestyle='--'); sns.despine(fig=None, ax=None, top=True, right=True, left=False, bottom=False, offset=None, trim=False); print('Auc : ', auc(fpr_lr, tpr_lr)) from sklearn.metrics import classification_report from sklearn.metrics import accuracy_score, f1_score print(classification_report(y_test,svm_pred)) print('Accuracy Score : ', accuracy_score(y_test,svm_pred)) print('F1 Score : ', f1_score(y_test,svm_pred))
SQL
복사
###### Grid Search Tunning ###### from sklearn.model_selection import GridSearchCV param_grid = {'C':[0.1,1,10,1000], 'gamma':[1,0.1,0.01,0.001]} grid = GridSearchCV(SVC(), param_grid, verbose=3) # fit the data grid.fit(X_train_resh,y_train_resh) # best parameters based on param grid grid.best_params_ # grid_model with best parameters grid.best_estimator_ grid_train_predict = grid.predict(X_train) grid_pred = grid.predict(X_test) # evaluation of grid classification_report(y_test,grid_pred) classification_report(y_train,grid_train_predict)
SQL
복사