📗강의노트/핸즈온 머신러닝

[핸즈온 머신러닝] 제 4장 연습문제 풀이

728x90
반응형

Hands on Machine-Learning

Part.4 - Training Model

1. 수백만 개의 특성을 가진 훈련 세트에서는 어떤 선형 회귀 알고리즘을 사용하나요?

보통은 확률적 경사 하강법(SGD), 미니배치 경사 하강법을 사용한다. 훈련 세트의 크기가 메모리에 맞다면 배치 경사 하강법도 가능하다. 

하지만 정규방정식은 계산 복잡도가 "특성의 갯수"에 따라 매우 빠르게 증가하기에 사용할 수 없다.

=> 확률적 경사 하강법(SGD), 미니배치 경사 하강법, 배치 경사 하강법 O / 정규방정식 X

2. 훈련 세트에 있는 특성들이 각기 다른 스케일을 갖고 있다. 이런 데이터에 잘 작동하지 

않는 알고리즘은? 그 이유는? 문제 해결은 어떻게 하는가?

길쭉한 타원형의 비용함수가 형성된다. 경사하강법 (GD) 알고리즘이 수렴하는데 오래걸린다. 그래서 이를 해결하기 위해서는 데이터의 

스케일을 조절해야한다. "정규방정식"은 스케일 조정없이도 잘 작동한다. 그리고 "규제가 있는 모델"은 특성의 스케일이 다르면 

지역 최적점에 수렴할 수도 있다. 실제로 규제는 가중치가 커지지 못하게 제약을 가하므로 값이 작으면 큰 값의 특성에 비해 무시되는 경향이 있다.

3. 경사 하강법으로 로지스틱 회귀 모델을 훈련시킬 때 지역 최솟값에 갇힐 가능성이

 있을까요?

로지스틱 회귀 모델의 비용함수는 볼록함수 (Convex) 하기 때문에 지역 최솟값에 갇힐 가능성이 없고, 전역 최솟값으로 향한다.

4. 충분히 오랫동안 실행하면 모든 경사 하강법 알고리즘이 같은 모델을 만들어낼까요?

최적화할 함수가 (logistic regression, linear regression처럼) 볼록 함수이고 학습률이 너무 크지 않다고 가정하면, 모든 경사 하강법 알고리즘이 

전역 최솟값에 도달하고 결국에는 비슷한 모델을 만들 것이다. 하지만 학습률을 점진적으로 감소시키기 않는다면, 확률적 경사 하강법(SGD) 과

 미니배치 경사하강법은 전역 최적점 주변을 맴돌게 될 것 이다. 즉, 오랫동안 훈련해도 다른 모델을 형성할 수 있다는 것이다.

5. 배치 경사 하강법을 사용하고 에프크마다 검증 오차를 그래프로 나타냈다. 검증 오차가 

일정하게 상승되고 있다면 어떤 일이 일어나고 있는 것 일까? 이 문제를 어떻게 해결할 수

 있는가.

학습률이 너무 높고 알고리즘이 발산하고 있을 가능성이 높다. 이는 과대적합(Overfittin)되고 있음을 의미하기도 한다. 

이럴 때는 검증 에러가 최솟값에 도달했을 때, 학습을 조기 종료하는 것이 해결책이다.

6. 검증 오차가 상승하면 미니배치 경사 하강법을 즉시 중단하는 것이 좋은 방법인가요?

무작위성 때문에 SGD나 미니배치 GD 모두 매 훈련 반복마다 학습의 진전을 보장하지 못한다. 일찍 멈추게 된다면, 최적점에 도달하기 전에 

멈추게 될 수도 있다. 더 나은 방법으로는 정기적으로 모델을 저장하고 오랫동안 진전이 없을 때, 저장된 것 중 가장 좋은 모델로 복원하는 것이다.

7. 어떤 경사 하강법 알고리즘이 가장 빠르게 최적 솔루션의 주변에 도달할까요? 실제로 

수렴하는 것은 어떤 것 인가요? 다른 방법들도 수렴하게 만들 수 있나요?

확률적 경사 하강법 (SGD)는 한 번에 하나의 훈련 샘플만 사용하기 때문에 훈련 반복이 가장 빠르다. 하지만 시간만 충분하다면 

배치 경사 하강법이 실제로 수렴한다. SGD나 미니 GD를 수렴하게 하려면 학습률을 점진적으로 감소시켜야 최적점 주변을 도는 것을 방지할 수 있다.

8. 다항 회귀를 사용했을 때 학습 곡선을 보니 훈련 오차와 검증 오차 사이에 간격이 큽니다. 

무슨 일이 생긴 걸까요? 이 문제를 해결하는 세 가지 방법은 무엇인가요?

검증 오차가 훈련 오차보다 훨씬 더 높으면 모델이 훈련 세트에 과대 적합 되었을 가능성이 높다. 이를 해결하기 위해서는

(1) 다항 차수를 낮춘다. 자유도를 낮춘다면 과대적합이 줄어들 것이다.

(2) 모델을 규제하는 것. 비용함수에 L2 penalty(Ridge)나 L1 penalty(Lasso)를 추가한다. 이 방법도 모델의 자유도를 낮춘다.

(3) 훈련 세트의 크기를 증가시킨다.

9. 릿지 회귀를 사용했을 때 훈련 오차와 검증 오차가 거의 비슷하고 둘 다 높았습니다.

 이 모델에는 높은 편향이 문제인가요, 아니면 높은 분산이 문제인가요? 

규제 하이퍼파라미터 를 증가시켜야 할까요, 아니면 줄여야 할까요?

훈련 오차와 검증 오차가 거의 비슷하고 매우 높다면 모델이 훈련 세트에 과소적합되었을 가능성이 높다. 즉, 높은 편향을 가진 모델이다. 

따라서 규제 하이퍼파라미터 를 감소시켜야한다. 를 증가시킬수록 복잡도가 떨어짐. 를 낮춰야 분산이 증가하고 편향이 감소함.

10. 다음과 같이 사용해야 하는 이유는?

  • 평범한 선형 회귀 (아무런 규제가 없음) 대신 릿지 회귀

규제가 있는 모델이 규제가 없는 모델에 비해 성능이 좋다. 그래서 평범한 회귀보다는 릿지 회귀가 선호된다.

  • 릿지 회귀 대신 라쏘 회귀

라쏘 회귀는  패널티를 사용하여 가중치를 완전히 0으로 만드는 경향이 있다. 가장 중요한 가중치를 제외하고는 모두 0이 되는

 희소한(Sparse)한 모델을 만든다. 그리고 "자동으로 특성 선택"의 효과를 가지므로 단지 몇 개의 특성만 실제 유용할 것이라고 의심될 때 

사용하면 좋다. 확신이 없다면 릿지 회귀를 사용해야 합니다.

  • 라쏘회귀 대신 엘라스틱넷

몇 개의 특성이 강하게 연관되어 있거나 / 특성 수가 훈련 샘플보다 많을 때 라쏘가 불규칙적으로 행동하여 엘라스틱넷이 더 선호된다.

 하지만 추가적인 하이퍼파라미터가 생기게 된다. 불규칙한 행동이 없는 라쏘를 원하면 엘라스틱넷에 l1_ratio를 1에 가깝게 하면 된다. 

(ratio = 0(Ridge) ~ 1(Lasso))

11. 사진을 낮과 밤, 실내와 실외로 분류하려 한다. 두 개의 로지스틱 회귀 분류기를 

만들어야 할까, 아니면 하나의 소프트맥스 회귀 분류기를 만들어야 할까?

배타적인 클래스가 아니기에 (4가지 조합 모두 가능) 두 개의 로지스틱 회귀 분류기를 훈련시켜야 한다.

12. 조기 종료를 사용한 배치 경사 하강법으로 소프트맥스 회귀를 구현해보세요.

In [143]:
import numpy as np
import matplotlib.pyplot as plt
# sklearn 사용 X
In [144]:
from sklearn import datasets
iris = datasets.load_iris()
X = iris['data'][:,(2,3)]  # 꽃잎 길이, 꽃잎 넓이
y = iris['target']
In [145]:
# Add Bias
X_with_bias = np.c_[np.ones([len(X),1]),X]   
# len(X) 개수 만큼 1로 채워진 [ ], [ ]......[ ] array
# 2열 -> 3열로 늘어남
In [146]:
# 결과를 일정하게 하기 위해, random seed 배정
np.random.seed(1234)
In [147]:
# 원래면 sklearn의 train_test_split을 사용하지만, 직접 만들면서 원리 이해하기
test_ratio = 0.2
validation_ratio = 0.2
total_size = len(X_with_bias)

test_size = int(total_size * test_ratio)
validation_size = int(total_size * validation_ratio)
train_size = total_size - test_size - validation_size

rnd_indices = np.random.permutation(total_size)    # 150을 무작위로 섞음 

X_train = X_with_bias[rnd_indices[:train_size]]
y_train = y[rnd_indices[:train_size]]
X_valid = X_with_bias[rnd_indices[train_size:-test_size]]  # test_size 만큼 남겨둠
y_valid = y[rnd_indices[train_size:-test_size]]
X_test = X_with_bias[rnd_indices[-test_size:]]   
y_test = y[rnd_indices[-test_size:]]
In [148]:
# 클래스를 OneHot Vector로 바꾸기 
def to_one_hot(y):
    n_classes = y.max()+1    # 0,1,2 라 max=2 / +1 하면 classes 개수
    m = len(y)                     # 총 150개의 라벨들
    y_one_hot = np.zeros((m,n_classes))
    y_one_hot[np.arange(m),y] = 1   # index의 행중에 y값을 1로 치환
    return y_one_hot
In [149]:
y_train[:10]
Out[149]:
array([1, 1, 2, 0, 1, 0, 0, 0, 1, 2])
In [150]:
to_one_hot(y_train[:10])   # one hot encoding이 된 것을 확인할 수 있다. 
Out[150]:
array([[0., 1., 0.],
       [0., 1., 0.],
       [0., 0., 1.],
       [1., 0., 0.],
       [0., 1., 0.],
       [1., 0., 0.],
       [1., 0., 0.],
       [1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]])
In [151]:
# 라벨 전부를 onehot encoding하기
y_train_one_hot = to_one_hot(y_train)
y_valid_one_hot = to_one_hot(y_valid)
y_test_one_hot = to_one_hot(y_test)

Softmax 함수

In [152]:
# Softmax 함수 만들기

def softmax(logits):
    exps = np.exp(logits)
    exp_sums = np.sum(exps,axis=1,keepdims=True)   # axis=1   ->  가장 안쪽의 [ ] 안의 성분의 합 / 각 exps들의 합
    return exps/exp_sums
In [153]:
# 입력과 출력의 갯수 정하기
n_inputs = X_train.shape[1]   # (90,3) 인데 1로 인덱싱 ==3
n_outputs = len(np.unique(y_train))  # y_train값을 중복되지 않는 값들을 출력  3

훈련 파트

*모두의 딥러닝 softmax cost 함수 참조

비용함수

그래디언트 공식 ( 비용함수를 에 대해 편미분 )

In [154]:
eta = 0.01
n_iteration = 5001
m = len(X_train)
epsilon = 1e-7 # ε : 입실론     nan값을 피하기 위해 logPi에 추가.

Theta = np.random.randn(n_inputs,n_outputs)

for i in range(n_iteration):
    logits = X_train.dot(Theta)
    Y_proba = softmax(logits)
    loss = -np.mean(np.sum(y_train_one_hot * np.log(Y_proba + epsilon), axis=1))
    error = Y_proba - y_train_one_hot
    if i % 500 == 0:
        print(i,loss)
    gradients = 1/m * X_train.T.dot(error)
    Theta = Theta - eta * gradients 
0 3.1114915802681646
500 0.8785451855619314
1000 0.7103533106437264
1500 0.6136193727911318
2000 0.5518526140625929
2500 0.5086585463656746
3000 0.4762907516068719
3500 0.45076167129780975
4000 0.42984346695974646
4500 0.41220116725499784
5000 0.3969868328943138
In [155]:
# 모델 파라미터 확인
Theta
Out[155]:
array([[ 2.68979228, -0.7826269 , -2.70314023],
       [-0.39955204,  0.45023269,  0.05089443],
       [-2.42533074, -0.66589863,  1.80156464]])
In [156]:
# 검증 세트에 대한 정확도 확인 
logits = X_valid.dot(Theta)
y_proba = softmax(logits)
y_predict = np.argmax(y_proba, axis=1)

accuracy = np.mean(y_predict == y_valid)
accuracy
# 모델이 매우 잘 작동하는 것으로 확인됨
# L2규제를 추가해보자
Out[156]:
0.9
In [157]:
eta = 0.1
n_iteration = 5001
m = len(X_train)
epilson = 1e-7
alpha = 0.1  # 규제 파라미터 

Theta = np.random.randn(n_inputs,n_outputs)

for i in range(n_iteration):
    logits = X_train.dot(Theta)
    Y_proba = softmax(logits)
    entropy_loss = -np.mean(np.sum(y_train_one_hot * np.log(Y_proba + epsilon), axis=1))
    l2_loss = 1/2 * np.sum(np.square(Theta[1:]))
    loss = entropy_loss + alpha * l2_loss
    error = Y_proba - y_train_one_hot
    if i % 500 == 0:
        print(i,loss)
    gradients = 1/m * X_train.T.dot(error) + np.r_[np.zeros([1,n_outputs]), alpha * Theta[1:]]
    Theta = Theta - eta * gradients
0 6.135593777893689
500 0.538428329796786
1000 0.5093655628163154
1500 0.5009568607421142
2000 0.4978808079290177
2500 0.4966559287329126
3000 0.49614679586131877
3500 0.49592990265413284
4000 0.4958361134372299
4500 0.4957951738633769
5000 0.4957771952771175
In [158]:
# l2 패널티 때문에 손실이 더 커보인다. 모델이 더 잘 작동하는지 확인해보자
logits = X_valid.dot(Theta)
Y_proba = softmax(logits)
y_predict = np.argmax(Y_proba,axis=1)

acc = np.mean(y_predict == y_valid)
acc
# 더 성능이 좋은 모델이 되었다. 
Out[158]:
0.9333333333333333
In [159]:
# 조기 종료 추가
eta = 0.1
m = len(X_train)
iteration= 5001
epsilon = 1e-7
alpha = 0.1
best_loss = np.infty

Theta = np.random.randn(n_inputs,n_outputs)

for i in range(iteration):
    logits = X_train.dot(Theta)
    Y_proba = softmax(logits)
    entropy_loss = -np.mean(np.sum(y_train_one_hot * np.log(Y_proba + epsilon), axis=1))
    l2_loss = 1/2 * np.sum(np.square(Theta[1:]))
    loss = entropy_loss + alpha * l2_loss
    error = Y_proba - y_train_one_hot
    gradients = 1/m * X_train.T.dot(error) + np.r_[(np.zeros([1,n_outputs]),alpha * Theta[1:])]
    Theta = Theta - eta * gradients
    
    logits = X_valid.dot(Theta)
    Y_proba = softmax(logits)
    xentropy_loss = -np.mean(np.sum(y_valid_one_hot * np.log(Y_proba + epsilon), axis=1))
    l2_loss = 1/2 * np.sum(np.square(Theta[1:]))
    loss = xentropy_loss + alpha * l2_loss
    if iteration % 500 == 0:
        print(i,loss)
    if loss < best_loss:
        best_loss = loss
    else:
        print(i-1, best_loss)
        print(i,loss,"Early Stopping!")
        break
3057 0.5616512161922435
3058 0.5616512162651615 Early Stopping!
In [160]:
logits = X_valid.dot(Theta)
Y_proba = softmax(logits)
y_predict = np.argmax(Y_proba, axis=1)

accuracy_score = np.mean(y_predict == y_valid)
accuracy_score

# 학습이 더 빠르게 종료되었다. 
Out[160]:
0.9333333333333333
In [164]:
# 한글출력
import matplotlib
matplotlib.rc('font', family='NanumBarunGothic')
plt.rcParams['axes.unicode_minus'] = False

x0, x1 = np.meshgrid(
        np.linspace(0, 8, 500).reshape(-1, 1),
        np.linspace(0, 3.5, 200).reshape(-1, 1),
    )
X_new = np.c_[x0.ravel(), x1.ravel()]
X_new_with_bias = np.c_[np.ones([len(X_new), 1]), X_new]

logits = X_new_with_bias.dot(Theta)
Y_proba = softmax(logits)
y_predict = np.argmax(Y_proba, axis=1)

zz1 = Y_proba[:, 1].reshape(x0.shape)
zz = y_predict.reshape(x0.shape)

plt.figure(figsize=(10, 4))
plt.plot(X[y==2, 0], X[y==2, 1], "g^", label="Iris-Virginica")
plt.plot(X[y==1, 0], X[y==1, 1], "bs", label="Iris-Versicolor")
plt.plot(X[y==0, 0], X[y==0, 1], "yo", label="Iris-Setosa")

from matplotlib.colors import ListedColormap
custom_cmap = ListedColormap(['#fafab0','#9898ff','#a0faa0'])

plt.contourf(x0, x1, zz, cmap=custom_cmap)
contour = plt.contour(x0, x1, zz1, cmap=plt.cm.brg)
plt.clabel(contour, inline=1, fontsize=12)
plt.xlabel("꽃잎 길이", fontsize=14)
plt.ylabel("꽃잎 폭", fontsize=14)
plt.legend(loc="upper left", fontsize=14)
plt.axis([0, 7, 0, 3.5])
plt.show()
In [162]:
# 최종 테스트 데이터 예측 
logits = X_test.dot(Theta)
Y_proba = softmax(logits)
y_predict = np.argmax(Y_proba, axis=1)

accuracy_score = np.mean(y_predict == y_test)
accuracy_score

# 매우 정확한 예측도를 보였다. 
Out[162]:
1.0


728x90
반응형