sklearn.linear_model의 작동원리

linear_model
Author

강신성

Published

2023-10-22

LogisticRegression의 작동원리와 sklearn.linear_model에 대해서 자세히 알아보자.

해당 자료는 전북대학교 통계학과 최규빈 교수님의 강의 내용을 토대로 재구성되었음을 밝힙니다.

1. 라이브러리 imports

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import sklearn.linear_model
import itertools

2. 로지스틱 회귀분석의 원리

  • 저번에 봤었던 취업 자료를 가져와보자.
df = pd.read_csv('https://raw.githubusercontent.com/guebin/MP2023/main/posts/employment.csv')
df
toeic gpa employment
0 135 0.051535 0
1 935 0.355496 0
2 485 2.228435 0
3 65 1.179701 0
4 445 3.962356 1
... ... ... ...
495 280 4.288465 1
496 310 2.601212 1
497 225 0.042323 0
498 320 1.041416 0
499 375 3.626883 1

500 rows × 3 columns

employment를 예측하려면…

## 1
X = df.drop(['employment'], axis = 1)
y = df.employment

## 2
predictr = sklearn.linear_model.LogisticRegression()

## 3
predictr.fit(X, y)

predictr.predict(X)  ## yhat
array([0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1,
       1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,
       0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0,
       0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0,
       1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0,
       1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1,
       0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1,
       1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1,
       1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1,
       0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0,
       1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0,
       0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0,
       0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
       0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0,
       0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1,
       0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1,
       0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1,
       0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0,
       0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1,
       1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1,
       0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0,
       0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1], dtype=int64)

\(\hat{y}\)은 어떻게 나왔는가?

- 아래 수식에 의하여 나왔음…

predictr.coef_, predictr.intercept_  ## 로지스틱임에도 기울기와 절편이 있다.
(array([[0.00571598, 2.46520018]]), array([-8.45433334]))
u = X.toeic*0.00571598 + X.gpa*2.46520018 - 8.45433334  ## yhat
v = 1/(1+np.exp(-u))  # v : 확률같은 거

v
0      0.000523
1      0.096780
2      0.453003
3      0.005627
4      0.979312
         ...   
495    0.976295
496    0.432939
497    0.000855
498    0.016991
499    0.932777
Length: 500, dtype: float64
((v > 0.5) == predictr.predict(X)).mean()  ## v가 0.5보다 클 경우 전부 True였음을 알 수 있음
1.0

해당 개체에 처리가 취해질 확률을 구하고, 그것이 0.5보다 크면 처리를 취한다.

- 만약 적합된 v값을 알고 싶다면…

v[:5].round(3)
0    0.001
1    0.097
2    0.453
3    0.006
4    0.979
dtype: float64
predictr.predict_proba(X)[:5].round(3)
array([[0.999, 0.001],
       [0.903, 0.097],
       [0.547, 0.453],
       [0.994, 0.006],
       [0.021, 0.979]])

우측의 값과 일치하는 것을 알 수 있다.(0번째 : 0일 확률, 1번째 : 1일 확률)

3. predictor 파고들기

  • 아래와 같은 데이터를 가공해서 0~7까지는 train, 8~9까지는 test 셋으로 쓰도록 하자.
df = pd.DataFrame({'X':np.arange(20,30),'y':-np.arange(10)+1+np.random.randn(10)*0.1})
df
X y
0 20 0.992487
1 21 -0.040013
2 22 -0.984351
3 23 -2.085536
4 24 -3.023587
5 25 -4.287162
6 26 -5.085849
7 27 -6.110568
8 28 -6.798420
9 29 -8.028488
df_train = df[:8]
df_test = df[8:]

df_train_X = df_train[['X']]
df_train_y = df_train[['y']]
df_test_X = df_test[['X']]
df_test_y = df_test[['y']]
## predictor 두 개를 만들도록 리스트 컴프리헨션
predictors = [sklearn.linear_model.LinearRegression() for i in range(2)]
predictors
[LinearRegression(), LinearRegression()]
predictors[0].fit(df_train_X, df_train_y)
LinearRegression()
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.

첫 번째 predictr에 적합하면 .score, .intercept_, .coef_가 해금된다. 두 번째 predictr에는 적용되지 않는다.~(당연한 거 아님?)~

  • X, y에 들어갈 수 있는 형식
Xs = {'DataFrame(2d)': df_train_X, 
      'Seires(1d)': df_train_X.X,
      'ndarray(2d)': np.array(df_train_X),
      'ndarray(1d)': np.array(df_train_X).reshape(-1),
      'list(2d)': np.array(df_train_X).tolist(),
      'list(1d)': np.array(df_train_X).reshape(-1).tolist()}
ys = {'DataFrame(2d)': df_train_y, 
      'Seires(1d)': df_train_y.y,
      'ndarray(2d)': np.array(df_train_y),
      'ndarray(1d)': np.array(df_train_y).reshape(-1),
      'list(2d)': np.array(df_train_y).tolist(),
      'list(1d)': np.array(df_train_y).reshape(-1).tolist()}
def test(X,y):
    try: 
        predictr = sklearn.linear_model.LinearRegression()
        predictr.fit(X,y)
        return 'no error'
    except:
        return 'error'  ## 예외사항(error) 발생 시의 output

가능한 형식들을 모두 모아놨다. 그럼 이것들을 가지고 어떤 녀석이 되는 지 딕셔너리 컴프리헨션을 해보자.

{('X='+i,'y='+j): test(Xs[i],ys[j]) for i,j in itertools.product(Xs.keys(),ys.keys())}

## itertools.product() : 원소들의 데카르트 곱을 리스트로 반환.
## itertools.product('ABCD', repeat = 2)의 경우 크기가 2인 앞의 string 조합을 모두 반환
{('X=DataFrame(2d)', 'y=DataFrame(2d)'): 'no error',
 ('X=DataFrame(2d)', 'y=Seires(1d)'): 'no error',
 ('X=DataFrame(2d)', 'y=ndarray(2d)'): 'no error',
 ('X=DataFrame(2d)', 'y=ndarray(1d)'): 'no error',
 ('X=DataFrame(2d)', 'y=list(2d)'): 'no error',
 ('X=DataFrame(2d)', 'y=list(1d)'): 'no error',
 ('X=Seires(1d)', 'y=DataFrame(2d)'): 'error',
 ('X=Seires(1d)', 'y=Seires(1d)'): 'error',
 ('X=Seires(1d)', 'y=ndarray(2d)'): 'error',
 ('X=Seires(1d)', 'y=ndarray(1d)'): 'error',
 ('X=Seires(1d)', 'y=list(2d)'): 'error',
 ('X=Seires(1d)', 'y=list(1d)'): 'error',
 ('X=ndarray(2d)', 'y=DataFrame(2d)'): 'no error',
 ('X=ndarray(2d)', 'y=Seires(1d)'): 'no error',
 ('X=ndarray(2d)', 'y=ndarray(2d)'): 'no error',
 ('X=ndarray(2d)', 'y=ndarray(1d)'): 'no error',
 ('X=ndarray(2d)', 'y=list(2d)'): 'no error',
 ('X=ndarray(2d)', 'y=list(1d)'): 'no error',
 ('X=ndarray(1d)', 'y=DataFrame(2d)'): 'error',
 ('X=ndarray(1d)', 'y=Seires(1d)'): 'error',
 ('X=ndarray(1d)', 'y=ndarray(2d)'): 'error',
 ('X=ndarray(1d)', 'y=ndarray(1d)'): 'error',
 ('X=ndarray(1d)', 'y=list(2d)'): 'error',
 ('X=ndarray(1d)', 'y=list(1d)'): 'error',
 ('X=list(2d)', 'y=DataFrame(2d)'): 'no error',
 ('X=list(2d)', 'y=Seires(1d)'): 'no error',
 ('X=list(2d)', 'y=ndarray(2d)'): 'no error',
 ('X=list(2d)', 'y=ndarray(1d)'): 'no error',
 ('X=list(2d)', 'y=list(2d)'): 'no error',
 ('X=list(2d)', 'y=list(1d)'): 'no error',
 ('X=list(1d)', 'y=DataFrame(2d)'): 'error',
 ('X=list(1d)', 'y=Seires(1d)'): 'error',
 ('X=list(1d)', 'y=ndarray(2d)'): 'error',
 ('X=list(1d)', 'y=ndarray(1d)'): 'error',
 ('X=list(1d)', 'y=list(2d)'): 'error',
 ('X=list(1d)', 'y=list(1d)'): 'error'}

- 결론 | X에는 2차원 데이터만 들어올 수 있지만, y에는 1ㆍ2차원 데이터 모두가 들어올 수 있다.

- 그리고 일반적으로, X에는 2차원 데이터 배열이 imput되기를 기대하고, y에는 1차원 데이터 배열이 imput되기를 기대한다.

4. 첨언 | 데이터셋 이름 설정에 대하여

  • 설명변수와 반응변수, 테스트 셋과 트레인 셋을 부르는 변수 명을 어떻게 설정해야 할 지 나타내겠다.

X : 설명변수 & Train

y : 반응변수 & Train

XX : 설명변수 & Test

yy : 반응변수 & Test

yhat : predictr.predict(X)

yyhat : predictr.predict(XX)