본문 바로가기
AI 컴퓨터 비전프로젝트

[ML]로지스틱 회귀_승진 될지 안 될지 분류해보기

by 바다의 공간 2024. 7. 7.

 

1. hr 데이터셋

  • 이번에는 승진관련(임의)데이터이고 여기서 이런 정보를 주면 is_promoted 분류를 해보려고 합니다.
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

hr_df = pd.read_csv('hr.csv')
hr_df.head()

hr_df.info()

hr_df.describe()

간단하게  데이터를 확인할 수 있습니다. 

이렇게 표로 보다는 barplot으로 한번 확인해보도록 하겠습니다.

즉 독립변수들이 is_promoted에 영향을 받는지 안 받는지를 확인하기 위해서 확인해보도록 하겠습니다.

sns.barplot(x='previous_year_rating', y=is_promoted, data=hr_df)

이렇게 독립변수로 사용할 수 있는 데이터들을 기억해두도록 합니다. 

이유는 종속변수에 영향을 주는 데이터들의 값들이기 때문입니다.

확인해보기

 

확실히 referred가 데이터 수가 더 없다는것을 확인해 볼 수 있습니다.

그래서 심지가 길게 표현됩니다.

 

마저 살펴보겠습니다.

성별

이 그래프는 성별에따른 승진은 크게 의미 없다는것을 알 수 있습니다.

부서관

그래프로 확인해보기

plt.figure(figsize=(12,9))
sns.barplot(x='region', y='is_promoted', data=hr_df)
plt.xticks(rotation=45)

 결측값 확인하기

hr_df.isna().mean()

결측값이 있는걸 확인해보기

hr_df['education'].value_counts()
hr_df['previous_year_rating'].value_counts()

이런 데이터들은 drop하는 방향으로 생각하려고 합니다.

hr_df = hr_df.dropna()
hr_df.info()

원핫인코딩대상(오브젝트)의 카테고리값들을 확인하기

for i in['department', 'region', 'education', 'gender', 'recruitment_channel']:
    print(i, hr_df[i].nunique())

지역 값들은 상관이 없을거같아서 지역 값은 제거하고 원핫인코딩을 하겠습니다.

hr_df = pd.get_dummies(hr_df, columns=['department', 'education', 'gender', 'recruitment_channel'])
hr_df.head()

hr_df.drop(['employee_id',
            'region'], axis=1, inplace=True)
            
hr_df.head()

 

 

테스트값 분리 모델이용

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(hr_df.drop('is_promoted', axis=1),
                                                    hr_df['is_promoted'],
                                                    test_size=0.2,
                                                    random_state=2024)
                                                    

#학습
X_train.shape, y_train.shape
#((38928, 23), (38928,))


#테스트
X_test.shape, y_test.shape
#((9732, 23), (9732,))

데이터 전처리 끝이 났습니다.


2. 로지스틱 회귀(Logistic Regression)

  • 둘 중의 하나를 결정하는 문제(이진 분류)를 풀기 위한 대표적인 알고리즘
  • 다항 분류 문제에도 확장될 수 있음
  • 종속 변수 Y는 두 가지 범주 중 하나를 가짐(예:0 또는 1)
  • 목표는 특정 범주의 속할 확률을 예측하는 것
  • 예측 모델은 아닙니다. 이 모델은 분류 모델입니다

 

로지스틱 회귀 모델은 일반화 선형 모델의 일종으로, 독립 변수의 선형 조합을

로지스틱 함수(시그모이드 함수)를 사용하여 종속 변수에 대한 확률 점수로 변환


from sklearn.linear_model import LogisticRegression

lr= LogisticRegression()

lr.fit(X_train, y_train)

pred = lr.predict(X_test)

from sklearn.metrics import accuracy_score

accuracy_score(y_test, pred)
#0.9205713111385121

정확도가 92%가 나왔지만

지금은 분류문제이기때문에 의미가 살짝 달라질 수 있습니다.

 

분류로 따지면 0의 값이 훨씬 많기때문에 그냥 0(승지못한거)으로 찍었을때 정확도가 높다는것을 확인할 수 있습니다.

그러니까 정확한 값이 아니라는말인거죠.

그냥 냅다 0으로 찍어버리면 정확도가 높은 느낌!

ai 입장 : 아! 0을 찍으면 정확도가 높아지는구나 

입니다. accuracy는 이것을 검증하지 못합니다.

그래서 검증하기 위해서 혼돈 행렬이 필요합니다.


3. 혼돈 행렬(confusion matrix)

  • 정밀도와 재현율(민감도)을 활용하는 평가용 지수를 이야기합니다.
from sklearn.metrics import confusion_matrix

confusion_matrix(y_test, pred)
#array([[8784,  100],
       [ 673,  175]])

 

 

T=TRUE N=NAGATIVE F=FALSE P=POSITIVE

*TN(진음성) : 승진하지 못했는데, 승진하지 못했다고 예측해서 맞춤

*FP(위양성) : 승진하지 못했는데, 승진했다고 예측해서 틀림

*FN(위음성) : 승진했는데, 승을 못했다고 예측해서 틀림

*TP(진양성) : 승진했는데 승진 했다고 예측해서 맞춤.


3-1.정밀도(Precision)

  • TP / (TP +FP)
  • 무조건 양성으로 판단해서 계산하는 방법
  • 실제 승진한 사람중에서 얼마만큼 승진했다고 맞췄는가?
 

3-2. 재현율(recall)

  • TP/ (TP+FN)
  • 정확하게 감지한 양성 샘플의 비율
  • 실제로 긍정인 것들 중에서 긍정으로 예측한 비율
  • 민감도 또는 TPR(True Positive Rate)라고도 부름

 

3-3. f1 score

  • 정밀도와 재현율의 조화평균을 나타내는 지표

조화평균은 정밀도와 재현율이 벌어짐에따라 달라집니다

쉽게말해서얼마나 조화로운지(?)를 생각하면 됩니다.

1번은 0.2

2번은 0.4

3번은 0 

각각 조화평균은 0.48, 0.42, 0.5로 얼마나 벌어지는지를 확인할 수 있습니다.

차이가 날수록 산술평균값을 비교했을 때 낮아집니다.

from sklearn.metrics import precision_score, recall_score, f1_score

#정밀도
precision_score(y_test, pred)
#0.6363636363636364

#재현율
recall_score(y_test, pred)
#0.20636792452830188

#f1_score
f1_score(y_test, pred)
#0.31166518254674974

 

lr.coef_

#array([[-0.51687179, -0.06608108,  0.38010122,  0.05941589,  3.53168128,
         0.11251127, -1.95349302, -0.35850951, -0.39898613, -0.3578027 ,
         0.49199051, -0.28645626, -1.19810409,  1.53916888, -1.24856123,
        -1.65001362, -1.02150699, -1.09923293, -1.80257786, -1.96817568,
        -1.67721465, -0.44336313, -1.65017577]])

각각 독립변수의 기울기(웨이트)값 입니다.

아직은 웨이트값이 너무 많기때문에 따로 몇개를 뽑았습니다.(가장 많이 영향을 줄 것 같은걸로)

# 독립변수
tempX= hr_df[['previous_year_rating', 'avg_training_score', 'awards_won?']]

#종속변수
tempy = hr_df['is_promoted']
temp_lr = LogisticRegression()

#학습
temp_lr.fit(tempX, tempy)
temp_df = pd.DataFrame({
    'previous_year_rating':[4.0, 5.0, 5.0],
    'avg_training_score':[100, 90, 100],
    'awards_won?':[0, 1, 1]})

이렇게 숫자를 낸 이유는 테스트데이터를 를 넣어준것입니다. 

참고로 승진이 가능한 학습자료를 해보려고 합니다.

pred=temp_lr.predict(temp_df)
pred

#array([0, 1, 1])

승진을 한 사람은 2명 못한사람은 1명

temp_lr.coef_ #기울기
#array([[0.51112537, 0.04309755, 2.09583168]])


temp_lr.intercept_#절편
#array([-7.20658921])


proba = temp_lr.predict_proba(temp_df)
#확률을 뽑아줌
proba
#array([[0.70105625, 0.29894375],
#       [0.21021186, 0.78978814],
#       [0.14746488, 0.85253512]])

왼쪽은 0일 확률 오른쪽은 1일 확률입니다.

즉 오른쪽이 승진이고 그 확률만 뽑아보고싶다면

proba = temp_lr.predict_proba(temp_df)[:,1]
proba

#array([0.29894375, 0.78978814, 0.85253512])

각 29% 79% 85% 정도 승진 확률이 있습니다.

#임계값 설정
threshold = 0.5
pred = (proba >= threshold).astype(int)
pred

#array([0, 1, 1])

만약 50%로 주고싶다 하면 0.5를 두고proba(확률뽑아줌) 예측하면

0,1,1이 됩니다 승진 누락, 승진, 승진 결과가 나옵니다.


4. 교차 검증(Cross Validation)

  • ML, DL에서도 나오니 알아둬야합니다!
  • train_test_split에서 발생하는 데이터의 섞임에 따라 성능이 좌우되는 문제를 해결하기 위한 방법
  • k겹(k-fold)교차 검증을 가장 많이 사용합니다.
from sklearn.model_selection import KFold

kf = KFold(n_splits=5)
kf

*n_splits=5의 뜻은 랜덤으로 5개로 나분리해서 test,train으로 각각 나눠서 

그 사이의 평균값을 찾는것입니다.

hr_df

for train_index, test_index in kf.split(range(len(hr_df))):
    print(train_index, test_index, len(train_index), len(test_index))
    

#[ 9732  9733  9734 ... 48657 48658 48659] [   0    1    2 ... 9729 9730 9731] 38928 9732
#[    0     1     2 ... 48657 48658 48659] [ 9732  9733  9734 ... 19461 19462 19463] 38928 9732
#[    0     1     2 ... 48657 48658 48659] [19464 19465 19466 ... 29193 29194 29195] 38928 9732
#[    0     1     2 ... 48657 48658 48659] [29196 29197 29198 ... 38925 38926 38927] 38928 9732
#[    0     1     2 ... 38925 38926 38927] [38928 38929 38930 ... 48657 48658 48659] 38928 9732

kf = KFold(n_splits=5, random_state=2024, shuffle=True)
kf

랜덤값을 2024로 맞춰주고 스플리트를 5로 맞춰줍니다.

일관적인 randome_state=2024으로 합니다.

acc_list = []

for train_index, test_index in kf.split(range(len(hr_df))):
    X = hr_df.drop('is_promoted', axis=1)
    y = hr_df['is_promoted']

    X_train = X.iloc[train_index]
    X_test = X.iloc[test_index]
    y_train = y.iloc[train_index]
    y_test = y.iloc[test_index]

    lr = LogisticRegression()
    lr.fit(X_train, y_train)
    pred = lr.predict(X_test)
    acc_list.append(accuracy_score(y_test, pred))

kf로 만든 랜덤값을 이용해서 학습을 시켰습니다.

 

acc_list
#[0.9202630497328401,
# 0.9217016029593095,
# 0.9324907521578298,
# 0.9276613234689683,
# 0.9189272503082614]
np.array(acc_list).mean()

#0.9242087957254418
#5번 섞고도 평균값이 92정도로 높습니다는것을 확인가능함

크로스벨리데이션(교차검증)을 사용하는 이유는 결과를 좋게하는것이 아니라, 믿을만한 검증을 하기 위함입니다.