다항 분류 (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값으로 표가 생성됨 / 어려우면 성능평가 영상 보기
'PYTHON > 빅데이터분석기사' 카테고리의 다른 글
ML_04 (regression s4-01~10 회귀 모델링) (2) | 2023.11.19 |
---|---|
11.15일 (model-1 DB연동/ 회원관리 및 게시판) (1) | 2023.11.15 |
ML_03 (classification s3-25 이항 분류 모델의 성능평가) (0) | 2023.11.14 |
ML_03 (classification s3-22~24 2회 기출/화물 정시 도착) (0) | 2023.11.14 |
ML_03 (classification s3-16~21 작업형 2 예시/고객 성별 예측) (0) | 2023.11.14 |