본문 바로가기

Machine Learning_모델설계_Python

로지스틱회귀_2 실습

안녕하세요 배도리입니다. 오늘 배울 사자성어는 "낭중지추"입니다. 주머니의 송곳은 끝이 뾰족해 밖으로 드러난다. 즉, 뛰어난 사람은 어디갈나 돋보인다. 이거에 대한 일화도 한번 살펴볼까요?

 

조나라의 평원군은 수많은 식객을 거느린 어진 사람으로 이름나 있었다. 그는 초나라에 지원군을 요청하러 떠나기 전에 식객들 가운데 문무를 갖춘 스무 명을 골라 함께 가기로 했다. 그런데 열아홉 명을 뽑은 뒤에는 더 이상 고를 만한 사람이 없었다. 그때 ‘모수’라는 사람이 앞으로 나와 말했다.

“저를 함께 데려가면 도움이 될 것입니다.”

평원군은 얼굴조차 처음 보는 듯 그가 낯설었다.

“그대는 내 집에 온 지 몇 해나 되었소?”
“3년쯤입니다.”
“무릇 현명한 선비가 세상에 있으면 송곳이 주머니에 있는 듯해서 그 끝이 밖으로 나타나기 마련이오. 그대는 내 집에서 3년이나 있었다고 하지만 나는 한 번도 이렇다 할 이야기를 듣지 못했소. 이는 그대가 남다른 재주를 갖고 있지 않다는 뜻이니 여기 그냥 남아 계시오.”

평원군이 고개를 가로젓자 모수가 말했다.

“맞습니다. 그래서 오늘, 저를 주머니에 넣어 주십사 청을 드리는 겁니다. 저를 더 일찍 주머니에 넣어 주셨더라면 송곳이 주머니를 뚫고 나와서 그 끝뿐만 아니라 자루까지 드러났을 것입니다.

이리하여 모수도 함께 가기로 했다.

 

ㅎㄷㄷ모수 센세 자신감 무엇이죠... 역시 수 천년이 지나도 회자되는 사람들은 이유가 있습니다. 아직 저는 뭉툭하니 열심히 저를 갈아서 바늘로 만든 후에 여러 주머니들을 다 뚫고 다녀야겠습니다. 로지스틱 실습하겠습니다. 이전에 설명드렸던 코드 내용들은 여기선 보통 설명하지 않았습니다. 궁금한게 있다면 이전 실습자료를 참고하시면됩니다!

 

 

실습1

이전에 kNN에 썼던 아이리스 데이터셋을 활용하여 꽃잎과 꽃받침의 크기를 기반으로 붓꽃의 종(species)을 예측하겠습니다. 우선 입력 변수와 타겟 변수로 분리하는 작업을 수행합니다.

이쁘다

import seaborn as sns #seaborn을 불러오고 네임스페이스를 sns로 축약
iris = sns.load_dataset('iris')  #iris라는 변수명으로 Iris 데이터셋 download
 
X = iris.drop('species', axis=1) #'species'열을 drop하고 input X를 정의
.
y = iris['species']

 

 

 

iris 데이터셋의 범주형 레이블(타겟 변수)인 'species'를 정수로 변환하겠습니다.


from sklearn.preprocessing import LabelEncoder
#사이킷런 라이브러리의 전처리 모듈에서 LabelEncoder 클래스를 임포트
 
classle = LabelEncoder() #class label
#클래스의 인스턴스를 생성하고, 이를 'classle'이라는 변수에 저장합니다.
 
y = classle.fit_transform(iris['species'].values) #species 열의 문자열을 범주형으로 변환
# 'classle' 인스턴스의 fit_transform 메소드를 사용하여, Iris 데이터셋의 'species' 열의 값들을 정수로 변환합니다. 변환된 값들은 'y'라는 변수에 저장됩니다.

 

 

 

전체 데이터를 학습과 테스트 데이터셋으로 분리하겠습니다.

 
from sklearn.model_selection import train_test_split
#X(입력변수)와 y(종속변수)로 이루어진 데이터를 학습과 테스트 데이터넷으로 각각 70%, 30%의 비율로 나눔
 
X_train,X_test,y_train,y_test=train_test_split(X,y, test_size=0.3, random_state=1, stratify=y)
#stratify=y: 레이블 y를 기준으로 데이터셋을 계층화하여 분할

 

 

 

데이터의 스케일이 동일해지기 위해 표준화를 진행합니다. 

 
from sklearn.preprocessing import StandardScaler #사이킷런에서 StandardScaler 클래스 호출
sc = StandardScaler()
sc.fit(X_train) #'sc'인스턴스를 사용하여 학습 데이터셋(X_train)의 표준화를 위한 평균과 표준편차를 계산합니다. 이렇게 계산된 평균과 표준편차는 sc 객체에 저장됩니다.
#이렇게 하면, sc.transform() 메서드를 호출할 때마다 앞서 계산된 평균과 표준편차를 사용하여 데이터를 표준화합니다.
#이를 통해 학습 데이터셋과 테스트 데이터셋에 동일한 표준화 과정을 적용할 수 있습니다.
 
X_train_std = sc.transform(X_train) #학습 데이터셋(X_train)에 sc표준화를 적용하고, 변환된 데이터를 X_train_std라는 변수에 저장합니다.
 
X_test_std = sc.transform(X_test)
#테스트 데이터셋을 표준화하면, 훈련 데이터셋에 적용한 전처리 과정과 동일한 조건에서 테스트 데이터셋을 사용하여 모델의 성능을 평가 이를 통해, 모델이 새로운 데이터에 대해 얼마나 잘 일반화되는지 확인

 

 

 

이제 로지스틱회귀를 적용하겠습니다.

# Logistic regression
from sklearn.linear_model import LogisticRegression
#scikit-learn 라이브러리에서 LogisticRegression 모델을 호출
Logit = LogisticRegression(C=200, random_state=1)  #C=1/λ, 디폴트:L2정규화
#로지스틱 회귀 모델의 인스턴스를 생성하고, 이를 'Logit'이라는 변수에 저장합니다. 여기서 C는 정규화 강도의 역수입니다.
#C 값이 작을수록 정규화 강도가 크며, 이는 모델의 가중치가 더 강하게 제한되는 것을 의미합니다. C=200은 상대적으로 약한 정규화를 적용함을 의미합니다.
Logit.fit(X_train_std, y_train)
y_train_pred = Logit.predict(X_train_std)
y_test_pred = Logit.predict(X_test_std)

 

 

 

정확도를 통해 성능평가 하겠습니다.

# Accuracy
#scikit-learn의 accuracy_score 메트릭을 사용하여 학습 데이터셋과 테스트 데이터셋에 대한 모델의 정확도를 계산합니다.
from sklearn.metrics import accuracy_score
print(accuracy_score(y_train,y_train_pred))  
print(accuracy_score(y_test,y_test_pred))    
 
0.9809523809523809
1.0

 

 

 

혼동행렬도 진행해보겠습니다.

# Confusion matrix
 
from sklearn.metrics import confusion_matrix
print(confusion_matrix(y_test, y_test_pred))  
 
[[15 0 0]
[ 0 15 0]
[ 0 0 15]]
 

 

 

 

실습2

이번 실습에서는 L1, L2정규화 및 규제화강도 조절해보겠습니다.

데이터는 http://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data 여기서 받으시면됩니다.

본 실습2의 데이터는 와인 관련 데이터셋 입니다. y값은 이미 범주형으로 되어있습니다(개이득).

import pandas as pd
import numpy as np
 
#andas의 read_csv 함수를 사용하여 UCI 머신러닝 저장소의 와인 데이터셋을 불러옵니다.
dat_wine.head()
dat_wine.columns = ['class label','alchohol','malic acid','ash','alcalinity of ash','magnesium',
                    'total phenols','flavanoids', 'nonflavanoid phenols','proanthocyanins',
                    'color intensity','hue','OD208', 'proline']  #Column names
print('class label:', np.unique(dat_wine['class label']))  #Class 출력
#이 코드는 데이터셋의 'class label' 열에 있는 고유한 클래스 레이블 값을 확인하는 데 사용됩니다.
dat_wine.head()

 

 

 

전체 data를 훈련과 시험 데이터셋으로 분리하겠습니다.

 
from sklearn.model_selection import train_test_split
X, y = dat_wine.iloc[:,1:].values, dat_wine.iloc[:,0].values
X_train, X_test, y_train,y_test = train_test_split(X, y, test_size=0.3, random_state=1, stratify=y)

 

 

 

표준화 진행합니다.

 
from sklearn.preprocessing import StandardScaler
std = StandardScaler()
X_train_std = std.fit_transform(X_train)
X_test_std = std.transform(X_test)

 

 

 

여기서 logistic_regression이라는 새로운 함수를 만들겠습니다. 이 함수는 scikit-learn 라이브러리의 LogisticRegression 클래스를 활용하고 로지스틱 회귀 모델을 학습하고 평가하는 역할을 합니다.

from sklearn.linear_model import LogisticRegression
#penalty: 정규화 유형을 설정합니다. 'l1', 'l2', 'elasticnet', 'none' 중 하나의 값을 가질 수 있습니다.
#C 정규화 강도의 역수입니다. C 값이 작을수록 정규화 강도가 높아집니다. λ는 정규화 강도를 나타냅니다 (λ = 1/C).
#solver: 로지스틱 회귀 문제를 해결하기 위한 최적화 알고리즘을 설정합니다. 예를 들어 'newton-cg', 'lbfgs', 'liblinear', 'sag', 'saga' 등이 있습니다.
#max_iter: 최적화 알고리즘에서 수행할 최대 반복 횟수를 설정합니다.
def logistic_regression(penalty, C, solver, max_iter):
    lr = LogisticRegression(penalty=penalty, C=C, solver=solver, max_iter=max_iter)
    lr.fit(X_train_std, y_train)

    print(f'\n---- Penalty:{penalty.upper()}, λ:{round(1/C,1)} ----'    )
    print(f'정확도(학습데이터): {round(lr.score(X_train_std, y_train),3)}')
    print(f'정확도(시험데이터): {round(lr.score(X_test_std, y_test),3)}')
    print(f'회귀계수:\n{lr.coef_}')

 

 

 

서로 다른 정규화 유형과 정규화 강도를 가진 여러 로지스틱 회귀 모델을 학습하고 평가하겠습니다.

# Logistic Regression with L2 or L1 Regularization
from sklearn.linear_model import LogisticRegression
logistic_regression(penalty='l2', C=10.0, solver='saga', max_iter=500) #L2 with C(=1/λ)=10
#saga 알고리즘은 penalty 값이 l2일 때는 사용할 수 없지만, solver 값으로 saga를 설정할 수는 있습니다.
# 이 경우에는 solver 값이 saga이지만, penalty 값이 l2이므로 saga 알고리즘 대신 sag, newton-cg, lbfgs 알고리즘 중 하나를 사용하게 됩니다
logistic_regression(penalty='l2', C=1.0, solver='saga', max_iter=500)  #L2 with C(=1/λ)=1
logistic_regression(penalty='l2', C=0.1, solver='saga', max_iter=500)  #L2 with C(=1/λ)=0.1

logistic_regression(penalty='l1', C=10.0, solver='saga', max_iter=500) #L1 with C(=1/λ)=10
logistic_regression(penalty='l1', C=1.0, solver='saga', max_iter=500)  #L1 with C(=1/λ)=1
logistic_regression(penalty='l1', C=0.1, solver='saga', max_iter=500)  #L1 with C(=1/λ)=0.1

---- Penalty:L2, λ:0.1 ----
정확도(학습데이터): 1.0
정확도(시험데이터): 0.981
회귀계수:
[[ 1.23671485  0.16332857  0.3038889  -1.5287351   0.09477582  0.39204783
   0.8931479  -0.26501989  0.10032183  0.16557971  0.08006779  1.02889995
   1.35644329]
 [-1.58934456 -0.44410579 -1.27866103  1.23683791 -0.32658909 -0.57862815
   0.99114985  0.37705299  0.42763229 -1.23306912  1.18257427  0.0896818
  -1.7460848 ]
 [ 0.35262971  0.28077723  0.97477212  0.29189719  0.23181327  0.18658032
  -1.88429775 -0.1120331  -0.52795412  1.06748941 -1.26264206 -1.11858175
   0.38964151]]

---- Penalty:L2, λ:1.0 ----
정확도(학습데이터): 1.0
정확도(시험데이터): 0.981
회귀계수:
[[ 0.75459712  0.06144096  0.23296855 -0.89225697  0.02617872  0.29447934
   0.56108706 -0.20730666  0.13473329  0.12772584  0.10218119  0.61811917
   0.90945771]
 [-0.98670011 -0.32312666 -0.65180888  0.6675048  -0.2294818  -0.20673167
   0.43842012  0.19796896  0.24447907 -0.77941891  0.63753547  0.08615153
  -1.03417142]
 [ 0.23210299  0.26168569  0.41884032  0.22475216  0.20330309 -0.08774767
  -0.99950719  0.00933771 -0.37921236  0.65169307 -0.73971666 -0.7042707
   0.12471371]]

---- Penalty:L2, λ:10.0 ----
정확도(학습데이터): 1.0
정확도(시험데이터): 1.0
회귀계수:
[[ 0.41027904 -0.03150478  0.13676247 -0.41132988  0.053817    0.22360309
   0.31669346 -0.15965255  0.11371852  0.0703978   0.11106994  0.3097968
   0.51689079]
 [-0.54265266 -0.20157121 -0.25666766  0.28070551 -0.14836176 -0.0405875
   0.12454659  0.08290512  0.10088007 -0.44574123  0.27320104  0.09646406
  -0.51870793]
 [ 0.13237362  0.233076    0.11990519  0.13062437  0.09454476 -0.18301559
  -0.44124005  0.07674743 -0.21459859  0.37534344 -0.38427098 -0.40626086
   0.00181714]]

---- Penalty:L1, λ:0.1 ----
정확도(학습데이터): 1.0
정확도(시험데이터): 0.981
회귀계수:
[[ 0.87623601  0.          0.         -1.86075581  0.          0.
   0.30166879  0.          0.          0.          0.          1.01718833
   1.31825486]
 [-2.3797156  -0.39164617 -1.78659151  0.93275404 -0.34170714 -0.42262763
   0.24004334  0.49732413  0.1352688  -1.39476123  1.21521752  0.
  -2.17544953]
 [ 0.          0.          0.71769916  0.          0.          0.
  -3.17553925  0.         -0.54778345  1.05366119 -1.22413218 -0.97287951
   0.        ]]

---- Penalty:L1, λ:1.0 ----
정확도(학습데이터): 1.0
정확도(시험데이터): 1.0
회귀계수:
[[ 0.          0.          0.         -1.20266054  0.          0.
   0.          0.          0.          0.          0.          0.69590708
   0.93269147]
 [-1.62521642 -0.12363355 -0.75777028  0.03110676 -0.08274387  0.
   0.          0.15076839  0.         -1.25904918  0.15075726  0.
  -1.15171146]
 [ 0.          0.          0.          0.          0.          0.
  -2.38852315  0.          0.          0.         -0.84656416 -0.55429233
   0.        ]]

---- Penalty:L1, λ:10.0 ----
정확도(학습데이터): 0.968
정확도(시험데이터): 0.944
회귀계수:
[[ 0.          0.          0.         -0.04185882  0.          0.
   0.23303315  0.          0.          0.          0.          0.
   0.84034785]

 

확실히 라쏘는 람다가 커지니 0이 많이 보입니다. 두개의 결과를 살펴보겠습니다.

 

---- Penalty:L2, λ:10.0 ---- ---- Penalty:L1, λ:1.0 ----
정확도(학습데이터): 1.0   정확도(학습데이터): 1.0

정확도(시험데이터): 1.0   정확도(시험데이터): 1.0 

 

흠 뭐를 선택할까요? 일단 두 모델 모두 학습 데이터와 시험 데이터에 대해 완벽한 정확도(1.0)를 보여주고 있으므로, 이 부분에서는 차이를 판단할 수 없습니다.
반면 회귀계수를 살펴보면, L1 정규화를 사용한 모델에서는 많은 계수가 0으로 설정되었습니다. 이는 L1 정규화의 특징이며, 이를 통해 모델이 더 단순해질 수 있습니다. 다시 말해, 더 적은 수의 특성을 사용하여 동일한 성능을 달성합니다. 이 점에서 L1 정규화 모델이 더 "좋다"고 볼 수 있습니다. 일반적으로, 모델의 복잡성이 낮을수록 이해하기 쉽고, 오버피팅(과적합)의 위험도 줄어듭니다. 이것이 더 단순한 모델이 종종 선호되는 이유 중 하나입니다. 모델이 너무 복잡하면, 훈련 데이터에 너무 잘 맞아 떨어져서 새로운 데이터에 대한 예측성능이 떨어질 수 있기 때문입니다.(예측정확도와 간명도는 트레이으오프)


그러나 이 두 모델 중 어떤 것이 "더 좋다"는 말은 문제 맥락과 더 큰 목표에 따라 달라집니다. 예를 들어 모든 특성을 활용하려는 경우 L2가 더 나을 수 있습니다.