# 일관된 출력을 위해 유사난수 초기화
def reset_graph(seed=42):
tf.reset_default_graph()
tf.set_random_seed(seed)
np.random.seed(seed)
앞 장에서 인공 신경망을 소개했고 두 개의 은닉층만 있는 매우 얕은 심층 신경망을 훈련시켰음
아주 복잡한 문제를 다뤄야 한다면 아마도 훨씬 더 깊은 심층 신경망을 훈련시켜야 함. 이건 그리 쉬운 일이 아님
이 장에서는 이 문제들을 차례대로 살펴보고 해결 방법을 제시하겠음
글로럿과 벤지오는 그들의 논문에서 이 문제를 크게 완화시키는 방법을 제안함.
(또는 팬인fan-in과 팬아웃fan-out으로 부름).
이를 variance_scaling_initializer() 함수를 사용하여 다음과 같이 He 초기화 방식으로 바꿀 수 있음.
import tensorflow as tf
n_inputs = 28 * 28 # MNIST
n_hidden1 = 300
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
he_init = tf.variance_scaling_initializer()
hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,
kernel_initializer=he_init, name="hidden1")
훈련하는 동안 일부 뉴런이 0 이외의 값을 출력하지 않는다는 의미로 죽었다고 말함.
하이퍼파라미터 $\alpha$가 이 함수가 '새는leaky' 정도를 결정. 새는 정도란 z<0일 때 이 함수의 기울기이며, 일반적으로 0.01로 설정.
이 작은 기울기가 LeakyReLU를 절대 죽지 않게 만들어줌.
이 함수도 꽤 잘 작동했으며 (훈련 세트의 과대적합 위험을 줄이는) 규제의 역할을 하는 것처럼 보였음.
이 함수는 대규모 이미지 데이터셋에서는 ReLU보다 성능이 크게 앞섰지만, 소규모 데이터셋에서는 훈련 세트에 과대적합될 위험이 있음
이 함수는 실험에서 다른 모든 ReLU 변종의 성능을 앞질렀음. 훈련 시간이 줄고 신경망의 테스트 세트 성능도 더 높았음.
이 함수의 정의는 [식 11-2]이고, 그 모습은 [그림 11-3]에 나와 있음.
이 함수는 몇 가지를 제외하고는 ReLU와 매우 비슷함.
ELU 활성화 함수의 주요 단점은 ReLU나 그 변종들보다 (지수 함수를 사용하기 때문에) 계산이 느리다는 것.
훈련하는 동안에는 수렴 속도가 빠르기 때문에 상쇄되지만 테스트 시에는 ELU 신경망이 ReLU 신경망보다 느릴 것임.
텐서플로는 신경망에 사용할 수 있는 elu() 함수를 제공함. 다음처럼 간단하게 dense() 함수를 호출할 때 activation 매개변수에 지정하기만 하면 됨.
reset_graph()
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.elu, name="hidden1")
LeakyReLU는 텐서플로가 기본으로 제공하지는 않지만, 다음처럼 간단히 만들어 사용할 수 있음.
reset_graph()
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
def leaky_relu(z, name=None):
return tf.maximum(0.01 * z, z, name=name) # 두 개의 매개변수중 큰 값을 반환
hidden1 = tf.layers.dense(X, n_hidden1, activation=leaky_relu, name="hidden1")
훈련하는 동안 다시 발생하지 않으리란 보장은 없음.
더 일반적으로는, 훈련하는 동안 이전 층의 파라미터가 변함에 따라 각 층에 들어오는 입력의 분포가 변화되는 문제임(내부 공변량 변화Internal Covariate Shift 문제라고 부름).
단순하게 입력 데이터의 평균을 0으로 만들고 정규화한 다음, 각 층에서 두 개의 새로운 파라미터로 결괏값의 스케일을 조정하고 이동시킴
(하나는 스케일 조정을 위해, 다른 하나는 이동을 위해 필요)
다시 말해 이 연산으로 모델이 층마다 입력 데이터의 최적 스케일과 평균을 학습함.
이를 위해 현재 미니배치에서 입력의 평균과 표준편차를 평가함(그래서 이름이 배치 정규화임)
이 값들은 훈련 과정에서 이동 평균moving average을 사용하여 효율적으로 계산되므로,
전체적으로 보면 배치 정규화된 층마다 $\gamma$(스케일), $\beta$(이동), $\mu$(평균), $\sigma$(표준편차) 네 개의 파라미터가 학습됨.
tanh나 심지어 로지스틱 활성화 함수 같이 수렴되는 활성화 함수를 사용하더라도 그래디언트 소실 문제가 크게 감소
또한 네트워크가 가중치 초기화에 훨씬 덜 민감해짐. 그리고 훨씬 큰 학습률을 사용할 수 있어 학습 속도를 크게 개선해줌.
마지막으로, 배치 정규화는 규제와 같은 역할을 하여 다른 규제 기법의 필요성을 줄여줌.
층마다 추가되는 계산이 신경망의 예측이 느려지게 함. 예측이 빨라야 한다면 배치 정규화를 사용하기 전에 ELU + He 초기화만으로 얼마나 잘 수행되는지 확인해보는 것이 좋음.
NOTE_ 경사 하강법은 각 층마다 최적의 스케일과 이동을 위한 파라미터를 찾느라 훈련 초기에는 오히려 느려질 수 있음. 하지만 적절한 값을 찾고 나면 점점 빨라짐.
텐서플로는 손쉽게 입력값을 중앙에 정렬하고 정규화해주는 tf.nn.batch_normalization() 함수를 제공하지만, 평균과 표준편차를 직접 계산해 이 함수의 매개변수로 전달해야 함.
그리고 스케일 조정과 이동을 위한 파라미터를 생성해야 함. 하지만 이는 편리한 방법은 아님.
대신 다음 코드처럼 이 모든 일을 처리해주는 tf.layers.batch_normalization() 함수를 사용하는 편이 좋음.
reset_graph()
# 입력과 출력 크기를 지정하고 은닉층의 뉴런 수를 설정
n_inputs = 28 * 28
n_hidden1 = 300
n_hidden2 = 100
n_outputs = 10
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
training = tf.placeholder_with_default(False, shape=(), name='training')
hidden1 = tf.layers.dense(X, n_hidden1, name="hidden1")
bn1 = tf.layers.batch_normalization(hidden1, training=training, momentum=0.9)
bn1_act = tf.nn.elu(bn1)
hidden2 = tf.layers.dense(bn1_act, n_hidden2, name="hidden2")
bn2 = tf.layers.batch_normalization(hidden2, training=training, momentum=0.9)
bn2_act = tf.nn.elu(bn2)
logits_before_bn = tf.layers.dense(bn2_act, n_outputs, name="outputs")
logits = tf.layers.batch_normalization(logits_before_bn, training=training,
momentum=0.9)
완전 연결 층에는 어떤 활성화 함수도 지정하지 않음. 그리고 training, momentum 매개변수와 함께 tf.layers.batch_normalization() 함수를 사용해 배치 정규화 층을 만듦
새로운 값 v가 주어지면 이동 평균 $\hat{v}$은 다음 식을 통해 갱신됨.
$\hat{v}$ ← $\hat{v}$ x momentum + v x (1-momentum)<br>
적절한 모멘텀 값은 일반적으로 1에 가까움. 예를 들면 0.9, 0.99, 0.999(데이터셋이 크고 미니배치가 작을 경우 9를 더 넣어 1에 더 가깝게 함).
이 코드는 같은 배치 정규화 매개변수가 계속 반복되기 때문에 코드 중복이 많음. 이런 중복을 피하기 위해 파이썬의 (표준 라이브러리에 포함된) functools 모듈 안에 있는
partial() 함수를 사용할 수 있음. 이 함수는 어떤 함수를 감싼 래퍼 함수를 생성하며 매개변수의 기본값을 지정할 수 있도록 도와줌.
이전 코드에서 신경망의 층을 만드는 코드를 다음과 같이 바꿀 수 있음.
reset_graph()
from functools import partial
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
training = tf.placeholder_with_default(False, shape=(), name='training')
my_batch_norm_layer = partial(tf.layers.batch_normalization,
training=training, momentum=0.9)
hidden1 = tf.layers.dense(X, n_hidden1, name="hidden1")
bn1 = my_batch_norm_layer(hidden1)
bn1_act = tf.nn.elu(bn1)
hidden2 = tf.layers.dense(bn1_act, n_hidden2, name="hidden2")
bn2 = my_batch_norm_layer(hidden2)
bn2_act = tf.nn.elu(bn2)
logits_before_bn = tf.layers.dense(bn2_act, n_outputs, name="outputs")
logits = my_batch_norm_layer(logits_before_bn)
데이터 로드
import numpy as np
(X_train, y_train), (X_test, y_test) = tf.keras.datasets.mnist.load_data()
X_train = X_train.astype(np.float32).reshape(-1, 28*28) / 255.0
X_test = X_test.astype(np.float32).reshape(-1, 28*28) / 255.0
y_train = y_train.astype(np.int32)
y_test = y_test.astype(np.int32)
X_valid, X_train = X_train[:5000], X_train[5000:]
y_valid, y_train = y_train[:5000], y_train[5000:]
def shuffle_batch(X, y, batch_size):
rnd_idx = np.random.permutation(len(X))
n_batches = len(X) // batch_size
for batch_idx in np.array_split(rnd_idx, n_batches):
X_batch, y_batch = X[batch_idx], y[batch_idx]
yield X_batch, y_batch
구성 단계의 나머지 부분은 비용 함수를 정의하고 옵티마이저를 생성해서 비용 함수를 최소화하도록 함수를 호출한 다음, 평가 연산을 정의하고 Saver 객체를 만드는 것 등임.
reset_graph()
batch_norm_momentum = 0.9
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int32, shape=(None), name="y")
training = tf.placeholder_with_default(False, shape=(), name='training')
with tf.name_scope("dnn"):
he_init = tf.variance_scaling_initializer()
my_batch_norm_layer = partial(
tf.layers.batch_normalization,
training=training,
momentum=batch_norm_momentum)
my_dense_layer = partial(
tf.layers.dense,
kernel_initializer=he_init)
hidden1 = my_dense_layer(X, n_hidden1, name="hidden1")
bn1 = tf.nn.elu(my_batch_norm_layer(hidden1))
hidden2 = my_dense_layer(bn1, n_hidden2, name="hidden2")
bn2 = tf.nn.elu(my_batch_norm_layer(hidden2))
logits_before_bn = my_dense_layer(bn2, n_outputs, name="outputs")
logits = my_batch_norm_layer(logits_before_bn)
with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
loss = tf.reduce_mean(xentropy, name="loss")
learning_rate = 0.01
with tf.name_scope("train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
with tf.name_scope("eval"):
correct = tf.nn.in_top_k(logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")
init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 20
batch_size = 200
실행단계
이 연산은 자동으로 UPDATE_OPS 컬렉션에 추가되므로 우리가 할 일은 컬렉션에서 이 연산들을 뽑아내어 훈련이 반복될 때마다 실행해주면 됨.
extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):
sess.run([training_op, extra_update_ops],
feed_dict={training: True, X: X_batch, y: y_batch})
accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})
print(epoch, "검증 세트 정확도:", accuracy_val)
save_path = saver.save(sess, "./my_model_final.ckpt")
0 검증 세트 정확도: 0.8952 1 검증 세트 정확도: 0.9202 2 검증 세트 정확도: 0.9318 3 검증 세트 정확도: 0.9422 4 검증 세트 정확도: 0.9468 5 검증 세트 정확도: 0.954 6 검증 세트 정확도: 0.9568 7 검증 세트 정확도: 0.96 8 검증 세트 정확도: 0.962 9 검증 세트 정확도: 0.9638 10 검증 세트 정확도: 0.9662 11 검증 세트 정확도: 0.9682 12 검증 세트 정확도: 0.9672 13 검증 세트 정확도: 0.9696 14 검증 세트 정확도: 0.9706 15 검증 세트 정확도: 0.9704 16 검증 세트 정확도: 0.9718 17 검증 세트 정확도: 0.9726 18 검증 세트 정확도: 0.9738 19 검증 세트 정확도: 0.9742
(순환 신경망에서 일반적으로 널리 사용됨)
일반적으로 배치 정규화를 선호
MNIST를 위한 간단한 신경망을 만들고 그래디언트 클리핑을 적용(시작 부분은 이전과 동일)
reset_graph()
n_inputs = 28 * 28 # MNIST
n_hidden1 = 300
n_hidden2 = 50
n_hidden3 = 50
n_hidden4 = 50
n_hidden5 = 50
n_outputs = 10
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int32, shape=(None), name="y")
with tf.name_scope("dnn"):
hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")
hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2")
hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3")
hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4")
hidden5 = tf.layers.dense(hidden4, n_hidden5, activation=tf.nn.relu, name="hidden5")
logits = tf.layers.dense(hidden5, n_outputs, name="outputs")
with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
loss = tf.reduce_mean(xentropy, name="loss")
learning_rate = 0.01
이제 그래디언트 클리핑을 적용
threshold = 1.0
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
grads_and_vars = optimizer.compute_gradients(loss)
capped_gvs = [(tf.clip_by_value(grad, -threshold, threshold), var)
for grad, var in grads_and_vars]
training_op = optimizer.apply_gradients(capped_gvs)
그러고 나서 이전과 마찬가지로 매 훈련 단계마다 training_op을 실행.
이 코드는 그래디언트를 계산하고 그것을 -1.0과 1.0 사이로 클리핑해서 적용함.
with tf.name_scope("eval"):
correct = tf.nn.in_top_k(logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")
init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 20
batch_size = 200
with tf.Session() as sess:
init.run()
for epoch in range(n_epochs):
for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):
sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})
print(epoch, "검증 세트 정확도:", accuracy_val)
save_path = saver.save(sess, "./my_model_final.ckpt")
0 검증 세트 정확도: 0.288 1 검증 세트 정확도: 0.7938 2 검증 세트 정확도: 0.8796 3 검증 세트 정확도: 0.9058 4 검증 세트 정확도: 0.9164 5 검증 세트 정확도: 0.9218 6 검증 세트 정확도: 0.9292 7 검증 세트 정확도: 0.9358 8 검증 세트 정확도: 0.938 9 검증 세트 정확도: 0.9416 10 검증 세트 정확도: 0.9456 11 검증 세트 정확도: 0.9472 12 검증 세트 정확도: 0.9476 13 검증 세트 정확도: 0.953 14 검증 세트 정확도: 0.9566 15 검증 세트 정확도: 0.9566 16 검증 세트 정확도: 0.9578 17 검증 세트 정확도: 0.9586 18 검증 세트 정확도: 0.9624 19 검증 세트 정확도: 0.9614
이 방법은 훈련 속도를 크게 높여줄 뿐만 아니라 필요한 훈련 데이터도 훨씬 적음.
예를 들어 동물, 식물, 자동차, 생활용품을 포함하여 100개의 카테고리로 구분된 이미지를 분류하도록 훈련시킨 DNN을 가지고 있다고 가정.
그리고 이제 구체적인 자동차의 종류를 분류하는 DNN을 훈련시키려 할 때, 이런 작업들은 비슷한 점이 많으므로 첫 번째 신경망의 일부를 재사용(그림 11-4).
NOTE_ 만약 원래 문제에서 사용한 것과 크기가 다른 이미지를 입력으로 사용한다면 원본 모델에 맞는 크기로 변경하는 전처리 단계를 추가해야 함.
일반적으로 전이 학습은 입력이 비슷한 저수준 특성을 가질 때 잘 작동함.
이 함수는 Saver 객체를 반환하는데, 나중에 저장된 모델 파라미터를 불러올 때 사용함.
기본적으로 Saver 객체는 .meta 확장자를 가진 파일에 그래프 구조를 저장하므로 이 파일을 로드해야 함.
reset_graph()
saver = tf.train.import_meta_graph("./my_model_final.ckpt.meta")
텐서 이름은 연산 이름 뒤에 :0을 붙임(두 번째 출력일 땐 :1, 세 번째일 땐 :2).
X = tf.get_default_graph().get_tensor_by_name("X:0")
y = tf.get_default_graph().get_tensor_by_name("y:0")
accuracy = tf.get_default_graph().get_tensor_by_name("eval/accuracy:0")
training_op = tf.get_default_graph().get_operation_by_name("GradientDescent")
이런 경우 텐서보드를 사용하여 그래프를 탐색하거나 get_operations() 메서드를 사용해 모든 연산의 리스트를 볼 수 있음.
for op in tf.get_default_graph().get_operations():
print(op.name)
X y hidden1/kernel/Initializer/random_uniform/shape hidden1/kernel/Initializer/random_uniform/min hidden1/kernel/Initializer/random_uniform/max hidden1/kernel/Initializer/random_uniform/RandomUniform hidden1/kernel/Initializer/random_uniform/sub hidden1/kernel/Initializer/random_uniform/mul hidden1/kernel/Initializer/random_uniform hidden1/kernel hidden1/kernel/Assign hidden1/kernel/read hidden1/bias/Initializer/zeros hidden1/bias hidden1/bias/Assign hidden1/bias/read dnn/hidden1/MatMul dnn/hidden1/BiasAdd dnn/hidden1/Relu hidden2/kernel/Initializer/random_uniform/shape hidden2/kernel/Initializer/random_uniform/min hidden2/kernel/Initializer/random_uniform/max hidden2/kernel/Initializer/random_uniform/RandomUniform hidden2/kernel/Initializer/random_uniform/sub hidden2/kernel/Initializer/random_uniform/mul hidden2/kernel/Initializer/random_uniform hidden2/kernel hidden2/kernel/Assign hidden2/kernel/read hidden2/bias/Initializer/zeros hidden2/bias hidden2/bias/Assign hidden2/bias/read dnn/hidden2/MatMul dnn/hidden2/BiasAdd dnn/hidden2/Relu hidden3/kernel/Initializer/random_uniform/shape hidden3/kernel/Initializer/random_uniform/min hidden3/kernel/Initializer/random_uniform/max hidden3/kernel/Initializer/random_uniform/RandomUniform hidden3/kernel/Initializer/random_uniform/sub hidden3/kernel/Initializer/random_uniform/mul hidden3/kernel/Initializer/random_uniform hidden3/kernel hidden3/kernel/Assign hidden3/kernel/read hidden3/bias/Initializer/zeros hidden3/bias hidden3/bias/Assign hidden3/bias/read dnn/hidden3/MatMul dnn/hidden3/BiasAdd dnn/hidden3/Relu hidden4/kernel/Initializer/random_uniform/shape hidden4/kernel/Initializer/random_uniform/min hidden4/kernel/Initializer/random_uniform/max hidden4/kernel/Initializer/random_uniform/RandomUniform hidden4/kernel/Initializer/random_uniform/sub hidden4/kernel/Initializer/random_uniform/mul hidden4/kernel/Initializer/random_uniform hidden4/kernel hidden4/kernel/Assign hidden4/kernel/read hidden4/bias/Initializer/zeros hidden4/bias hidden4/bias/Assign hidden4/bias/read dnn/hidden4/MatMul dnn/hidden4/BiasAdd dnn/hidden4/Relu hidden5/kernel/Initializer/random_uniform/shape hidden5/kernel/Initializer/random_uniform/min hidden5/kernel/Initializer/random_uniform/max hidden5/kernel/Initializer/random_uniform/RandomUniform hidden5/kernel/Initializer/random_uniform/sub hidden5/kernel/Initializer/random_uniform/mul hidden5/kernel/Initializer/random_uniform hidden5/kernel hidden5/kernel/Assign hidden5/kernel/read hidden5/bias/Initializer/zeros hidden5/bias hidden5/bias/Assign hidden5/bias/read dnn/hidden5/MatMul dnn/hidden5/BiasAdd dnn/hidden5/Relu outputs/kernel/Initializer/random_uniform/shape outputs/kernel/Initializer/random_uniform/min outputs/kernel/Initializer/random_uniform/max outputs/kernel/Initializer/random_uniform/RandomUniform outputs/kernel/Initializer/random_uniform/sub outputs/kernel/Initializer/random_uniform/mul outputs/kernel/Initializer/random_uniform outputs/kernel outputs/kernel/Assign outputs/kernel/read outputs/bias/Initializer/zeros outputs/bias outputs/bias/Assign outputs/bias/read dnn/outputs/MatMul dnn/outputs/BiasAdd loss/SparseSoftmaxCrossEntropyWithLogits/Shape loss/SparseSoftmaxCrossEntropyWithLogits/SparseSoftmaxCrossEntropyWithLogits loss/Const loss/loss gradients/Shape gradients/grad_ys_0 gradients/Fill gradients/loss/loss_grad/Reshape/shape gradients/loss/loss_grad/Reshape gradients/loss/loss_grad/Shape gradients/loss/loss_grad/Tile gradients/loss/loss_grad/Shape_1 gradients/loss/loss_grad/Shape_2 gradients/loss/loss_grad/Const gradients/loss/loss_grad/Prod gradients/loss/loss_grad/Const_1 gradients/loss/loss_grad/Prod_1 gradients/loss/loss_grad/Maximum/y gradients/loss/loss_grad/Maximum gradients/loss/loss_grad/floordiv gradients/loss/loss_grad/Cast gradients/loss/loss_grad/truediv gradients/zeros_like gradients/loss/SparseSoftmaxCrossEntropyWithLogits/SparseSoftmaxCrossEntropyWithLogits_grad/PreventGradient gradients/loss/SparseSoftmaxCrossEntropyWithLogits/SparseSoftmaxCrossEntropyWithLogits_grad/ExpandDims/dim gradients/loss/SparseSoftmaxCrossEntropyWithLogits/SparseSoftmaxCrossEntropyWithLogits_grad/ExpandDims gradients/loss/SparseSoftmaxCrossEntropyWithLogits/SparseSoftmaxCrossEntropyWithLogits_grad/mul gradients/dnn/outputs/BiasAdd_grad/BiasAddGrad gradients/dnn/outputs/BiasAdd_grad/tuple/group_deps gradients/dnn/outputs/BiasAdd_grad/tuple/control_dependency gradients/dnn/outputs/BiasAdd_grad/tuple/control_dependency_1 gradients/dnn/outputs/MatMul_grad/MatMul gradients/dnn/outputs/MatMul_grad/MatMul_1 gradients/dnn/outputs/MatMul_grad/tuple/group_deps gradients/dnn/outputs/MatMul_grad/tuple/control_dependency gradients/dnn/outputs/MatMul_grad/tuple/control_dependency_1 gradients/dnn/hidden5/Relu_grad/ReluGrad gradients/dnn/hidden5/BiasAdd_grad/BiasAddGrad gradients/dnn/hidden5/BiasAdd_grad/tuple/group_deps gradients/dnn/hidden5/BiasAdd_grad/tuple/control_dependency gradients/dnn/hidden5/BiasAdd_grad/tuple/control_dependency_1 gradients/dnn/hidden5/MatMul_grad/MatMul gradients/dnn/hidden5/MatMul_grad/MatMul_1 gradients/dnn/hidden5/MatMul_grad/tuple/group_deps gradients/dnn/hidden5/MatMul_grad/tuple/control_dependency gradients/dnn/hidden5/MatMul_grad/tuple/control_dependency_1 gradients/dnn/hidden4/Relu_grad/ReluGrad gradients/dnn/hidden4/BiasAdd_grad/BiasAddGrad gradients/dnn/hidden4/BiasAdd_grad/tuple/group_deps gradients/dnn/hidden4/BiasAdd_grad/tuple/control_dependency gradients/dnn/hidden4/BiasAdd_grad/tuple/control_dependency_1 gradients/dnn/hidden4/MatMul_grad/MatMul gradients/dnn/hidden4/MatMul_grad/MatMul_1 gradients/dnn/hidden4/MatMul_grad/tuple/group_deps gradients/dnn/hidden4/MatMul_grad/tuple/control_dependency gradients/dnn/hidden4/MatMul_grad/tuple/control_dependency_1 gradients/dnn/hidden3/Relu_grad/ReluGrad gradients/dnn/hidden3/BiasAdd_grad/BiasAddGrad gradients/dnn/hidden3/BiasAdd_grad/tuple/group_deps gradients/dnn/hidden3/BiasAdd_grad/tuple/control_dependency gradients/dnn/hidden3/BiasAdd_grad/tuple/control_dependency_1 gradients/dnn/hidden3/MatMul_grad/MatMul gradients/dnn/hidden3/MatMul_grad/MatMul_1 gradients/dnn/hidden3/MatMul_grad/tuple/group_deps gradients/dnn/hidden3/MatMul_grad/tuple/control_dependency gradients/dnn/hidden3/MatMul_grad/tuple/control_dependency_1 gradients/dnn/hidden2/Relu_grad/ReluGrad gradients/dnn/hidden2/BiasAdd_grad/BiasAddGrad gradients/dnn/hidden2/BiasAdd_grad/tuple/group_deps gradients/dnn/hidden2/BiasAdd_grad/tuple/control_dependency gradients/dnn/hidden2/BiasAdd_grad/tuple/control_dependency_1 gradients/dnn/hidden2/MatMul_grad/MatMul gradients/dnn/hidden2/MatMul_grad/MatMul_1 gradients/dnn/hidden2/MatMul_grad/tuple/group_deps gradients/dnn/hidden2/MatMul_grad/tuple/control_dependency gradients/dnn/hidden2/MatMul_grad/tuple/control_dependency_1 gradients/dnn/hidden1/Relu_grad/ReluGrad gradients/dnn/hidden1/BiasAdd_grad/BiasAddGrad gradients/dnn/hidden1/BiasAdd_grad/tuple/group_deps gradients/dnn/hidden1/BiasAdd_grad/tuple/control_dependency gradients/dnn/hidden1/BiasAdd_grad/tuple/control_dependency_1 gradients/dnn/hidden1/MatMul_grad/MatMul gradients/dnn/hidden1/MatMul_grad/MatMul_1 gradients/dnn/hidden1/MatMul_grad/tuple/group_deps gradients/dnn/hidden1/MatMul_grad/tuple/control_dependency gradients/dnn/hidden1/MatMul_grad/tuple/control_dependency_1 clip_by_value/Minimum/y clip_by_value/Minimum clip_by_value/y clip_by_value clip_by_value_1/Minimum/y clip_by_value_1/Minimum clip_by_value_1/y clip_by_value_1 clip_by_value_2/Minimum/y clip_by_value_2/Minimum clip_by_value_2/y clip_by_value_2 clip_by_value_3/Minimum/y clip_by_value_3/Minimum clip_by_value_3/y clip_by_value_3 clip_by_value_4/Minimum/y clip_by_value_4/Minimum clip_by_value_4/y clip_by_value_4 clip_by_value_5/Minimum/y clip_by_value_5/Minimum clip_by_value_5/y clip_by_value_5 clip_by_value_6/Minimum/y clip_by_value_6/Minimum clip_by_value_6/y clip_by_value_6 clip_by_value_7/Minimum/y clip_by_value_7/Minimum clip_by_value_7/y clip_by_value_7 clip_by_value_8/Minimum/y clip_by_value_8/Minimum clip_by_value_8/y clip_by_value_8 clip_by_value_9/Minimum/y clip_by_value_9/Minimum clip_by_value_9/y clip_by_value_9 clip_by_value_10/Minimum/y clip_by_value_10/Minimum clip_by_value_10/y clip_by_value_10 clip_by_value_11/Minimum/y clip_by_value_11/Minimum clip_by_value_11/y clip_by_value_11 GradientDescent/learning_rate GradientDescent/update_hidden1/kernel/ApplyGradientDescent GradientDescent/update_hidden1/bias/ApplyGradientDescent GradientDescent/update_hidden2/kernel/ApplyGradientDescent GradientDescent/update_hidden2/bias/ApplyGradientDescent GradientDescent/update_hidden3/kernel/ApplyGradientDescent GradientDescent/update_hidden3/bias/ApplyGradientDescent GradientDescent/update_hidden4/kernel/ApplyGradientDescent GradientDescent/update_hidden4/bias/ApplyGradientDescent GradientDescent/update_hidden5/kernel/ApplyGradientDescent GradientDescent/update_hidden5/bias/ApplyGradientDescent GradientDescent/update_outputs/kernel/ApplyGradientDescent GradientDescent/update_outputs/bias/ApplyGradientDescent GradientDescent eval/in_top_k/InTopKV2/k eval/in_top_k/InTopKV2 eval/Cast eval/Const eval/accuracy init save/Const save/SaveV2/tensor_names save/SaveV2/shape_and_slices save/SaveV2 save/control_dependency save/RestoreV2/tensor_names save/RestoreV2/shape_and_slices save/RestoreV2 save/Assign save/Assign_1 save/Assign_2 save/Assign_3 save/Assign_4 save/Assign_5 save/Assign_6 save/Assign_7 save/Assign_8 save/Assign_9 save/Assign_10 save/Assign_11 save/restore_all
또 다른 방법은 다른 사람을 위해 중요한 연산들을 모아놓은 컬렉션을 만드는 것.
for op in (X, y, accuracy, training_op):
tf.add_to_collection("my_important_ops", op)
X, y, accuracy, training_op = tf.get_collection("my_important_ops")
with tf.Session() as sess:
saver.restore(sess, "./my_model_final.ckpt")
# 나만의 데이터로 모델 훈련하기
for epoch in range(n_epochs):
for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):
sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})
print(epoch, "검증 세트 정확도:", accuracy_val)
save_path = saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt 0 검증 세트 정확도: 0.9726 1 검증 세트 정확도: 0.973 2 검증 세트 정확도: 0.9748 3 검증 세트 정확도: 0.976 4 검증 세트 정확도: 0.9752 5 검증 세트 정확도: 0.9762 6 검증 세트 정확도: 0.975 7 검증 세트 정확도: 0.9772 8 검증 세트 정확도: 0.978 9 검증 세트 정확도: 0.9766 10 검증 세트 정확도: 0.9776 11 검증 세트 정확도: 0.9764 12 검증 세트 정확도: 0.9776 13 검증 세트 정확도: 0.9778 14 검증 세트 정확도: 0.9772 15 검증 세트 정확도: 0.9778 16 검증 세트 정확도: 0.9782 17 검증 세트 정확도: 0.9784 18 검증 세트 정확도: 0.979 19 검증 세트 정확도: 0.9786
그러나 이경우에는 훈련된 모델을 복원하는 Saver 객체가 필요하고 새로운 모델을 저장하는 또 다른 Saver 객체가 있어야 함.
다음 코드는 은닉층 1, 2, 3만 복원
tf.reset_default_graph()
# 이전과 같은 은닉층 1-3을 가진 새로운 모델을 만듦
n_inputs = 28 * 28 # MNIST
n_hidden1 = 300 # 재사용
n_hidden2 = 50 # 재사용
n_hidden3 = 50 # 재사용
n_hidden4 = 20 # 새로 만듦!
n_outputs = 10 # 새로 만듦!
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int32, shape=(None), name="y")
with tf.name_scope("dnn"):
hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1") # 재사용
hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2") # 재사용
hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3") # 재사용
hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4") # 새로 만듦!
logits = tf.layers.dense(hidden4, n_outputs, name="outputs") # 새로 만듦!
with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
loss = tf.reduce_mean(xentropy, name="loss")
with tf.name_scope("eval"):
correct = tf.nn.in_top_k(logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")
with tf.name_scope("train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
scope="hidden[123]") # 정규표현식, 은닉층 1~3 모든 변수 목록을 얻음
restore_saver = tf.train.Saver(reuse_vars) # 1-3층 복원시킬 Saver 객체 만듦
init = tf.global_variables_initializer() # 새 변수, 구 변수 모두 초기화하는 연산을 생성
saver = tf.train.Saver() # 새로운 모델 저장할 두 번째 Saver 객체 생성
with tf.Session() as sess:
init.run() # 모든 변수 초기화
restore_saver.restore(sess, "./my_model_final.ckpt") # 원본 모델의 은닉층 1~3까지의 변수를 복원
for epoch in range(n_epochs): # 새로운 모델 훈련시키고 저장
for X_batch, y_batch in shuffle_batch(X_train, y_train, batch_size):
sess.run(training_op, feed_dict={X: X_batch, y: y_batch})
accuracy_val = accuracy.eval(feed_dict={X: X_valid, y: y_valid})
print(epoch, "검증 세트 정확도:", accuracy_val)
save_path = saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt 0 검증 세트 정확도: 0.911 1 검증 세트 정확도: 0.9358 2 검증 세트 정확도: 0.944 3 검증 세트 정확도: 0.9496 4 검증 세트 정확도: 0.9538 5 검증 세트 정확도: 0.9568 6 검증 세트 정확도: 0.9578 7 검증 세트 정확도: 0.9588 8 검증 세트 정확도: 0.9582 9 검증 세트 정확도: 0.9624 10 검증 세트 정확도: 0.9628 11 검증 세트 정확도: 0.964 12 검증 세트 정확도: 0.9664 13 검증 세트 정확도: 0.9656 14 검증 세트 정확도: 0.967 15 검증 세트 정확도: 0.9654 16 검증 세트 정확도: 0.969 17 검증 세트 정확도: 0.9684 18 검증 세트 정확도: 0.969 19 검증 세트 정확도: 0.9692
다음 코드는 다른 프레임워크를 사용해 만든 모델의 첫 번째 은닉층으로부터 가중치와 편향을 어떻게 복사하는지 보여줌.
reset_graph()
n_inputs = 2
n_hidden1 = 3
# 재사용할 모델 파라미터를 추출
original_w = [[1., 2., 3.], [4., 5., 6.]] # 다른 프레임워크로부터 가중치를 로드
original_b = [7., 8., 9.] # 다른 프레임워크로부터 편향을 로드
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1")
# [...] 모델의 나머지 부분을 구성
# 텐서플로 변수는 초기화와 연관된 할당 연산(변수 이름에 '/Assign'이 붙은 이름)을 가지고 있음
# hidden1 변수의 할당 노드에 대한 핸들을 구합니다
graph = tf.get_default_graph()
assign_kernel = graph.get_operation_by_name("hidden1/kernel/Assign")
assign_bias = graph.get_operation_by_name("hidden1/bias/Assign")
# 할당 연산의 두 번째 입력값(변수에 할당될 값)의 핸들을 구함, 여기서는 변수의 초깃값
init_kernel = assign_kernel.inputs[1]
init_bias = assign_bias.inputs[1]
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init, feed_dict={init_kernel: original_w, init_bias: original_b})
# [...] 새 작업에 모델을 훈련시킵니다
하위층의 가중치가 고정되면 상위층의 가중치를 훈련시키기 쉬움.
reset_graph()
n_inputs = 28 * 28 # MNIST
n_hidden1 = 300 # 재사용
n_hidden2 = 50 # 재사용
n_hidden3 = 50 # 재사용
n_hidden4 = 20 # 새로 만듦!
n_outputs = 10 # 새로 만듦!
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int32, shape=(None), name="y")
with tf.name_scope("dnn"):
hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu, name="hidden1") # 재사용
hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu, name="hidden2") # 재사용
hidden3 = tf.layers.dense(hidden2, n_hidden3, activation=tf.nn.relu, name="hidden3") # 재사용
hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="hidden4") # 새로 만듦!
logits = tf.layers.dense(hidden4, n_outputs, name="outputs") # 새로 만듦!
with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
loss = tf.reduce_mean(xentropy, name="loss")
with tf.name_scope("eval"):
correct = tf.nn.in_top_k(logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")
with tf.name_scope("train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
# 은닉층 3, 4와 출력층에 있는 학습할 변수 목록을 모두 구함. 은닉층 1, 2에 있는 변수는 남겨둠
train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
scope="hidden[34]|outputs")
training_op = optimizer.minimize(loss, var_list=train_vars) # 학습할 변수 목록을 옵티마이저의 minimize() 함수에 제공
reset_graph()
n_inputs = 28 * 28 # MNIST
n_hidden1 = 300 # 재사용
n_hidden2 = 50 # 재사용
n_hidden3 = 50 # 재사용
n_hidden4 = 20 # 새로 만듦!
n_outputs = 10 # 새로 만듦!
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
y = tf.placeholder(tf.int32, shape=(None), name="y")
with tf.name_scope("dnn"):
hidden1 = tf.layers.dense(X, n_hidden1, activation=tf.nn.relu,
name="hidden1") # 동결층 재사용
hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=tf.nn.relu,
name="hidden2") # 동결층 재사용
hidden2_stop = tf.stop_gradient(hidden2)
hidden3 = tf.layers.dense(hidden2_stop, n_hidden3, activation=tf.nn.relu,
name="hidden3") # 동결하지 않고 재사용
hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu,
name="hidden4") # 새로 만듦!
logits = tf.layers.dense(hidden4, n_outputs, name="outputs") # 새로 만듦!
with tf.name_scope("loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)
loss = tf.reduce_mean(xentropy, name="loss")
with tf.name_scope("eval"):
correct = tf.nn.in_top_k(logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")
with tf.name_scope("train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
reuse_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,
scope="hidden[123]") # 정규 표현식
restore_saver = tf.train.Saver(reuse_vars) # 1-3층 복원
init = tf.global_variables_initializer()
saver = tf.train.Saver()
import numpy as np
n_batches = len(X_train) # batch_size
with tf.Session() as sess:
init.run()
restore_saver.restore(sess, "./my_model_final.ckpt")
h2_cache = sess.run(hidden2, feed_dict={X: X_train})
h2_cache_valid = sess.run(hidden2, feed_dict={X: X_valid})
for epoch in range(n_epochs):
shuffled_idx = np.random.permutation(len(X_train))
hidden2_batches = np.array_split(h2_cache[shuffled_idx], n_batches) # 은닉층 2의 출력을 배치로 만듦
y_batches = np.array_split(y_train[shuffled_idx], n_batches) # 타깃
for hidden2_batch, y_batch in zip(hidden2_batches, y_batches):
# 훈련 연산 실행(1,2층 동결), 두 번째 은닉층의 출력을 배치로 만들어 주입
sess.run(training_op, feed_dict={hidden2:hidden2_batch, y:y_batch})
accuracy_val = accuracy.eval(feed_dict={hidden2: h2_cache_valid,
y: y_valid})
print(epoch, "검증 세트 정확도:", accuracy_val)
save_path = saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt 0 검증 세트 정확도: 0.9506 1 검증 세트 정확도: 0.9508 2 검증 세트 정확도: 0.9598 3 검증 세트 정확도: 0.9576 4 검증 세트 정확도: 0.9588 5 검증 세트 정확도: 0.9458 6 검증 세트 정확도: 0.9616 7 검증 세트 정확도: 0.9624 8 검증 세트 정확도: 0.9536 9 검증 세트 정확도: 0.9598 10 검증 세트 정확도: 0.9582 11 검증 세트 정확도: 0.9634 12 검증 세트 정확도: 0.9562 13 검증 세트 정확도: 0.96 14 검증 세트 정확도: 0.9662 15 검증 세트 정확도: 0.954 16 검증 세트 정확도: 0.9664 17 검증 세트 정확도: 0.9614 18 검증 세트 정확도: 0.9648 19 검증 세트 정확도: 0.9656
그래서 재사용할 적절한 층의 개수를 알아야 함.
2.모델을 훈련시키고 얼마나 성능이 나오는지 지켜봄.
3.가장 위쪽의 은닉층 한 개나 두 개의 동결을 해제해서 역전파로 가중치가 변경되게 하고 성능이 향상되는지 확인
훈련 데이터가 많을수록 많은 층을 동결 해제할 수 있음.
재사용에 적절한 층의 개수를 찾을 때까지 반복.
그리고 코드와 미리 학습된 모델, 잘 알려진 이미지 데이터셋을 내려받기 위한 도구도 포함.
여러 가지 데이터셋에 훈련시킨 많은 컴퓨터 비전 모델이 있음.
이런 방식으로 모든 층이 훈련되면 지도 학습으로(즉, 역전파 알고리즘을 사용해) 신경망을 세밀하게 튜닝할 수 있음.
그리고 이 신경망의 하위층을 실제 작업을 위해 재사용함.
두 개의 다른 이미지가 같은 사람의 것인지 감지하는 첫 번째 신경망을 훈련시킬 수 있음.
경사 하강법은 가중치에 대한 비용 함수 J($\theta$)의 그래디언트( $\nabla$$\theta$J($\theta$) )에 학습률 $\eta$를 곱한 것을 바로 차감하여 가중치 $\theta$를 갱신함. 공식은 $\theta$ $\leftarrow$ $\theta$ - $\eta$$\nabla$<sub>$\theta$</sub>J($\theta$)
이 식은 이전 그래디언트가 얼마였는지 고려하지 않아 국부적으로 그래디언트가 아주 작으면 매우 느려질 것.
또한 이 기법은 지역 최적점local optima을 건너 뛰도록 하는 데도 도움이 됨.
모멘텀 0.9에서 보통 잘 작동하며 경사 하강법보다 거의 항상 더 빠름.
optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate,
momentum=0.9)
기본 모멘텀 최적화보다 거의 항상 더 빠름. 기본 아이디어는 현재 위치가 아니라 모멘텀의 방향으로 조금 앞서서 비용 함수의 그래디언트를 계산하는 것(식 11-5).
기본 모멘텀 최적화와 다른 점은 $\theta$가 아니라 $\theta$ - $\beta$m 에서 그래디언트를 측정하는 것뿐.
그래서 [그림 11-6]처럼 원래 위치에서의 그래디언트를 사용하는 것보다 그 방향으로 조금 더 나아가서 측정한 그래디언트를 사용하는 것이 약간 더 정확.
($\nabla$1은 시작점 $\theta$에서 측정한 비용 함수의 그래디언트를 나타내고, $\nabla$2는 $\theta$ - $\beta$m에서 측정한 그래디언트를 나타냄).
optimizer = tf.train.MomentumOptimizer(learning_rate=learning_rate,
momentum=0.9, use_nesterov=True)