티스토리 뷰
# 파이썬 ≥3.5 필수
import sys
assert sys.version_info >= (3, 5)
# 사이킷런 ≥0.20 필수
import sklearn
assert sklearn.__version__ >= "0.20"
# 텐서플로 ≥2.0 필수
import tensorflow as tf
assert tf.__version__ >= "2.0"
# 공통 모듈 임포트
import numpy as np
import os
# 노트북 실행 결과를 동일하게 유지하기 위해
np.random.seed(42)
# 깔끔한 그래프 출력을 위해
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.style.use('default')
mpl.rc('axes', labelsize=14)
mpl.rc('xtick', labelsize=12)
mpl.rc('ytick', labelsize=12)
케라스를 사용한 인공 신경망¶
드디어 신경망으로 넘어왔다.
다층 퍼셉트론을 이용하여 유연하고 높은 표현력을 가진 신경망을 케라스로 만들자
from tensorflow import keras
fashion_mnist = keras.datasets.fashion_mnist
(X_train_full, y_train_full), (X_test, y_test) = fashion_mnist.load_data()
print(X_train_full.shape)
print(X_train_full.dtype)
(60000, 28, 28)
uint8
X_valid, X_train = X_train_full[:5000] / 255., X_train_full[5000:] / 255.
y_valid, y_train = y_train_full[:5000], y_train_full[5000:]
X_test = X_test / 255.0
class_names = [
"T-shirt/top", "Trouser", "Pullover", "Dress", "Coat", "Sandal", "Shirt",
"Sneaker", "Bag", "Ankle boot"
]
시퀸셜 API를 이용한 분류 모델 생성¶
keras.models.Sequential()을 이용하여 순서대로 층을 연결하여 모델을 만들어보자.
모델 구성¶
model = keras.models.Sequential()
# 입력 이미지를 reshape해서 1D로 변환
model.add(keras.layers.Flatten(input_shape=[28, 28]))
# N개의 뉴런을 가진 Dense층을 추가, 활성화 함수는 ReLU 함수.
model.add(keras.layers.Dense(300, activation="relu"))
model.add(keras.layers.Dense(100, activation="relu"))
# 마지막 출력을 위한 10개의 뉴런을 가진 Dense층. softmax 활성화를 통해 클래스 별 확률을 출력.
model.add(keras.layers.Dense(10, activation="softmax"))
# 한번에 리스트를 넘기는 것도 가능하다.
model = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28]),
keras.layers.Dense(300, activation="relu"),
keras.layers.Dense(100, activation="relu"),
keras.layers.Dense(10, activation="softmax")
])
# summary()와 layers attribs를 이용하여 정보를 얻어낼 수 있다.
print(model.summary())
model.layers
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten_1 (Flatten) (None, 784) 0
dense_3 (Dense) (None, 300) 235500
dense_4 (Dense) (None, 100) 30100
dense_5 (Dense) (None, 10) 1010
=================================================================
Total params: 266,610
Trainable params: 266,610
Non-trainable params: 0
_________________________________________________________________
None
[<keras.layers.core.flatten.Flatten at 0x23b62b3d760>,
<keras.layers.core.dense.Dense at 0x23b62b3d730>,
<keras.layers.core.dense.Dense at 0x23b62b35550>,
<keras.layers.core.dense.Dense at 0x23b62b35280>]
hidden1 = model.layers[1]
print(hidden1.name)
model.get_layer('dense_3') is hidden1
dense_3
True
weights, biases = hidden1.get_weights()
print(weights)
print(weights.shape)
print(biases)
print(biases.shape)
[[ 0.04447937 -0.04099248 -0.0193208 ... -0.01441845 -0.03774612
-0.0159855 ]
[-0.03694651 -0.03802623 0.01243072 ... -0.05406223 -0.06578163
0.0449766 ]
[ 0.03936975 0.03783122 0.05599502 ... 0.04105379 0.04598705
-0.04587892]
...
[-0.05520768 0.05928649 0.03959306 ... 0.03314824 -0.06225937
-0.04303161]
[-0.06377283 0.06684908 0.02799835 ... -0.04380647 -0.01737352
-0.04024508]
[ 0.00231768 -0.01694362 -0.04072764 ... -0.02322719 0.01753905
0.04779348]]
(784, 300)
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
(300,)
레이블이 정수로 되어있고 배타적이므로 sparse_categorical_crossentropy 를 손실함수로 이용
단, 원-핫 벡터와 같이 샘플마다 클래스 별 타깃 확률을 가지고 있다면 categorical_crossentropy 를 이용.
model.compile(loss="sparse_categorical_crossentropy",
optimizer="sgd",
metrics=["accuracy"])
모델 훈련 및 학습 곡선 확인¶
fit() 메서드가 반환하는 History 객체에는 훈련 파라미터와 에포크 리스트가 담겨 있다.
특히 훈련셋과 검증셋에 대한 손실과 측정지표가 담긴 딕셔너리가 중요한데 이를 통해 학습 곡선을 손쉽게 제작가능하다.
validation_data 대신 validation_split을 설정해주면 훈련셋의 마지막 일부를 검증셋으로 이용한다.
class_weight와 sample_weight를 통해 편향된 데이터에 대하여 가중치를 조정 해줄 수 있다.
history = model.fit(X_train,
y_train,
epochs=30,
validation_data=(X_valid, y_valid))
import pandas as pd
pd.DataFrame(history.history).plot(figsize=(8, 5))
plt.grid(True)
plt.gca().set_ylim(0, 1)
plt.show()
모델 평가 및 예측¶
은닉층의 개수와 학습률 같은 하이퍼파라미터를 튜닝해가며 모델을 훈련시키고 마지막에 평가 할 때는
evaluate() 메서드를 이용하여 테스트셋을 평가하자.
model.evaluate(X_test,y_test)
313/313 [==============================] - 0s 635us/step - loss: 0.3259 - accuracy: 0.8820
[0.32585012912750244, 0.8820000290870667]
그 다음 제출할 파일을 제작하기 위하여 predict() 메서드를 이용하여 새로운 샘플에 대한 예측을 만들어 낸다.
X_new = X_test[:3]
y_proba = model.predict(X_new)
y_proba.round(2)
array([[0. , 0. , 0. , 0. , 0. , 0.01, 0. , 0.02, 0. , 0.97],
[0. , 0. , 0.99, 0. , 0.01, 0. , 0. , 0. , 0. , 0. ],
[0. , 1. , 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ]],
dtype=float32)
predict만 놓고보면 softmax를 이용했으므로 확률이 출력된다.
만약 가장 높은 확률을 가진 클래스에만 관심이 있다면 predict_classes() 메서드를 이용할... 수 있었지만
사라졌다. 그러니 그냥 argmax를 이용해서 뽑아주자
# y_pred = model.predict_classes(X_new) <- deprecated
y_pred = np.argmax(model.predict(X_new), axis=-1)
print(y_pred)
np.array(class_names)[y_pred]
[9 2 1]
array(['Ankle boot', 'Pullover', 'Trouser'], dtype='<U11')
시퀸셜 API를 이용한 회귀 모델 생성¶
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
housing = fetch_california_housing()
X_train_full, X_test, y_train_full, y_test = train_test_split(housing.data, housing.target, random_state=42)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_full, y_train_full, random_state=42)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)
model = keras.models.Sequential([
keras.layers.Dense(30,activation="relu", input_shape=X_train.shape[1:]),
keras.layers.Dense(1)
])
model.compile(loss="mean_squared_error", optimizer="sgd")
history = model.fit(X_train, y_train, epochs=20, validation_data=(X_valid,y_valid))
mse_test = model.evaluate(X_test,y_test)
X_new = X_test[:3]
y_pred = model.predict(X_new)
Epoch 1/20
363/363 [==============================] - 0s 801us/step - loss: 0.8736 - val_loss: 17.8288
Epoch 2/20
363/363 [==============================] - 0s 649us/step - loss: 0.5267 - val_loss: 23.9346
Epoch 3/20
363/363 [==============================] - 0s 648us/step - loss: 0.5855 - val_loss: 1.4132
Epoch 4/20
363/363 [==============================] - 0s 619us/step - loss: 0.4250 - val_loss: 0.3848
Epoch 5/20
363/363 [==============================] - 0s 635us/step - loss: 0.4014 - val_loss: 0.3834
Epoch 6/20
363/363 [==============================] - 0s 654us/step - loss: 0.3918 - val_loss: 0.4302
Epoch 7/20
363/363 [==============================] - 0s 638us/step - loss: 0.3842 - val_loss: 0.3935
Epoch 8/20
363/363 [==============================] - 0s 626us/step - loss: 0.3785 - val_loss: 0.3848
Epoch 9/20
363/363 [==============================] - 0s 627us/step - loss: 0.3728 - val_loss: 0.3642
Epoch 10/20
363/363 [==============================] - 0s 619us/step - loss: 0.3683 - val_loss: 0.4224
Epoch 11/20
363/363 [==============================] - 0s 641us/step - loss: 0.3667 - val_loss: 0.3636
Epoch 12/20
363/363 [==============================] - 0s 649us/step - loss: 0.3632 - val_loss: 0.3788
Epoch 13/20
363/363 [==============================] - 0s 619us/step - loss: 0.3602 - val_loss: 0.3495
Epoch 14/20
363/363 [==============================] - 0s 624us/step - loss: 0.3595 - val_loss: 0.3500
Epoch 15/20
363/363 [==============================] - 0s 635us/step - loss: 0.3570 - val_loss: 0.3706
Epoch 16/20
363/363 [==============================] - 0s 710us/step - loss: 0.3544 - val_loss: 0.3505
Epoch 17/20
363/363 [==============================] - 0s 647us/step - loss: 0.3538 - val_loss: 0.3526
Epoch 18/20
363/363 [==============================] - 0s 613us/step - loss: 0.3511 - val_loss: 0.3601
Epoch 19/20
363/363 [==============================] - 0s 611us/step - loss: 0.3497 - val_loss: 0.3558
Epoch 20/20
363/363 [==============================] - 0s 602us/step - loss: 0.3488 - val_loss: 0.3804
162/162 [==============================] - 0s 423us/step - loss: 0.3459
함수형 API를 사용해 복잡한 모델 생성하기¶
지금까진 순차적으로 sequential을 이용하여 모델을 생성했지만 모델이 꼭 순차적일 필요는 없다.
신경망이 깊게 쌓일 수도 있지만 곧바로 깊은 층으로 건너 뛸수도 있다.
이렇게 구현하면 신경망이 복잡한 패턴과 단순한 규칙을 모두 학습할 수 있게 된다.
keras.backend.clear_session()
# 여기까진 순차적이다만...
input_ = keras.layers.Input(shape=X_train.shape[1:])
hidden1 = keras.layers.Dense(30, activation="relu")(input_)
hidden2 = keras.layers.Dense(30, activation="relu")(hidden1)
# input layer와 hidden2 layer를 바로 이어준다.
concat = keras.layers.concatenate([input_, hidden2])
output = keras.layers.Dense(1)(concat)
model = keras.models.Model(inputs=[input_], outputs=[output])
model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_1 (InputLayer) [(None, 8)] 0 []
dense (Dense) (None, 30) 270 ['input_1[0][0]']
dense_1 (Dense) (None, 30) 930 ['dense[0][0]']
concatenate (Concatenate) (None, 38) 0 ['input_1[0][0]',
'dense_1[0][0]']
dense_2 (Dense) (None, 1) 39 ['concatenate[0][0]']
==================================================================================================
Total params: 1,239
Trainable params: 1,239
Non-trainable params: 0
__________________________________________________________________________________________________
여러 입력을 받고 출력하기¶
또 일부 특성은 깊게, 나머지 특성을 얕게 보내버리고 싶다면 다음과 같이 하면된다.
이 코드에서는 5개의 특성을 곧바로 concat layer에 보내고 6개의 특성을 2개의 은닉층을 거치게 만든다.
keras.backend.clear_session()
input_A = keras.layers.Input(shape=[5], name="wide_input")
input_B = keras.layers.Input(shape=[6], name="deep_input")
hidden1 = keras.layers.Dense(30, activation="relu")(input_B)
hidden2 = keras.layers.Dense(30, activation="relu")(hidden1)
concat = keras.layers.concatenate([input_A, hidden2])
output = keras.layers.Dense(1, name="output")(concat)
model = keras.models.Model(inputs=[input_A, input_B], outputs=[output])
model.summary()
Model: "model"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
deep_input (InputLayer) [(None, 6)] 0 []
dense (Dense) (None, 30) 210 ['deep_input[0][0]']
wide_input (InputLayer) [(None, 5)] 0 []
dense_1 (Dense) (None, 30) 930 ['dense[0][0]']
concatenate (Concatenate) (None, 35) 0 ['wide_input[0][0]',
'dense_1[0][0]']
output (Dense) (None, 1) 36 ['concatenate[0][0]']
==================================================================================================
Total params: 1,176
Trainable params: 1,176
Non-trainable params: 0
__________________________________________________________________________________________________
model.compile(loss="mse", optimizer=keras.optimizers.SGD(learning_rate=1e-3))
X_train_A, X_train_B = X_train[:, :5], X_train[:, 2:]
X_valid_A, X_valid_B = X_valid[:, :5], X_valid[:, 2:]
X_test_A, X_test_B = X_test[:, :5], X_test[:, 2:]
X_new_A, X_new_B = X_test_A[:3], X_test_B[:3]
history = model.fit((X_train_A, X_train_B), y_train, epochs=20,
validation_data=((X_valid_A, X_valid_B), y_valid))
mse_test = model.evaluate((X_test_A, X_test_B), y_test)
y_pred = model.predict((X_new_A, X_new_B))
다수의 출력을 만들어내기¶
출력이 여러 개 필요한 상황이 많은데 이 경우에는 보조 출력을 만들어 주자.
# 여기까진 위의 모델과 비슷해보인다.
input_A = keras.layers.Input(shape=[5], name="wide_input")
input_B = keras.layers.Input(shape=[6], name="deep_input")
hidden1 = keras.layers.Dense(30, activation="relu")(input_B)
hidden2 = keras.layers.Dense(30, activation="relu")(hidden1)
concat = keras.layers.concatenate([input_A, hidden2])
output = keras.layers.Dense(1, name="main_output")(concat)
# hidden2 layer에서 곧바로 보조 출력을 뽑아준다.
aux_output = keras.layers.Dense(1, name="aux_output")(hidden2)
model = keras.models.Model(inputs=[input_A, input_B],
outputs=[output, aux_output])
# 각 출력 층에 대한 손실함수와 최적화를 진행할 때 얼마나 가중치를 부여할 것인지를 정해주자.
model.compile(loss=["mse", "mse"],
loss_weights=[0.9, 0.1],
optimizer=keras.optimizers.SGD(learning_rate=1e-3))
history = model.fit([X_train_A, X_train_B], [y_train, y_train],
epochs=20,
validation_data=([X_valid_A, X_valid_B], [y_valid, y_valid]))
# 각각의 loss가 출력층 이름의 맞추어 저장된다.
history.history.keys()
dict_keys(['loss', 'main_output_loss', 'aux_output_loss', 'val_loss', 'val_main_output_loss', 'val_aux_output_loss'])
total_loss, main_loss, aux_loss = model.evaluate([X_test_A, X_test_B],
[y_test, y_test])
y_pred_main, y_pred_aux = model.predict([X_new_A, X_new_B])
162/162 [==============================] - 0s 530us/step - loss: 0.4705 - main_output_loss: 0.4264 - aux_output_loss: 0.8671
서브클래싱 API로 동적 모델 만들기¶
미리 모델의 레이어의 크기와 연결 방식을 정의해야하는 정적 모델과 달리
반복문과 다양한 크기뿐만 아니라 조건문을 가지는 등, 동적인 모델을 짜야한다면 서브클래싱 API를 이용하자.
class WideAndDeepModel(keras.models.Model):
def __init__(self, units=30, activation="relu", **kwargs):
super().__init__(**kwargs) # 표준 매개변수를 처리한다.
# units와 activation func을 지정해줄 수 있다.
self.hidden1 = keras.layers.Dense(units, activation=activation)
self.hidden2 = keras.layers.Dense(units, activation=activation)
self.main_output = keras.layers.Dense(1)
self.aux_output = keras.layers.Dense(1)
def call(self, inputs):
input_A, input_B = inputs
hidden1 = self.hidden1(input_B)
hidden2 = self.hidden2(hidden1)
concat = keras.layers.concatenate([input_A, hidden2])
main_output = self.main_output(concat)
aux_output = self.aux_output(hidden2)
return main_output, aux_output
model = WideAndDeepModel(30, activation="relu")
모델 저장하고 복원하기¶
모델을 HDF5 포맷을 이용하여 모델 뿐만 아니라 하이퍼파라미터와 파라미터까지도 저장할 수 있다.
또한 옵티마이저와 metrics까지도 저장되어 나중에 다시 꺼내어 쓸 수 있다.
model = keras.models.Sequential([
keras.layers.Dense(30, activation="relu", input_shape=[8]),
keras.layers.Dense(30, activation="relu"),
keras.layers.Dense(1)
])
model.compile(loss="mse", optimizer=keras.optimizers.SGD(learning_rate=1e-3))
history = model.fit(X_train,
y_train,
epochs=10,
validation_data=(X_valid, y_valid))
# 저장하기
model.save("my_keras_model.h5")
# 가져오기
get_model = keras.models.load_model("my_keras_model.h5")
Epoch 1/10
363/363 [==============================] - 0s 851us/step - loss: 1.7515 - val_loss: 0.8723
Epoch 2/10
363/363 [==============================] - 0s 655us/step - loss: 0.6843 - val_loss: 0.6053
Epoch 3/10
363/363 [==============================] - 0s 682us/step - loss: 0.5995 - val_loss: 0.5547
Epoch 4/10
363/363 [==============================] - 0s 682us/step - loss: 0.5577 - val_loss: 0.5242
Epoch 5/10
363/363 [==============================] - 0s 674us/step - loss: 0.5262 - val_loss: 0.5105
Epoch 6/10
363/363 [==============================] - 0s 658us/step - loss: 0.5017 - val_loss: 0.4710
Epoch 7/10
363/363 [==============================] - 0s 655us/step - loss: 0.4819 - val_loss: 0.4476
Epoch 8/10
363/363 [==============================] - 0s 652us/step - loss: 0.4669 - val_loss: 0.4487
Epoch 9/10
363/363 [==============================] - 0s 674us/step - loss: 0.4550 - val_loss: 0.4298
Epoch 10/10
363/363 [==============================] - 0s 688us/step - loss: 0.4458 - val_loss: 0.4254
콜백으로 체크포인트 저장하기¶
fit() 메서드의 callbacks 파라미터를 사용하면 케라스가 훈련의 시작과 끝에 호출할 객체 리스트를 지정할 수 있다
기본적으로 하나의 에포크가 끝날 때 마다 호출된다.
model = keras.models.Sequential([
keras.layers.Dense(30, activation="relu", input_shape=[8]),
keras.layers.Dense(30, activation="relu"),
keras.layers.Dense(1)
])
model.compile(loss="mse", optimizer=keras.optimizers.SGD(learning_rate=1e-3))
# 이러면 검증셋에서 가장 최고 점수를 기록한 모델이 저장된다.
checkpoint_cb = keras.callbacks.ModelCheckpoint("my_keras_model.h5",
save_best_only=True)
# 10번 기다렸는데도 정확도 진전이 없으면 종료시켜버린다.
# restore_best_weights 를 True로 놓으면 최상의 metrics에서 가중치를 유지시킨다.
early_stopping_cb = keras.callbacks.EarlyStopping(patience=10,
restore_best_weights=True)
# 훈련이 Epoch 55에서 조기종료 됨을 볼 수 있다.
history = model.fit(X_train,
y_train,
epochs=100,
validation_data=(X_valid, y_valid),
callbacks=[checkpoint_cb, early_stopping_cb])
model = keras.models.load_model("my_keras_model.h5") # 최상의 모델로 롤백
mse_test = model.evaluate(X_test, y_test)
사용자 정의 콜백을 만드는 것도 가능하다.
https://www.tensorflow.org/guide/keras/custom_callback?hl=ko
여기 참고하는 게 더 빠를듯
class PrintValTrainRatioCallback(keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs):
print("\nval/train: {:.2f}".format(logs["val_loss"] / logs["loss"]))
val_train_ratio_cb = PrintValTrainRatioCallback()
history = model.fit(X_train,
y_train,
epochs=3,
validation_data=(X_valid, y_valid),
callbacks=[val_train_ratio_cb])
Epoch 1/3
286/363 [======================>.......] - ETA: 0s - loss: 0.3514
val/train: 1.10
363/363 [==============================] - 0s 721us/step - loss: 0.3547 - val_loss: 0.3912
Epoch 2/3
317/363 [=========================>....] - ETA: 0s - loss: 0.3518
val/train: 1.19
363/363 [==============================] - 0s 633us/step - loss: 0.3537 - val_loss: 0.4222
Epoch 3/3
318/363 [=========================>....] - ETA: 0s - loss: 0.3577
val/train: 1.16
363/363 [==============================] - 0s 638us/step - loss: 0.3529 - val_loss: 0.4089
텐서보드를 사용하여 시각화하기¶
텐서플로를 설치하면 자동으로 설치되는 텐서보드를 활용하여 다차원 데이터를 시각화하고 통계를 확인하자.
텐서보드는 이벤트 파일이라는 이진 로그파일을 통해 자동으로 변경사항을 읽어 업데이트 해준다.
일반적으로 텐서보드 서버가 루트 로그 디렉터리를 가리키면 프로그램은 각각의 다른 서브디렉터리에 이벤트를 기록한다.
import os
root_logdir = os.path.join(os.curdir, "my_logs")
def get_run_logdir():
import time
run_id = time.strftime("run_%Y_%m_%d-%H_%M_%S")
return os.path.join(root_logdir, run_id)
run_logdir = get_run_logdir()
run_logdir
'.\\my_logs\\run_2022_04_18-14_06_05'
keras.backend.clear_session()
np.random.seed(42)
tf.random.set_seed(42)
model = keras.models.Sequential([
keras.layers.Dense(30, activation="relu", input_shape=[8]),
keras.layers.Dense(30, activation="relu"),
keras.layers.Dense(1)
])
model.compile(loss="mse", optimizer=keras.optimizers.SGD(learning_rate=1e-3))
tensorboard_cb = keras.callbacks.TensorBoard(run_logdir)
history = model.fit(X_train,
y_train,
epochs=30,
validation_data=(X_valid, y_valid),
callbacks=[checkpoint_cb, tensorboard_cb])
신경망 튜닝하기¶
신경망이 유연해서 좋긴하다만 조정할 하이퍼파라미터 또한 유연하게 많다.
어떤 조합이 문제에 대해 최적인지 알려면 물론 bruteforce로 밀어버리면 되겠지만 GridSearch를 이용할 수 있다.
우선 신경망 모델을 사이킷런 추정기처럼 보이도록 만들어주고 GridSearch를 이용하자.
keras.backend.clear_session()
np.random.seed(42)
tf.random.set_seed(42)
def build_model(n_hidden=1, n_neurons=30, learning_rate=3e-3, input_shape=[8]):
model = keras.models.Sequential()
model.add(keras.layers.InputLayer(input_shape=input_shape))
for layer in range(n_hidden):
model.add(keras.layers.Dense(n_neurons, activation="relu"))
model.add(keras.layers.Dense(1))
optimizer = keras.optimizers.SGD(learning_rate=learning_rate)
model.compile(loss="mse", optimizer=optimizer)
return model
KerasRegressor 객체는 build_model로 만들어진 케라스 모델을 감싸주는 wrapper이다.
이러면 이 케라스 모델을 마치 사이킷런의 추정기처럼 이용할 수 있다.
keras_reg = keras.wrappers.scikit_learn.KerasRegressor(build_model)
keras_reg.fit(X_train,
y_train,
epochs=100,
validation_data=(X_valid, y_valid),
callbacks=[keras.callbacks.EarlyStopping(patience=10)])
mse_test = keras_reg.score(X_test, y_test)
y_pred = keras_reg.predict(X_new)
Epoch 1/100
1/363 [..............................] - ETA: 45s - loss: 6.4368
C:\Users\hesh0\AppData\Local\Temp\ipykernel_18548\2686937247.py:1: DeprecationWarning: KerasRegressor is deprecated, use Sci-Keras (https://github.com/adriangb/scikeras) instead. See https://www.adriangb.com/scikeras/stable/migration.html for help migrating.
keras_reg = keras.wrappers.scikit_learn.KerasRegressor(build_model)
모델에 대한 여러 하이퍼파라미터를 조정해보자.
미리 build_model에서 지정해준 하이퍼파라미터들을 쪼개어 검사를 해보자.
from scipy.stats import reciprocal
from sklearn.model_selection import RandomizedSearchCV
param_distribs = {
"n_hidden": [0, 1, 2, 3],
"n_neurons": np.arange(1, 100).tolist(),
"learning_rate": reciprocal(3e-4, 3e-2).rvs(1000).tolist(),
}
rnd_search_cv = RandomizedSearchCV(keras_reg,
param_distribs,
n_iter=10,
cv=3,
verbose=2)
rnd_search_cv.fit(X_train,
y_train,
epochs=100,
# k-fold 방식을 이용하기에 validation은 조기종료에만 이용된다.
validation_data=(X_valid, y_valid),
callbacks=[keras.callbacks.EarlyStopping(patience=10)])
RandomizedSearchCV(cv=3,
estimator=<keras.wrappers.scikit_learn.KerasRegressor object at 0x0000023B6B968E50>,
param_distributions={'learning_rate': [0.001683454924600351,
0.02390836445593178,
0.008731907739399206,
0.004725396149933917,
0.0006154014789262348,
0.0006153331256530192,
0.0003920021771415983,
0.01619845322936229,
0.004779156784872302,
0.007821074275112...
0.005021425736625637,
0.0005703073595961105,
0.001151888789941251,
0.001621231156394198,
0.0024505367684280487,
0.011155092541719619,
0.0007524347058135697,
0.0032032448128444043,
0.004591455636549438,
0.0003715541189658278, ...],
'n_hidden': [0, 1, 2, 3],
'n_neurons': [1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27,
28, 29, 30, ...]},
verbose=2)
print(rnd_search_cv.best_params_)
print(rnd_search_cv.best_score_)
{'n_neurons': 80, 'n_hidden': 3, 'learning_rate': 0.0059640580092043885}
-0.3252355257670085
Evolutional autoML¶
말은 이렇게 했지만 요즘은 최적의 하이퍼파라미터 뿐만 아니라 신경망 구조 자체를 진화론적 관점으로 접근한다.
https://ai.googleblog.com/2018/03/using-evolutionary-automl-to-discover.html
요약하면 뉴런도 없는 하나의 입력층으로 시작해서 더 좋은 결과를 내는 모델을 선택해가며 신경망을 진화시킨다.
하이퍼파라미터는 당연하고 경사 하강법을 대체해서 개별 신경망을 만들어 냈다.
물론 이렇게 기술이 발전하더라도 블로그에서 언급했던 좋은 모델부터 시작하면 더 좋은 결과가 나오기에 미리 잘 고르자.
은닉층 개수¶
당연히도 복잡한 문제일수록 심층 신경망이 얕은 신경망보다 더 잘 작동한다.
현실의 문제가 계층적인 경우가 많으므로 신경망이 깊어질수록 계층적으로 작동하게 되고 좋은 솔루션으로 빨리 수렴할 수 있다.
물론 성능의 차이가 막 크지 않다고 알려져있지만 심층 신경망일수록 더 적은 뉴런으로 해결할 수 있다.
(얕은 경우 뉴런을 많이 써야하지만 층이 많다면 뉴런을 적게 써도 비슷한 성능이 나온다)
이런 방식을 이용하면 저수준 구조를 학습하지 않고도 다른 모델에서 고수준 구조만 따로 학습시킬 수 있다.
예를 들어 얼굴을 인식하는 저수준 구조 모델이 있다면 이를 이용하여 가중치, 편향을 그대로 두고 다음 층에서
헤어 스타일, 쌍커플등만 학습하면 되는데 이를 전이학습이라 부른다.
은닉층의 뉴런 개수¶
수 많은 저수준의 특성들은 비교적 적은 고수준의 특성으로 합쳐질 수 있다. 따라서 보통 입력층에서 뉴런을 크게 넣고 하위 층에서 점차 뉴런을 줄여나간다. (물론 같은 크기를 써도 동일하거나 더 나을 때가 있다)
층의 개수처럼 과대적합이 되기 전까지 뉴런의 개수또한 늘려가도 되지만 실제로는 처음부터 확 늘려두고
조기 종료나 규제 기법을 쓰는 것이 간단하고 효과적이다.
이는 구글에서 stretch pants라고 부르는데 바지가 크면 어찌어찌 입을 수 있는 것과 유사하기 때문이다.
- 일반적으로 은닉층의 뉴런 개수를 늘리기보단, 은닉층의 개수를 늘리는 편이 이득이 크다.
다른 하이퍼 파라미터¶
- 학습률
보통 최적의 학습률은 훈련 알고리즘이 발산하는 학습률인 최대 학습률의 절반 정도 이다.
이 학습률을 찾기 위하여 매우 낮은 학습률 ($10^{-5}$)에서 시작해서 점진적으로 늘려가고
이를 수백번 반복하여 모델을 훈련시킨다. 반복적으로 일정한 값을 학습률에 곱해주면 학습률에 대한 그래프가 나오는데
가장 저점이 아닌 10배 낮은 지점이 최적의 지점이다. - 옵티마이저
당연하게도 구식 경사하강법보다 RMSProp이라던가 더 좋은 옵티마이저를 선택하고 고른
옵티마이저의 하이퍼파라미터를 튜닝하는 것이 중요하다. - 배치 크기
배치가 클수록 GPU와 같은 병렬처리기를 효율적으로 활용할 수 있다. 실제로 많은 연구자들이 GPU 램에 맞는 가장 큰 배치를 이용할 것을 권고하지만 종종 훈련 초기에 불안정하게 훈련되는 경우가 발생할 수 있다.
그래서 그런지 배치를 적게 써야한다는 사람도 있고 이에 또 반대해서 학습률을 점차 늘려간다면
매우 큰 배치를 이용할 수 있다고 주장하는 사람도 있다.
확실히 뭐가 됬던 배치가 클수록 훈련 시간 자체는 짧아진다. 만약 훈련이 불안정하고 결과가 안좋다면 배치 크기를 줄이자. - 활성화 함수
- 반복 횟수
그런데 조기 종료를 이용하는 경우가 다반사라 튜닝할 필요가 거의 없다.
'AI' 카테고리의 다른 글
[핸즈온 머신러닝] chapter 12. 텐서플로에서의 사용자 정의 모델과 훈련 (0) | 2022.04.28 |
---|---|
[핸즈온 머신러닝] chapter 11. 심층 신경망 훈련 (0) | 2022.04.22 |
[핸즈온 머신러닝] chapter 9. 비지도 학습 (0) | 2022.04.22 |
[핸즈온 머신러닝] chapter 8. 차원 축소 (0) | 2022.04.22 |
[핸즈온 머신러닝] chapter 7. 앙상블 학습과 랜덤 포레스트 (0) | 2022.04.22 |
- Total
- Today
- Yesterday