Python/머신러닝(ML)
Python(22)- 로지스틱 회귀(Losistic Regression), 혼돈행렬
두설날
2024. 6. 18. 17:48

*이 글을 읽기전에 작성자 개인의견이 있으니, 다른 블로그와 교차로 읽는것을 권장합니다.*
1. hr 데이터셋
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
hr_df = pd.read_csv('/content/drive/MyDrive/KDT/6. 머신러닝과 딥러닝/Data/hr.csv')
hr_df

hr_df.info()

어떤 회사의 승진 관련 데이터
- employee_id: 임의의 직원 아이디
- department: 부서
- region: 지역
- education: 학력
- gender: 성별
- recruitment_channel: 채용 방법
- no_of_trainings: 트레이닝 받은 횟수
- age: 나이
- previous_year_rating: 이전 년도 고과 점수
- length_of_service: 근속 년수
- awards_won: 수상 경력
- avg_training_score: 평균 고과 점수
- is_promoted: 승진 여부
hr_df.describe()
# 통계치

sns.barplot(x='previous_year_rating', y='is_promoted', data=hr_df)
# 가중치를 줘야하는 field를 선택해야 하는 판단 필요, 어떤 field가 영향을 주는지 판단
# 영향주는 field 검증 알고리즘 이용.

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

sns.lineplot(x='avg_training_score', y='is_promoted', data=hr_df)
# avg_training_score와 is_promoted 연관성 비교분석

sns.barplot(x='recruitment_channel', y='is_promoted', data=hr_df)
# sourcing: 소개,중개, other rec~: 타채널, refferred: 추천제
# 막대그래프를 보면 심지가 다름. 길면 길수록 판단범위가 넓어져 판단 힘들다는 뜻.
# 막대그래프 심지가 짧으면 데이터가 상대적으로 많고, 길면 상대적으로 적다.

hr_df['recruitment_channel'].value_counts()
# refferred의 빈도 데이터가 작기때문에, 비교가 힘들다.

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

hr_df['gender'].value_counts()
# f가 상대적으로 적다.

sns.barplot(x='department', y='is_promoted', data=hr_df)
plt.xticks(rotation=45)

hr_df['department'].value_counts()

hr_df.isnull().mean()

hr_df['education'].value_counts()
# Bachelor: 학사, Master: 석박, Below: 고졸이하

hr_df['previous_year_rating'].value_counts()

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

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

plt.figure(figsize=(12,9))
sns.barplot(x='region', y='is_promoted', data=hr_df)
plt.xticks(rotation=45)
# region 제외하고 나머지만 원핫 인코딩


hr_df.isna().mean()

hr_df = pd.get_dummies(hr_df, columns=['department', 'education', 'gender', 'recruitment_channel'])
hr_df.head()
# employee_id, region column은 DataFrame에서 제외

# 라벨인코딩 완료
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

X_test.shape, y_train.shape

2. 로지스틱 회귀(Logistic Regression)
- 분류분석(Classification) 알고리즘
- 독립 변수의 선형 결합을 이용하여 사건의 발생 가능성을 예측하는 데 사용되는 통계 기법
- y = wx+b
- 둘 중의 하나를 결정하는 문제(이진 분류)를 풀기 위한 대표적인 알고리즘
- 입력 데이터와 가중치의 선형 조합으로 선형 방정식을 만듦 -> 선형 방정식의 결과(y)를 0과 1사이의 확률값으로 변환(시그모이드 함수)
- 3개 이상의 클래스에 대한 판별도 할 수 있음
- OvR(One-vs-Rest): 각 클래스마다 하나의 이진 분류기를 만들고, 해당 클래스를 기준으로 그 클래스와 나머지 모든 클래스를 구분하는 이진 분류를 실행 -> 가장 높은 확률을 가진 클래스를 선택
- OvO(One-vs-One): 클래스의 개수가 N인 경우 N(N-1)/2개의 이진 분류기를 만듦 -> 입력 데이터를 각 이진 분류기에 넣어 가장 많이 선택된 클래스를 최종 선택
- 대부분 OvR전략을 선호, 클래스 간의 구분이 명확하지 않거나 데이터가 한쪽으로 치우친 경우 OvO를 고려
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) # (y_test, x_test)
# 92%
# 회귀였으면 92%가 잘 맞춘것이지만, 로짓회귀이기 때문에 맞춘걸로 의미가 없을 수 있다.

hr_df['is_promoted'].value_counts()

3. 혼돈 행렬(confusion matrix)
- 정밀도와 재현율(민감도)을 활용하는 평가용 지수
- F1-score 표 참조
```
TN(8784) FP(100)
FN(673) TP(175)
```
- ( 결과, 예측 )
- TN : 승진못했는데, 승진못했다 예측 ( -, - )
- FN : 승진못했는데, 승진했다 예측 ( -, + )
- FP : 승진했는데, 승진못했다 예측 ( +, -)
- TP : 승진했는데, 승진했다 예측
- T : 예측성공 F : 예측실패 -> T, F는 예측 성공여부
- P : 결과 성공 N : 결과 실패 -> P, N은 결과 성공여부
from sklearn.metrics import confusion_matrix # 혼동행렬 모듈
confusion_matrix(y_test, pred)
# 어떤쪽이 어떤 결과가 나왔는지 행렬로 보여주는

3-1. 정밀도(Precision)
- TP / (TP+FP)
- 결과성공 예측성공 / 결과성공
- 무조건 양성으로 판단해서 계산하는 방법
- 실제 1인 것 중에서 얼마 만큼을 제대로 맞췄는가? (실제 양수)

3-2. 재현율(recall)
- TP / (TP+FN)
- 결과성공 예측성공 / 예측+
- 정확하게 감지한 양성 샘플의 비율
- 1이라고 예측한 것 중, 얼마만큼을 제대로 맞췄는가?
- 민감도 또는 TDR(True Positive Rate)라고도 부름

3-3. f1 score
- 정밀도와 재현율의 조화평균을 나타내는 지표
- 정밀도와 재현율의 거리가 멀어질수록 조화평균값도 하락

정밀도 재현율 산술평균 조화평균
0.4 0.6 0.5 0.48
0.3 0.7 0.5 0.42
0.5 0.5 0.5 0.5
from sklearn.metrics import precision_score, recall_score, f1_score
# 정밀도
precision_score(y_test, pred)

recall_score(y_test, pred)
# 재현율

f1_score(y_test, pred)
# f1 score

lr.coef_
# 독립변수 갯수와 동일
# y=wx+b에서 w에 해당하는 기울기(가중치) 출력

coef는 coefficient 의 준말로, 계수를 말합니다. 여기서 가중치(W)를 계수(coefficient)로 출력.
# 독립변수
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, 0, 1]
})
temp_df

pred = temp_lr.predict(temp_df)
pred
temp_lr.coef_ # 기울기

temp_lr.intercept_ # 절편

intercept는 수학에서 y절편(β0)을 말하는데, 통계학과 머신러닝에서 y절편(β0)은 편향(bias)를 말합니다. 따라서, intercept(y절편)는 bias(편향)으로 모델의 예측을 조정하는 상수부분을 출력합니다.
proba = temp_lr.predict_proba(temp_df)
proba
# 인공지능이 판단하는건 없고, 확률중 높은 확률을 선택함.
# 임계치 0.05나 0.01설정
# 1열이 승진못할 확률, 2열이 승진할 확률
# y = 0.51112537x1 + 0.04309755x2 + 2.09583168x3 -7.20658921
# x1 = previous_year_rating, x2 = avg_training_score, x3 = awards_won?
# 따라서, temp_df의 DataFrame의 행이 결과 행렬의 순서 그대로 출력됨.

# 승진할 확률
proba = temp_lr.predict_proba(temp_df)[:,1]
proba

# 임계값 설정
# 기본 임계값은 0.5
threshold= 0.5
pred = (proba > threshold).astype(int)
pred
# 실패, 실패, 승진성공

4. 교차 검증(Cross Validation)
- train_test_split에서 발생하는 데이터의 섞임에 따라 성능이 좌우되는 문제를 해결하기 위한 방법
- K겹(K-Fold)교차 검증을 가장 많이 사용
- 최적의 하이퍼 파라미터(k갯수)를 찾기 위해 사용
- 데이터를 k개의 폴드로 나누고, 각 폴드를 한 번씩 테스트 셋으로 사용하고 나머지 폴드를 훈련 셋으로 사용하여, 총 k번 모델을 훈련하고 평가하는 방식
from sklearn.model_selection import KFold
kf =KFold(n_splits=5) # n_splits = k갯수
kf
# random_state=None -> 고정되지 않음
# shuffle=False -> 데이터를 섞지 않고 원래 순서대로 k개의 폴드로 나눕니다
# shuffle=True ->

shuffle = True
- 데이터를 k개의 폴드로 나누기 전에 무작위로 섞습니다.
- 데이터의 순서가 훈련 셋과 테스트 셋의 분포에 영향을 미치지 않도록 합니다.
- 데이터셋이 특정 순서(예: 시간순, 그룹별)로 정렬되어 있을 때 특히 유용합니다.
- 모델이 데이터의 특정 순서나 패턴에 영향을 받지 않고 보다 일반화된 성능을 평가할 수 있도록 합니다.
shuffle = False
- 데이터를 섞지 않고 원래 순서대로 k개의 폴드로 나눕니다.
- 데이터의 순서가 중요한 경우(예: 시계열 데이터) 사용될 수 있습니다.
- 데이터가 이미 무작위로 섞여 있거나 순서가 모델 평가에 영향을 미치지 않는다고 확신할 때 사용됩니다.
for train_index, test_index in kf.split(range(len(hr_df))):
print(train_index, test_index, len(train_index), len(test_index))
# n_splits이 5이기때문에 5개로 쪼갬
# train_index 훈련 데이터 길이 = 38928, test_index 테스트 데이터 길이 = 9732
# shuffle 하기전
# 왼쪽은 훈련 데이터, 오른쪽은 테스트 데이터로 분할

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

# shuffle한 후
for train_index, test_index in kf.split(range(len(hr_df))):
print(train_index, test_index, len(train_index), len(test_index))

# KFold(n=5)를 사용하여 위 데이터를 LogisticRegression 모델로 학습을 시키고
# 각 n마다 예측결과를 accuracy_score 값으로 출력
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)
# accuracy_score정확도 값들 리스트에 저장
acc_list.append(accuracy_score(y_test, pred))

acc_list

np.array(acc_list).mean()

교차검증을 사용하는 이유: 결과를 좋게하기 위함이 아니라, 검증을 하기 위한 용도.
교차검증을 하는이유? 성능을 확인할 때, 데이터의 속성에 따라 바뀔 수 있으니 모델성능의 평균치를 파악하기 위해서
정확도: accuracy가 높으면 좋으나, 분류에서 정확도가 높아도 데이터가 편향될 수 있음. -> 조화평균으로 데이터 편향여부 확인 필요. 조화평균이 너무 높으면 데이터 편향의심.
정밀도와 재현율은 반비례 관계
선형모양 예측은 쉬우나, 비선형 모양 예측은 어려움