본문 바로가기

SK네트웍스 Family AI캠프 10기/Daily 회고

21일차. Supervised Learning - Regression & Classification

더보기

 

21일 차 회고.

 

 토이 프로젝트에 경진대회에 자격증까지 해야 할 일이 많아서 너무 힘든 날이었다.

 

 

 

 

1. Supervised Learning - Regression

 

 

1-1. Regression 평가 지표

 

Linear Regression

from sklearn.datasets import load_diabetes

diabetes = load_diabetes()

data = diabetes.data
target = diabetes.target

 

from sklearn.model_selection import train_test_split

SEED = 42
x_train, x_valid , y_train, y_valid = train_test_split(data, target, random_state=SEED)

 

from sklearn.linear_model import LinearRegression

model = LinearRegression()

model.fit(x_train,y_train)

pred = model.predict(x_valid)

 

R2(Coefficient of Determination; 결정계수)

  • 회귀식이 얼마나 정확한지를 나타낸다.

  • R2의 값은 0에서 1 사이의 값을 가진다.

from sklearn.metrics import r2_score

r2 = r2_score(y_valid, pred)

 

MSE(Mean Squared Error)

  • 실제값과 예측값의 차이의 제곱의 평균을 나타낸다.

  • MSE는 이상치에 민감하며, 직관적이지 못하다.
from sklearn.metrics import mean_squared_error

mse = mse_squared_error(y_valid, pred)

 

RMSE(Root Mean Squared Error)

  • MSE의 제곱근을 나타낸다.

  • RMSE 또한 이상치에 민감하다.
import numpy as np

np.sqrt(mse)

 

RMSLE(Root Mean Squared Logarithmic Error)

  • 실제값과 예측값의 로그 차이의 제곱의 평균의 제곱근을 나타낸다.

  • 값이 작을수록 오류가 적다.
df rmsle(y, pred, converetExp=False):
    if convertExp:
        y = np.exp(y)
        pred = np.exp(pred)
    
    log1 = np.nan_to_num(np.array([np.log(v + 1) for v in y]))
    log2 = np.nan_to_num(np.array([np.log(v + 1) for v in pred]))
    calc = (log1 - log2)**2
    
    return np.sqrt(np.mean(calc))

rmsle_score = rmsle(y_valid, pred)

 

MAE(Mean Absolute Error)

  • 실제값과 예측값의 차이의 절댓값의 평균을 나타낸다.

  • 손실함수로 주로 사용한다.
from sklearn.metrics import mean_absolute_error

mae = mean_absolute_error(y_valid, pred)

 

MAPE(Mean Absolute Percentage Error)

  • 실제값에 대한 절대오차 비율의 평균을 백분율로 표현한다.

  • MAPE는 0에서 1 사이의 값을 가진다.
    • 0에 가까울수록 학습이 잘 됐다는 것을 나타낸다.
from sklearn.metrics import mean_absolute_percentage_error

mape = mean_absolute_percentage_error(y_valid, pred)

 

SMAPE(Symmetric Mean Absolute Percentage Error)

  • 실제값에 0이 존재해도 계산이 가능하다.
def smape(true, pred):
    error = np.abs(true - pred) / (np.abs(true) + np.abs(pred))
    return np.mean(error)

smape(y_valid, pred)

 

 

1-2. 경사하강법

 

Linear Regression

  • 단순 선형 회귀식

  • 가중치(w)와 절편(b)을 MSE를 최소화하는 방법으로 구할 수 있다. 

 

손실함수

 

Learning Rate

  • Learning Rate가 작을 경우, 학습이 오래 걸린다.
  • Learning Rate가 클 경우, 학습이 되지 않을 수 있다.

 

코드 구현

import numpy as np
import matplotlib.pyplot as plt

X = np.random.rand(100)
Y = 0.3 * X + 1.5

plt.figure(figsize=(8, 6))
plt.scatter(X, Y)
plt.show()

def plot_prediction(pred, y, W, b):
    plt.figure(figsize=(8, 6))
    plt.scatter(X, y)
    plt.scatter(X, pred)
    plt.title(f"W: {W} / b: {b}")
    plt.show()

W = np.random.uniform(-1, 1)
b = np.random.uniform(-1, 1)
learning_rate = 0.7

for epoch in range(200):
    Y_pred = W * X + b
    
    error = np.abs(Y_pred - Y).mean()
    if error < 0.001:				# 학습 완료
        break
    
    w_grad = learning_rate * ((Y_pred - Y) * X).mean()
    b_grad = learning_rate * ((Y_pred - Y) * 1).mean()
    
    W = W - w_grad
    b = b - b_grad
    
    if epoch % 10 == 0:
        Y_pred = W * X + b
        plot_prediction(Y_pred, Y, W, b)

 

 

1-3. 모델

 

필수 라이브러리

!pip install --upgrade joblib==1.1.0
!pip install --upgrade scikit-learn==1.1.3
!pip install mglearn

import logging
logging.getLogger('matplotlib.font_manager').setLevel(logging.ERROR)

import mglearn
from sklearn.model_selection import train_test_split

import pandas as pd
import numpy as np

import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn as sns

%matplotlib inline

 

Linear Regression

  • 단순 선형 회귀 분석(Simple Linear Regression Analysis)
  • 다중 선형 회귀 분석(Multiple Linear Regression Analysis)

 

정규화(Regularization)

모델을 학습할 때는 비용(cost) 즉, 오류를 최소화하는 방향으로 진행한다. 

  • L1 Regularization

  • L2 Regularization

 

Linear Regression

  • Linear Regression
from sklearn.linear_model import LinerRegression

# 데이터 로드
X, y = mglearn.datasets.load_extended_boston()

# 데이터 분리
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.25, random_state=0)

# 선형 회귀 모델 정의
lr = LinearRegression()

# 모델 학습
lr.fit(X_tr, y_tr)

# bias: lr.intercept_
# weights: lr.coef_

# 모델을 이용한 검증 데이터 예측
pred = lr.predict(X_te)

# 모델 평가
print(f'훈련용 평가지표: {lr.score(X_tr, y_tr)} / 테스트용 평가지표: {lr.score(X_te, y_te)}')
# 훈련용 평가지표: 0.9520519609032728 / 테스트용 평가지표: 0.6074721959665773
  • Ridge with L2
from sklearn.linear_model import Ridge

ridge = Ridge().fit(X_tr, y_tr)
print(f'훈련용 평가지표: {ridge.score(X_tr, y_tr)} / 테스트용 평가지표: {ridge.score(X_te, y_te)}')
# 훈련용 평가지표: 0.8857966585170941 / 테스트용 평가지표: 0.7527683481744751

# alpha 값을 낮추면 규제의 효과가 없어서 overfitting 가능성이 높아진다.
ridge01 = Ridge(alpha=0.1).fit(X_tr, y_tr)
print(f'훈련용 평가지표: {ridge01.score(X_tr, y_tr)} / 테스트용 평가지표: {ridge01.score(X_te, y_te)}')
# 훈련용 평가지표: 0.9282273685001992 / 테스트용 평가지표: 0.7722067936479814

# alpha 값을 높이면 훈련 세트의 성능은 나빠지지만 일반화에는 도움을 줄 수 있다.
ridge10 = Ridge(alpha=10).fit(X_tr, y_tr)
print(f'훈련용 평가지표: {ridge10.score(X_tr, y_tr)} / 테스트용 평가지표: {ridge10.score(X_te, y_te)}')
# 훈련용 평가지표: 0.7882787115369614 / 테스트용 평가지표: 0.635941148917731

 

plt.figure(figsize=(10,8))

plt.plot(ridge10.coef_, '^', label='Ridge alpha=10')
plt.plot(ridge.coef_, 's', label='Ridge alpha=1')
plt.plot(ridge01.coef_, 'v', label='Ridge alpha=0.1')

plt.plot(lr.coef_, 'o', label='LinearRegression')

plt.xlabel('alpha list') # 계수 목록
plt.ylabel('alpha size') # 계수 크기
plt.hlines(0, 0, len(lr.coef_))
plt.ylim(-25, 25)
plt.legend()

  • Lasso with L1
from sklearn.linear_model import Lasso

lasso = Lasso().fit(X_tr, y_tr)
print(f'훈련용 평가지표: {lasso.score(X_tr, y_tr)} / 테스트용 평가지표: {lasso.score(X_te, y_te)}')
print('-'*80)
print(f'사용한 특성의 수: {np.sum(lasso.coef_ != 0)} / 전체 특성 수: {X_tr.shape[1]}')
# 훈련용 평가지표: 0.29323768991114607 / 테스트용 평가지표: 0.20937503255272294
# --------------------------------------------------------------------------------
# 사용한 특성의 수: 4 / 전체 특성 수: 104

lass001 = Lasso(alpha=0.01, max_iter=100000).fit(X_tr, y_tr)
print(f'훈련용 평가지표: {lass001.score(X_tr, y_tr)} / 테스트용 평가지표: {lass001.score(X_te, y_te)}')
print('-'*80)
print(f'사용한 특성의 수: {np.sum(lass001.coef_ != 0)} / 전체 특성 수: {X_tr.shape[1]}')
# 훈련용 평가지표: 0.9507158754515463 / 테스트용 평가지표: 0.6437467421272821
# --------------------------------------------------------------------------------
# 사용한 특성의 수: 96 / 전체 특성 수: 104

lass00001 = Lasso(alpha=0.0001, max_iter=100000).fit(X_tr, y_tr)
print(f'훈련용 평가지표: {lass00001.score(X_tr, y_tr)} / 테스트용 평가지표: {lass00001.score(X_te, y_te)}')
print('-'*80)
print(f'사용한 특성의 수: {np.sum(lass00001.coef_ != 0)} / 전체 특성 수: {X_tr.shape[1]}')
# 훈련용 평가지표: 0.9507158754515463 / 테스트용 평가지표: 0.6437467421272821
# --------------------------------------------------------------------------------
# 사용한 특성의 수: 96 / 전체 특성 수: 104
plt.figure(figsize=(10,8))

plt.plot(lasso.coef_, 's', label='Lasso alpha=1')
plt.plot(lass001.coef_, '^', label='Lasso alpha=0.01')
plt.plot(lass00001.coef_, 'v', label='Lasso alpha=0.0001')

plt.plot(ridge01.coef_, 'o', label='Ridge alpha=0.1')

plt.xlabel('alpha list') # 계수 목록
plt.ylabel('alpha size') # 계수 크기
plt.hlines(0, 0, len(lr.coef_))
plt.ylim(-25, 25)
plt.legend(ncol=2, loc=(0, 1.05))

  • ElasticNet
from sklearn.linear_model import ElasticNet

elnet = ElasticNet().fit(X_tr, y_tr)
print(f'훈련용 평가지표: {elnet.score(X_tr, y_tr)} / 테스트용 평가지표: {elnet.score(X_te, y_te)}')
print('-'*80)
print(f'사용한 특성의 수: {np.sum(elnet.coef_ != 0)} / 전체 특성 수: {X_tr.shape[1]}')
# 훈련용 평가지표: 0.32837814485847916 / 테스트용 평가지표: 0.2217004367773664
# --------------------------------------------------------------------------------
# 사용한 특성의 수: 38 / 전체 특성 수: 104

alpha=0.01
ratios = [0.2, 0.5, 0.8]

for ratio in ratios:
    elnet = ElasticNet(alpha=alpha, l1_ratio=ratio, random_state=42).fit(X_tr, y_tr)
    print(f'ratio: {ratio}')
    print(f'훈련용 평가지표: {elnet.score(X_tr, y_tr)} / 테스트용 평가지표: {elnet.score(X_te, y_te)}')
    print(f'사용한 특성의 수: {np.sum(elnet.coef_ != 0)} / 전체 특성 수: {X_tr.shape[1]}')
    print('-'*80)
# ratio: 0.2
# 훈련용 평가지표: 0.8421033820826037 / 테스트용 평가지표: 0.7055067157435387
# 사용한 특성의 수: 93 / 전체 특성 수: 104
# --------------------------------------------------------------------------------
# ratio: 0.5
# 훈련용 평가지표: 0.8553665697077995 / 테스트용 평가지표: 0.7209054027265394
# 사용한 특성의 수: 84 / 전체 특성 수: 104
# --------------------------------------------------------------------------------
# ratio: 0.8
# 훈련용 평가지표: 0.8752442706471416 / 테스트용 평가지표: 0.741439523522267
# 사용한 특성의 수: 63 / 전체 특성 수: 104
# --------------------------------------------------------------------------------

alphas=[0.001, 0.01, 0.1]
ratio = 0.8

for alpha in alphas:
    elnet = ElasticNet(alpha=alpha, l1_ratio=ratio, random_state=42).fit(X_tr, y_tr)
    print(f'alpha: {alpha}')
    print(f'훈련용 평가지표: {elnet.score(X_tr, y_tr)} / 테스트용 평가지표: {elnet.score(X_te, y_te)}')
    print(f'사용한 특성의 수: {np.sum(elnet.coef_ != 0)} / 전체 특성 수: {X_tr.shape[1]}')
    print('-'*80)
# alpha: 0.001
# 훈련용 평가지표: 0.9271504286329 / 테스트용 평가지표: 0.7813331506633265
# 사용한 특성의 수: 91 / 전체 특성 수: 104
# --------------------------------------------------------------------------------
# alpha: 0.01
# 훈련용 평가지표: 0.8752442706471416 / 테스트용 평가지표: 0.741439523522267
# 사용한 특성의 수: 63 / 전체 특성 수: 104
# --------------------------------------------------------------------------------
# alpha: 0.1
# 훈련용 평가지표: 0.7399596108844948 / 테스트용 평가지표: 0.5768205411208096
# 사용한 특성의 수: 33 / 전체 특성 수: 104
# --------------------------------------------------------------------------------

 

 

 

2. Supervised Learning - Classification

 

 

2-1. Confusion Matrix

 

혼동행렬(Confusion Matrix)

  • 예측과 실제 값 사이의 관계를 행렬로 표현한 것을 말한다.

  • Sensitivity(Recall): 민감도(재현도)
    • 실제로 참인 것들 중에 예측이 참인 비율
  • Specificity: 특이도
  • Precision: 정밀도
    • 예측이 참인 것들 중에 실제로 참인 비율
  • Accuracy: 정확도
    • 전체 문제 중에서 정답을 맞힌 비율
  • TP(True Positive, 참긍정)
    • 예측이 참인데 실제로도 참인 경우
  • TN(True Negative, 참부정)
    • 예측이 거짓인데 실제로도 거짓인 경우
  • FP(False Positive, 거짓긍정)
    • 예측이 참인데 실제로는 거짓인 경우
  • FN(False Negative, 거짓부정)
    • 예측이 거짓인데 실제로는 참인 경우

 

코드 구현

  • Load Data
import numpy as np
import pandas as pd

from sklearn.datasets import load_digits

digits = load_digits()
digits.data[0].reshape(8, 8)
import matplotlib.pyplot as plt

plt.imshow(digits.data[0].reshape(8, 8))
plt.colorbar()
plt.show()

data = digits.data

# Binary Classifier(이진 분류)
target = (digits.target == 5).astype(int)
from sklearn.model_selection import train_test_split

SEED = 42
x_train, x_valid, y_train, y_valid = train_test_split(data, target, random_state=SEED)

digit = data[5]
import matplotlib as mpl
import matplotlib.pyplot as plt

import seaborn as sns

digit_img = digit.reshape(8, 8)

plt.imshow(digit_img, cmap="gray")
plt.axis("off")
plt.show()

  • Modeling
from sklearn.linear_model import SGDClassifier

sgd_clf = SGDClassifier(random_state=42)

sgd_clf.fit(x_train, y_train)

sgd_clf.predict([digit])
from sklearn.model_selection import cross_val_predict

y_train_pred = cross_val_predict(sgd_clf, x_train, y_train, cv=10)
  • Confusion Matrix
from sklearn.metrics import confusion_matrix

conf_mx = confusion_matrix(y_train, y_train_pred, normalize="true")

plt.figure(figsize=(7,5))

sns.heatmap(conf_mx, annot=True, cmap="coolwarm", linewidth=0.5)

plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.show()

 

평가지표

  • Dummy 예측
from sklearn.dummy import DummyClassifier

# 모델 정의
dummy = DummyClassifier(strategy='most_frequent')

# 모델 학습
dummy.fit(x_train,y_train)

# 모델 예측
pred_dummy = dummy.predict(x_valid)
  • 모델 예측
from sklearn.linear_model import SGDClassifier

# 모델 정의
sgd_clf = SGDClassifier(random_state=42)

# 모델 학습
sgd_clf.fit(x_train, y_train)

# 모델 예측
pred_clf = sgd_clf.predict(x_valid)
  • Accuracy(정확도)
    • 음성(0)이 양성(1)보다 훨씬 많은 경우, 음성(0)으로만 예측해도 높은 정확도를 보이기 때문에 적절한 성능지표가 될 수 없다.

from sklearn.metrics import accuracy_score

score = accuracy_score(y_valid, pred_dummy)
print(f"dummy: {score}")

score = accuracy_score(y_valid, pred_clf)
print(f"model: {score}")

# dummy: 0.8688888888888889
# model: 0.9866666666666667
  • Precision(정밀도)
    • FP를 줄이는 것이 목표일 때 사용한다.

from sklearn.metrics import precision_score

score = precision_score(y_valid, pred_dummy)
print(f"dummy: {score}")

score = precision_score(y_valid, pred_clf)
print(f"model: {score}")

# dummy: 0.0
# model: 1.0
  • Sensitivity(민감도) = Recall(재현도)
    • FN을 줄이는 것이 목표일 때 사용한다.

from sklearn.metrics import recall_score

score = recall_score(y_valid, pred_dummy)
print(f"dummy: {score}")

score = recall_score(y_valid, pred_clf)
print(f"model: {score}")

# dummy: 0.0
# model: 0.8983050847457628
  • F1 Score
    • 불균형한 데이터가 잘 동작하는지에 대한 평가지표

from sklearn.metrics import f1_score

score = f1_score(y_valid, pred_dummy)
print(f"dummy: {score}")

score = f1_score(y_valid, pred_clf)
print(f"model: {score}")

# dummy: 0.0
# model: 0.9464285714285714
from sklearn.metrics import classification_report

print(classification_report(y_valid, pred_dummy))
print('-'*100)
print(classification_report(y_valid, pred_clf))

#               precision    recall  f1-score   support
# 
#            0       0.87      1.00      0.93       391
#            1       0.00      0.00      0.00        59
# 
#     accuracy                           0.87       450
#    macro avg       0.43      0.50      0.46       450
# weighted avg       0.75      0.87      0.81       450
#
# ----------------------------------------------------------------------------------------------------
#               precision    recall  f1-score   support
#
#            0       0.98      1.00      0.99       391
#            1       1.00      0.90      0.95        59
#
#     accuracy                           0.99       450
#    macro avg       0.99      0.95      0.97       450
# weighted avg       0.99      0.99      0.99       450
  • Thresholds(임계값)
pred_proba = sgd_clf.decision_function(x_valid)

threshold = 0				# default
pred = np.where(pred_proba > threshold, 1, 0)
precision_score(y_valid, pred), recall_score(y_valid, pred)
# (1.0, 0.8983050847457628)

threshold = 30000
pred = np.where(pred_proba > threshold, 1, 0)
precision_score(y_valid, pred), recall_score(y_valid, pred)
# (0.0, 0.0)

threshold = -30000
pred = np.where(pred_proba > threshold, 1, 0)
precision_score(y_valid, pred), recall_score(y_valid, pred)
# (0.13111111111111112, 1.0)
from sklearn.tree import DecisionTreeClassifier

# 모델 생성
tree = DecisionTreeClassifier(max_depth=3, random_state=SEED)

# 모델 학습
tree.fit(x_train,y_train)

# 모델 평가
pred_tree = tree.predict(x_valid)

tree.predict_proba(x_valid)[0]
# array([0.97628458, 0.02371542]) -> array([0일 확률, 1일 확률])

pred_proba = tree.predict_proba(x_valid)[:, 1]

threshold = 0.5
pred = np.where(pred_proba > threshold, 1, 0)
precision_score(y_valid, pred), recall_score(y_valid, pred)
# (0.9811320754716981, 0.8813559322033898)

threshold = 0.9
pred = np.where(pred_proba > threshold, 1, 0)
precision_score(y_valid, pred), recall_score(y_valid, pred)
# (0.9811320754716981, 0.8813559322033898)

threshold = 0.1
pred = np.where(pred_proba > threshold, 1, 0)
precision_score(y_valid, pred), recall_score(y_valid, pred)
# (0.8813559322033898, 0.8813559322033898)

 

 

2-2. Binary Classification

 

이진 분류 모형

  • ROC(Receiver Operating Characteristic) Curve
    • FPR를 x축으로, TPR을 y축으로 정의하여 둘 간의 관계를 표현한 그래프

  • AUROC(Area Under ROC Curve)
    • ROC Curve의 밑부분 면적
    • 넓을수록 모형 성능이 좋다.
    • 임계값(threshold)과 무관하게 모델의 예측 품질을 측정할 수 있다.
      • Poor model: 0.5 ~ 0.7
      • Fair model: 0.7 ~ 0.8
      • Good model: 0.8 ~ 0.9
      • Excellent model: 0.9 ~ 1.0

 

코드 구현

  • Load Data
import numpy as np
import pandas as pd

from sklearn.datasets import load_digits

digits = load_digits()
import matplotlib.pyplot as plt

plt.imshow(digits.data[0].reshape(8, 8))
plt.colorbar()
plt.show()

data = digits.data

# Binary Classifier
target = (digits.target == 5).astype(int)
from sklearn.model_selection import train_test_split

SEED = 42
x_train, x_valid, y_train, y_valid = train_test_split(data, target, random_state=SEED)
  • Modeling
from sklearn.dummy import DummyClassifier

dummy = DummyClassifier(strategy='most_frequent')

dummy.fit(x_train,y_train)

pred_dummy = dummy.predict_proba(x_valid)[:,1]
from sklearn.tree import DecisionTreeClassifier

tree = DecisionTreeClassifier(max_depth=3, random_state=SEED)

tree.fit(x_train,y_train)

# pred_tree = tree.predict(x_valid)
pred_tree = tree.predict_proba(x_valid)[:,1]
  • 평가
from sklearn.metrics import roc_curve, auc

fpr, tpr, thresholds = roc_curve(y_valid,pred_dummy)
print(f'dummy: {auc(fpr, tpr)}')
print('-'*50)
fpr, tpr, thresholds = roc_curve(y_valid,pred_tree)
print(f'model: {auc(fpr, tpr)}')

# dummy: 0.5
# --------------------------------------------------
# model: 0.9479604664268065
from sklearn.metrics import RocCurveDisplay

fig,ax = plt.subplots()
RocCurveDisplay.from_predictions(y_valid,pred_dummy,ax=ax)
RocCurveDisplay.from_predictions(y_valid,pred_tree,ax=ax)
plt.show()

import seaborn as sns
from sklearn.metrics import confusion_matrix

pred_tree = tree.predict(x_valid)
conf_mx = confusion_matrix(y_valid, pred_tree, normalize="true")

plt.figure(figsize=(7,5))

sns.heatmap(conf_mx, annot=True, cmap="coolwarm", linewidth=0.5)

plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.show()