Skip to content

Latest commit

 

History

History
354 lines (237 loc) · 13.5 KB

logistic-regression.md

File metadata and controls

354 lines (237 loc) · 13.5 KB

로지스틱 회귀 (Logistic Regression)

로지스틱 회귀(Logistic Regression)는 선형 회귀 방식을 응용해 분류 (Classification)에 적용한 모델입니다. 로지스틱 회귀는 간단하면서도 파라미터 수가 많지 않아 빠르게 예측할 수 있습니다. 예) 주차장에 빈자리가 있는지를 추정

로지스틱 회귀는 Sigmoid function을 활용해 타깃값에 포함될 확률을 예측합니다.

특징

퍼셉트론 (Perceptron)과 비슷한 특징을 가집니다.

  • 출력과 별도로 출력값에 해당하는 클래스에 속할 확률을 계산할 수 있습니다.
  • 온라인 학습과 배치 학습에 모두 사용 가능하다.
  • 예측 성능은 보통이지만 학습 속도가 빠르다
  • 과적합을 방지하는 규제항이 추가되어 있다.
  • 특히 출력의 확률을 계산할 수 있다는 특성을 가지고 있어서 광고로 인한 클릭 예측과 같은 분야에도 활용된다.
  • Logistic Regression은 선형 분리 가능한 대상을 분리하는 알고리즘이므로 결정경계가 직선입니다.

Logistric Regression과 Perceptron의 차이점

  • 활성화함수Sigmoid 함수 또는 Logistic sigmoid function을 씁니다.
  • 손실함수로 교차 엔트로피 오차 함수(Cross-entropy error function)을 사용합니다.
  • 규제항(Regularization term)이 추가되어서 과적합을 방지할 수 있습니다.
  • 온라인학습과 배치학습에 모두 적용할 수 있습니다.

Linear Regression Vs. Logistic Regression

Logistric Regression은 Linear Regression과 비슷한 방법으로 동작합니다. Logistic Regression은 각 특성(feature)에 대해 모델 정확도를 최대화하는 적절한 가중치(weight) 또는 계수(coefficient)를 찾습니다. 선형회귀처럼 각 항의 합의 그대로 출력하는 대신 Logistic Regression은 Sigmoid Function을 적용합니다.

Linear regression gives you a continuous output, but logistic regression provides a constant output. An example of the continuous output is house price and stock price. Example's of the discrete output is predicting whether a patient has cancer or not, predicting whether the customer will churn. Linear regression is estimated using Ordinary Least Squares (OLS) while logistic regression is estimated using Maximum Likelihood Estimation (MLE) approach.

image

Logstic Regression 예제

logistic_regression.ipynb를 아래와 같이 설명합니다.

데이터 준비

아래와 같이 pandas를 통해 fish data를 로딩합니다. 이 데이터는 캐글의 Red Wine Quality에 있습니다. 또한, fish_csv_data혼자 공부하는 머신러닝+딥러닝에서 가져왔습니다.

import pandas as pd

fish = pd.read_csv('https://bit.ly/fish_csv_data')
fish.head()

아래와 같은 fish 데이터를 로드하였습니다. 표준점수(z)는 "z = a * Weight + b * Length + c * Diagonal + d * Height + e * Width + f"와 같이 표현됩니다.

image

이때 읽어온 fish 데이터는 "Bream", "Roach", "Whitefish", "Parkki", "Perch", "Pike", "Smelt"이 있습니다.

Numpy포맷으로 변환합니다.

fish_input = fish[['Weight','Length','Diagonal','Height','Width']].to_numpy()
print(fish_input[:5])

[[242.      25.4     30.      11.52     4.02  ]
 [290.      26.3     31.2     12.48     4.3056]
 [340.      26.5     31.1     12.3778   4.6961]
 [363.      29.      33.5     12.73     4.4555]
 [430.      29.      34.      12.444    5.134 ]]
 
fish_target = fish['Species'].to_numpy()
print(fish_target)

['Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream'
 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream'
 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream'
 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Bream' 'Roach'
 'Roach' 'Roach' 'Roach' 'Roach' 'Roach' 'Roach' 'Roach' 'Roach' 'Roach'
 'Roach' 'Roach' 'Roach' 'Roach' 'Roach' 'Roach' 'Roach' 'Roach' 'Roach'

Train, Test Set을 준비합니다.

from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(
    fish_input, fish_target, random_state=42)

StandardScaler을 이용하여 정규화를 합니다.

from sklearn.preprocessing import StandardScaler

ss = StandardScaler()
ss.fit(train_input)
train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)

K 최근접 분류

Logistic regression과 비교하기 위하여 K 최근접 분류를 수행해 보았습니다.

from sklearn.neighbors import KNeighborsClassifier

kn = KNeighborsClassifier(n_neighbors=3)
kn.fit(train_scaled, train_target)

print(kn.score(train_scaled, train_target))
print(kn.score(test_scaled, test_target))

0.8907563025210085
0.85

이진 Logistric Regression

이진분류에서는 표준점수(z)을 확율로 바꾸기 위하여 Sigmoid 함수를 사용합니다.

데이터를 준비하고, 이진 로지스틱 회귀를 수행합니다.

bream_smelt_indexes = (train_target == 'Bream') | (train_target == 'Smelt')
train_bream_smelt = train_scaled[bream_smelt_indexes]
target_bream_smelt = train_target[bream_smelt_indexes]

from sklearn.linear_model import LogisticRegression

lr = LogisticRegression()
lr.fit(train_bream_smelt, target_bream_smelt)

print(kn.score(train_bream_smelt, target_bream_smelt))
0.9696969696969697

아래와 같이 분류 항목을 classes로 확인하고, 계수(coeffcient)들과 절편(intercept)을 확인할 수 있습니다. 이때, Bream, Smelt의 계산된 값을 proba로 찍어보면 0-1의 확률로 아래와 같이 계산됨을 알 수 있습니다. 표준점수(z)는 "z = a * Weight + b * Length + c * Diagonal + d * Height + e * Width + f"로 표현되므로, 이때 a, b, c, d, e는 "lr.coef_"로 적용됨을 아래와 같이 알 수 있습니다.

print(lr.classes_)
print(lr.coef_, lr.intercept_)
print(lr.predict(train_bream_smelt[:5]))
print(lr.predict_proba(train_bream_smelt[:5]))

['Bream' 'Smelt']
[[-0.4037798  -0.57620209 -0.66280298 -1.01290277 -0.73168947]] [-2.16155132]
['Bream' 'Smelt' 'Bream' 'Bream' 'Bream']
[[0.99759855 0.00240145]
 [0.02735183 0.97264817]
 [0.99486072 0.00513928]
 [0.98584202 0.01415798]
 [0.99767269 0.00232731]]

표준점수를 decision_function으로 구하고, 이를 Sigmoid 함수에 해당하는 expit()로 구하면, 상기의 확율과 같은 값임을 알수 있습니다.

decisions = lr.decision_function(train_bream_smelt[:5])
print(decisions)

from scipy.special import expit
print(expit(decisions))

[-6.02927744  3.57123907 -5.26568906 -4.24321775 -6.0607117 ]
[0.00240145 0.97264817 0.00513928 0.01415798 0.00232731]

다중 Logistic Regression

표준점수(z)을 확율로 바꾸기 위하여 Softmax 함수를 사용합니다. 다중분류를 쓰는 로지스틱 회귀에서는 C를 이용해 규제 (L2 규제를 기본적용)를 하는데, C 값이 클수록 규제가 약해집니다. 아래와 같이 다중분류로 Logistric regression을 수행합니다. K 최근접 분류보다 좋은 결과를 얻고 있습니다.

lr = LogisticRegression(C=20, max_iter=1000)
lr.fit(train_scaled, train_target)

print(lr.score(train_scaled, train_target))
print(lr.score(test_scaled, test_target))

0.9327731092436975
0.925

이때 내부에서 게산된 값을 아래와 같이 predict와 predict_proba로 확인 할 수 있습니다.

print(lr.classes_)
print(lr.coef_.shape, lr.intercept_.shape)

proba = lr.predict_proba(test_scaled[:5])

import numpy as np
print(np.round(proba, decimals=3))

['Bream' 'Parkki' 'Perch' 'Pike' 'Roach' 'Smelt' 'Whitefish']
(7, 5) (7,)
[[0.    0.014 0.841 0.    0.136 0.007 0.003]
 [0.    0.003 0.044 0.    0.007 0.946 0.   ]
 [0.    0.    0.034 0.935 0.015 0.016 0.   ]
 [0.011 0.034 0.306 0.007 0.567 0.    0.076]
 [0.    0.    0.904 0.002 0.089 0.002 0.001]]

마찬가지로 표준점수를 계산하고, Softmax로 확율을 계산하면 predict_proba로 얻어진 결과와 같습니다.

decision = lr.decision_function(test_scaled[:5])
print(np.round(decision, decimals=2))

from scipy.special import softmax

proba = softmax(decision, axis=1)
print(np.round(proba, decimals=3))

[[ -6.5    1.03   5.16  -2.73   3.34   0.33  -0.63]
 [-10.86   1.93   4.77  -2.4    2.98   7.84  -4.26]
 [ -4.34  -6.23   3.17   6.49   2.36   2.42  -3.87]
 [ -0.68   0.45   2.65  -1.19   3.26  -5.75   1.26]
 [ -6.4   -1.99   5.82  -0.11   3.5   -0.11  -0.71]]
 
[[0.    0.014 0.841 0.    0.136 0.007 0.003]
 [0.    0.003 0.044 0.    0.007 0.946 0.   ]
 [0.    0.    0.034 0.935 0.015 0.016 0.   ]
 [0.011 0.034 0.306 0.007 0.567 0.    0.076]
 [0.    0.    0.904 0.002 0.089 0.002 0.001]]

SGDClassifier

확률적 경사하강법(SGD, Stochastic Gradient Descent)을 이용하여 선형모델로 Linear classifiers을 구현할 수 있습니다. SGDClassifier.ipynb에 대해 설명합니다.

from sklearn.model_selection import cross_validate
from sklearn.linear_model import SGDClassifier

sc = SGDClassifier(loss='log', max_iter=5, random_state=42)

scores = cross_validate(sc, train_scaled, train_target, n_jobs=-1)

import numpy as np
print(np.mean(scores['test_score']))

상기의 sample 사용시 3 class가 n_splits보다 작으므로 결과는 좋지 않습니다.

/home/ec2-user/anaconda3/envs/python3/lib/python3.8/site-packages/sklearn/model_selection/_split.py:676: UserWarning: The least populated class in y has only 3 members, which is less than n_splits=5.
  warnings.warn(

0.7144927536231884

Low Accuracy Case

Logistic Regression 적용시에 정확도가 떨어지는 케이스에 대해 설명합니다.

  1. 데이터를 준비합니다.

pandas로 데이터를 로드합니다.

import pandas as pd

wine = pd.read_csv('https://bit.ly/wine_csv_data')

wine.head()

이때 데이터의 형태는 아래와 같이 "alcohol", "sugar", "pH"와 같은 항목을 가지고 있습니다.

image

여기의 데이터의 형태는 아래와 같습니다.

wine.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6497 entries, 0 to 6496
Data columns (total 4 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   alcohol  6497 non-null   float64
 1   sugar    6497 non-null   float64
 2   pH       6497 non-null   float64
 3   class    6497 non-null   float64
dtypes: float64(4)
memory usage: 203.2 KB

Train, Test Set을 아래와 같이 준비합니다.

data = wine[['alcohol', 'sugar', 'pH']].to_numpy()
target = wine['class'].to_numpy()

from sklearn.model_selection import train_test_split

train_input, test_input, train_target, test_target = train_test_split(
    data, target, test_size=0.2, random_state=42)

print(train_input.shape, test_input.shape)
(5197, 3) (1300, 3)

아래와 같이 정규화를 합니다.

from sklearn.preprocessing import StandardScaler

ss = StandardScaler()
ss.fit(train_input)

train_scaled = ss.transform(train_input)
test_scaled = ss.transform(test_input)
  1. Logistic Regression으로 결정계수를 구합니다.

아래와 같이 과소적합(Underfitting)의 결과를 얻습니다.

from sklearn.linear_model import LogisticRegression

lr = LogisticRegression()
lr.fit(train_scaled, train_target)

print(lr.score(train_scaled, train_target))
print(lr.score(test_scaled, test_target))

0.7808350971714451
0.7776923076923077

이때, "alcohol", "sugar", "pH"에 대한 기울기는 아래와 같습니다.

print(lr.coef_, lr.intercept_)

[[ 0.51270274  1.6733911  -0.68767781]] [1.81777902]

Reference

혼자 공부하는 머신러닝+딥러닝

sklearn.linear_model.SGDClassifier

[Machine Learning at Work - 한빛미디어]

Using Machine Learning to Predict Parking Difficulty

Understanding Logistic Regression in Python Tutorial