3. 경사 하강법으로 로지스틱 회귀 모델을 훈련시킬 때, 지역 최솟값에 갇힐 가능성이 있을까요?
검증 오차가 일정하게 상승되고 있다면 어떤 일이 일어나고 있는 걸까요?
6. 검증 오차가 상승하면 미니배치 경사 하강법을 즉시 중단하는 것이 좋은 방법일까요?
무슨 일이 생긴 걸까요? 이 문제를 해결하는 세 가지 방법은?
9. 릿지 회귀를 사용했을 때 훈련 오차와 검증 오차가 거의 비슷하고 둘다 높았습니다.
이 모델에는 높은 편향이 문제인가요, 높은 분산이 문제인가요?
10. 다음과 같이 사용해야 하는 이유는?
* 라쏘 회귀 대신 엘라스틱넷
12. 조기 종료를 사용한 배치 경사 하강법으로 소프트맥스 회귀를 구현해보세요.(사이킷런 사용 X)
X = iris['data'][:, (2,3)] #Petal Length, Petal Width
y = iris['target']
#모든 샘플에 편향 추가 (x0=1)
#np.c는 R의 cbind와 유사합니다. 즉 column처럼 이어붙입니다. 비슷한 것으로 np.vstack입니다. vertically (수직)
#np.r은 R의 rbind와 유사합니다. 즉, row bind입니다. 비슷한 것은 np.hstack. horizontally 로 이해하시면 됩니다.(수평)
#np.ones는 새로운 Array를 반환하며, 1로 채워집니다. [len(X), 1]은 shape를 나타냅니다.
#따라서 X, 1 shape의 array와 X array를 columns 형태로 이어붙입니다.
X_with_bias = np.c_[np.ones([len(X), 1]), X]
#결과를 일정하게 유지하기 위해 랜덤시드 고정
np.random.seed(2042)
#데이터셋을 훈련/검증/테스트 Set로 나누는 가장 쉬운 방법은 사이킷런의 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
#numpy.random.permutation(x)는 무작위로 순서를 바꿉니다.
#np.random.permutation(10)하면 무작위로 10개의 숫자를 나열한 array가 나옵니다.
#따라서 total_size의 length 길이안에서 섞입니다. 그 섞인 값을 rnd_indices로 지정합니다.
rnd_indices = np.random.permutation(total_size)
#지정된 rnd__indices는 값이 일정하지 않고 마구 섞여있겠죠. 그 안에서 train_size만큼 분할합니다.
X_train = X_with_bias[rnd_indices[:train_size]]
y_train = y[rnd_indices[:train_size]]
#rnd_indices는 이미 지정된 값이기 때문에 계속해서 섞이지 않습니다. 따라서 train_size부터~ test_size까지 나누게 됩니다.
X_valid = X_with_bias[rnd_indices[train_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:]]
#타깃은 클래스 인덱스(0, 1 그리고 2) 이지만, 소프트맥스 회귀 모델을 훈련시키기 위해 필요한 것은
#타깃 클래스의 확률입니다. 각 샘플에서 확률이 1인 타깃 클래스를 제외한 다른 클래스의 확률은 0
#(다른 말로 주어진 샘플에 대한 클래스 확률이 원-핫 벡터입니다.)
#클래스는 0,1,2이므로 max는 2, 클래스의 개수는 3개이므로 +1
#앞서 봤던 np.ones는 1로 채운다면 zeros는 0으로 채웁니다.
#Y_one_hot은 len_y의 길이만큼의 행, 3개의 열 구조로 생성되고, 모두 0으로만 채워져 있는데,
#np.arange(m), y는 행 열을 선택하게 됩니다.
#행과 열이 모두 0부터 시작하며, iris_target 값이 y이므로 y의 값이 0인경우 첫번째에 1,
#1인 경우 2번째에 1, 2인경우 3번째 열에 1로 바꿉니다.
#즉 Y_one_hot은 빈 틀을 만들어 준 셈이고, [ ]를 통해 행 열을 지정하게 됩니다.
#np.arange(m)은 길이가 맞아야 하며 m이 10인 경우 [0,1,2,3,4,5,6,7,8,9]가 생기고,
# y_train값 [0,1,2,1,1,0,1,1,1,0]이 차례대로 들어갑니다.
#따라서 (0,0)=1, (1,1)=1, (2,2)=1, (3, 1)=1 이런식으로 되어 one-hot 벡터처럼 바꿀 수 있습니다.
def to_one_hot(y) :
n_classes = y.max() + 1
m = len(y)
Y_one_hot = np.zeros((m, n_classes))
Y_one_hot[np.arange(m), y] = 1
return Y_one_hot
#10개의 샘플만 넣어 함수 테스트
y_train[:10]
array([0, 1, 2, 1, 1, 0, 1, 1, 1, 0])
to_one_hot(y_train[:10])
array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.], [0., 1., 0.], [0., 1., 0.], [1., 0., 0.], [0., 1., 0.], [0., 1., 0.], [0., 1., 0.], [1., 0., 0.]])
#잘 이해한게 맞네요..
#이제 train, validation, test set 모두 바꿔줍니다.
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)
#이제 소프트맥스 함수를 봅니다. (지난 포스팅에서 봄)
[소프트맥스 함수]
s(x) 는 샘플 x에 대한 각 클래스의 점수를 담고 있는 벡터, k는 클래스 수,
* 는 샘플 x에 대한 각 클래스의 점수가 주어졌을 때 이 샘플이 클래스 k에 속할 추정 확률
#즉 클래스 k에 속할 추정 확률을 구합니다.
def softmax (logits):
exps = np.exp(logits)
exp_sums = np.sum(exps, axis=1, keepdims =True)
return exps / exp_sums
#이제 입력과 출력의 개수를 정의합니다.
n_inputs = X_train.shape[1] # (90,3)[1] ==3 (특성 2와 편향)
n_outputs = len(np.unique(y_train)) # len(array([0,1,2]) ==3 (3개의 붓꽃 클래스)
#이제 비용함수와 그래디언트 공식을 구현합니다.
[크로스 엔트로피 비용 함수]
* i번째 샘플에 대한 타깃 클래스가 k일 때, 가 1이고 그 외에는 0입니다.
* 딱 2개의 클래스가 있을 때 (K=2), 이 비용 함수는 로지스틱 회귀 비용 함수와 같습니다.
[클래스 K에 대한 크로스 엔트로피의 그래디언트 벡터]
** 이면 를 계산할 수 없습니다. nan
값을 피하기 위해 에 아주 작은 값 을 추가.
eta = 0.01
n_iterations = 5001
m = len(X_train)
epsilon = 1e-7
Theta = np.random.randn(n_inputs, n_outputs)
for iteration in range(n_iterations): #5001 -> 0~5000
logits = X_train.dot(Theta) #(Theta행렬 * X_train행렬)
Y_proba = softmax(logits) #Y_proba = softmax(logits) => 소프트맥스 함수
loss = -np.mean(np.sum(Y_train_one_hot * np.log(Y_proba + epsilon), axis=1)) #크로스 엔트로피 비용 함수, epsilon 추가
error = Y_proba - Y_train_one_hot #클래스 k에 속할 확률과 실제 간 차이
if iteration % 500 == 0:
print(iteration, loss)
gradients = 1/m * X_train.T.dot(error) #클래스 K에 대한 크로스 엔트로피의 그레디언트 벡터
Theta = Theta - eta * gradients #Theta = Theta - (학습률 * 그레디언트 벡터)
#Theta가 for문을 돌면서 점진적으로 하강하게 됨. 그리고 전체 셋을 사용하고 있으므로 배치 경사하강법.
0 2.0828851886739566 500 0.7785748213387484 1000 0.6476766176998905 1500 0.5697121770727602 2000 0.5182045898073839 2500 0.48105733738538503 3000 0.4524974072153004 3500 0.4295195976924987 4000 0.4104187525819136 4500 0.39415208327581297 5000 0.3800408408828462
Theta
#검증 세트에 대한 예측과 정확도 확인
#validation(검증 세트)를 사용
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
#ㄷㄷ 완-벽
#사실 교재에서느느 0.966666정도가 나와서 규제가 들어가야되는데
#완-벽 해서 이거 규제를 해야 되나 싶네요.... 그새 알고리즘이 발전했는지..?.. 뭔지..
#연습을 위해 l2 규제를 가하고, 학습률을 증가시켜봅니다.
#완벽하지만 연습을 위해 l2 규제를 추가해보고, 학습률 증가시켜봅니다.
#손실에 l2 페널티를 추가하고, 그레디언트 항에도 추가합니다.
#Theta의 첫 번째 원소는 편향이므로 규제하지 않습니다.
eta = 0.1
n_iterations = 5001
m = len(X_train)
epsilon = 1e-7
alpha = 0.1 # 규제 하이퍼파라미터
Theta = np.random.randn(n_inputs, n_outputs)
for iteration in range(n_iterations):
logits = X_train.dot(Theta)
Y_proba = softmax(logits)
#numpy.square()는 배열 원소들을 제곱시킵니다.
xentropy_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 = xentropy_loss + alpha * l2_loss
error = Y_proba - Y_train_one_hot
if iteration % 500 == 0:
print(iteration, loss)
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)
y_predict = np.argmax(Y_proba, axis=1)
accuracy_score = np.mean(y_predict == y_valid)
accuracy_score
# 역시나 모델이 더 좋아질 수 없죠 ?
# 이제 조기 종료를 추가해봅니다. 아마도 조기 종료의 다른 이점도 있지만 5001번 안에 끝날 수도 있겠네요.
# 왜냐하면 값을 보면 알 수 있듯이 2500번부터는 소숫점 3, 4자리수가 변화하고 있습니다.
# 매 반복에서, 검증 세트에 대한 손실을 계산하여 오차가 증가하기 시작할 때, 멈춰야 한다.
eta = 0.1
n_iterations = 5001
m = len(X_train)
epsilon = 1e-7
alpha = 0.1 # 규제 하이퍼파라미터
best_loss = np.infty
Theta = np.random.randn(n_inputs, n_outputs)
for iteration in range(n_iterations):
logits = X_train.dot(Theta)
Y_proba = softmax(logits)
xentropy_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 = xentropy_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(iteration, loss)
if loss < best_loss:
best_loss = loss
else:
print(iteration - 1, best_loss)
print(iteration, loss, "early stopping!")
break
# best_loss를 np.infty라고 하였는데 infty는 inf와 동일한 alias 입니다. 따라서 무한.
# 즉 한없이 큰 수로 지정함 뭐 이거는 왜 이렇게 했는지 모르겠지만 어떤 값도 기록한다는 의미일까요?..
0 1.3104654618363918 500 0.5638848652699527 1000 0.5411567036472769 1500 0.5348667504218284 2000 0.5329992890318709 2500 0.5325642697019287 2670 0.5325458209208133 2671 0.5325458209570282 early stopping!
#아까의 예측대로 2500대 근처에서 멈췄군요. 훨씬 시간을 절약했습니다~~~
#검증
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
#이제 전체 예측에 대한 그래프를 만들어봅니다. (필수는 아님..)
#전체 예측에 대한 그래프
#matplotlib 안에 한글을 적용하려면 일시적인 방편으로 포스팅한 것을 보시면 됩니다.
#귀찮다면 직접 영어로 바꾸시길..ㅠㅠ (전 안바꿨어요 나중에 영구적으로 적용하는 방법을 포스팅 하겠습니다.)
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()
마지막으로 테스트 셋에 적용해서 마무리합니다.
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
0.9333333333333333
최종 모델의 성능이 조금 떨어졌지만 데이터셋이 작아서라고 합니다.
저자는 훈련 세트와 검증 세트, 테스트 세트를 어떻게 샘플링 하냐에 따라 결과가 달라질 수 있으므로, 랜덤 시드를 바꾸면서 해보면 달라질 것이라고 합니다.
하지만 뭐 93.3%도 매우 높다고 생각되네요.
6.7%는 변종일까 궁금하네요
다음 포스팅은 5장 서포트 벡터머신의 심화과정을 포스팅하겠습니다!
블로그
출처
이 글의 상당 부분은 [핸즈온 머신러닝, 한빛미디어/오렐리앙 제롱/박해선] 서적을 참고하였습니다.
나머지는 부수적인 함수나 메서드에 대해 부족한 설명을 적어두었습니다.
학습용으로 포스팅 하는 것이기 때문에 복제보다는 머신러닝에 관심이 있다면 구매해보시길 추천합니다.
도움이 되셨다면 로그인 없이 가능한
아래 하트♥공감 버튼을 꾹 눌러주세요!
'## 오래된 게시글 (미관리) ## > Python (Linux)' 카테고리의 다른 글
36. Python - 서포트 벡터 머신(라지 마진 분류) (0) | 2019.01.31 |
---|---|
35. Python - Matplotlib 한글 설정 (환경 설정으로 고정), 마이너스 깨짐 (0) | 2019.01.31 |
33. Python - 소프트맥스 회귀(다항 로지스틱 회귀) (0) | 2019.01.29 |
32. Python - 로지스틱 회귀 (0) | 2019.01.28 |
31. Python - 규제(릿지 회귀, 라쏘 회귀, 엘라스틱넷, 조기종료) (0) | 2019.01.27 |