4장 모델 훈련

주요 내용

참고: 핵심 설명과 코드는 🔑로 표시되었으며 굳이 알아둘 필요가 없는 코드는 ✋로 표시되었다.

기본 설정

4.1 선형 회귀 (p. 158)

아래 코드는 선형 회귀 학습 과정을 설명하기 위해 사용되는 하나의 특성을 사용하는 간단한 훈련 데이터를 생성한다.

참고: 정규분포를 따르는 부동소수점 100개를 무작위로 생성하여 잡음으로 사용하였다.

특성 x1과 레이블 y의 관계를 그리면 다음과 같다. 기본적으로 y = 4 + 3 * x 의 선형관계를 갖지만 잡음으로 인해 데이터가 퍼져 있다.

4.1.1 정규 방정식 (p. 160)

일반적으로 $n$개의 특성을 갖는 임의의 크기의 데이터셋 X가 주어졌을 때, $(\mathbf{X}^T \mathbf{X})^{-1}$의 역행렬이 존재하고 실제로 일정 시간 내에 계산이 가능하다면 최적의 파라미터 조합 $\boldsymbol{\hat\theta}$ 를 아래 정규 방정식으로 직접 구할 수 있다.

$$ \hat{\boldsymbol{\theta}} = (\mathbf{X}^T \mathbf{X})^{-1} \mathbf{X}^T \mathbf{y} $$

아래 코드는 위 정규 방정식을 위에서 생성한 훈련 세트에 대해 계산한다.

주의사항: 1 벡터를 추가하는 일은 여기서만 설명을 위해 사용된다. 사이킷런의 모델을 사용할 때는 모델이 알아서 처리해준다.

계산된 $\boldsymbol{\hat\theta} = [\theta_0, \theta_1]$ 은 다음과 같다.

계산된 $\boldsymbol{\hat\theta}$ 를 이용하여 x1=0x1=2일 때의 예측값 $\hat y$는 단순한 행렬 곱셈에 불과하다.

$$\hat{y} = \mathbf{X}\, \boldsymbol{\hat{\theta}}$$

예측을 위해서도 먼저 x0=1 열을 추가해야 한다.

주의사항: $\mathbf{X}$ 와 $\hat\theta$ 의 순서에 너무 민감해할 필요는 없다. 설명하는 방식에 따라 순서가 바뀌거나 전치행렬이 사용될 뿐이며, 결과는 모두 동일하다.

행렬 곱셈에 의한 예측값은 다음과 같다.

$\theta_0$ 을 절편으로, $\theta_1$ 을 기울기로 하는 직선은 아래와 같다.

사이킷런의 LinearRegression 모델

LinearRegression 모델은 특잇값 분해(SVD) 방식을 이용하여 계산된 무어-펜로즈의 유사 역행렬 $\mathbf{X}^+$를 이용하여 파라미터 $\hat\theta$를 계산한다.

$$ \hat{\boldsymbol{\theta}} = \mathbf{X}^+ \mathbf{y} $$

훈련된 모델의 intercept_coef_ 속성에 절편 $\theta_0$ 과 기울기 $\theta_1$ 이 저장된다.

x1=0x1=2에 대한 예측값과 앞서 수동으로 계산된 값과 동일하다.

참고: LinearRegression 클래스는 scipy.linalg.lstsq() 함수를 사용하므로 이 함수를 직접 사용할 수도 있다.

numpy.linalg.lstsq() 함수도 동일하게 작동한다.

✋ 무어-펜로즈의 유사 역행렬 $\mathbf{X}^{+}$ 는 np.linalg.pinv()을 사용해서 구해지며, 이를 아래 수식처럼 이용하면 동일한 최적 파라미터를 구한다.

$$\boldsymbol{\hat{\theta}} = \mathbf{X}^{+}\hat{y}$$

4.2 경사 하강법 (p. 164)

앞선 언급한 무어-펜로즈 유사 역행렬을 구하는 알고리즘의 계산 복잡도는 $O(n^2)$이다. 즉 특성 수가 늘어날 수록 계산 시간은 제곱 지수승으로 늘어난다. 따라서 많은 수의 특성을 사용하는 데이터에 대해서는 SVD 방식을 사용하는 사이킷런의 LinearRegrssion 모델을 사용할 수 없으며 배치 경사 하강법에 기반한 모델을 대신 사용해야 한다.

4.2.1 배치 경사 하강법 (p.168)

임의로 지정된 파라미터 $\boldsymbol{\hat\theta}$로 시작한 후에 비용 함수(MSE)가 작아지는 방향으로 조금씩 파라미터를 조정한다.

$$ \begin{align*} \mathrm{MSE}(\theta) & = \mathrm{MSE}(\mathbf X, h_\theta) \\ & = \frac 1 m \sum_{i=1}^{m} \big(\hat y^{(i)} - y^{(i)}\big)^2 \\[2ex] \hat y^{(i)} & = \theta^{T}\, \mathbf{x}^{(i)} \\ & = \theta_0 + \theta_1\, \mathbf{x}_1^{(i)} + \cdots + \theta_n\, \mathbf{x}_n^{(i)} \end{align*} $$

파라미터를 조정할 때 사용하는 기준은 비용 함수의 그레이디언트 벡터의 방향과 크기이다.

$$ \nabla_\theta \text{MSE}(\boldsymbol{\theta}) = \frac{2}{m}\, \mathbf{X}^T (\mathbf{X} \boldsymbol{\theta} - \mathbf{y}) $$

파라미터 조정은 아래 방식으로 이루어진다. 단, $\eta$는 학습률을 가리킨다.

$$ \boldsymbol{\theta}^{(\text{next step})} = \boldsymbol{\theta}^{(\text{previous step})} - \eta\cdot \nabla_\theta \text{MSE}(\boldsymbol{\theta}) $$

아래 코드는 $\theta_0$과 $\theta_1$을 무작위로 지정한 다음에 앞서 언급한 학습률 조정 과정을 수동으로 1,000번 진행한 결과를 보여준다.

앞서 얻은 결과와 동일한 최적의 파라미터가 계산된다.

학습률과 모델 학습

✋ 아래 코드는 학습률에 따라 파라미터 학습과정이 어떻게 달라질 수 있는지를 보여주기 위해 세 개의 그래프를 그려준다.

파랑 직선은 초반 10번의 모델 학습과정을 보여준다. 학습률(eta)에 따라 학습 과정이 많이 다를 수 있음을 잘 보여준다.

참고: 사이킷런은 배치 경사 하강법을 지원하는 모델을 제공하지 않는다.

4.2.2 확률적 경사 하강법 (p.172)

아래 코드는 확률적 경사 하강법의 작동 과정을 보여주는 코드이다.

모델이 수렴값 근처에서 진동하는 것을 확인할 수 있다.

그렇지만 50번만의 에포크 만으로 상당히 좋은 파라미터를 찾는다.

사이킷런의 SGDRegressor 모델

SGDRegressor 모델은 확률적 경사 하강법을 사용하며 따라서 매우 빠르게 학습한다.

4.2.3 미니배치 경사 하강법 (p.175)

아래 코드는 크기 20인 배치를 활용하는 미니배치 경사 하강법을 구현한다.

학습 스케줄이 사용된 미니배치 수 t에 의존한다.

배치/확률적/미니배치 경사 하강법 파라미터 학습과정 비교

아래 코드는 아래 세 개의 변수에 저정된 파라미터 값들을 도표로 보여준다.

4.3 다항 회귀 (p.177)

아래 코드는 무작위로 100개의 훈련 데이터를 생성한다. 한 개의 특성만 사용하며 타깃은 아래 2차 다항식으로 생성한 다음에 잡음을 추가한다.

$$y = 0.5\cdot x_1^2 + 2$$

주의사항: 여기서 2차 함수를 이용하여 타깃을 계산하였지만 이는 단순히 예제를 위해서이다. 일반적으로는 특성과 타깃 사이에 어떤 연관성이 있는지 미리 알 수 없다는 것을 염두해야 한다.

훈련 세트의 산점도는 다음과 같다.

선형 회귀 모델을 바로 적용하면 좋은 모델을 얻을 수 없다. 따라서 다항 회귀 기법을 사용하여 보다 좋은 모델을 훈련시킬 수 있는지 확인하려 한다.

아래 코드는 2차 다항 회귀 변환기 이용하여 하나의 특성 x1을 갖는 데이터를 x1x1**2두 개의 특성을 갖는 데이터로 변환하는 것을 보여준다.

첫째 훈련 데이터는 원래 특성 $x_1$만 갖는다.

반면에 변환된 첫째 훈련 데이터는 두 개의 특성 $x_1, x_1^2$ 를 갖는다.

변환된 데이터를 이용하여 선형 회귀 모델을 학습시킨다.

학습된 모델의 절편과 기울기는 다음과 같다.

참고: 2차 다항 회귀 모델을 적용하기 위해 변환된 데이터는 절편에 대한 파라미터 $\theta_0$와 두 개의 특성 $x_1$과 $x_1^2$ 각각에 대한 파라미터 $\theta_1$과 $\theta_2$를 갖는다.

아래 코드는 새로운 샘플 100개에 대해 앞서 훈련시킨 다항 회귀 모델을 이용하여 예측을 수행한 후 예측 결과를 그래프로 그린다.

4.4 학습 곡선 (p. 179)

아래 코드는 아래 세 모델을 사용하여 얻어진 예측값을 비교한다.

학습 곡선

훈련 세트와 검증 세트에 대한 모델 성능의 변화를 추적하는 곡선이 학습 곡선이다. 모델 성능의 변화는 훈련 세트의 크기를 1부터 시작해서 최대값까지 바꾸면서 훈련된 모델의 성능을 훈련 세트와 검증 세트 각각에 대해 측정하는 방식으로 확인한다.

아래 코드는 훈련 세트와 검증 세트에 대한 평균 제곱근 오차(RMSE)의 변화를 추적하는 그래프를 그린다.

과소 적합 모델의 학습 곡선 특징

2차 다항식으로 생성된 데이터셋에 대한 선형 회귀 모델의 학습 곡선은 다음과 같으며, 전형적인 과소 적합의 양태를 잘 보여준다.

과대 적합 모델의 학습 곡선 특징

2차 다항식으로 생성된 데이터셋에 대해 10차 다항 회귀를 적용한 선형 회귀 모델의 학습 곡선은 다음과 같으며, 전형적인 과대 적합의 양태를 잘 보여준다.

4.5 규제를 사용하는 선형 모델 (p. 184)

모델의 학습 과정에 규제를 가하는 방식으로 과대 적합을 감소시킬 수 있다. 다항 회귀 모델의 경우 다항식의 차수를 감소시키는 방식으로 규제할 수 있다.

선형 회귀 모델의 규제는 학습되는 일반적으로 파라미터에 제한을 두는 방식으로 이루어지며, 여기서는 다른 규제 방식이 적용된 세 모델을 소개한다.

또한 경사하강법과 같은 반복적인 학습 알고리즘의 검증 오류가 최솟값에 다달은 순간에 훈련을 중지시키는 조기 종료 기법도 소개한다.

4.5.1 릿지 회귀 (p. 184)

아래 코드는 연습을 위해 1차 함수를 이용하여 20개의 훈련 데이터를 생성한다.

릿지 회귀의 비용 함수

$$ \begin{align*} J(\boldsymbol{\theta}) & = \text{MSE}(\boldsymbol{\theta}) + \dfrac{\alpha}{2}\sum\limits_{i=1}^{n}{\theta_i}^2 \\ & = \text{MSE}(\boldsymbol{\theta}) + \dfrac{\alpha}{2}\left (\theta_1^2 + \cdots + \theta_n^2 \right ) \end{align*} $$

Ridge 모델

릿지 규제를 지원한다.

아래 코드는 다양한 alpha 값의 영향력를 그래프로 보여준다. 오른편 그래프는 10차 다항 회귀가 적용된 경우 3 개를 보여준다. alpha를 크게 할 수록 예측값의 그래프가 직선에 가까워짐을 볼 수 있다.

SGDRegressor 와 릿지 회귀

SGDRegressor 모델의 penalty 하이퍼파라미터를 이용하면 릿지 회귀와 동일하게 작동한다.

$$\theta_1^2 + \cdots + \theta_n^2$$

참고: max_iter=1000tol=1e-3이 기본 값으로 사용된다.

4.5.2 라쏘 회귀 (p.187)

라쏘 회귀의 비용 함수

$$ \begin{align*} J(\boldsymbol{\theta}) & = \text{MSE}(\boldsymbol{\theta}) + \alpha \sum\limits_{i=1}^{n}\left| \theta_i \right| \\ & = \text{MSE}(\boldsymbol{\theta}) + \alpha \left (\, \left| \theta_1 \right| + \cdots + \left| \theta_n \right|\, \right ) \end{align*} $$

Lasso 모델

라쏘 규제를 지원한다.

참고: 릿지 회귀에 사용된 alpha 값보다 작은 값을 사용하였다. 이유는 릿지 회귀는 파라미터를 제곱해서 사용하는 반면에 라쏘 회귀에서는 파라미터의 절댓값을 사용하기 때문에 alpha가 작더라도 규제에 큰 영향을 주기 때문이다.

아래 코드는 라쏘 규제에 사용되는 다양한 alpha 값의 영향을 비교해준다. 오른편 그래프는 10차 다항회귀에 적용된 라쏘 회귀 모델의 예측값을 보여준다. 10차 다항 회귀라 하더라도 규제를 세개 주면 마차 3차 다항회귀를 준 것처럼 작동하게 함을 잘 보여준다.

SGDRegressor 와 라쏘 회귀

SGDRegressor 모델의 penalty 하이퍼파라미터를 이용하면 릿지 회귀와 동일하게 작동한다.

$$\sum_{i=1}^n \left | \theta_i \right | = \left | \theta_1 \right | + \cdots + \left | \theta_n \right |$$

참고: max_iter=1000tol=1e-3이 기본 값으로 사용된다.

✋ 라쏘 회귀와 릿지 회귀 비교 (그림 4-19, 책 189쪽)

아래 코드는 라쏘 회귀와 릿지 회귀 모델 학습 과정 동안 경사 하강법이 적용될 때 파라미터가 움직이는 특성을 비교해서 보여주기 위한 코드이다.

핵심은 다음과 같으며 보다 자세한 설명은 책 188~189쪽을 참조하기 바란다.

4.5.3 엘라스틱 넷 (p. 190)

릿지 회귀와 라쏘 회귀의 절충 모델을 지원한다.

엘라스틱넷 비용 함수

$$ J(\boldsymbol{\theta}) = \text{MSE}(\boldsymbol{\theta}) + r \alpha \sum\limits_{i=1}^{n}\left| \theta_i \right| + \dfrac{1 - r}{2} \alpha \sum\limits_{i=1}^{n}{{\theta_i}^2} $$

ElasticNet 모델

엘라스틱넷을 지원한다.

SGDRegressor 와 엘라스틱넷

SGDRegressor 모델의 penalty 하이퍼파라미터를 이용하면 릿지 회귀와 동일하게 작동한다.

참고: max_iter=1000tol=1e-3이 기본 값으로 사용된다.

4.5.4 조기 종료 (p. 191)

아래 코드는 조기 종료 설명을 위해 사용될 100개의 데이터를 5:5 비율로 훈련/검증 세트로 분할한다.

조기 종료 예제:

아래 코드는 에포크를 늘려 가면서 훈련 세트와 검증 세트의 성능(RMSE)을 측정해서 기록한다. 에포크는 최대 500번이다. 이후에 훈련/검증 세트의 기록을 각각 빨강 점선과 파랑 실선 그래프로 그린다.

가장 좋은 성능의 모델이 훈련된 에포크 수와 모델은 다음과 같음.

4.6 로지스틱 회귀 (p.193)

파라미터와 특성값들의 선형합에 시그모이드 함수를 적용하여 양성 확률을 추정하는 이진 분류 모델이다.

4.6.1 확률 추정 (p. 193)

확률 추정은 아래 식을 통해 이루어진다.

$$ \hat p = h_\theta(x) = \sigma(\theta^T\, \mathbf{x}) $$

$\sigma()$ 는 시그모이드 함수를 가리킨다.

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

샘플 $\mathbf{x}$에 대한 로지스틱 회귀 예측값은 다음과 같다.

$$ \hat y = \begin{cases} 0 & \hat p < 0.5 \text{ 일때}\\ 1 & \hat p \ge 0.5 \text{ 일때} \end{cases} $$

✋ 아래 코드는 시그모이드 함수의 그래프를 그려준다.

4.6.2 훈련과 비용 함수 (p. 195)

하나의 훈련 샘플에 대한 비용은 다음과 같다.

$$ c(\boldsymbol{\theta}) = \begin{cases} -\log(\hat{p}) & \text{if } y = 1, \\ -\log(1 - \hat{p}) & \text{if } y = 0. \end{cases} $$

예측을 잘못하면 비용이 매우 커진다.

로지스틱 회귀 비용 함수(로그 손실)는 하나의 훈련 샘플에 대한 비용의 평균값이다.

$$ J(\boldsymbol{\theta}) = -\dfrac{1}{m} \sum\limits_{i=1}^{m}{\left[ y^{(i)} log\left(\hat{p}^{(i)}\right) + (1 - y^{(i)}) log\left(1 - \hat{p}^{(i)}\right)\right]} $$

로지스틱 비용 함수의 편도 함수는 다음과 같다. 선형 회귀 비용 함수의 편도 함수와 매우 비슷하다. 단지 시그모이드 함수가 사용되었을 뿐이다.

$$ \dfrac{\partial}{\partial \theta_j} \text{J}(\boldsymbol{\theta}) = \dfrac{1}{m}\sum\limits_{i=1}^{m}\left(\mathbf{\sigma(\boldsymbol{\theta}}^T \mathbf{x}^{(i)}) - y^{(i)}\right)\, x_j^{(i)} $$

참고: 편도 함수가 선형 회귀의 경우와 매우 비슷한 것에 대한 확률론적 근거가 있다. 이에 대한 자세한 설명은 앤드류 응(Andrew Ng) 교수의 Stanford CS229를 참조할 수 있다.

4.6.3 결정 경계 (p. 196)

붓꽃(iris) 데이터셋을 이용하여 로지스틱 회귀를 이용한 분류에 활용되는 결정 경계를 설명한다. 아래 코드는 사이킷런에서 제공하는 붓꽃 데이터셋을 번치(Bunch) 자료형으로 불러온다.

참고: Bunch 자료형은 기본적으로 dict 자료형과 동일하다. 차이점은 인덱싱을 속성(attribute)처럼 처리할 수 있다는 것 뿐이다.

데이터셋에 사용된 키는 다음과 같다.

'data' 키에 해당하는 값은 (150, 4) 모양의 넘파이 어레이다.

아래 코드는 처음 5개의 샘플 데이터를 보여준다.

참고: 원래 iris['data'][:5]로 값을 확인하지만 Bunch 자료형이기에 아래와 같이 클래스의 속성 형식으로 확인할 수도 있다. 인용부호를 사용하지 않음에 주의해야 한다.

'target' 키는 150개 붓꽃에 대한 품종을 1차원 어레이로 담고 있다.

언급된 순서대로 50개씩 세 품종의 타깃이 저장되어 있으며, 처음 5개의 샘플은 세토사 품종임을 아래처럼 확인할 수 있다.

50번 인덱스부터는 버시컬러 품종이다.

100번 인덱스부터는 버지니카 품종이다.

이런 정보가 'DESCR' 속성에 저장되어 있다.

이진 분류: 로지스틱 회귀

로지스틱 회귀는 기본적으로 이진 분류 모델이며, 일대다(OvR) 전략을 이용하여 다중 클래스 분류도 지원한다. 하지만 다중 클래스를 기본으로 지원하는 소프트맥스 회귀를 잠시 뒤에 소개한다.

예제 1

아래 코드는 이진 분류 설명을 위해 꽃잎 너비(petal width) 특성을 이용하여 버지니카 품종 여부를 판정하는 데에 사용되는 데이터셋을 지정한다.

이제 사이킷런의 LogisticRegression 모델을 훈련시킨다.

참고: 사이킷런 0.22 버전의 기본값인 solver="lbfgs"로 지정

1000개의 샘플을 새로 생성하여 훈련된 로지스틱 회귀 모델의 예측 결과를 도표로 확인하면 다음과 같다.

✋ 아래 코드는 보다 많은 정보를 도표와 함께 전달한다.

양성 확률이 0.5가 되는 꽃잎 너비의 길이, 즉 결정 경계는 1.66이다.

실제로 1.66보다 크면 양성, 작으면 음성으로 판정한다.

예제 2

아래 코드는 이진 분류 설명을 위해 꽃잎 길이와 너비 두 개의 특성을 이용하여 버지니카 품종 여부를 판정하는 모델을 훈련한다.

훈련 결과를 도표로 그리면 다음과 같다.

소프트맥스 회귀 (p. 199)

소프트맥스 회귀는 다중 클래스 분류를 지원한다. 샘플 $\mathbf{x}$가 주어지면 먼저 각 클래스별로 속할 점수 $s_k(\mathbf{x})$를 계산한 후에 아래 소프트맥스 함수를 이용하여 각 클래스에 속할 확률을 계산한다.

$$ \hat{p}_k = \sigma\left(\mathbf{s}(\mathbf{x})\right)_k = \dfrac{\exp\left(s_k(\mathbf{x})\right)}{\sum\limits_{j=1}^{K}{\exp\left(s_j(\mathbf{x})\right)}} $$

예측값

소프트맥스 회귀 분류기의 예측값은 추정 확률값이 가장 큰 클래스 $k$이다.

$$ \hat y = \mathrm{argmax}_k s_k(\mathbf x) $$

비용함수와 그레이디언트 벡터

소프트맥스 회귀의 (크로스 엔트로피) 비용 함수는 다음과 같다.

$$ J(\boldsymbol{\Theta}) = - \dfrac{1}{m}\sum\limits_{i=1}^{m}\sum\limits_{k=1}^{K}{y_k^{(i)}\log\left(\hat{p}_k^{(i)}\right)} $$

클래스 $k$에 대한 (크로스 엔트로피) 비용함수의 그레이디언트 벡터는 다음과 같다.

$$ \nabla_{\boldsymbol{\theta}^{(k)}} \, J(\boldsymbol{\Theta}) = \dfrac{1}{m} \sum\limits_{i=1}^{m}{ \left ( \hat{p}^{(i)}_k - y_k^{(i)} \right ) \mathbf{x}^{(i)}} $$

예제 3

꽃잎의 길이와 너비 특성을 이용하여 붓꽃 샘플을 세 개의 품종으로 분류한다. LogisticRegression 모델을 이용하여 소프트맥스 회귀를 실행할 수 있다.

훈련 결과를 도표로 그리면 다음과 같다.

꽃잎의 길이 5, 꽃잎의 너비 2인 붓꽃의 품종은 버지니카(2)이다.

품종별 추정 확률은 다음과 같다.

연습문제 해답

1번부터 11번 문제에 대한 설명과 정답은 책의 부록 A를 참조하라.

문제 12

조기 종료를 사용한 배치 경사 하강법으로 소프트맥스 회귀를 구현하라. 단, 사이킷런을 전혀 사용하지 않아야 한다. 이런 경험을 통해 머신러닝 모델의 작동원리를 보다 깊이 이해할 수 있다.

단계 1: 데이터 준비

붓꽃 데이터셋의 꽃잎 길이(petal length)와 꽃잎 너비(petal width) 특성만 이용한다.

모든 샘플에 편향을 추가한다. 이유는 아래 수식을 행렬 연산으로 보다 간단하게 처리하기 위해 0번 특성값 $x_0$이 항상 1이라고 가정하기 때문이다.

$$ \theta_0\cdot 1 + \theta_1\cdot x_1 + \cdots + \theta_n\cdot x_n = \theta_0\cdot x_0 + \theta_1\cdot x_1 + \cdots + \theta_n\cdot x_n $$

결과를 일정하게 유지하기 위해 랜덤 시드를 지정합니다:

단계 2: 데이터셋 분할

데이터셋을 훈련, 검증, 테스트 용도로 6대 2대 2의 비율로 무작위로 분할한다.

아래 코드는 사이킷런의 train_test_split() 함수를 사용하지 않고 수동으로 무작위 분할하는 방법을 보여준다. 먼저 각 세트의 크기를 결정한다.

np.random.permutation() 함수를 이용하여 인덱스를 무작위로 섞는다.

인덱스가 무작위로 섞였기 때문에 무작위로 분할하는 효과를 얻는다. 방법은 섞인 인덱스를 이용하여 지정된 6:2:2의 비율로 훈련, 검증, 테스트 세트로 분할하는 것이다.

단계 3: 타깃 변환

타깃은 0, 1, 2로 설정되어 있다. 차례대로 세토사, 버시컬러, 버지니카 품종을 가리킨다. 훈련 세트의 첫 5개 샘플의 품종은 다음과 같다.

학습을 위해 타깃을 원-핫 벡터로 변환해야 한다. 이유는 소프트맥스 회귀는 샘플이 주어지면 각 클래스별로 속할 확률을 구하고 구해진 결과를 실제 확률과 함께 이용하여 비용함수를 계산하기 때문이다.

붓꽃 데이터의 경우 세 개의 품종 클래스별로 속할 확률을 계산해야 하기 때문에 품종을 0, 1, 2 등의 하나의 숫자로 두기 보다는 해당 클래스는 1, 나머지는 0인 확률값으로 이루어진 어레이로 다루어야 소프트맥스 회귀가 계산한 클래스별 확률과 연결된다.

아래 함수 to_one_hot() 함수는 길이가 m이면서 0, 1, 2로 이루어진 1차원 어레이가 입력되면 (m, 3) 모양의 원-핫 벡터를 반환한다.

샘플 5개에 대해 잘 작동하는 것을 확인할 수 있다.

이제 훈련/검증/테스트 세트의 타깃을 모두 원-핫 벡터로 변환한다.

단계 4: 소프트맥스 함수 구현

아래처럼 정의된 소프트맥스 함수를 파이썬 함수로 구현한다.

$$ \hat p_k = \sigma\left(\mathbf{s}(\mathbf{x})\right)_k = \dfrac{\exp\left(s_k(\mathbf{x})\right)}{\sum\limits_{j=1}^{K}{\exp\left(s_j(\mathbf{x})\right)}} $$ $$ s_k(\mathbf{x}) = (\theta^{(k)})^T\, \mathbf{x} $$

$\theta^{(k)}$ 벡터의 길이는 특성 수와 동일하다. 예를 들어, 붓꽃 데이터셋의 경우 3 개의 품종 $k \in (0, 1, 2)$ 각각에 대해 길이가 3(세 개의 특성: 편향, 꽃잎 길이, 꽃잎 너비)인 벡터 $(\theta^{(k)}_0, \theta^{(k)}_1, \theta^{(k)}_2)$가 파라미터로 주어져야 한다. 따라서 총 9개의 파라미터가 (3, 3)-행렬 모양의 2차원 어레이로 훈련 과정에서 학습된다.

$$ \Theta = \begin{bmatrix} \theta_0^{(0)} & \theta_0^{(1)} & \theta_0^{(2)}\\ \theta_1^{(0)} & \theta_1^{(1)} & \theta_1^{(2)}\\ \theta_2^{(0)} & \theta_2^{(1)} & \theta_2^{(2)} \end{bmatrix} $$

아래에서 정의되는 softmax() 함수는 $s_k(\mathbf{x}) = (\theta^{(k)})^T\, \mathbf{x}$로 계산되는 각 샘플에 대한 클래스별 점수로 이루어진 (샘플 수, 클래스 수)-행렬 모양의 인자를 기대한다.

예를 들어, 붓꽃 훈련 세트의 크기가 90개이므로 아래 훈련 과정에서 사용되는 softmax() 함수의 인자는 다음과 같은 모양의 2차원 넘파이 어레이다. 단, $\mathbf{x}^{(i)}$는 $i$번째 샘플을 가리킨다.

$$ \mathbf{X}_{\textit{train}}\, \Theta = \begin{bmatrix} s_0(\mathbf{x}^{(1)}) & s_1(\mathbf{x}^{(1)}) & s_2(\mathbf{x}^{(1)})\\ s_0(\mathbf{x}^{(2)}) & s_1(\mathbf{x}^{(2)}) & s_2(\mathbf{x}^{(2)})\\ & \vdots & \\ s_0(\mathbf{x}^{(90)}) & s_1(\mathbf{x}^{(90)}) & s_2(\mathbf{x}^{(90)}) \end{bmatrix} $$

반면에 반환되는 값은 각 샘플에 대한 클래스별 확률로 구성된 아래 모양의 2차원 넘파이 어레이다. $\hat p_k^{(n)}$은 $n$번째 샘플이 클래스 $k$에 속할 예측 확률을 가리킨다.

$$ \hat P = \begin{bmatrix} \hat p_0^{(1)} & \hat p_1^{(1)} & \hat p_2^{(1)}\\ \hat p_0^{(2)} & \hat p_1^{(2)} & \hat p_2^{(2)}\\ & \vdots & \\ \hat p_0^{(90)} & \hat p_1^{(90)} & \hat p_2^{(90)} \end{bmatrix} $$

단계 5: 경사하강법 활용 훈련

경사하강법을 구현하기 위해 아래 비용함수와 비용함수의 그레이디언트를 파이썬으로 구현할 수 있어야 한다.

$$ J(\mathbf{\Theta}) = - \dfrac{1}{m}\sum\limits_{i=1}^{m}\sum\limits_{k=1}^{K}{y_k^{(i)}\log\left(\hat{p}_k^{(i)}\right)} $$ $$ \nabla_{\mathbf{\theta}^{(k)}} \, J(\mathbf{\Theta}) = \dfrac{1}{m} \sum\limits_{i=1}^{m}{ \left ( \hat{p}^{(i)}_k - y_k^{(i)} \right ) \mathbf{x}^{(i)}} $$

주의사항: 수학적으로 $\hat{p}_k^{(i)} = 0$이면 $\log\left(\hat{p}_k^{(i)}\right)$는 정의되지 않는다. NaN 값을 피하기 위해 $\hat{p}_k^{(i)}$에 작은 값 epsilon 추가한다. 여기서는 1e-7을 사용한다. 사실 너무 작은 epsilon을 0에 더하면 컴퓨터가 그래도 0으로 취급할 수 있음에 조심해야 한다.

이제 훈련 코드를 작성할 수 있다. 클래스별 파라미터로 이루어진 $(n+1, K)$ 행렬 모양의 2차원 넘파이 어레이 $\Theta$를 생성하기 위해 $n$과 $K$를 확인한다.

파라미터 $\Theta$를 무작위로 초기 설정한다.

배치 경사하강법 훈련은 아래 코드를 통해 이루어진다.

학습된 파라미터는 다음과 같다.

검증 세트에 대한 예측과 정확도는 다음과 같다. logits, Y_proba를 검증 세트인 X_valid를 이용하여 계산한다. 예측 클래스는 Y_proba에서 가장 큰 값을 갖는 인덱스로 선택한다.

단계 6: 규제가 추가된 경사하강법 활용 훈련

$\ell_2$ 규제가 추가된 경사하강법 훈련을 구현한다. 코드는 기본적으로 동일하다. 다만 손실(비용)에 $\ell_2$ 페널티가 추가되었고 그래디언트에도 항이 추가되었다(Theta의 첫 번째 원소는 편향이므로 규제하지 않습니다).

추가된 $\ell_2$ 페널티 때문에 이전보다 손실이 조금 커졌지만 검증 세트에 대한 정확도는 오히려 높아졌다.

정확도 1이면 완벽하다는 의미이지만 검증 세트에 대한 운이 좋았을 가능성이 매우 높다.

단계 7: 조기 종료 추가

위 규제가 사용된 모델의 훈련 과정에서 매 에포크마다 검증 세트에 대한 손실을 계산하여 오차가 줄어들다가 증가하기 시작할 때 멈추도록 한다.

훈련이 조기 종료 되었지만 검증 세트에 대한 정확도는 여전히 1이다.

단계 8: 전체 데이터셋 대한 예측 결과 그래프

전체 데이터셋에 대한 모델의 예측을 그래프로 그린다.

단계 9: 테스트 세트 평가

마지막으로 테스트 세트에 대한 모델의 최종 성능을 정확도로 측정한다.

완벽했던 최종 모델의 성능이 조금 떨어졌다. 데이터셋이 작기 때문일 것으로 보인다. 또한 훈련/검증/테스트 세트의 분할 결과에도 영향을 받는다. 랜덤 시드를 바꾸고 이 코드를 다시 실행해 보면 결과가 달라질 것이다.

과제

과제 1

조기 종료를 사용한 배치 경사 하강법으로 로지스틱 회귀를 구현하라. 단, 사이킷런을 전혀 사용하지 않아야 한다.

과제 2

과제 1에서 구현된 로지스틱 회귀 알고리즘에 일대다(OvR) 방식을 적용하여 붓꽃에 대한 다중 클래스 분류 알고리즘을 구현하라. 단, 사이킷런을 전혀 사용하지 않아야 한다.

과제 3

A. 사진을 낮과 밤으로 분류하는 로지스틱 회귀 모델을 구현하라.

B. 사진을 낮과 밤, 실내와 실외로 분류하는 다중 레이블 분류 모델을 두 개의 로지스틱 회귀 모델을 이용하여 구현하라.

C. 과제 1에서 구현한 자신의 알고리즘과 사이킷런에서 제공하는 LogisticRegression 모델의 성능을 비교하라.

단, 모델 구현에 필요한 사진을 직접 구해야 한다. 최소 100장 이상의 사진 활용해야 한다.