일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 빅데이터
- NLP
- 데이터분석
- Keras
- 공공빅데이터청년인재양성
- 오버샘플링
- datascience
- 데이터전처리
- 공빅데
- 텍스트마이닝
- textmining
- ADSP
- k-means
- 2023공빅데
- 2023공공빅데이터청년인재양성
- DeepLearning
- DL
- data
- machinelearning
- 공빅
- Kaggle
- 공공빅데이터청년인턴
- 2023공공빅데이터청년인재양성후기
- ADsP3과목
- SQL
- 분석변수처리
- 클러스터링
- ML
- 머신러닝
- decisiontree
- Today
- Total
愛林
[Python/ML] 분류 알고리즘 (Classification Algorithm) : 로지스틱 회귀 (Logistic Regression) (확률적 판별 모델) 본문
[Python/ML] 분류 알고리즘 (Classification Algorithm) : 로지스틱 회귀 (Logistic Regression) (확률적 판별 모델)
愛林 2022. 9. 6. 14:57
분류 알고리즘에는 확률적 생성 모델과 확률적 판별 모델이 있다.
앞서서 살펴보았던 나이브 베이즈는 확률적 생성모델이었다.
확률적 생성모델은
y의 클래스 값에 따른 x의 분포에 대한 정보를 먼저 알아낸 후,
베이즈 정리를 이용하여 주어진 x에 대한 y의 조건부 확률 분포를 간접적으로 구하는
모델이었다.
확률적 판별모델은
직접 조건부 함수 모델을 추정하는 모델이다.
로지스틱 회귀와 의사결정나무가 여기에 해당하는데,
이번에는 제법 유명한 로지스틱 회귀에 대하여 알아보자.
로지스틱 회귀 (Logistic Regression)
로지스틱 회귀는 , 분석 대상이 여러 집단으로 나누어진 경우,
독립변수의 선형 결합을 이용하여 개별 관측치가 어느 집단에 속하는 지에 대한 확률을
계산하는 분류 기법이다.
보통 사건의 발생 가능성 확률을 구한다.
주요 개념으로는 승산비가 있고, 0 과 1 사이의 값을 취하기 위해 log를 사용하여 변환한다. (로짓변환)
최종적으로 로지스틱 함수를 얻어서 분석에 이용하게 된다.
로지스틱 회귀는 회귀분석 문제와 분류 문제 모두에 사용할 수 있다.
먼저,
전체 중에서 사건이 차지하는 크기를 나타내는 비율(Proportion) 이 있다.
클래스에 0에 속하는 확률에 대한, 클래스 1에 속하는 확률의 비를
우리는 오즈(Odds) 라고 한다.
쉽게 말하면 성공확률(p) / 실패확률(1-p)이다.
그리고 로지스틱 회귀에서 핵심적인 개념인 승산비 (Odds Ratio) 는
사건이 발생할 확률과 발생하지 않을 확률간의 비율이다.
오즈와 개념이 유사하며, 오즈를 비율로 만든 것과 같다.
이 odds 에 log 를 씌우면 마이너스 무한대에서 무한대 값이 가능해서,
회귀 모델이 성립이 가능하게 된다.
그래서 이 odds 값에 Log를 취하면 Logit 을 얻을 수 있다.
Logit 은 0과 1사이의 값을 갖는다.
확률의 선형 모델을 표시했을 때,
y = a + bx
의 식으로 선형 모델이 나온다. (x는 독립변수 , a와 b는 계수)
이제 여기에 odds 를 적용한다.
오즈는 위에서 성공확률 / 실패확률이라고 했는데,
예를 들어 성공확률이 2/10, 실패확률이 8/10 일 때의 오즈는
2/8 = 1/4 가된다.
오즈는 0 ~ ∞ 사이의 값을 가지게 된다.
이제 이렇게 만든 오즈를 Logit 변환한다.
y = log (odds)
로 표현할 수 있다.
로그변환된 오즈 (Logit) 은 -∞에서 ∞사이의 값을 가지게 된다.
이는 확률의 범위이다.
확률의 범위는 0에서 1사이의 값을 가지고,
확률의 비율인 오즈의 범위는 [0,∞] 의 값을 가진다.
이제 판별함수 수식으로 선형함수를 사용할 것이다.
음의 무한대에서 양의 무한대의 값을 가지는 입력변수(Logit) 을
Logistic Function = 시그모이드
를 사용해서 (쉽게 말하면 그냥 로짓함수의 역함수)
입력변수를 0에서 1사이의 값을 가지는 출력변수로 변환한다.
이항 로지스틱회귀는 종속변수의 결과가 성공, 실패 2개의 카테고리로 결정이 된다.
다항 로지스틱회귀는 종속변수의 결과가 성공, 실패의 3개 이상의 카테고리로 결정된다.
예를 들면 (맑음, 흐림, 비) 가 있다.
로지스틱 회귀 실습
# 패키지 임포트
import os
import warnings
# FutureWarning 제거
warnings.simplefilter(action='ignore', category=FutureWarning)
import pandas as pd # pandas package
import numpy as np # numpy package
import scipy as sp # Scipy Package
from scipy import stats # Scipy Package
from sklearn import datasets # sklearn dataset
from sklearn.model_selection import train_test_split # 데이터셋 분리
# 시각화 패키지
import matplotlib.pyplot as plt # matplotlib 시각화 패키지
import seaborn as sns
%matplotlib inline
# os에 따른 Font 깨짐 제거를 위해 Font 지정
import os
if os.name == 'nt' : # Windows OS
font_family = "Malgun Gothic"
else: # MAC OS
font_family = "AppleGothic"
# - 값이 깨지는 문제 해결을 위해 파라미터값 설정
sns.set(font=font_family, rc ={"axes.unicode_minus" : False})
Logit Function 을 시각화 해보자.
## 로짓 펑션 시각화
p = np.arange(0.001, 1, 0.001)
logit_df = pd.DataFrame({
'p' : p,
'logit' : np.log(p / (1-p)),
'odds' : p / (1-p),
})
fig, ax = plt.subplots(figsize = (5,4))
ax.axhline(0, color = 'grey', linestyle = '--')
ax.axvline(0.5, color = 'grey', linestyle = '--')
ax.plot(logit_df['p'], logit_df['logit'])
ax.set_xlabel("Probability")
ax.set_ylabel("logit(p)")
plt.tight_layout()
plt.show()
닿지않는 무한대가 나온다.
## logitstic Function = sigmoid
x = logit_df['logit']
phi = 1 / (1 + np.exp(-x)) # x :판별함수
plt.figure(figsize = (5,4))
plt.plot(x, phi)
plt.xlabel('x')
plt.ylabel("Probability")
plt.show()
이제 Logistic Function = Sigmoid 를 확인해보자.
0에서 1사이의 값이 나온다.
절대로 0이랑 1에 닿지는 않는다.
# 사례 결과 확인
phi = 1 / (1 + np.exp(-2))
print(phi)
0.8807970779778823
위스콘신 유방암 데이터 (load_breast_cancer data) 를 활용하여
이항 로지스틱 회귀를 실습해보자.
데이터셋 로드
## 데이터셋 로드
from sklearn.datasets import load_breast_cancer
data = load_breast_cancer()
# 독립변수 input data
b_input_data = data.data
# 종양 : target data
# 악성 (0) mal , 양성(1) benign
b_target_data = data.target
# 종양 구분
tumar = data.target_names
# 속성 명칭
feature_names = data.feature_names
print('종양 여부 결정 속성 : {}'.format(feature_names))
print("종양 구분 : {}".format(tumar))
breast_df = pd.DataFrame(b_input_data, columns = feature_names)
breast_df['target'] = b_target_data
# 속성명 공백에 "_" 달기
breast_df.columns = [col.replace(" ","_") for col in breast_df.columns]
종양 여부 결정 속성 : ['mean radius' 'mean texture' 'mean perimeter' 'mean area'
'mean smoothness' 'mean compactness' 'mean concavity'
'mean concave points' 'mean symmetry' 'mean fractal dimension'
'radius error' 'texture error' 'perimeter error' 'area error'
'smoothness error' 'compactness error' 'concavity error'
'concave points error' 'symmetry error' 'fractal dimension error'
'worst radius' 'worst texture' 'worst perimeter' 'worst area'
'worst smoothness' 'worst compactness' 'worst concavity'
'worst concave points' 'worst symmetry' 'worst fractal dimension']
종양 구분 : ['malignant' 'benign']
## 데이터 확인
# 데이터 차원
print(breast_df.shape)
# label 데이터 비율 확인
breast_df['target'].value_counts()
(569, 31)
1 357
0 212
Name: target, dtype: int64
## 학습 데이터셋 생성
# 평균 관련된 3개의 속성으로 종양 여부 판단
model_feature_name = ['mean_radius', 'mean_texture', 'mean_perimeter']
X = breast_df[model_feature_name]
Y = breast_df['target']
# 80% 데이터를 학습 데이터로, 20% 를 테스트 데이터로.
X_train, X_test, y_train, y_test = train_test_split(X, Y,
test_size = 0.2, random_state= 20)
## 표준화 데이터 스케일링
from sklearn.preprocessing import StandardScaler
## 스케일링 & 정규화
scaler = StandardScaler()
X_train_std = scaler.fit_transform(X_train)
X_test_std = scaler.fit_transform(X_test)
로지스틱 회귀
from sklearn.linear_model import LogisticRegression
- 파라미터
- penalty : 규제에 사용된 기준을 지정
- dual : 이중 또는 초기 공식
- fit_intercept : 모델에 상수항이 있는가 없는가를 결정하는 인수 (default : True)
- intercept_scaling : 정규화 효과 정도
- class_weight : 클래스의 가중치
- random_state : 난수 seed 설정
- solver : 최적화 문제에 사용하는 알고리즘
- max_iter : 계산에 사용할 작업 수
- multi_class : 다중 분류 시에 (ovr, multinomial, auto) 로 설정
- verbose : 동작 과정에 대한 출력 메시지
- warm_start : 이전 모델을 초기화로 적합하게 사용할 것인지 여부
- n_jobs : 병렬 처리할 때 사용되는 CPU 코어 수
- l1_ratio : L1 규제의 비율. Elastic-Net 믹싱 파라미너 경우에만 사용한다.
-속성
- classes__ : 클래스
- coef_ : 회귀계수
- intercept_ : 절편
from sklearn.linear_model import LogisticRegression
## 반복횟수(max_iter)를 100으로 지정한 로지스틱 회귀모델
lr = LogisticRegression(max_iter = 100)
lr.fit(train_scaled, y_train)
## 테스트 데이터 정확도확인
# 결과 확인
print("test데이터셋 정확도 : ", lr.score(test_scaled, y_test))
test데이터셋 정확도 : 0.9298245614035088
Max_iter = 100이란, 100번 반복한다는 뜻이다.
모델의 반복횟수를 100번으로 한 모델을 만들고, train 데이터를 넣어준다.
test 데이터의 정확도는 약 93% 정도가 나왔다.
## 회귀계수 확인
print("coefficient : \n", lr.coef_)
print()
print("intercept : \n", lr.intercept_)
coefficient :
[[-0.57412744 -0.95219005 -2.85929651]]
intercept :
[0.75640782]
회귀계수는 coefficient 의 결과와 같이 나왔고,
절편은 0.7564가 나왔다.
회귀 식이
y = -0.57412744*x1 - 0.95219005*x2 - 2.85929651*x3 + 0.75640782
인 것이다.
## 테스트 데이터 각 클래스별 확률 확인
# 어떤 클래스가 있는 지 확인
print("Class : ", lr.classes_)
# 테스트 데이터
print("테스트 데이터 : \n", test_scaled[:5])
# 테스트 데이터의 각 클래스 확률 확인
print("테스트 데이터의 각 클래스 확률 : \n", lr.predict_proba(test_scaled[:5]))
## 테스트 데이터 에측 결과
Y_pred = lr.predict(test_scaled[:5])
print("예측결과 : ", Y_pred)
Class : [0 1]
테스트 데이터 :
[[-0.96666522 0.32786912 -0.93579507]
[ 0.4242379 0.0712003 0.33778671]
[ 1.28148839 1.52735117 1.37335484]
[-0.60048868 2.32022913 -0.63222328]
[ 0.02115985 1.19698535 0.04006087]]
테스트 데이터의 각 클래스 확률 :
[[0.02472466 0.97527534]
[0.62733186 0.37266814]
[0.99532336 0.00467664]
[0.33190545 0.66809455]
[0.62481681 0.37518319]]
예측결과 : [1 0 0 1 0]
클래스를 확인해보았을 때, 0과 1 (악성과 양성) 2개의 클래스가 있고,
위와 같은 테스트 데이터로 확인해보았을 때 각 클래스는
0번째 데이터는 1클래스일 확률이 97%로 1로 판단되었고,
1번째 데이터는 0클래스일 확률이 63% 로 0으로 판단되었다.
나머지도 위와 같이 계산되어
예측결과가 [ 1 0 0 1 0 ] 이 도출되었다.
## 판별함수 (Decision_function) 으로 z값 산출
# 테스트 데이터 확인
print("테스트 데이터 : \n", test_scaled[:5])
# 이진 분류의 decision_function 은 1 클래스(양성)에 대응하는 값을 반환
decision = lr.decision_function(test_scaled[:5])
print()
print("decision_function = ", np.round(decision[:5], decimals = 2))
print()
# 로지스틱 함수 양성(1) 클래스의 확률
phi = 1 / (1 + np.exp(-decision))
# 로지스틱 함수 악성(0) 클래스의 확률
n_phi = 1 - (phi)
print("클래스1 확률 : ", phi)
print("클래스0 확률 : ", n_phi)
테스트 데이터 :
[[-0.96666522 0.32786912 -0.93579507]
[ 0.4242379 0.0712003 0.33778671]
[ 1.28148839 1.52735117 1.37335484]
[-0.60048868 2.32022913 -0.63222328]
[ 0.02115985 1.19698535 0.04006087]]
decision_function = [ 3.67 -0.52 -5.36 0.7 -0.51]
클래스1 확률 : [0.97527534 0.37266814 0.00467664 0.66809455 0.37518319]
클래스0 확률 : [0.02472466 0.62733186 0.99532336 0.33190545 0.62481681]
다음은 판별함수로 z값을 산출해볼 것이다.
판별함수는 판별변수들의 선형조합으로, 집단의 수 1개와 독립변수의 수 중 작은 값만큼
도출할 수 있다. 판별함수로 집단을 정확하게 분류할 수 있도록 예측력을 높인다.
위와 같이 테스트 데이터 5개를 이용한다.
이진 분류의 decision_function 은 1클래스(양성) 에 해당하는 값을 반환한다.
각 5개 클래스의 양성에 해당하는 z값은
[ 3.67, -0.52, -5.36. 0.7, -0.51 ] 로 도출되었다. 이게 z값이다.
이를 시그모이드 함수에 넣어서 확률을 확인할 수 있다.
위의 z값을 넣어서 도출해낸 확률이 위와 같은 클래스에 속할 확률이다.
모델을 평가해보자.
## 모델 평가
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
# 에측값
Y_pred = lr.predict(test_scaled)
# 성능 리포트값 출력
print(classification_report(y_test, lr.predict(test_scaled)))
# 주요 성능 지표
print()
print('confusion matrix = \n', confusion_matrix(y_true = y_test, y_pred=Y_pred))
print()
print('accuracy = \n', accuracy_score(y_true = y_test, y_pred=Y_pred))
print('precision = \n', precision_score(y_true = y_test, y_pred=Y_pred))
print('recall = \n', recall_score(y_true = y_test, y_pred=Y_pred)) # 재현율
print('f1 score = \n', f1_score(y_true = y_test, y_pred=Y_pred))
precision recall f1-score support
0 0.93 0.90 0.91 48
1 0.93 0.95 0.94 66
accuracy 0.93 114
macro avg 0.93 0.93 0.93 114
weighted avg 0.93 0.93 0.93 114
confusion matrix =
[[43 5]
[ 3 63]]
accuracy =
0.9298245614035088
precision =
0.9264705882352942
recall =
0.9545454545454546
f1 score =
0.9402985074626866
성능리포트, 혼동행렬, 정확도, 정밀도, 재현율, f1 score 로 각각 성능을 확인해보았다.
혼동행렬 Confusion Matrix 를 이용하여 확인해보았을 때,
106개 정도를 옳게 분류하고, 8개정도의 오류가 있었다.
정확도는 93%, 정밀도 또한 93%, 재현율 95%, f1 score 0.94로
높은 성능의 모델이 만들어졌다는 것을 알 수 있다.
혼동행렬을 시각화해서 보았을 때,
# 시각화
# Confusion_matrix
from sklearn.metrics import plot_confusion_matrix
labels = ['malignant(악성)', 'benign(양성)']
disp = plot_confusion_matrix(lr, test_scaled, y_test,
display_labels = labels,
cmap = plt.cm.Blues,
normalize = None)
disp.ax_.set_title('Confusion Matrix')
위와 같이 나왔다.
ROC Curve 를 그려서 성능을 확인해보면,
## ROC Curve
from sklearn.metrics import roc_curve, roc_auc_score, auc
# 검증 데이터가 클래스 1에 해당하는 데이터
Y_score = lr.predict_proba(test_scaled)[:,1]
fpr,tpr,thresholds = roc_curve(y_true = y_test, y_score=Y_score)
plt.plot(fpr,tpr,label = 'roc_curve(area = %0.3f)' % auc(fpr,tpr))
plt.plot([0,1], [0,1], linestyle = '--', label = 'random')
plt.plot([0,0,1], [0,1,1], linestyle = '--', label = 'ideal')
plt.legend()
plt.xlabel('false positive rate')
plt.ylabel('true positive rate')
plt.show()
# AUC score
print('auc = ', roc_auc_score(y_true=y_test, y_score=Y_score))
auc = 0.9690656565656566
이상적인 쪽에 가까운 것을 알 수 있다.
ROC 커브의 안의 영역을 확인하는 AUC score 을 확인했을 때도
0.969 로 높은 수치를 나타냈다.
Statsmodel 을 이용한 로지스틱 회귀
Statsmodel 을 이용한 로지스틱 회귀를 진행해보자.
적합시켜준 후 , 모델 결과를 리포트로 확인했다.
## 로지스틱 모델 적합, y = F(x)
import statsmodels.api as sm
# 상수항 추가
X = sm.add_constant(train_scaled)
model_s = sm.Logit(y_train, X)
results = model_s.fit()
## 결과 리포트
results.summary()
# 로지스틱의 log 형태가 취해진 결과
results.params
const 0.650922
x1 22.329805
x2 -1.089819
x3 -26.874841
dtype: float64
# 시그모이드
np.exp(results.params)
const 1.917308e+00
x1 4.985529e+09
x2 3.362773e-01
x3 2.130124e-12
dtype: float64
이제 위의 결과로
우리의 테스터셋을 예측해보자.
# 5개 테스트셋 예측
test_scaled[:5]
array([[-0.96666522, 0.32786912, -0.93579507],
[ 0.4242379 , 0.0712003 , 0.33778671],
[ 1.28148839, 1.52735117, 1.37335484],
[-0.60048868, 2.32022913, -0.63222328],
[ 0.02115985, 1.19698535, 0.04006087]])
테스터 데이터는 우리가 위에서 사용한 테스트 데이터와 같다.
위와 결과를 비교해보자.
# 상수항 추가
X_test = sm.add_constant(test_scaled)
# 예측 ( 클래스가 1일 확률 )
results.predict(X_test[:5])
array([9.79316159e-01, 7.24822900e-01, 9.07854265e-05, 8.46108119e-01,
2.21368457e-01])
상수항을 추가해주고 클래스 1일 확률을 예측해보았을 때,
위의 결과와는 다른 것 같다.
'Data Science > Machine Learning' 카테고리의 다른 글
[Python/ML] 음성 데이터 분석(Sound Processing) - (2) (3) | 2022.12.20 |
---|---|
[Python/ML] 음성 데이터 분석(Sound Processing) - (1) (3) | 2022.12.19 |
[Python/ML] 분류 알고리즘 (Classification Algorithm) : 나이브 베이즈 (Naive Bayes) (2) | 2022.08.31 |
[Python/ML] 앙상블 알고리즘 (Ensemble Algorithms) : 부스팅 (Boosting) (3) | 2022.08.26 |
[Python/MachineLearning] 앙상블 알고리즘 (Ensemble Algorithms) : 랜덤 포레스트 (Random Forest) (3) | 2022.08.23 |