본문 바로가기
PYTHON/빅데이터분석기사

ML_03 (classification s3-26~30 다항 분류)

by 쿠룽지 2023. 11. 15.
728x90
반응형
728x90

 

 

다항 분류 (Multi Classification)

: 3개 이상의 Label을 갖는 데이터에 대한 분류 

(이항분류- 2가지 값 예측 / 다항분류- 2가지 값보다 더 많은 예측을 해야하는 경우/ class가 3개 이상인 경우)

 

 

 

1. 함수 생성

3과목의 평균에 따라 A, B, C, D, F 학점으로 분류

  • 90 이상 : 'A'
  • 80 이상 : 'B'
  • 70 이상 : 'C'
  • 60 이상 : 'D'
  • 60 미만 : 'F'

1) 사용할 lib import

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.matrics import roc_auc_score

 

 

2) 함수 생성

원하는 수의 행(sample)을 갖는 데이터 생성

#seedno: 랜덤 수 생성 규칙
#size: 랜덤 수 생성 행의 수
#step=0 비교적 균형 step=다른 수 > 불균형

def make_sample(seedno, size, step=0):
    np.random.seed(seedno)
    if step == 0: #균형데이터
        size = int(size/4)
        A = pd.DataFrame(np.random.randint(0, 101, (size, 3)))
        B = pd.DataFrame(np.random.randint(85, 101, (size, 3)))
        C = pd.DataFrame(np.random.randint(70, 95, (size, 3)))
        D = pd.DataFrame(np.random.randint(60, 80, (size, 3)))
        data = pd.concat([A, B, C, D], ignore_index=True)
    
    else: #불균형데이터
        data = pd.DataFrame(np.random.randint(0, 101, (size, 3)))
        
    data.columns = ['국어', '영어', '수학']
    s = data.mean(axis=1) #평균
    data['학점'] =  s.apply(lambda x: 'A' if x >=90 else 'B' if x >= 80 else
                          'C' if x >= 70 else 'D' if x >= 60 else 'F')
    print(data['학점'].value_counts().sort_index())
    data['학점'] = data['학점'].replace({'A':0, 'B':1, 'C':2, 'D':3, 'F':4})
    return data

 

 

 

모든 경우의 수로 데이터 작성

def make_all():
    colnames = ['국어', '영어', '수학']
    data = [[kor, eng, mat] for kor in range(101)
                            for eng in range(101)
                            for mat in range(101)]
    data = pd.DataFrame(data, columns=colnames)
    s = data.mean(axis=1)
    data['학점'] = s.apply(lambda x: 'A' if x >=90 else 'B' if x >= 80 else
                          'C' if x >= 70 else 'D' if x >= 60 else 'F')
    print(data['학점'].value_counts().sort_index())
    data['학점'] = data['학점'].replace({'A':0, 'B':1, 'C':2, 'D':3, 'F':4})
    return data

 

 

모델 학습 및 성능 평가 (train 데이터 사용)

from sklearn.preprocessing import StandardScaler
def ModelTrain(model, df):
    # X, Y 데이터 준비
    Y = df['학점']
    X = df.drop(columns=['학점'])
    # X_scaled = StandardScaler(X).fit_transform(X)

    # train, test 데이터 분할
    xtrain, xtest, ytrain, ytest = train_test_split(X, Y,
                                                    test_size=0.3,
                                                    stratify=Y,
                                                    random_state=0)
    # 모델 선택 및 학습
    #model = LogisticRegression(max_iter=5000)
    model.fit(xtrain, ytrain)

    # 성능평가 - accuracy, roc_auc_score
    A = model.score(xtrain, ytrain)
    B = model.score(xtest, ytest)
    ypred = model.predict_proba(xtest) 
    C = roc_auc_score(ytest, ypred, multi_class='ovo', average='weighted')
    print(f'{A:.4f} {B:.4f} {C:.4f}')
    return model
더보기

 

다항 분류는 multi_class/ average 파라미터가 들어가야함

 

multi_class

         #multi_class ='raise' : 멀티클래스일 경우에 사용 시 오류 발생
           multi_class='ovo' : One-vs-one을 의미 (가능한 모든 클래스 쌍 조합의 평균 AUC를 계산)

            multi_class='ovr' : One-vs-rest를 의미 (나머지에 대해 각 클래스의 AUC를 계산)

 

average

클래스 불균형은 각 '나머지'그룹의 구성에 영향을 미치기 때문에 average == 'macro' 일 때도 클래스 불균형에 민감
average='macro' : 레이블 불균형은 고려되지 않음
average='weighted' : 레이블에 가중치가 부여된 평균

https://runebook.dev/ko/docs/scikit_learn/modules/generated/sklearn.metrics.roc_auc_score


 

결과 분석용 함수

def get_result(data, model):
    df = pd.DataFrame(data, columns=['국어', '영어', '수학'])
    df['평균'] = df.mean(axis=1) # df['평균'] 컬럼 추가
    
    df['학점'] = df['평균'].apply(lambda x: 'A' if x >=90 else 'B' if x >= 80 else
                          'C' if x >= 70 else 'D' if x >= 60 else 'F')
    df['예측'] = model.predict(df[['국어', '영어', '수학']])  #  값 형태: 0, 1, 2, 3, 4 -> 변환
    df['예측'] = df['예측'].replace({0:'A', 1:'B', 2:'C', 3:'D', 4:'F'})
    return df

>>

df - DataFrame

평균 = df.mean(axis=1)

학점 = 평균.apply(람다 A,B,C,D,F)

예측 = model.predict(국영수) / replace 0-A 1-B 2-C 3-D 4-F

 


 

 

2. 데이터 생성 및 분류

1) 사용 모델 import 및 환경 설정

from sklearn.linear_model import LogisticRegression

data = make_sample(seedno=1234, size=30000)
model_A = LogisticRegression(max_iter=1000)
ModelTrain(model_A, data)

 

 

 

2) 결과 확인 (경계값 중심으로 데이터 넣어보기)

data = [[9, 100, 100], [89, 92, 88], [100, 98, 72],
        [60, 60, 60], [60, 60, 59], [59, 60, 60],
        [90, 98, 82], [100, 100, 10], [100, 100, 9]]
get_result(data, model_A)

>> get_result > data와 model을 주었을 때 해당 모델로 얼마나 data를 맞출 수 있는지 표로 나타냄

경계값 중심의 data와 LogisticRegression result




3) 데이터 개수를 줄이고 불균형으로 만들어서 다시 돌려보기

data = make_sample(seedno=1234, size=5000, step=1)
model_B = LogisticRegression(max_iter=8000) #데이터 불균형/ 반복횟수 8000으로 늘림
ModelTrain(model_B, data)

 

 

3)으로 get_result 보이기

data = [[9, 100, 100], [89, 92, 88], [100, 98, 72],
        [60, 60, 60], [60, 60, 59], [59, 60, 60],
        [90, 98, 82], [100, 100, 10], [100, 100, 9]]
get_result(data, model_B)

 

 

 

4) 모든 경우의 수(dataAll) 생성

dataAll = make_all()
dataAll.shape

# dataAll을 사용해 modelA, modelB를 평가
data = dataAll[['국어', '영어', '수학']]
resultA = get_result(data, model_A)
print('model_A :', sum(resultA['학점'] != resultA['예측']))
resultB = get_result(data, model_B)
print('model_B :', sum(resultB['학점'] != resultB['예측']))

 

위 sum(resultA['학점'] != resultA['예측'])) = 틀린 전체 개수를 뜻함

resultA는 0 B는 1187개인걸 알 수 있음

 

 

5) 예측이 틀린 값들의 목록 생성

#목록
wrong = resultB[resultB['학점'] != resultB['예측']]
wrong.tail(5)

#피벗테이블 (오분류표)
resultB.pivot_table(index='학점', columns='예측',
      values='국어', aggfunc='count', fill_value=0)

 

 

 

 

오분류표 작성

from sklearn.metrics import confusion_matrix
cm = confusion_matrix(resultB['학점'], resultB['예측']) #matrix(x, y)
feature_names = list("ABCDF")
df_cm = pd.DataFrame(cm, columns=feature_names, index=feature_names)
df_cm.index.name = '실제'
df_cm.columns.name = '예측'
df_cm

 

 


 

사용해야 하는 모델

모델 선택 시

1) accuracy(train, test), roc_auc_score(test) 가 모두 좋은 것

2) train, test 결과가 비슷한 것

 



다항분류 모델의 성능평가


다항분류 모델의 성능평가

(시험에 나오는건 아니지만 알고 있으면 좋은거라 함)
이항분류랑 거의 같은데 average parameter가 있다 (micro, macro, weighted를 사용)

 

  • sklearn.metrics.accuracy_score(y_true, y_pred) = (TP/TN) / (TP+TN+FP+FN)
  • sklearn.metrics.precision_score(y_true, y_pred) = TP / (TP + FP)
  • sklearn.metrics.recall_score(y_true, y_pred) = TP / (TP + FN)
  • sklearn.metrics.f1_score(y_true, y_pred = 2 * (Precison * Recall) / (Precision + Recall)

precision_score, recall_score, f1_score의 average parameter에 None, "micro", "macro", "weighted"를 사용해야함 (class가 binary가 아닌 경우 필수)
참고 : https://gaussian37.github.io/ml-concept-ml-evaluation/

 

from sklearn.metrics import confusion_matrix, precision_score, recall_score, accuracy_score, f1_score
import numpy as np

y_true = [0, 1, 0, 0, 1, 2]  #  불균형 데이터 [3, 2, 1]개 데이터
y_pred = [0, 2, 1, 0, 0, 1]
print(confusion_matrix(y_true, y_pred))
print('accuracy  : %.4f' % accuracy_score(y_true, y_pred))
print('-' * 20)
# Precision : 예측이 True 인 것 중 실제도 True
# average=None, 각 class 별로 precision을 구해라
print(precision_score(y_true, y_pred, average=None))
print('precision (micro): %.4f' % precision_score(y_true, y_pred, average='micro'))    # 2/6
print('precision (macro): %.4f' % precision_score(y_true, y_pred, average='macro'))    # 0.66 / 3
print('precision (weighted): %.4f' % precision_score(y_true, y_pred, average='weighted')) # 가중치준 precision의 합/6

 

 

 

 

from sklearn.metrics import classification_report

creport = classification_report(y_true, y_pred)
print(creport)

 

 

 

# average=None
# 예측을 0으로 한 것 중 실제 0인 것, 예측을 1로 한 것 중 실제 1, 예측을 2로 한 것 중 실제 2
a = [2/3, 0/2, 0/1]
print(a)


# average = 'micro' -> accuracy
# 전체 중에서 맞춘 것
2/6



# average = 'macro' -> average=None일때의 모든 값을 더해 class 개수로 나눈 것
sum(a)/3



# precision 의 average='weighted'
# average=None일때의 모든 값에 각 class의 데이터 개수를 곱하여 합을 구하고
# 전체 데이터 개수로 나눈 것
import pandas as pd
a = [2/3, 0/2, 0/1]
b = [3, 2, 1]
a = pd.Series(a)
b = pd.Series(b)
(a * b).sum() / 6

 

 


예측이 x 실제가 y값으로 표가 생성됨 / 어려우면 성능평가 영상 보기



 

728x90
반응형