본문 바로가기

AI/ML PyTorch Scikit-Learn

Ch3. Logistic Regression & Sigmoid

<Logistic Regression with Scikit-learn>

로지스틱 회귀(Logistic Regression)란?

=> Adaline model에서 Activation function을 sigmoid 함수로 쓴 회귀 모델

 

Logistic Regression

 

 

 

sigmoid 함수가 어떻게 나왔는지 알아보자

Odds(승산) : 실패 비율 대비 성공 비율

성공확률을 $ p $라 하면, $$ Odds:=\frac{p}{1-p} $$

 

Logit : logistic + probit

$$ Logit(L):=ln(Odds)=ln \frac{p}{1-p} $$

 

위의 Logit에 대해 생각해보자

어떤 input $ x $가 주어지면 그에 대한 확률 $ p $는 고정적이다

p가 최대가 되는 것이 목적이므로 Logit에 따른 p의 함수로 변환해야 한다

 

$$ L= ln \frac{p}{1-p} $$

$$ e^L=\frac{p}{1-p}, \;\frac{1}{e^L}=\frac{1-p}{p}=-1+\frac{1}{p} $$

$$ \frac{1}{e^L}+1=\frac{e^L+1}{e^L}=\frac{1}{p} $$

$$ \therefore p=\frac{e^L}{e^L+1}=\frac{1}{1+e^{-L}} $$

 

이를 선형 회귀에 대입하면,

$$ L=\sum^n_{i=1}(w^T_ix_i)+b=z $$

$$ a=\sigma(z)=\frac{1}{1+e^{-z}} $$

 

 

 

<Chain Rule>

특정 방식을 사용해서 Loss(L)를 a를 이용하여 계산한다

따라서 다음과 같은 미분법이 가능하다

$$ \frac{\partial L}{\partial w_j}=\frac{\partial L}{\partial a}\frac{\partial a}{\partial z}\frac{\partial z}{\partial w_j} $$

 

 

Logistic Regression에서는 Binary Cross-Entropy Loss를 Loss function으로 사용한다

$$ BCE:=-(ylog(a)+(1-y)log(1-a)) $$

 

 

따라서 각각을 미분해보면 다음과 같다,

$$ \frac{\partial L}{\partial a}=\frac{a-y}{a(1-a)} $$

$$ \frac{\partial a}{\partial z}=a(1-a) $$

$$ \frac{\partial z}{\partial w_j}=x_j $$

 

$$ \frac{\partial L}{\partial w_j}=\frac{a-y}{a(1-a)}\times a(1-a)\times x_j $$

$$ =x_j(a-y) $$

 

 

 

<Logistic Regression Gradient Descent>

class LogisticRegressionGD:
    def __init__(self, eta=0.01, n_iter=50, random_state=1):
        self.eta = eta
        self.n_iter = n_iter
        self.random_state = random_state

    def fit(self, X, y):
        rgen = np.random.RandomState(self.random_state)
        self.w_ = rgen.normal(loc=0.0, scale=0.01, size=X.shape[1])
        self.b_ = np.float_(0.)
        self.losses_ = []

        for i in range(self.n_iter):
            net_input = self.net_input(X)
            output = self.activation(net_input)
            errors = (y - output)
            self.w_ += self.eta * X.T.dot(errors) / X.shape[0]
            self.b_ += self.eta * errors.mean()
            loss = -y.dot(np.log(output)) - ((1 - y).dot(np.log(1 - output))) / X.shape[0]
            self.losses_.append(loss)
        return self

    def net_input(self, X):
        return np.dot(X, self.w_) + self.b_

    def activation(self, z):
        return 1. / (1. + np.exp(-np.clip(z, -250, 250)))

    def predict(self, X):
        return np.where(self.activation(self.net_input(X)) >= 0.5, 1, 0)

 

 

 

아래와 같이 X.shape[0] 으로 나누는 이유는 뭘까

self.w_ += self.eta * X.T.dot(errors) / X.shape[0]
self.b_ += self.eta * errors.mean()
loss = -y.dot(np.log(output)) - ((1 - y).dot(np.log(1 - output))) / X.shape[0]

 

Gradient Descent를 하는 과정에서 하나의 데이터로 업데이트하면 편향될 가능성이 크다

따라서 평균을 취해주는 것이다

 

 

다음과 같이 결과가 나온다

Logistic Regression with Iris dataset