Note

λ¨Έμ‹ λŸ¬λ‹μ€ 데이터 가곡/λ³€ν™˜, λͺ¨λΈ ν•™μŠ΅/예츑, 그리고 평가(Evaluation)의 ν”„λ‘œμ„ΈμŠ€λ‘œ ꡬ성

λ¨Έμ‹ λŸ¬λ‹ λͺ¨λΈμ€ μ—¬λŸ¬ κ°€μ§€ λ°©λ²•μœΌλ‘œ 예츑 μ„±λŠ₯을 평가할 수 있음

μ„±λŠ₯ 평가 μ§€ν‘œ(Evaluation Metric)λŠ” 일반적으둜 λͺ¨λΈμ΄ λΆ„λ₯˜λƒ νšŒκ·€λƒμ— 따라 μ—¬λŸ¬ μ’…λ₯˜λ‘œ λ‚˜λ‰¨

νšŒκ·€μ˜ 경우 λŒ€λΆ€λΆ„ μ‹€μ œ κ°’κ³Ό μ˜ˆμΈ‘κ°’μ˜ 였차 평균값에 기반

  • 예λ₯Ό λ“€μ–΄ μ˜€μ°¨μ— μ ˆλŒ“κ°’μ„ μ”Œμš΄ λ’€ 평균 였차λ₯Ό κ΅¬ν•˜κ±°λ‚˜ 였차의 제곱 값에 루트λ₯Ό μ”Œμš΄ λ’€ 평균 였차λ₯Ό κ΅¬ν•˜λŠ” 방법 λ“±
  • 기본적으둜 예츑 였차λ₯Ό κ°€μ§€κ³  μ •κ·œν™” μˆ˜μ€€μ„ μž¬κ°€κ³΅ ν•˜λŠ” 방법이 νœ˜κ·€μ˜ μ„±λŠ₯ 평가 μ§€ν‘œ μœ ν˜•

λΆ„λ₯˜μ˜ 평가 방법도 μΌλ°˜μ μœΌλ‘œλŠ” μ‹€μ œ κ²°κ³Ό 데이터와 예츑 κ²°κ³Ό 데이터가 μ–Όλ§ˆλ‚˜ μ •ν™•ν•˜κ³  였λ₯˜κ°€ 적게 λ°œμƒν•˜λŠ” 가에 κΈ°λ°˜ν•˜μ§€λ§Œ, λ‹¨μˆœνžˆ μ΄λŸ¬ν•œ μ •ν™•λ„λ§Œ κ°€μ§€κ³  νŒλ‹¨ν–ˆλ‹€κ°€λŠ” 잘λͺ»λœ 평가 결과에 빠질 수 있음

λ³Έ μž₯μ—μ„œλŠ” λΆ„λ₯˜μ— μ‚¬μš©λ˜λŠ” μ„±λŠ₯ 평가 μ§€ν‘œμ— λŒ€ν•΄μ„œ μ§‘μ€‘μ μœΌλ‘œ μ„€λͺ…함

  • 특히 0κ³Ό 1둜 결정값이 ν•œμ •λ˜λŠ” 이진 λΆ„λ₯˜μ˜ μ„±λŠ₯ 평가 μ§€ν‘œμ— λŒ€ν•΄μ„œ μ§‘μ€‘μ μœΌλ‘œ μ„€λͺ…!

λΆ„λ₯˜μ˜ μ„±λŠ₯ 평가 μ§€ν‘œ

  • 정확도 (Accuracy)
  • μ˜€μ°¨ν–‰λ ¬ (Confusion Matrix)
  • 정밀도 (Precision)
  • μž¬ν˜„μœ¨ (Recall)
  • F1 μŠ€μ½”μ–΄
  • ROC곑선과 AUC
  • λΆ„λ₯˜λŠ” κ²°μ • 클래슀 κ°’ μ’…λ₯˜μ˜ μœ ν˜•μ— 따라 긍정/λΆ€μ •κ³Ό 같은 2개의 κ²°κ΄κ°’λ§Œμ„ κ°€μ§€λŠ” 이진 λΆ„λ₯˜μ™€ μ—¬λŸ¬ 개의 κ²°μ • 클래슀 값을 κ°€μ§€λŠ” λ©€ν‹° λΆ„λ₯˜λ‘œ λ‚˜λ‰  수 있음
    • κ²°μ • ν΄λž˜μŠ€λŠ” β€œκ°€λŠ₯ν•œ μ •λ‹΅μ˜ 집합”이고 label은 β€œκ° λ°μ΄ν„°μ˜ μ‹€μ œ 정닡”
  • μœ„μ—μ„œ μ–ΈκΈ‰ν•œ λΆ„λ₯˜μ˜ μ„±λŠ₯ μ§€ν‘œλŠ” 이진/λ©€ν‹° λΆ„λ₯˜ λͺ¨λ‘μ— μ μš©λ˜λŠ” μ§€ν‘œμ΄μ§€λ§Œ, 특히 이진 λΆ„λ₯˜μ—μ„œ λ”μš± μ€‘μš”ν•˜κ²Œ κ°•μ‘°ν•˜λŠ” μ§€ν‘œ!

μ™œ μœ„ μ§€ν‘œκ°€ λͺ¨λ‘ 이진 λΆ„λ₯˜μ—μ„œ μ€‘μš”ν• κΉŒ?

정확도 (Accuracy)

μ‹€μ œ λ°μ΄ν„°μ—μ„œ 예츑 데이터가 μ–Όλ§ˆλ‚˜ 같은지λ₯Ό νŒλ‹¨ν•˜λŠ” μ§€ν‘œ 즉, 예츑 κ²°κ³Όκ°€ λ™μΌν•œ 데이터 건수λ₯Ό 전체 예츑 데이터 건수둜 λ‚˜λˆˆ κ°’

  • μ •ν™•λ„λŠ” μ§κ΄€μ μœΌλ‘œ λͺ¨λΈ 예츑 μ„±λŠ₯을 λ‚˜νƒ€λ‚΄λŠ” 평가 μ§€ν‘œ
  • ν•˜μ§€λ§Œ 이진 λΆ„λ₯˜μ˜ 경우 λ°μ΄ν„°μ˜ ꡬ성에 따라 ML λͺ¨λΈμ˜ μ„±λŠ₯을 μ™œκ³‘ν•  수 있기 λ•Œλ¬Έμ— 정확도 수치 ν•˜λ‚˜λ§Œ κ°€μ§€κ³  μ„±λŠ₯을 ν‰κ°€ν•˜μ§€ μ•ŠμŒ

κ·Έλ ‡λ‹€λ©΄ 정확도 μ§€ν‘œκ°€ μ–΄λ–»κ²Œ ML λͺ¨λΈμ˜ μ„±λŠ₯을 μ™œκ³‘ν• κΉŒ?

  • μ•žμ˜ 타이타닉 예제 μˆ˜ν–‰ κ²°κ³Όλ₯Ό 보면 ν•œ κ°€μ§€ μ˜κ΅¬μ‹¬μ΄ 생길 수 있음
  • ML μ•Œκ³ λ¦¬μ¦˜μ„ μ μš©ν•œ ν›„ 예츑 μ •ν™•λ„μ˜ κ²°κ³Όκ°€ 보톡 80%λŒ€μ˜€μ§€λ§Œ, νƒ‘μŠΉκ°μ΄ λ‚¨μžμΈ κ²½μš°λ³΄λ‹€ μ—¬μžμΈ κ²½μš°μ— 생쑴 ν™•λ₯ μ΄ λ†’μ•˜κΈ° λ•Œλ¬Έμ— 별닀λ₯Έ μ•Œκ³ λ¦¬μ¦˜μ˜ 적용 없이 무쑰건 성별이 μ—¬μžμΈ 경우 μƒμ‘΄μœΌλ‘œ, λ‚¨μžμΈ 경우 μ‚¬λ§μœΌλ‘œ 예츑 κ²°κ³Όλ₯Ό μ˜ˆμΈ‘ν•΄λ„ 이와 λΉ„μŠ·ν•œ μˆ˜μΉ˜κ°€ λ‚˜μ˜¬ 수 있음
    • 단지 성별 쑰건 ν•˜λ‚˜λ§Œμ„ κ°€μ§€κ³  κ²°μ •ν•˜λŠ” 별거 μ•„λ‹Œ μ•Œκ³ λ¦¬μ¦˜λ„ 높은 정확도λ₯Ό λ‚˜νƒ€λ‚΄λŠ” 상황 λ°œμƒ!

λ‹€μŒ μ˜ˆμ œμ—μ„œλŠ” μ‚¬μ΄ν‚·λŸ°μ˜ BaseEstimator 클래슀λ₯Ό 상속받아 μ•„λ¬΄λŸ° ν•™μŠ΅μ„ ν•˜μ§€ μ•Šκ³ , 성별에 따라 μƒμ‘΄μžλ₯Ό μ˜ˆμΈ‘ν•˜λŠ” λ‹¨μˆœν•œ Classifier 생성

  • μ‚¬μ΄ν‚·λŸ°μ€ BaseEstimatorλ₯Ό μƒμ†λ°›μœΌλ©΄ Customized ν˜•νƒœμ˜ Estimatorλ₯Ό κ°œλ°œμžκ°€ 생성 κ°€λŠ₯
  • μ—¬κΈ°μ„œ fit()λ©”μ„œλ“œλŠ” 아무것도 μˆ˜ν–‰ X, predict() λ©”μ„œλ“œλŠ” λ‹¨μˆœνžˆ Sex featureκ°€ 1이면 0, κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ 1둜 예츑
import numpy as np
from sklearn.base import BaseEstimator
 
class MyDummyClassifier(BaseEstimator):
Β  Β  # fit( ) λ©”μ†Œλ“œλŠ” 아무것도 ν•™μŠ΅ν•˜μ§€ μ•ŠμŒ.
Β  Β  def fit(self, X , y=None):
Β  Β  Β  Β  pass
 
Β  Β  # predict( ) λ©”μ†Œλ“œλŠ” λ‹¨μˆœνžˆ Sex featureκ°€ 1 이면 0 , κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ 1 둜 μ˜ˆμΈ‘ν•¨.
Β  Β  def predict(self, X):
Β  Β  Β  Β  pred = np.zeros((X.shape[0], 1))
Β  Β  Β  Β  for i in range (X.shape[0]) :
Β  Β  Β  Β  Β  Β  if X['Sex'].iloc[i] == 1:
Β  Β  Β  Β  Β  Β  Β  Β  pred[i] = 0
Β  Β  Β  Β  Β  Β  else :
Β  Β  Β  Β  Β  Β  Β  Β  pred[i] = 1
 
Β  Β  Β  Β  return pred

MyDummyClassifierλ₯Ό μ΄μš©ν•΄ μ•ž μž₯의 타이타닉 μƒμ‘΄μž 예츑 μˆ˜ν–‰

  • μ•žμ—μ„œ μ„€μ •ν•œ 데이터 μ „μ²˜λ¦¬ ν•¨μˆ˜
from sklearn.preprocessing import LabelEncoder
 
# Null 처리 ν•¨μˆ˜
def fillna(df):
Β  Β  df['Age'] = df['Age'].fillna(df['Age'].mean(), inplace=True)
Β  Β  df['Cabin'] = df['Cabin'].fillna('N', inplace=True)
Β  Β  df['Embarked'] = df['Embarked'].fillna('N', inplace=True)
Β  Β  df['Fare'] = df['Fare'].fillna(0, inplace=True)
Β  Β  return df
 
# λ¨Έμ‹ λŸ¬λ‹ μ•Œκ³ λ¦¬μ¦˜μ— λΆˆν•„μš”ν•œ ν”Όμ²˜ 제거
def drop_features(df):
Β  Β  df.drop(['PassengerId', 'Name', 'Ticket'], axis=1, inplace=True)
Β  Β  return df
 
# λ ˆμ΄λΈ” 인코딩 μˆ˜ν–‰.
def format_features(df):
Β  Β  df['Cabin'] = df['Cabin'].str[:1]
Β  Β  features = ['Cabin', 'Sex', 'Embarked']
Β  Β  for feature in features:
Β  Β  Β  Β  le = LabelEncoder()
Β  Β  Β  Β  le = le.fit(df[feature])
Β  Β  Β  Β  df[feature] = le.transform(df[feature])
Β  Β  return df
 
# μ•žμ—μ„œ μ„€μ •ν•œ 데이터 μ „μ²˜λ¦¬ ν•¨μˆ˜ 호좜
def transform_features(df):
Β  Β  df = fillna(df)
Β  Β  df = drop_features(df)
Β  Β  df = format_features(df)
Β  Β  return df
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
 
# 원본 데이터λ₯Ό μž¬λ‘œλ”©, 데이터 가곡, ν•™μŠ΅ 데이터/ν…ŒμŠ€νŠΈ 데이터 λΆ„ν• .
titanic_df = pd.read_csv('train.csv')
y_titanic_df = titanic_df['Survived']
X_titanic_df= titanic_df.drop('Survived', axis=1)
X_titanic_df = transform_features(X_titanic_df)
X_train, X_test, y_train, y_test=train_test_split(X_titanic_df, y_titanic_df,
Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  Β  test_size=0.2, random_state=0)
 
# μœ„μ—μ„œ μƒμ„±ν•œ Dummy Classifierλ₯Ό μ΄μš©ν•΄ ν•™μŠ΅/예츑/평가 μˆ˜ν–‰.
myclf = MyDummyClassifier()
myclf.fit(X_train, y_train)
 
mypredictions = myclf.predict(X_test)
print('Dummy Classifier의 μ •ν™•λ„λŠ”: {0:.4f}'.format(accuracy_score(y_test, mypredictions)))
 
>>> Dummy Classifier의 μ •ν™•λ„λŠ”: 0.7877
  • μ΄λ ‡κ²Œ λ‹¨μˆœν•œ μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œ μ˜ˆμΈ‘μ„ ν•˜λ”λΌλ„ λ°μ΄ν„°μ˜ ꡬ성에 따라 정확도 κ²°κ³ΌλŠ” μ•½ 78.77%둜 κ½€ 높은 μˆ˜μΉ˜κ°€ λ‚˜μ˜¬ 수 μžˆκΈ°μ— 정확도λ₯Ό 평가 μ§€ν‘œλ‘œ μ‚¬μš©ν•  λ•ŒλŠ” 맀우 신쀑해야 함!
  • 특히 μ •ν™•λ„λŠ” λΆˆκ· ν˜•ν•œ(imbalanced) label κ°’ λΆ„ν¬μ—μ„œ ML λͺ¨λΈμ˜ μ„±λŠ₯을 νŒλ‹¨ν•  경우, μ ν•©ν•œ 평가 μ§€ν‘œκ°€ μ•„λ‹˜
    • 예λ₯Ό λ“€μ–΄ 100개의 데이터가 있고 이 쀑에 90개의 데이터 label이 0, 단 10개의 데이터 label이 1이라고 ν•œλ‹€λ©΄ 무쑰건 0으둜 예츑 κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜λŠ” ML λͺ¨λΈμ˜ κ²½μš°λΌλ„ 정확도가 90%λ‚˜ 됨

MNIST 데이터 μ„ΈνŠΈλ₯Ό λ³€ν™˜ν•΄ λΆˆκ· ν˜•ν•œ 데이터 μ„ΈνŠΈλ‘œ λ§Œλ“  뒀에 정확도 μ§€ν‘œ μ μš©μ‹œ 문제점 확인

  • MNIST 데이터 μ„ΈνŠΈνŠΈ 0λΆ€ν„° 9κΉŒμ§€μ˜ 숫자 μ΄λ―Έμ§€μ˜ ν”½μ…€ 정보λ₯Ό κ°€μ§€κ³  있으며, 이λ₯Ό 기반으둜 숫자 Digitλ₯Ό μ˜ˆμΈ‘ν•˜λŠ” 데 μ‚¬μš©
  • μ‚¬μ΄ν‚·λŸ°μ€ load_digits() APIλ₯Ό 톡해 MNIST 데이터 μ„ΈνŠΈ 제곡
  • μ›λž˜ MNIST 데이터 μ„ΈνŠΈλŠ” label 값이 0λΆ€ν„° 9κΉŒμ§€ μžˆλŠ” λ©€ν‹° label λΆ„λ₯˜λ₯Ό μœ„ν•œ κ²ƒμ΄μ§€λ§Œ, 이것을 label=7인 κ²ƒλ§Œ True, λ‚˜λ¨Έμ§€λŠ” λͺ¨λ‘ False둜 λ³€ν™˜ν•΄ 이진 λΆ„λ₯˜ 문제둜 λ³€κ²½

Attention

아무것도 ν•˜μ§€ μ•Šκ³  무쑰건 νŠΉμ •ν•œ 결과둜 예츑 κ²°κ³Όλ₯Ό λ°˜ν™˜ν•΄λ„ 데이터 뢄포도가 κ· μΌν•˜μ§€ μ•Šμ€ 경우 높은 μˆ˜μΉ˜κ°€ λ‚˜νƒ€λ‚  수 μžˆλŠ” 것이 정확도 평가 μ§€ν‘œμ˜ 맹점!

  • λΆˆκ· ν˜•ν•œ 데이터 μ„ΈνŠΈμ™€ Dummy Classifier 생성
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.base import BaseEstimator
from sklearn.metrics import accuracy_score
import numpy as np
import pandas as pd
 
class MyFakeClassifier(BaseEstimator):
Β  Β  def fit(self,X,y):
Β  Β  Β  Β  pass
 
Β  Β  # μž…λ ₯κ°’μœΌλ‘œ λ“€μ–΄μ˜€λŠ” X 데이터 μ…‹μ˜ 크기만큼 λͺ¨λ‘ 0κ°’μœΌλ‘œ λ§Œλ“€μ–΄μ„œ λ°˜ν™˜
Β  Β  def predict(self,X):
Β  Β  Β  Β  return np.zeros( (len(X), 1) , dtype=bool)
 
# μ‚¬μ΄ν‚·λŸ°μ˜ λ‚΄μž₯ 데이터 셋인 load_digits( )λ₯Ό μ΄μš©ν•˜μ—¬ MNIST 데이터 λ‘œλ”©
digits = load_digits()
 
print(digits.data)
print("### digits.data.shape:", digits.data.shape)
print(digits.target)
print("### digits.target.shape:", digits.target.shape)