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

ML_03 (classification s3-10~15 분류 모델)

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

 

 

 

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

이항분류

  • 합격/ 불합격 판정
  • 3과목의 평균 60점 이상 합격 (단, 과락 40점 미만은 불합격)

 

1. 함수 생성

1-1) 사용할 lib import

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

 

 

 

1-2) 데이터 생성

# sample로 사용할 DataSet을 생성하는 함수 작성
# X: 국어, 영어, 수학 점수
# Y: 합격여부 (X의 평균 60이상, 과락 40점 미만)

# seedno : 랜덤 수 생성 규칙
# size : 랜덤 수 생성 행의 수
# step=0 (균형), step=다른수 (불균형) / step=0 균형 다른 수=불균형
def make_sample(seedno, size, step=0):
    colnames=['국어', '영어', '수학']
    np.random.seed(seedno) #랜덤수를 발생
    A = np.random.randint(0, 101, (size, 3)) #0~100사이 수 (행, 열)
    df = pd.DataFrame(A, columns=colnames)
    df['합격여부'] = (df.mean(axis=1) >= 60) & (df.min(axis=1)>=40) #가로로 (행별 합계) mean
    df #이렇게만 하면 점수가 너무 낮아서 다 False가 됨 (0~100 비율이 무작위라서)
    # 출력결과 (size=20000)
    #False    16340 / True      3660 --> 불균형 데이터

    if step == 0:
    F, T = df['합격여부'].value_counts()
    B = np.random.randint(60,101,(F-T, 3)) #B=합격자 / f-t 수만큼의 행+3과목
    df2 = pd.DataFrame(B, columns=colnames)
    df2['합격여부'] = True
    df = pd.concat([df, df2])
    df['합격여부'].value_counts()
    df.index = pd.RangeIndex(len(df)) #df의 index 변경
    #합격 여부 TF이 아닌 01로 변경
    df['합격여부'] = df['합격여부'].replace({True:1, False:0}) #합격1 불합격0
    return df

 

 

 

1-3) 모델 학습 및 성능 평가 함수 생성 (순서 중요!)

1)  X, Y 데이터 분리

2) 학습, 평가 데이터로 분리 (train_test_split)

3) 분리된 데이터의 shape 출력

4) 학습 모델 선택, 학습

5) 성능 평가

 

model, data가 파라미터인 ModelTrain 함수 생성

def ModelTrain(model, data):
    # 1) X, Y 데이터 분리
    Y = data['합격여부']
    X = data.drop(columns=['합격여부']) #data에서 합격여부 컬럼을 제외한 모든 컬럼

    # 2) 학습, 평가 데이터로 분리
    x_train, x_test, y_train, y_test = train_test_split(X, Y,
                                                      test_size=0.2,
                                                      stratify=Y,
                                                      random_state=0)

    # 3) 분리된 데이터의 shape 출력
    print([ x.shape for x in [x_train, x_test, y_train, y_test]])
    #print(x_train.shape, x_test.shape, y_train.shape, y_test.shape)

    # 4) 학습 모델 선택, 학습
    #model = LogisticRegression(max_iter=1000) < 반복수행=1000
    model.fit(x_train, y_train)

    # 5) 성능 평가 - 정확도로 평가할 것 (Accuracy)
    #train, test 성능을 모두 확인해야 과대적합 여부 확인
    print('train 성능 : ', model.score(x_train, y_train))
    print('test 성능 : ', model.score(x_test, y_test))
    return model

 

 

 

2. 데이터의 중요성 알아보기

model1: 데이터 양 多 균형 데이터

for no in [1234, 1225, 1245] :
    model1 = LogisticRegression(max_iter=1000) #max_iter=hyperparameter
    data = make_sample(seedno=no, size=20000) #seedno=난수발생에 사용되는 숫자, size=행
    ModelTrain(model1, data)

 

 

model2: 데이터 양 多 불균형 데이터

for no in [1234, 1225, 1245] :
	model2 = LogisticRegression(max_iter=1000)
    data = make_sample(seedno=no, size=32000, step=1) # step=1 불균형
    ModelTrain(model2, data)

균형데이터보다 성능이 떨어지지만 데이터 양이 많아 낮진 않음

 

 

model3: 데이터 양 小 균형 데이터

for no in [1234, 1225, 1245] :
	model3 = LogisticRegression(max_iter=1000)
    data = make_sample(seedno=no, size=40)
    ModelTrain(model3, data)

균형데이터지만 데이터의 양이 적어 성능이 떨어짐

 

 

model4: 데이터 양 小 불균형 데이터

for no in [1234, 1225, 1245] :
	model4 = LogisticRegression(max_iter=1000)
    data = make_sample(no, 60, step=1)
    ModelTrain(model4, data)

 

 

 

3. 파생변수

합격/ 불합격 여부를 결정하는 평균, 과락에 관련된 파생변수를 추가

(but 파생변수를 추가한다고 모든 데이터의 성능 향상에 도움이 되는건 아니다)

# 데이터 생성
data = make_sample(seedno=1245, size=20000)

# 파생변수 생성/ 추가
data['평균'] = data[['국어', '영어', '수학']].mean(axis=1) # 행
data['최저'] = data[['국어', '영어', '수학']].min(axis=1)

# 평균, 최저 파생변수 생성
for no in [1234, 1225, 1245] :
	model5 = LogisticRegression(max_iter=1000)
    data = make_sample(seedno=no, size=20000)
    data['평균'] = data[['국어', '영어', '수학']].mean(axis=1)
	data['최저'] = data[['국어', '영어', '수학']].min(axis=1)
    ModelTrain(model5, data)

model1~5 중 가장 높은 성능

 

 

 

# 모든 값을 사용한 예측 결과 
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=colanmes)

    data['평균'] = data[['국어','영어','수학']].mean(axis=1)
    data['최저'] = data[['국어', '영어', '수학']].min(axis=1)
    data['합격여부'] = (data['평균'] >= 60) & (data['최저'] >= 40)
    data['합격여부'] = data['합격여부'].replace({True:1, Fales:0}) #합격:1 불합격:0
    return data

 

data = make_all()
X1 = data.iloc[:, :3]
X2 = data.drop(columns=['합격여부'])
Y = data['합격여부']
print(Y.value_counts())

for x in model1, model2, model3, model4:
    print(x.score(X1, Y))
print(model5.score(X2, Y))

# 1-데이터多균형 2-데이터多불균형 3-데이터小균형 4-데이터小불균형 5-파생변수추가

 

 

 

 

4. 스케일러 

for no in [1234, 1225, 1245] :
    model6 = LogisticRegression(max_iter=1000)
    data = make_sample(seedno=no, size=20000)
    data['국어'] *= 500
    data['수학'] *= 1000
    ModelTrain(model6, data)

 

 

# StandardScaler 사용하여 정규 분포 만들기
from sklearn.preprocessing import StandardScaler

for no in [1234, 1225, 1245]:
    model7 = LogisticRegression(max_iter=1000)
    data = make_sample(seedno=no, size=20000)
    data['국어'] *= 500
    data['수학'] *= 1000
    X = data[['국어', '영어', '수학']]
    Y = data['합격여부']
    scaledX = StandardScaler().fit_transform(X)
    scaledX = pd.DataFrame(scaledX, columns=['국어', '영어', '수학'])
    data = pd.concat([scaledX, Y], axis=1)
    ModelTrain(model7, data)

# 수가 차이가 클 경우 Scaler를 하는 것이 도움이 될 수도 있다

 

 

 

 

5. 다양한 모델의 사용

#선형모델
from sklearn.linear_model import LogisticRegression
#근접이웃
from sklearn.neighbors import KNeighborsClassifier
#트리
from sklearn.tree import DecisionTreeClassifier
#앙상블
from sklearn.ensemble import RandomForestClassifier
from xgboost import XGBClassifier

 

 

 

데이터 생성

# 학습용 데이터
data = make_sample(seedno=1234, size=20000)

# 평가용 데이터
all = make_all() #모든 케이스
X = all.iloc[:, :3]
Y = all['합격여부']

 

 

 


분류 모델

 

 

A) LogisticRegression

  • 독립 변수의 선형 결합을 이용하여 사건의 발생 가능성을 예측하는데 사용되는 통계 기법
  • 반복하면서 기울기 값 갱신, 기울기 미분값이 0이 되는 지점을 찾음
  • max_iter ,tol (자주 사용되는 값) 등을 변경 -> 성능 개선 가능
    • max_iter = 반복횟수 (학습이 부족할 시 반복횟수 up/ 과대적합 시 반복횟수 down)
    • tol = 허용오차, 반복을 중단하는 조건으로 사용
    • panalty = panalty 종류
    • C = panalty 세기

  • LinearRegression : y가 0보다 작거나 1보다 큰 값도 존재
    • ex)  y=0 불합격 y=1 합격이라고 한다면 LinearRegression 모델 사용 시 y<0 | y>1 나올 수도 있기 때문에 부적합
  • LogisticRegression : y가 0과 1사이의 확률값
    • ex) 0과 1사이의 값이 나오기 때문에 확률값을 구하고, 그 확률이 두 가지 값이라면 0.5보다 컸을 때 원하는 결과를 구할 수 있음 (분류모델- 확률값을 가지고 있고, 그 확률이 가장 큰 값을 찾는 것임)

 

#LogisticRegression 모델 사용 시 성능 평가
model_lr = LogisticRegression(max_iter=1000)
ModelTrain(model_lr, data)
print(model_lr.score(X, Y)

#model_lr = LogisticRegression(max_iter=1000, C=0.3, tol=0.01)
#위처럼 C와 tol값을 줬을 때 변화가 크진 않음 -> 무조건 개선되거나 변화되는 것은 아니라는 것 알기

 

 


 

 

B) KNeighborsClassifier

  • k개의 근접 이웃을 확인하여 클래스를 선택함
  • n_neighbors를 변경 -> 성능 개선 가능 (확인할 근접 이웃 개수)
  • 이웃 값을 구해야하기 때문에 다른 모델보다 조금 느림

#knn 모델 사용 시 성능 평가
model_knn = KNeighborsClassifier(n_neighbors = 5)
ModelTrain(model_knn, data)
print(model_knn.score(X, Y))

 

 

#n_neighbors를 범주로 주고 싶을 때 (ex-3~9)
for k in range(3, 10) :
	model_knn = KNeighborsClassifier(n_neighbors=k)
    ModelTrain(model_knn, data)
    print(f'k={k}, {model_knn.score(X, Y)}')

따로 설정한 값이 없는데도 LogisticRegression보다 좋게 나옴

-> 각 유형별로 알맞는 모델이 달라서 많은 모델을 써보고 제일 성능값이 좋은 모델을 택하는 게 좋음

 

선택해야 하는 모델

1) train 성능과 test 성능의 차이값이 적은 것

2) 두 성능 값이 모두 높은 것

 


 

 

C) DecisionTreeClassfier

  • overfitting 경향이 있음
  • max_depth를 줄이는 방법으로 overfitting 해결 가능

#DecisionTreeClassifier 모델 사용 시 성능 평가
model_dc1 = DecisionTreeClassfier()
ModelTrain(model_dc1, data)
print(model_dc1.score(X, Y))

 

 

depth 구하기

.get_depth()

#depth가 어디까지 쓰였는지 확인
model_dc1.get_depth() #12 출력됨

#depth=6으로 줄여보기
#train-test 성능 차가 클 때 depth를 줄이는 방법으로 overfitting을 줄일 수 있다
model_dc2 = DecisionTreeClassifier(max_depth=6)
ModelTrain(model_dc2, data)
print(model_dc2.score(X, Y))

 

 

# depth=3으로 줄이기
model_dc3 = DecisionTreeClassifier(max_depth=3)
ModelTrain(model_dc3, data)
print(model_dc3.score(X, Y))

 


 

D) RandomForestClassfier

  • DecisionTreeClassifier를 100개 사용하는 앙상블 모델 (트리 기반)
  • n_estimators 의 개수를 늘리거나, max_depth를 조절하여 성능 조절 가능

 

model_rf1 = RandomForestClassifier()
ModelTrain(model_rf1, data)
print(model_rf1.score(X, Y))

 

 

#estimators 늘리기 (기본 100)
model_rf2 = RandomForestClassifier(n_estimators=500)
ModelTrain(model_rf2, data)
print(model_rf2.score(X, Y))

성능이 더 좋아짐

 

 


 

 

E) XGBClassifier

from xgboost import XGBClassifier
model_xgb1 = XGBClassifier()
ModelTrain(model_xgb1, data)
print(model_rf1.score(X, Y))

 

 

#n_estimator 높이고, max_dapth=5로 줄이기
model_xgb2 = XGBClassifier(n_estimator=500, max_depth=5)
ModelTrain(model_xgb2, data)
print(model_xgb2.score(X, Y))

 

 


성능 평가 (rf, xgb)

 

 

성능이 좋았던 RandomForestClassfier을 이용해 모델 생성

data = make_sample(seedno=1234, size=50000)
model_rf = RandomForestClassifier(n_estimators=500)
ModelTrain(model_rf, data)

 

 

 

성능이 좋았던 XGBClassifier을 이용해 모델 생성

data = make_sample(seedno=1234, size=50000)
model_xgb = XGBClassifier(n_estimators=500)
ModelTrain(model_xgb, data)

 

 

 

오분류표 생성

  • sklearn.metrics.confusion_matrix(y_true, y_pred, *, labels=None, sample_weight=None, normalize=None)
    • y_true: 실제값
    • y_pred: 예측값

 

 

1) rf 를 이용해 각 종류별 정확도 확인

from sklearn.metrics import confusion_matrix
label=['불합격', '합격']
print(model_rf.score(X, Y))
y_pred = model_rf.predict(X)
a = confusion_matrix(Y, y_pred)
# a만으로는 컬럼, 인덱스로 보이지 않아서 DataFrame으로 변환 후 보여줌
b = pd.DataFrame(a, columns=label, index=label)
b

 

 

 

2) xgb를 이용해 각 종류별 정확도 확인

from sklearn.metrics import confusion_matrix
label = ['불합격', '합격']
print(model_xgb.score(X, Y))
y_pred = model_xgb.predict(X)
a = confusion_matrix(Y, y_pred)

b = pd.DataFrame(a, columns=label, index=label)
b

 

 

 

3) 합격일 확률 구하기 (다른 데이터 사용)

data = make_sample(seedno=1234, size=6)
x_test = data[['국어', '영어', '수학']]
y_test = data['합격여부']
print(y_test.to_numpy()) # 실제값
print(model_xgb.predict(x_test))  # 예측값
proba = model_xgb.predict_proba(x_test)
print(proba)

 

 

 

예측값 저장

# test데이터에서 '합격'일 확률에 대한 정보를 저장 후 출력
submission = pd.DataFrame()
submission['id'] = pd.RangeIndex(1, len(X) + 1) #일련번호
submission['prob'] = model_xgb.predict_proba(X)[:, 1] #1일 확률만 빼서 만듦
submission.to_csv('submission.csv', index=False) #파일명:submission, index 저장 X

--> id, proba :, 1 (2열까지) 저장

 

 

 

더보기

모델 학습 및 성능 평가 함수 생성

1) X, Y  데이터 분리

2) 학습, 평가 데이터로 분리 (train_test_split)

3) 분리된 데이터의 shape 출력

4) 학습 모델 선택, 학습

5) 성능 평가(정확도로 평가했음/ train, test 성능을 모두 확인해야 과대적합 여부 확인 가능)

 

성능 조절

+파생변수

+스케일러 (from sklearn.preprocessing import StandardScaler)

 

분류모델

1) LogisticRegression

from sklearn.linear_model import LogisticRegression

2) KNeighborsClassifier

from sklearn.neighbors import KNeighborsClassifier

3) DecisionTreeClassifier

from sklearn.tree import DecisionTreeClassifier

4) RandomForestClassifier

from sklearn.ensemble import RandomForestClassifier

5) XGBClassifier

from xgboost import XGBClassifier

728x90
반응형