● 책 '파이썬 머신러닝 완벽 가이드' 참고

● 예시로 '월간 데이콘 쇼츠 - 초전도체 임계온도 예측 AI 헤커톤'의 데이터 사용(train, test, submission 파일 3개 존재)

 

1. 모듈 임포트

import os
# Seed 고정
def seed_everything(seed):
  random.seed(seed)
  os.environ['PYTHONHASHSEED'] = str(seed)
  np.random.seed(seed)
seed_everything(42)
# warning 무시
import warnings
warnings.filterwarnings("ignore")

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

from sklearn.preprocessing import LabelEncoder, OneHotEncoder
from sklearn.preprocessing import StandardScaler, MinMaxScaler

from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
from sklearn.tree import DecisionTreeClassifier, DicisionTreeRegressor
from sklearn.linear_model import LogisticRegression, LinearRegression

from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import GridSearchCV

from sklearn.metrics import accuracy_score, mean_squared_error, mean_absolute_error, r2_score

 

 

2. 데이터 불러오기

train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
submission = pd.read_csv('sample_submission.csv')

 

 

3. 데이터 탐색

train.head()  # 처음 5개의 데이터 (전체적인 데이터 구조 확인)
train.info()  # 데이터에 대한 개략적인 정보 (행과 열 개수, non-null값 개수, 데이터 타입)
train.describe()  # 숫자형 데이터 요약 (개수, 평균, 표준편차, 최소값, 25%, 50%, 75%, 최대값)
train.isna().sum()  # 각 데이터의 결측값 개수
# 이외에도 이해가 가지 않는 행이나 열만 따로 뽑아서 탐색, 데이터 도메인 지식 등을 찾아보며 데이터에 대한 이해 필요
train = train.drop('ID', axis = 1)  # train 데이터 탐색 결과 단순 식별자 변수인 ID는 필요 없는 것으로 판단, 삭제
feature = train.copy()  # 밑에서 target변수를 제거하고 feature 변수만 남긴 데이터
target = feature.pop('critical_temp') # target 데이터 추출, train에는 feature 데이터만 남음

  - train.head(): 처음 5개의 데이터 (전체적인 데이터 구조 확인)

  - train.info(): 데이터에 대한 개략적인 정보 (행과 열 개수, non-null값 개수, 데이터 타입)

  - train.describe(): 숫자형 데이터 요약 (개수, 평균, 표준편차, 최소값, 25%, 50%, 75%, 최대값)

  - train.isna().sum(): 각 데이터의 결측값 개수

 

 

4. 데이터 시각화

  - 박스플롯으로 이상값 등 데이터 분포 확인

scaler = StandardScaler() # 박스플롯 출력 전 하나의 박스플롯에서 데이터를 한눈에 보기 위해 같은 범위로 임시 스케일링
plt.figure(figsize = (15, 10))
sns.boxplot(scaler.fit_transform(feature))

 

  - 각 feature 데이터의 바이오린 플롯과 히스토그램 출력하여 데이터 분포 확인

for col in train.columns:
  fig, axs = plt.subplots(nrows = 1, ncols = 2, figsize = (10, 4))
  sns.violinplot(x = col, data = train, kde = True, ax = axs[0])
  sns.histplot(x = col, data = train, kde = True, ax = axs[1])

 

  - 히트맵으로 각 feature간 상관관계 파악

plt.figure(figsize = (15, 15))
sns.heatmap(train.corr(), cmap = 'vlag')

 

  - 히트맵으로 결측값 분포 확인(결측값이 있다면 그 부분이 살구색으로 표현됨)

plt.figure(figsize = (15, 15))
sns.heatmap(train.isnull())

 

 

5. 데이터 전처리(결측값 처리)

# 방법 1. 결측값이 있는 행 제거
feature = feature.dropna(axis = 0, how = 'any') # 각 행에서 하나의 값이라도 NaN이면 해당 행 제거
feature = feature.dropna(axis = 0, how = 'all') # 각 행의 모든 값이 NaN이면 해당 행 제거

# 방법 2. 결측값 채우기(특정 값으로 채우기)
feature = feature.fillna(0) # 0으로 채우기
feature = feature.fillna(method = 'pad')  # 바로 앞의 값으로 채우기
feature = feature.fillna(method = 'bfill')  # 바로 뒤의 값으로 채우기
feature = feature.fillna(feature.mean())  # 해당 열의 평균값으로 채우기

# 방법 3. 결측값 채우기(보간법)
feature = feature.interpolate(method = 'time') # 인덱스가 날짜/시간일 때 날짜나 시간에 따라 보간
feature = feature.interpolate(method = 'nearest', limit_direction = 'forward') # 가장 가까운 값으로 보간
feature = feature.interpolate(method = 'linear', limit_direction = 'forward') # 양 끝값에 따라 선형식에 적용하여 보간
feature = feature.interpolate(method = 'polynomial', order = 2) # 양 끝값에 따라 다항식에 적용하여 보간 (order은 다항식 차수)

# 방법 4. 결측값이 있는 열을 해당 열의 결측값이 아닌 행과 다른 feature 변수들로 예측하여 채우기
feature_target = feature.pop('결측값 열') # 다른 행들로부터 결측값을 예측할 목표열 분리
target_index = feature_target[feature_target['결측값 열'] == np.nan].index  # 목표열 중 결측값인 행의 인덱스 추출
feature_train = feature.iloc[~target_index] # 목표열이 아닌 다른 열에서 결측값이 있는 행의 인덱스가 아닌 행 추축(train 용)
feature_test = feature.iloc[target_index] # 목표열이 아닌 다른 열에서 결측값이 있는 행의 인덱스인 행 추출(test 용)
feature_target_train = feature_target[~target_index]  # 목표열 중 결측값이 아닌 행 추출(train 용)
rf = RandomForestRegressor()  # 랜덤포레스트로 예측
rf.fit(feature_train, feature_target_train) # 랜덤포레스트에 목표열 중 결측값이 아닌 행을 target 변수, 다른 열의 해당 행들을 feature 변수로 모델 피팅
pred = rf.predict(feature_test) # 피팅된 모델로 목표열에서 결측값인 행 예측
feature.iloc[target_index]['결측값 열'] = pred  # 원래 데이터프레임에서 목표열의 결측값인 값을 위에서 예측한 값으로 대체

 

 

6. 데이터 전처리(이상값 처리)

# IQR으로 이상값 판하여 절단
def outlier(df, col):
  tar = df[col]
  quantile_25 = np.percentile(tar.values, 25)
  quantile_75 = np.percentile(tar.values, 75)
  iqr = quantile_75 - quantile_25
  low = quantile_25 - iqr * 1.5
  high = quantile_75 + iqr * 1.5
  outlier_index = tar[(tar < low) | (tar > high)].index
  del_outlier = df.drop(outlier_index, axis = 0)
  return del_outlier

 

 

7. 데이터 전처리(표준화 및 정규화 = 피처 스케일링)

# 방법 1. StandardScaler
scaler = StandardScaler()
feature[[feature.select_dtypes([float, int]).columns]] = scaler.fit_transform(feature.select_dtypes([float, int]))
test[[test.select_dtypes([float, int]).columns]] = scaler.transform(test.select_dtypes([float, int]))

  -  평균 0, 분산 1

  - SVM, 선형회귀, 로지스틱 회귀는 데이터가 가우시안 정규 분포를 가지고 있다고 가정하고 구현되어 있기 때문에 StandardScaler가 예측 성능 향상에 도움

 

# 방법 2. MinMaxScaler
scaler = MinMaxScaler()
feature[[feature.select_dtypes([float, int]).columns]] = scaler.fit_transform(feature.select_dtypes([float, int]))
test[[test.select_dtypes([float, int]).columns]] = scaler.transform(test.select_dtypes([float, int]))

  - 최소값 0, 최대값 1

  - 음수값이 있다면 최소값 -1, 최대값 1

  - 데이터가 가우시안 정규 분포가 아닐 때 적용해볼 수 있음

 

 

8. 데이터 전처리(인코딩)

# 방법 1. 문자형 변수 라벨 인코딩
encoder = LabelEncoder()
feature[[feature.select_dtypes(object).columns]] = encoder.fit_transform(feature.select_dtypes(object))
test[[test.select_dtypes(object).columns]] = encoder.transform(test.select_dtypes(object))

 

# 방법 2. 문자형 변수 원핫 인코딩
encoder = OneHotEncoder()
feature[[feature.select_dtypes(object).columns]] = encoder.fit_transform(feature.select_dtypes(object).toarray())
test[[test.select_dtypes(object).columns]] = encoder.transform(test.select_dtypes(object).toarray())

  - 라벨 인코딩은 문자형 변수 고윳값의 개수에 따라 0부터 n-1까지의 숫자로 변환되어 ML에서 왜곡된 결과를 불러올 수 있음

  - 원핫 인코딩은 이런 문제를 해결하기 위해 고윳값의 개수만큼 새로운 feature 변수를 생성하여 각 행에서 고윳값에 해당하는 feature만 1, 나머지는 0으로 표시

 

 

9. 모델 피팅 및 예측

# feature 변수와 target 변수 분리
X = feature
y = target

# 학습용 데이터(X_train, y_train)와 검증용 데이터(X_test, y_test)로 분리
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)

# 모델 선언(사용할 모델로 선정, 예시로는 RandomForestRegressor 선정)
model = RandomForestRegressor()

# 모델 학습
model.fit(X_train, y_train)

# 학습된 모델로 검증용 데이터 예측
y_pred = rf.predict(X_test)

# 실제 검증용 데이터와 예측한 검증용 데이터 비교하여 점수 출력
print(f'mse: {mean_squared_error(y_test, y_pred)}\nmae: {mean_absolute_error(y_test. y_pred)}\naccuracy: {accuracy_score(y_test, y_pred)}')

 

 

10. 교차 검증

# 방법 1. KFold
def exec_kfold(model, folds = 5):
  kfold = KFold(n_splits = folds)
  scores = []

  for iter, (train_index, test_index) in enumerate(kfold.split(X)):
    X_train, X_test = X.values[train_index], X.values[test_index]
    y_train, y_test = y.values[train_index], y.values[test_index]
    model.fit(X_train, y_train)
    y_pred = model.predict(X_test)
    accuracy = accuracy_score(y_test, y_pred)
    scores.append(accuracy)
    print(f'교차 검증 {iter} 정확도: {accuracy:.4f}')

  print(f'평균 정확도: {np.mean(scores):.4f}')

exec_kfold(model, folds = 5)
# 방법 2. cross_val_score
scores = cross_val_score(model, X, y, cv = 5)
for iter, accuracy in enumerate(scores):
  print(f'교차 검증 {iter} 정확도: {accuracy:.4f}')
print(f'평균 정확도: {np.mean(scores):.4f}')

  - KFold와 cross_val_score에서 각각 평균 정확도가 다른 이유는 cross_val_score가 StratifiedKFold를 이용해 폴드 세트를 분할하기 때문

 

# 방법 3. GridSearchCV
params = {'max_depth': [2, 3, 5, 10], 'min_samples_split': [2, 3, 5], 'min_samples_leaf': [1, 5, 8]}  # 사용할 모델에 맞는 하이퍼 파라미터 중 탐색하고 싶은 범위 지정
grid_model = GridSearchCV(model, param_grid = params, scoring = 'accuracy', cv = 5, verbose = 3)  # GridSearchCV에 model, 하이퍼 파라미터, 점수, 교차검증 횟수 등을 파라미터로 지정
grid_model.fit(X_train, y_train)  # GridSearchCV 모델에 train 데이터 피팅
print(f'GridSearchCV 최적 하이퍼 파라미터: {grid_model.best_params_}')  # 최적 하이퍼 파라미터
print(f'GridSearchCV 최고 정확도: {grid_model.best_score_:.4f}')  # 최적 하이퍼 파라미터일 때 나온 최고 점수
best_model = grid_model.best_estimator_ # 최적 하이퍼 파라미터로 최고 점수가 나온 모델을 best_model로 저장

y_pred = best_model.predict(X_test) # best_model로 test 데이터 예측
accuracy = accuracy_score(y_test, y_pred) # 점수 계산
print(f'테스트 세트에서의 model 정확도: {accuracy:.4f}')

 

 

11. 기본 분석 이후

  - pycaret, autogluon 등 auto ML을 사용해 최적의 모델 선택

  - 데이터에 대한 깊은 이해를 통해 더 최적의 변수 선택 및 전처리, 차원 축소 및 군집화 등으로 파생변수 생성

+ Recent posts