본문 바로가기

Data Science/Pandas

데이터 분석 및 학습 (전체 과정)

728x90

이번 포스팅에서는 실제로 데이터를 이용하여 데이터 전처리부터 특정 머신러닝 모델을 활용하여 학습을 하는 과정까지를 정리해 보았습니다. 


1. 데이터 준비

아래와 같이 diabetes 데이터를 불러옵니다. 이 데이터를 통해 우리는 Outcome 변수를 예측해야 합니다.

 


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.read_csv("diabetes.csv")
df.head()

diabetes data


2. EDA

a. 시각화 1

먼저 종속변수 Outcome의 평균이 컬럼별로 어떤지 시각화해 보겠습니다.

 


diabetes = df.groupby('Outcome').mean()

fig, axes = plt.subplots(2,4, figsize=(20,14))

for i in range(4):
    sns.barplot(diabetes.index, diabetes.iloc[:, i], ax = axes[0][i])
    axes[0][i].set_title(diabetes.columns[i])
    
for i in range(4):
    sns.barplot(diabetes.index, diabetes.iloc[:, i+4], ax = axes[1][i])
    axes[1][i].set_title(diabetes.columns[i + 4])

Visualization

위 그림에서 볼 수 있듯이 모든 컬럼들이 Outcome이 1인 데이터의 평균이 0인 데이터보다 크다는 것을 알 수 있습니다.


 

b. 다중공선성 확인

다음으로는 독립변수끼리 강한 상관관계를 가지고 있는지 correlation 값과 vif 값을 이용하여 확인해 보겠습니다. 

 


sns.heatmap(df.drop(columns='Outcome').corr(), annot=True)

Correlation

우선 Correlation Matrix에서 강한 상관관계가 있는 독립변수는 없는 것 같습니다. (보통 0.7 이상이면 상관관계가 꽤 있다고 봅니다.) 여기까지 해서 다중공선성 확인을 마무리할 수 있지만 VIF를 통해 한 번 더 확인해 보도록 하겠습니다.

 


from statsmodels.stats.outliers_influence import variance_inflation_factor as VIF
from patsy import dmatrices

y, X = dmatrices('Outcome ~ '+'+'.join(df.drop(columns='Outcome').columns[:]), data=df)

vif_df = pd.DataFrame()
vif_df['VIF'] = [VIF(X, i) for i in range(len(df.drop(columns='Outcome').columns)+1)]
vif_df['Columns'] = ['Intercept']+ df.drop(columns='Outcome').columns.tolist()
vif_df = vif_df.set_index('Columns')
vif_df

VIF

위 그림에서도 독립변수의 VIF 값이 크지 않습니다 .(보통 10 이상이면 다중 공선성이 있다고 판단합니다.) 따라서, drop 해야 할 독립변수는 없어 보입니다.

 


c. 이상치 확인

이번에는 각 컬럼별로 이상치를 확인해 보겠습니다. 먼저 아래는 seaborn의 boxplot 함수를 이용하여 이상치 유무를 시각화한 결과입니다.

 


columns = df.drop(columns='Outcome').columns
fig, axes = plt.subplots(2, 4, figsize=(20, 14))

for i in range(4):
    sns.boxplot(data=df[columns[i]], ax = axes[0][i])
    axes[0][i].set_title(columns[i])
    
for i in range(4):
    sns.boxplot(data=df[columns[i+4]], ax = axes[1][i])
    axes[1][i].set_title(columns[i + 4])

boxplot

 

 

Boxplot 결과, 여러 개의 컬럼에서 이상치가 존재하는 것 같습니다. 그중에서 Age 변수의 이상치를 제거하고, 0이 존재하면 안되는 Glucose 값과 BloodPressure 값을 전처리 해 보겠습니다.

 


q3 = df['Age'].quantile(0.75)
q1 = df['Age'].quantile(0.25)
iqr = q3-q1
m = df['Age'].mean()

outlier_index = df[(df['Age']>=m + iqr)].index
sub_df1 = df.copy()
sub_df1.loc[outlier_index, 'Age'] = df['Age'].median() # 이상치 제거
sns.boxplot(sub_df1['Age'])

Age 이상치 제거

 


outlier_index2 = df[df['Glucose']==0].index
sub_df1.loc[outlier_index2, 'Glucose'] = sub_df1['Glucose'].median()
outlier_index3 = df[df['BloodPressure']==0].index
sub_df1.loc[outlier_index3, 'BloodPressure'] = sub_df1['BloodPressure'].median()

Glucose, BloodPressure 0 데이터 제거

 


 

d. OverSampling

종속변수 Outcome의 데이터의 분포를 보면 Outcome이 0인 데이터가 Outcome이 1인 데이터보다 더 많이 포함되어 있습니다. Outcome이 1인 데이터를 Oversampling 하여 Outcome 데이터를 균형 있게 만들어 보겠습니다. (RandomOverSampler 이용)

 


from imblearn.over_sampling import RandomOverSampler

X = sub_df1.drop(['Outcome'], axis=1)
y = sub_df1[['Outcome']]

ros = RandomOverSampler()
X_oversampling, y_oversampling = ros.fit_resample(X,y)

print('기존')
print(sub_df1['Outcome'].value_counts()/len(sub_df1))
print('-'*10)
print('Oversampling 후')
print(y_oversampling['Outcome'].value_counts()/len(y_oversampling))

Original vs Oversampling


 

3. Model 학습

EDA도 어느 정도 마쳤으니 이제 Model을 실제로 학습시켜 보겠습니다.  3가지 모델인 LogisticRegression, XGBClassifier, Support Vector Classifier을 활용하여 각 모델의 학습 속도와 학습 정확도를 비교해 보겠습니다.

 


from sklearn.linear_model import LogisticRegression
from xgboost import XGBClassifier
from sklearn.svm import SVC
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
from sklearn.metrics import accuracy_score
import time
from imblearn.over_sampling import SMOTE


# 모델 준비
log = LogisticRegression()
xgb = XGBClassifier()
svm_clf = SVC(kernel='linear')

# OverSampling - SMOTE를 이용
smote = SMOTE(random_state=0)

# KFold 교차검증
kfold = KFold()

def model_result(model):
    pred_li=[]
    for train_index, test_index in kfold.split(X):
        X_train, X_test = X.iloc[train_index,:], X.iloc[test_index,:]
        y_train, y_test = y.iloc[train_index, :], y.iloc[test_index,:]
        
        X_train_resample, y_train_resample = smote.fit_resample(X_train, y_train) # Oversampling
        
        start = time.time()
        model.fit(X_train_resample, y_train_resample)
        end=time.time()
        
        pred = model.predict(X_test)
        pred_li.append(accuracy_score(pred, y_test['Outcome']))
    print(f"{end-start:.5f} sec")
    print(np.mean(pred_li))
    
    
print(model_result(log)) # 시간: 0.03257 sec, 정확도: 0.756574144809439
print(model_result(xgb)) # 시간: 0.15453 sec, 정확도: 0.729191070367541
print(model_result(svm_clf)) # 시간: 2.48340 sec, 정확도: 0.7734997029114675

 

학습 결과, Support Vector 머신의 정확도가 가장 높았고, Logitic Regression 모델의 속도가 가장 빨랐습니다. 

위에서 학습 속도를 조금 더 줄이기 위하여 PCA를 이용한 주성분 추출을 통해 학습 데이터의 차원을 줄여보겠습니다.

 


from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler

def pca_model_result(model):
    pred_li = []
    for train_index, test_index in kfold.split(X):
        X_train, X_test = X.iloc[train_index,:], X.iloc[test_index, :]
        y_train, y_test = y.iloc[train_index,:], y.iloc[test_index, :]
        
        # OverSampling with SMOTE
        X_train_resample, y_train_resample = smote.fit_resample(X_train, y_train)
        
        # Scaling for PCA
        scaler = StandardScaler() 
        X_train_scaled = scaler.fit_transform(X_train_resample) 
        X_test_scaled = scaler.transform(X_test)
        
        # PCA
        pca = PCA(n_components=5)
        X_train_pca = pca.fit_transform(X_train_scaled)
        X_test_pca = pca.transform(X_test_scaled) # fit_transform 아님!
        
        start = time.time()
        model.fit(X_train_pca, y_train_resample)
        end = time.time()
        
        pred = model.predict(X_test_pca)
        pred_li += [accuracy_score(y_test, pred)]
        
    print(f'time: {end-start} sec')
    print(np.mean(pred_li))

print(pca_model_result(log)) # 시간: 0.004052162170410156 sec, 정확도: 0.7487649605296663
print(pca_model_result(xgb)) # 시간: 0.15052270889282227 sec, 정확도: 0.7005602240896358
print(pca_model_result(svc)) # 시간: 0.01500 sec, 정확도: 0.7422544775485951

 

PCA를 통해 데이터의 차원을 줄이니 학습 속도는 아까보다 빨라졌습니다. 하지만 정확도 측면에서 조금 떨어지네요. 만약 데이터의 개수가 더 많다면 PCA 사용 시, 학습 속도 측면에서는 큰 이점을 볼 수 있을 것 같습니다.

 


 

여기까지 간단하게 데이터를 활용하여 모델을 학습하는 총과정을 밟아 보았습니다. 지금까지 배운 내용을 적용하는 느낌으로 작성하였고 여기에 더하여 GridSearchCV를 이용하여 모델의 최적의 파라미터를 찾는다거나, EDA 과정에서 데이터 전처리를 더 정교하게 하는 등의 여러 가지 방법으로 모델의 성능을 향상할 수 있을 것 같습니다!

 

 

 

 

출처

[데싸라면, 빨간색 물고기, 자투리코드] 파이썬 한권으로 끝내기: 데이터분석전문가(ADP) + 빅데이터분석기사 실기대비, 시대고시기획(2022)

728x90
반응형