%load_ext watermark
%watermark -v -p numpy,sklearn,scipy,matplotlib,tensorflow
CPython 3.6.8 IPython 7.2.0 numpy 1.15.4 sklearn 0.20.2 scipy 1.1.0 matplotlib 3.0.2 tensorflow 1.13.1
11장 – 심층 신경망 훈련
이 노트북은 11장에 있는 모든 샘플 코드를 가지고 있습니다.
파이썬 2와 3을 모두 지원합니다. 공통 모듈을 임포트하고 맷플롯립 그림이 노트북 안에 포함되도록 설정하고 생성한 그림을 저장하기 위한 함수를 준비합니다:
# 파이썬 2와 파이썬 3 지원
from __future__ import division, print_function, unicode_literals
# 공통
import numpy as np
import os
# 일관된 출력을 위해 유사난수 초기화
def reset_graph(seed=42):
tf.reset_default_graph()
tf.set_random_seed(seed)
np.random.seed(seed)
# 맷플롯립 설정
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
# 한글출력
plt.rcParams['font.family'] = 'NanumBarunGothic'
plt.rcParams['axes.unicode_minus'] = False
# 그림을 저장할 폴더
PROJECT_ROOT_DIR = "."
CHAPTER_ID = "deep"
def save_fig(fig_id, tight_layout=True):
path = os.path.join(PROJECT_ROOT_DIR, "images", CHAPTER_ID, fig_id + ".png")
if tight_layout:
plt.tight_layout()
plt.savefig(path, format='png', dpi=300)
def logit(z):
return 1 / (1 + np.exp(-z))
z = np.linspace(-5, 5, 200)
plt.plot([-5, 5], [0, 0], 'k-')
plt.plot([-5, 5], [1, 1], 'k--')
plt.plot([0, 0], [-0.2, 1.2], 'k-')
plt.plot([-5, 5], [-3/4, 7/4], 'g--')
plt.plot(z, logit(z), "b-", linewidth=2)
props = dict(facecolor='black', shrink=0.1)
plt.annotate('수렴', xytext=(3.5, 0.7), xy=(5, 1), arrowprops=props, fontsize=14, ha="center")
plt.annotate('수렴', xytext=(-3.5, 0.3), xy=(-5, 0), arrowprops=props, fontsize=14, ha="center")
plt.annotate('선형', xytext=(2, 0.2), xy=(0, 0.5), arrowprops=props, fontsize=14, ha="center")
plt.grid(True)
plt.title("로지스틱 활성화 함수", fontsize=14)
plt.axis([-5, 5, -0.2, 1.2])
save_fig("sigmoid_saturation_plot")
plt.show()
import tensorflow as tf
reset_graph()
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")
WARNING:tensorflow:From <ipython-input-7-da109dac52d3>:3: dense (from tensorflow.python.layers.core) is deprecated and will be removed in a future version. Instructions for updating: Use keras.layers.dense instead. WARNING:tensorflow:From /home/haesun/anaconda3/envs/handson-ml/lib/python3.6/site-packages/tensorflow/python/framework/op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version. Instructions for updating: Colocations handled automatically by placer.
def leaky_relu(z, alpha=0.01):
return np.maximum(alpha*z, z)
plt.plot(z, leaky_relu(z, 0.05), "b-", linewidth=2)
plt.plot([-5, 5], [0, 0], 'k-')
plt.plot([0, 0], [-0.5, 4.2], 'k-')
plt.grid(True)
props = dict(facecolor='black', shrink=0.1)
plt.annotate('통과', xytext=(-3.5, 0.5), xy=(-5, -0.2), arrowprops=props, fontsize=14, ha="center")
plt.title("Leaky ReLU 활성화 함수", fontsize=14)
plt.axis([-5, 5, -0.5, 4.2])
save_fig("leaky_relu_plot")
plt.show()
텐서플로에서 Leaky ReLU 구현하기:
텐서플로 1.4에서 tf.nn.leaky__relu(z, alpha) 함수가 추가되었습니다._
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")
Leaky ReLU를 사용하여 신경망을 훈련시켜 보죠. 먼저 그래프를 정의합니다:
reset_graph()
n_inputs = 28 * 28 # MNIST
n_hidden1 = 300
n_hidden2 = 100
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=leaky_relu, name="hidden1")
hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=leaky_relu, name="hidden2")
logits = tf.layers.dense(hidden2, 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
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))
init = tf.global_variables_initializer()
saver = tf.train.Saver()
데이터를 로드합니다:
주의: tf.examples.tutorials.mnist
은 삭제될 예정이므로 대신 tf.keras.datasets.mnist
를 사용하겠습니다.
(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
# from tensorflow.examples.tutorials.mnist import input_data
# mnist = input_data.read_data_sets("/tmp/data/")
n_epochs = 40
batch_size = 50
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})
if epoch % 5 == 0:
acc_batch = accuracy.eval(feed_dict={X: X_batch, y: y_batch})
acc_valid = accuracy.eval(feed_dict={X: X_valid, y: y_valid})
print(epoch, "배치 데이터 정확도:", acc_batch, "검증 세트 정확도:", acc_valid)
save_path = saver.save(sess, "./my_model_final.ckpt")
0 배치 데이터 정확도: 0.86 검증 세트 정확도: 0.9046 5 배치 데이터 정확도: 0.94 검증 세트 정확도: 0.9496 10 배치 데이터 정확도: 0.92 검증 세트 정확도: 0.9652 15 배치 데이터 정확도: 0.94 검증 세트 정확도: 0.9708 20 배치 데이터 정확도: 1.0 검증 세트 정확도: 0.9762 25 배치 데이터 정확도: 1.0 검증 세트 정확도: 0.9776 30 배치 데이터 정확도: 0.98 검증 세트 정확도: 0.9782 35 배치 데이터 정확도: 1.0 검증 세트 정확도: 0.9788
def elu(z, alpha=1):
return np.where(z < 0, alpha * (np.exp(z) - 1), z)
plt.plot(z, elu(z), "b-", linewidth=2)
plt.plot([-5, 5], [0, 0], 'k-')
plt.plot([-5, 5], [-1, -1], 'k--')
plt.plot([0, 0], [-2.2, 3.2], 'k-')
plt.grid(True)
plt.title(r"ELU 활성화 함수 ($\alpha=1$)", fontsize=14)
plt.axis([-5, 5, -2.2, 3.2])
save_fig("elu_plot")
plt.show()
텐서플로에서 ELU를 구현하는 것은 간단합니다. 층을 구성할 때 활성화 함수에 지정하기만 하면 됩니다:
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")
이 활성화 함수는 Günter Klambauer, Thomas Unterthiner, Andreas Mayr가 2017년에 쓴 논문에서 소개되었습니다(나중에 책에 추가하겠습니다). 훈련할 때 SELU 활성화 함수를 사용한 완전 연결 신경망은 스스로 정규화를 합니다. 각 층의 출력은 훈련하는 동안 같은 평균과 분산을 유지하려는 경향이 있어 그래디언트 소실과 폭주 문제를 해결합니다. 이 활성화 함수는 심층 신경망에서 다른 활성화 함수보다 뛰어난 성능을 내므로 꼭 이 함수를 시도해봐야 합니다.
def selu(z,
scale=1.0507009873554804934193349852946,
alpha=1.6732632423543772848170429916717):
return scale * elu(z, alpha)
plt.plot(z, selu(z), "b-", linewidth=2)
plt.plot([-5, 5], [0, 0], 'k-')
plt.plot([-5, 5], [-1.758, -1.758], 'k--')
plt.plot([0, 0], [-2.2, 3.2], 'k-')
plt.grid(True)
plt.title(r"SELU 활성화 함수", fontsize=14)
plt.axis([-5, 5, -2.2, 3.2])
save_fig("selu_plot")
plt.show()
기본적으로 SELU 하이퍼파라미터(scale
과 alpha
)는 평균이 0, 표준 편차가 1에 가깝게 유지되도록 조정합니다(입력도 평균이 0, 표준 편차가 1로 표준화되었다고 가정합니다). 이 활성화 함수를 사용하면 100층으로 된 심층 신경망도 그래디언트 소실/폭주 문제없이 모든 층에서 대략 평균이 0이고 표준 편차가 1을 유지합니다:
np.random.seed(42)
Z = np.random.normal(size=(500, 100))
for layer in range(100):
W = np.random.normal(size=(100, 100), scale=np.sqrt(1/100))
Z = selu(np.dot(Z, W))
means = np.mean(Z, axis=1)
stds = np.std(Z, axis=1)
if layer % 10 == 0:
print("층 {}: {:.2f} < 평균 < {:.2f}, {:.2f} < 표준 편차 < {:.2f}".format(
layer, means.min(), means.max(), stds.min(), stds.max()))
층 0: -0.26 < 평균 < 0.27, 0.74 < 표준 편차 < 1.27 층 10: -0.24 < 평균 < 0.27, 0.74 < 표준 편차 < 1.27 층 20: -0.17 < 평균 < 0.18, 0.74 < 표준 편차 < 1.24 층 30: -0.27 < 평균 < 0.24, 0.78 < 표준 편차 < 1.20 층 40: -0.38 < 평균 < 0.39, 0.74 < 표준 편차 < 1.25 층 50: -0.27 < 평균 < 0.31, 0.73 < 표준 편차 < 1.27 층 60: -0.26 < 평균 < 0.43, 0.74 < 표준 편차 < 1.35 층 70: -0.19 < 평균 < 0.21, 0.75 < 표준 편차 < 1.21 층 80: -0.18 < 평균 < 0.16, 0.72 < 표준 편차 < 1.19 층 90: -0.19 < 평균 < 0.16, 0.75 < 표준 편차 < 1.20
텐서플로 1.4 버전에 tf.nn.selu()
함수가 추가되었습니다. 이전 버전을 사용할 때는 다음 구현을 사용합니다:
def selu(z,
scale=1.0507009873554804934193349852946,
alpha=1.6732632423543772848170429916717):
return scale * tf.where(z >= 0.0, z, alpha * tf.nn.elu(z))
하지만 SELU 활성화 함수는 일반적인 드롭아웃과 함께 사용할 수 없습니다(드롭아웃은 SELU 활성화 함수의 자동 정규화 기능을 없애버립니다). 다행히 같은 논문에 실린 알파 드롭아웃(Alpha Dropout)을 사용할 수 있습니다. 텐서플로 1.4에 tf.contrib.nn.alpha_dropout()
이 추가되었습니다(Linz 대학교 생물정보학 연구소(Institute of Bioinformatics)의 Johannes Kepler가 만든 구현을 확인해 보세요).
SELU 활성화 함수를 사용한 신경망을 만들어 MNIST 문제를 풀어 보겠습니다:
reset_graph()
n_inputs = 28 * 28 # MNIST
n_hidden1 = 300
n_hidden2 = 100
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=selu, name="hidden1")
hidden2 = tf.layers.dense(hidden1, n_hidden2, activation=selu, name="hidden2")
logits = tf.layers.dense(hidden2, 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
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))
init = tf.global_variables_initializer()
saver = tf.train.Saver()
n_epochs = 40
batch_size = 50
이제 훈련할 차례입니다. 입력을 평균 0, 표준 편차 1로 스케일 조정해야 합니다:
means = X_train.mean(axis=0, keepdims=True)
stds = X_train.std(axis=0, keepdims=True) + 1e-10
X_val_scaled = (X_valid - means) / stds
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):
X_batch_scaled = (X_batch - means) / stds
sess.run(training_op, feed_dict={X: X_batch_scaled, y: y_batch})
if epoch % 5 == 0:
acc_batch = accuracy.eval(feed_dict={X: X_batch_scaled, y: y_batch})
acc_valid = accuracy.eval(feed_dict={X: X_val_scaled, y: y_valid})
print(epoch, "배치 데이터 정확도:", acc_batch, "검증 세트 정확도:", acc_valid)
save_path = saver.save(sess, "./my_model_final_selu.ckpt")
0 배치 데이터 정확도: 0.88 검증 세트 정확도: 0.923 5 배치 데이터 정확도: 0.98 검증 세트 정확도: 0.9576 10 배치 데이터 정확도: 1.0 검증 세트 정확도: 0.9662 15 배치 데이터 정확도: 0.96 검증 세트 정확도: 0.9684 20 배치 데이터 정확도: 1.0 검증 세트 정확도: 0.9692 25 배치 데이터 정확도: 1.0 검증 세트 정확도: 0.9688 30 배치 데이터 정확도: 1.0 검증 세트 정확도: 0.9694 35 배치 데이터 정확도: 1.0 검증 세트 정확도: 0.97
각 은닉층의 활성화 함수 전에 배치 정규화를 추가하기 위해 ELU 활성화 함수를 배치 정규화 층 이후에 수동으로 적용하겠습니다.
노트: tf.layers.dense()
함수가 (책에서 사용하는) tf.contrib.layers.arg_scope()
와 호환되지 않기 때문에 대신 파이썬의 functools.partial()
함수를 사용합니다. 이를 사용해 tf.layers.dense()
에 필요한 매개변수가 자동으로 설정되도록 my_dense_layer()
를 만듭니다(그렇지 않으면 my_dense_layer()
를 호출할 때마다 덮어씌여질 것입니다). 다른 코드는 이전과 비슷합니다.
reset_graph()
import tensorflow as tf
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)
WARNING:tensorflow:From <ipython-input-33-2f36d39a8af9>:15: batch_normalization (from tensorflow.python.layers.normalization) is deprecated and will be removed in a future version. Instructions for updating: Use keras.layers.batch_normalization instead.
reset_graph()
X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")
training = tf.placeholder_with_default(False, shape=(), name='training')
같은 매개변수를 계속 반복해서 쓰지 않도록 파이썬의 partial()
함수를 사용합니다:
from functools import partial
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)
각 층에 ELU 활성화 함수와 배치 정규화를 사용하여 MNIST를 위한 신경망을 만듭니다:
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")
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))
init = tf.global_variables_initializer()
saver = tf.train.Saver()
WARNING:tensorflow:From /home/haesun/anaconda3/envs/handson-ml/lib/python3.6/site-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version. Instructions for updating: Use tf.cast instead.
노트: 배치 정규화를 위해 별도의 업데이트 연산을 실행해 주어야 합니다(sess.run([training_op, extra_update_ops],...
).
n_epochs = 20
batch_size = 200
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 정확도가 좋지 않네요. 물론 훈련을 더 오래하면 정확도가 높아지겠지만 이런 얕은 신경망에서는 배치 정규화와 ELU가 큰 효과를 내지 못합니다. 대부분 심층 신경망에서 빛을 발합니다.
업데이트 연산에 의존하는 훈련 연산을 만들 수도 있습니다:
with tf.name_scope("train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
with tf.control_dependencies(extra_update_ops):
training_op = optimizer.minimize(loss)
이렇게 하면 훈련할 때 training_op
만 평가하면 텐서플로가 업데이트 연산도 자동으로 실행할 것입니다:
sess.run(training_op, feed_dict={training: True, X: X_batch, y: y_batch})
한가지 더, 훈련될 변수 개수가 전체 전역 변수 개수보다 적습니다. 이동 평균을 위한 변수는 훈련되는 변수가 아니기 때문입니다. 미리 학습한 신경망을 재사용할 경우(아래 참조) 이런 훈련되지 않는 변수를 놓쳐서는 안됩니다.
[v.name for v in tf.trainable_variables()]
['hidden1/kernel:0', 'hidden1/bias:0', 'batch_normalization/gamma:0', 'batch_normalization/beta:0', 'hidden2/kernel:0', 'hidden2/bias:0', 'batch_normalization_1/gamma:0', 'batch_normalization_1/beta:0', 'outputs/kernel:0', 'outputs/bias:0', 'batch_normalization_2/gamma:0', 'batch_normalization_2/beta:0']
[v.name for v in tf.global_variables()]
['hidden1/kernel:0', 'hidden1/bias:0', 'batch_normalization/gamma:0', 'batch_normalization/beta:0', 'batch_normalization/moving_mean:0', 'batch_normalization/moving_variance:0', 'hidden2/kernel:0', 'hidden2/bias:0', 'batch_normalization_1/gamma:0', 'batch_normalization_1/beta:0', 'batch_normalization_1/moving_mean:0', 'batch_normalization_1/moving_variance:0', 'outputs/kernel:0', 'outputs/bias:0', 'batch_normalization_2/gamma:0', 'batch_normalization_2/beta:0', 'batch_normalization_2/moving_mean:0', 'batch_normalization_2/moving_variance:0']
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
이제 그래디언트 클리핑을 적용합니다. 먼저 그래디언트를 구한 다음 clip_by_value()
함수를 사용해 클리핑하고 적용합니다:
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)
나머지는 이전과 동일합니다:
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.2876 1 검증 세트 정확도: 0.7942 2 검증 세트 정확도: 0.8794 3 검증 세트 정확도: 0.9058 4 검증 세트 정확도: 0.9166 5 검증 세트 정확도: 0.9216 6 검증 세트 정확도: 0.9296 7 검증 세트 정확도: 0.9356 8 검증 세트 정확도: 0.9382 9 검증 세트 정확도: 0.9416 10 검증 세트 정확도: 0.9456 11 검증 세트 정확도: 0.947 12 검증 세트 정확도: 0.9478 13 검증 세트 정확도: 0.9534 14 검증 세트 정확도: 0.9566 15 검증 세트 정확도: 0.9566 16 검증 세트 정확도: 0.9578 17 검증 세트 정확도: 0.9586 18 검증 세트 정확도: 0.962 19 검증 세트 정확도: 0.9612
먼저 그래프 구조를 로드해야 합니다. import_meta_graph()
함수가 그래프 연산들을 로드하여 기본 그래프에 적재하고 모델의 상태를 복원할 수 있도록 Saver
객체를 반환합니다. 기본적으로 Saver
객체는 .meta
확장자를 가진 파일에 그래프 구조를 저장하므로 이 파일을 로드해야 합니다:
reset_graph()
saver = tf.train.import_meta_graph("./my_model_final.ckpt.meta")
다음으로 훈련해야 할 모든 연산을 가져와야 합니다. 그래프 구조를 모를 때는 모든 연산을 출력해 볼 수 있습니다:
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/filename/input save/filename 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
웁스, 연산이 엄청 많네요! 텐서보드로 그래프를 시각화해보는 것이 더 좋을 것 같습니다. 다음 코드는 주피터에서 그래프를 그려줍니다(만약 브라우저에서 보이지 않는다면 FileWriter
로 그래프를 저장한 다음 텐서보드에서 열어 보세요):
from tensorflow_graph_in_jupyter import show_graph
show_graph(tf.get_default_graph())
필요한 연산을 찾았다면 그래프의 get_operation_by_name()
이나 get_tensor_by_name()
메서드를 사용하여 추출할 수 있습니다:
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")
원본 모델을 만들 때 다른 사람이 재사용하기 쉽게 연산에 명확한 이름을 부여하고 문서화를 하는 것이 좋습니다. 또 다른 방법은 처리해야 할 중요한 연산들을 모두 모아 놓은 컬렉션을 만드는 것입니다:
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")
# 모델 훈련 계속하기...
WARNING:tensorflow:From /home/haesun/anaconda3/envs/handson-ml/lib/python3.6/site-packages/tensorflow/python/training/saver.py:1266: checkpoint_exists (from tensorflow.python.training.checkpoint_management) is deprecated and will be removed in a future version. Instructions for updating: Use standard file APIs to check for files with this prefix. INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt
실제로 테스트를 해보죠!
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.9632 1 검증 세트 정확도: 0.963 2 검증 세트 정확도: 0.9652 3 검증 세트 정확도: 0.9648 4 검증 세트 정확도: 0.9644 5 검증 세트 정확도: 0.9648 6 검증 세트 정확도: 0.9688 7 검증 세트 정확도: 0.9684 8 검증 세트 정확도: 0.9684 9 검증 세트 정확도: 0.9686 10 검증 세트 정확도: 0.9704 11 검증 세트 정확도: 0.9712 12 검증 세트 정확도: 0.9674 13 검증 세트 정확도: 0.97 14 검증 세트 정확도: 0.971 15 검증 세트 정확도: 0.9722 16 검증 세트 정확도: 0.9724 17 검증 세트 정확도: 0.9712 18 검증 세트 정확도: 0.9714 19 검증 세트 정확도: 0.9714
또 다른 방법으로 원본 그래프를 만든 파이썬 코드에 접근할 수 있다면 import_meta_graph()
를 대신 사용할 수 있습니다:
reset_graph()
n_inputs = 28 * 28 # MNIST
n_hidden1 = 300
n_hidden2 = 50
n_hidden3 = 50
n_hidden4 = 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")
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")
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)
init = tf.global_variables_initializer()
saver = tf.train.Saver()
그 다음 훈련을 계속할 수 있습니다:
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.9642 1 검증 세트 정확도: 0.9626 2 검증 세트 정확도: 0.9654 3 검증 세트 정확도: 0.9652 4 검증 세트 정확도: 0.964 5 검증 세트 정확도: 0.9646 6 검증 세트 정확도: 0.9686 7 검증 세트 정확도: 0.9682 8 검증 세트 정확도: 0.9684 9 검증 세트 정확도: 0.9686 10 검증 세트 정확도: 0.9704 11 검증 세트 정확도: 0.9712 12 검증 세트 정확도: 0.967 13 검증 세트 정확도: 0.97 14 검증 세트 정확도: 0.9712 15 검증 세트 정확도: 0.9722 16 검증 세트 정확도: 0.9716 17 검증 세트 정확도: 0.9712 18 검증 세트 정확도: 0.9714 19 검증 세트 정확도: 0.9716
일반적으로 하위층만 재사용할 것입니다. import_meta_graph()
를 사용하면 전체 그래프를 로드하지만 필요하지 않은 부분은 무시하면 됩니다. 이 예에서는 학습된 3번째 층 위에 4번째 은닉층을 새로 추가합니다(원래 4번째 층은 무시됩니다). 새로운 출력층도 추가하고 이 출력으로 손실을 계산하고 이를 최소화하기 위한 새로운 옵티마이저를 만듭니다. 전체 그래프(원본 그래프 전체와 새로운 연산)를 저장할 새로운 Saver
객체와 새로운 모든 변수를 초기화할 초기화 연산도 필요합니다:
reset_graph()
n_hidden4 = 20 # 새 층
n_outputs = 10 # 새 층
saver = tf.train.import_meta_graph("./my_model_final.ckpt.meta")
X = tf.get_default_graph().get_tensor_by_name("X:0")
y = tf.get_default_graph().get_tensor_by_name("y:0")
hidden3 = tf.get_default_graph().get_tensor_by_name("dnn/hidden3/Relu:0")
new_hidden4 = tf.layers.dense(hidden3, n_hidden4, activation=tf.nn.relu, name="new_hidden4")
new_logits = tf.layers.dense(new_hidden4, n_outputs, name="new_outputs")
with tf.name_scope("new_loss"):
xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=new_logits)
loss = tf.reduce_mean(xentropy, name="loss")
with tf.name_scope("new_eval"):
correct = tf.nn.in_top_k(new_logits, y, 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy")
with tf.name_scope("new_train"):
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
training_op = optimizer.minimize(loss)
init = tf.global_variables_initializer()
new_saver = tf.train.Saver()
새로운 모델을 훈련시킵니다:
with tf.Session() as sess:
init.run()
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 = new_saver.save(sess, "./my_new_model_final.ckpt")
INFO:tensorflow:Restoring parameters from ./my_model_final.ckpt 0 검증 세트 정확도: 0.9188 1 검증 세트 정확도: 0.9396 2 검증 세트 정확도: 0.9488 3 검증 세트 정확도: 0.953 4 검증 세트 정확도: 0.9554 5 검증 세트 정확도: 0.9558 6 검증 세트 정확도: 0.9572 7 검증 세트 정확도: 0.9608 8 검증 세트 정확도: 0.9612 9 검증 세트 정확도: 0.964 10 검증 세트 정확도: 0.9652 11 검증 세트 정확도: 0.9658 12 검증 세트 정확도: 0.9642 13 검증 세트 정확도: 0.967 14 검증 세트 정확도: 0.9688 15 검증 세트 정확도: 0.9684 16 검증 세트 정확도: 0.9698 17 검증 세트 정확도: 0.9678 18 검증 세트 정확도: 0.9694 19 검증 세트 정확도: 0.9702
원본 모델을 만든 파이썬 코드에 접근할 수 있다면 필요한 부분만 재사용하고 나머지는 버릴 수 있습니다:
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)
training_op = optimizer.minimize(loss)
그러나 이전에 학습된 모델을 복원하기 위해 (복원할 변수 리스트를 전달합니다. 그렇지 않으면 그래프와 맞지 않는다고 에러를 낼 것입니다) Saver
객체를 하나 만들고 훈련이 끝난 후 새로운 모델을 저장하기 위해 또 다른 Saver
객체를 만들어야 합니다:
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()
with tf.Session() as sess:
init.run()
restore_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.9022 1 검증 세트 정확도: 0.9336 2 검증 세트 정확도: 0.943 3 검증 세트 정확도: 0.947 4 검증 세트 정확도: 0.9518 5 검증 세트 정확도: 0.9532 6 검증 세트 정확도: 0.9556 7 검증 세트 정확도: 0.959 8 검증 세트 정확도: 0.9586 9 검증 세트 정확도: 0.961 10 검증 세트 정확도: 0.9624 11 검증 세트 정확도: 0.9622 12 검증 세트 정확도: 0.9638 13 검증 세트 정확도: 0.9662 14 검증 세트 정확도: 0.9662 15 검증 세트 정확도: 0.9664 16 검증 세트 정확도: 0.967 17 검증 세트 정확도: 0.9676 18 검증 세트 정확도: 0.9684 19 검증 세트 정확도: 0.9674
이 예에서는 재사용하려는 각 변수에 대해 변수 초기화 할당 연산을 찾고, 초기화 될 값에 해당하는 두 번째 입력 핸들을 구합니다. 초기화가 실행될 때 여기에 feed_dict
매개변수를 사용하여 초깃값 대신 원하는 값을 주입합니다:
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")
# [...] 모델의 나머지 부분을 구성
# 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})
# [...] 새 작업에 모델을 훈련시킵니다
print(hidden1.eval(feed_dict={X: [[10.0, 11.0]]})) # 책에는 없음
[[ 61. 83. 105.]]
또 다른 방법은 전용 할당 노드와 플레이스홀더를 만든는 것입니다. 이 방법은 더 번거롭고 효율적이지 않지만 하려는 방식이 잘 드러나는 방법입니다:
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")
# [...] 모델의 나머지를 구성
# hidden1 변수의 할당 노드에 대한 핸들을 구합니다
with tf.variable_scope("", default_name="", reuse=True): # 루트 범위
hidden1_weights = tf.get_variable("hidden1/kernel")
hidden1_biases = tf.get_variable("hidden1/bias")
# 전용 플레이스홀더와 할당 노드를 만듭니다
original_weights = tf.placeholder(tf.float32, shape=(n_inputs, n_hidden1))
original_biases = tf.placeholder(tf.float32, shape=n_hidden1)
assign_hidden1_weights = tf.assign(hidden1_weights, original_weights)
assign_hidden1_biases = tf.assign(hidden1_biases, original_biases)
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
sess.run(assign_hidden1_weights, feed_dict={original_weights: original_w})
sess.run(assign_hidden1_biases, feed_dict={original_biases: original_b})
# [...] 새 작업에 모델을 훈련시킵니다
print(hidden1.eval(feed_dict={X: [[10.0, 11.0]]}))
[[ 61. 83. 105.]]
get_collection()
에 scope
를 지정하여 변수의 핸들을 가져올 수도 있습니다:
tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope="hidden1")
[<tf.Variable 'hidden1/kernel:0' shape=(2, 3) dtype=float32_ref>, <tf.Variable 'hidden1/bias:0' shape=(3,) dtype=float32_ref>]
또는 그래프의 get_tensor_by_name()
메서드를 사용할 수 있습니다:
tf.get_default_graph().get_tensor_by_name("hidden1/kernel:0")
<tf.Tensor 'hidden1/kernel:0' shape=(2, 3) dtype=float32_ref>
tf.get_default_graph().get_tensor_by_name("hidden1/bias:0")
<tf.Tensor 'hidden1/bias:0' shape=(3,) dtype=float32_ref>
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) # 책에는 없음
train_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES,
scope="hidden[34]|outputs")
training_op = optimizer.minimize(loss, var_list=train_vars)
init = tf.global_variables_initializer()
new_saver = tf.train.Saver()
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()
with tf.Session() as sess:
init.run()
restore_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.8962 1 검증 세트 정확도: 0.9292 2 검증 세트 정확도: 0.94 3 검증 세트 정확도: 0.9442 4 검증 세트 정확도: 0.9482 5 검증 세트 정확도: 0.9506 6 검증 세트 정확도: 0.9506 7 검증 세트 정확도: 0.9536 8 검증 세트 정확도: 0.9556 9 검증 세트 정확도: 0.9566 10 검증 세트 정확도: 0.956 11 검증 세트 정확도: 0.9564 12 검증 세트 정확도: 0.9568 13 검증 세트 정확도: 0.9576 14 검증 세트 정확도: 0.9592 15 검증 세트 정확도: 0.9574 16 검증 세트 정확도: 0.9572 17 검증 세트 정확도: 0.96 18 검증 세트 정확도: 0.9586 19 검증 세트 정확도: 0.9602
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()
with tf.Session() as sess:
init.run()
restore_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.9022 1 검증 세트 정확도: 0.931 2 검증 세트 정확도: 0.9434 3 검증 세트 정확도: 0.9474 4 검증 세트 정확도: 0.9514 5 검증 세트 정확도: 0.9524 6 검증 세트 정확도: 0.9522 7 검증 세트 정확도: 0.9558 8 검증 세트 정확도: 0.9556 9 검증 세트 정확도: 0.9562 10 검증 세트 정확도: 0.957 11 검증 세트 정확도: 0.955 12 검증 세트 정확도: 0.9572 13 검증 세트 정확도: 0.958 14 검증 세트 정확도: 0.9578 15 검증 세트 정확도: 0.9572 16 검증 세트 정확도: 0.9564 17 검증 세트 정확도: 0.9576 18 검증 세트 정확도: 0.9592 19 검증 세트 정확도: 0.9582
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.float3