In [ ]:
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
|
|
|
Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도 불구하고 공식 영문 문서의 내용과 일치하지 않을 수 있습니다. 이 번역에 개선할 부분이 있다면 tensorflow/docs-l10n 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다. 문서 번역이나 리뷰에 참여하려면 docs-ko@tensorflow.org로 메일을 보내주시기 바랍니다.
이 문서는 저수준 텐서플로 API를 사용자를 위한 가이드입니다.
만약 고수준 API(tf.keras
)를 사용하고 있다면 텐서플로 2.0으로 바꾸기 위해 할 일이 거의 없습니다:
여전히 텐서플로 1.X 버전의 코드를 수정하지 않고 텐서플로 2.0에서 실행할 수 있습니다(contrib
모듈은 제외):
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
하지만 이렇게 하면 텐서플로 2.0에서 제공하는 많은 장점을 활용할 수 없습니다. 이 문서는 성능을 높이면서 코드는 더 간단하고 유지보수하기 쉽도록 업그레이드하는 방법을 안내합니다.
첫 번째 단계는 업그레이드 스크립트를 사용해 보는 것입니다.
이는 텐서플로 2.0으로 업그레이드하기 위해 처음 시도할 일입니다. 하지만 이 작업이 기존 코드를 텐서플로 2.0 스타일로 바꾸어 주지는 못합니다. 여전히 플레이스홀더(placeholder)나 세션(session), 컬렉션(collection), 그외 1.x 스타일의 기능을 사용하기 위해 tf.compat.v1
아래의 모듈을 참조하고 있을 것입니다.
즉시 실행, v1.enable_eager_execution()
: 암묵적으로 tf.Graph
를 사용하는 모든 코드는 실패할 것입니다. 코드를 with tf.Graph().as_default()
컨택스트(context)로 감싸야 합니다.
리소스(resource) 변수, v1.enable_resource_variables()
: 일부 코드는 TF 레퍼런스 변수의 결정적이지 않은 행동에 영향을 받을 수 있습니다.
리소스 변수는 저장되는 동안 잠깁니다. 따라서 이해하기 쉬운 일관성을 보장합니다.
tf.Variable
생성자에 use_resource=False
를 전달하여 비활성화할 수 있습니다.텐서 크기, v1.enable_v2_tensorshape()
: TF 2.0에서 텐서 크기는 간단합니다. t.shape[0].value
대신에 t.shape[0]
을 사용할 수 있습니다. 변경 사항이 작기 때문에 당장 고치는 것이 좋습니다. TensorShape 예를 참고하세요.
제어 흐름, v1.enable_control_flow_v2()
: TF 2.0 제어 흐름 구현이 간단하게 바뀌었기 때문에 다른 그래프 표현을 만듭니다. 이슈가 있다면 버그를 신고해 주세요.
tf.Session.run
호출을 바꾸세요.모든 tf.Session.run
호출을 파이썬 함수로 바꾸어야 합니다.
feed_dict
와 tf.placeholder
는 함수의 매개변수가 됩니다.fetches
는 함수의 반환값이 됩니다.pdb
를 사용하여 쉽게 디버깅할 수 있습니다.그다음 그래프 모드에서 효율적으로 실행할 수 있도록 tf.function
데코레이터를 추가합니다. 더 자세한 내용은 오토그래프 가이드를 참고하세요.
노트:
v1.Session.run
과 달리 tf.function
은 반환 시그니처(signature)가 고정되어 있고 항상 모든 출력을 반환합니다. 성능에 문제가 된다면 두 개의 함수로 나누세요.
tf.control_dependencies
나 비슷한 연산이 필요없습니다: tf.function
은 쓰여진 순서대로 실행됩니다. 예를 들어 tf.Variable
할당이나 tf.assert
는 자동으로 실행됩니다.
TF 2.0에서 이름 기반 변수 추적은 매우 권장되지 않습니다. 파이썬 객체로 변수를 추적하세요.
v1.get_variable
대신에 tf.Variable
을 사용하세요.
모든 v1.variable_scope
는 파이썬 객체로 바꾸어야 합니다. 일반적으로 다음 중 하나가 될 것입니다:
tf.keras.layers.Layer
tf.keras.Model
tf.Module
만약 (tf.Graph.get_collection(tf.GraphKeys.VARIABLES)
처럼) 변수의 리스트가 필요하다면 Layer
와 Model
객체의 .variables
이나 .trainable_variables
속성을 사용하세요.
Layer
와 Model
클래스는 전역 컬렉션이 필요하지 않도록 몇 가지 다른 속성들도 제공합니다. .losses
속성은 tf.GraphKeys.LOSSES
컬렉션 대신 사용할 수 있습니다.
자세한 내용은 케라스 가이드를 참고하세요.
경고: tf.compat.v1
의 상당수 기능은 암묵적으로 전역 컬렉션을 사용합니다.
풀려는 문제에 맞는 고수준 API를 사용하세요. 훈련 루프(loop)를 직접 만드는 것보다 tf.keras.Model.fit
메서드를 사용하는 것이 좋습니다.
고수준 함수는 훈련 루프를 직접 만들 때 놓치기 쉬운 여러 가지 저수준의 세부 사항들을 관리해 줍니다. 예를 들어 자동으로 규제(regularization) 손실을 수집하거나 모델을 호출할 때 training=True
로 매개변수를 설정해 줍니다.
데이터 입력을 위해 tf.data
데이터셋을 사용하세요. 이 객체는 효율적이고 간결하며 텐서플로와 잘 통합됩니다.
tf.keras.Model.fit
메서드에 바로 전달할 수 있습니다.
model.fit(dataset, epochs=5)
파이썬에서 직접 반복시킬 수 있습니다:
for example_batch, label_batch in dataset:
break
compat.v1
에서 마이그레이션 하기tf.compat.v1
모듈에는 완전한 텐서플로 1.x API가 들어 있습니다.
TF2 업그레이드 스크립트는 안전할 경우 이와 동일한 2.0 버전으로 바꿉니다. 즉 2.0 버전의 동작이 완전히 동일한 경우입니다(예를 들면, v1.arg_max
가 tf.argmax
로 이름이 바뀌었기 때문에 동일한 함수입니다).
업그레이드 스크립트가 코드를 수정하고 나면 코드에 compat.v1
이 많이 등장할 것입니다. 코드를 살펴 보면서 수동으로 2.0 버전으로 바꿉니다(2.0 버전이 있다면 로그에 언급되어 있을 것입니다).
In [ ]:
import tensorflow as tf
import tensorflow_datasets as tfds
다음 코드는 텐서플로 1.x를 사용한 코드에서 볼 수 있는 패턴입니다.
in_a = tf.placeholder(dtype=tf.float32, shape=(2))
in_b = tf.placeholder(dtype=tf.float32, shape=(2))
def forward(x):
with tf.variable_scope("matmul", reuse=tf.AUTO_REUSE):
W = tf.get_variable("W", initializer=tf.ones(shape=(2,2)),
regularizer=tf.contrib.layers.l2_regularizer(0.04))
b = tf.get_variable("b", initializer=tf.zeros(shape=(2)))
return W * x + b
out_a = forward(in_a)
out_b = forward(in_b)
reg_loss = tf.losses.get_regularization_loss(scope="matmul")
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
outs = sess.run([out_a, out_b, reg_loss],
feed_dict={in_a: [1, 0], in_b: [0, 1]})
변환된 코드의 패턴은 다음과 같습니다:
forward
함수는 여전히 필요한 계산을 정의합니다.Session.run
호출은 forward
함수를 호출하는 것으로 바뀝니다.tf.function
데코레이터는 선택 사항으로 성능을 위해 추가할 수 있습니다.
In [ ]:
W = tf.Variable(tf.ones(shape=(2,2)), name="W")
b = tf.Variable(tf.zeros(shape=(2)), name="b")
@tf.function
def forward(x):
return W * x + b
out_a = forward([1,0])
print(out_a)
In [ ]:
out_b = forward([0,1])
regularizer = tf.keras.regularizers.l2(0.04)
reg_loss = regularizer(W)
v1.layers
모듈은 변수를 정의하고 재사용하기 위해 v1.variable_scope
에 의존하는 층 함수를 포함합니다.
def model(x, training, scope='model'):
with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):
x = tf.layers.conv2d(x, 32, 3, activation=tf.nn.relu,
kernel_regularizer=tf.contrib.layers.l2_regularizer(0.04))
x = tf.layers.max_pooling2d(x, (2, 2), 1)
x = tf.layers.flatten(x)
x = tf.layers.dropout(x, 0.1, training=training)
x = tf.layers.dense(x, 64, activation=tf.nn.relu)
x = tf.layers.batch_normalization(x, training=training)
x = tf.layers.dense(x, 10, activation=tf.nn.softmax)
return x
train_out = model(train_data, training=True)
test_out = model(test_data, training=False)
tf.keras.Sequential
이 적합합니다. (복잡한 모델인 경우 사용자 정의 층과 모델이나 함수형 API를 참고하세요.)v1.layers
에서 tf.keras.layers
로 바로 매핑되기 때문에 일대일로 변환됩니다.대부분 매개변수는 동일합니다. 다른 부분은 다음과 같습니다:
training
매개변수가 전달됩니다.model
함수의 첫 번째 매개변수(입력 x
)는 사라집니다. 층 객체가 모델 구축과 모델 호출을 구분하기 때문입니다.추가 노트:
tf.contrib
에서 규제를 초기화했다면 다른 것보다 매개변수 변화가 많습니다.v1.losses.get_regularization_loss
와 같은 함수는 값을 반환하지 않습니다. 이는 훈련 루프를 망가뜨릴 수 있습니다.
In [ ]:
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(32, 3, activation='relu',
kernel_regularizer=tf.keras.regularizers.l2(0.04),
input_shape=(28, 28, 1)),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Flatten(),
tf.keras.layers.Dropout(0.1),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Dense(10)
])
train_data = tf.ones(shape=(1, 28, 28, 1))
test_data = tf.ones(shape=(1, 28, 28, 1))
In [ ]:
train_out = model(train_data, training=True)
print(train_out)
In [ ]:
test_out = model(test_data, training=False)
print(test_out)
In [ ]:
# 훈련되는 전체 변수
len(model.trainable_variables)
In [ ]:
# 규제 손실
model.losses
기존 코드는 종종 저수준 TF 1.x 변수와 고수준 v1.layers
연산을 혼용합니다.
def model(x, training, scope='model'):
with tf.variable_scope(scope, reuse=tf.AUTO_REUSE):
W = tf.get_variable(
"W", dtype=tf.float32,
initializer=tf.ones(shape=x.shape),
regularizer=tf.contrib.layers.l2_regularizer(0.04),
trainable=True)
if training:
x = x + W
else:
x = x + W * 0.5
x = tf.layers.conv2d(x, 32, 3, activation=tf.nn.relu)
x = tf.layers.max_pooling2d(x, (2, 2), 1)
x = tf.layers.flatten(x)
return x
train_out = model(train_data, training=True)
test_out = model(test_data, training=False)
이런 코드를 변환하려면 이전 예제처럼 층별로 매핑하는 패턴을 사용하세요.
v1.variable_scope
는 기본적으로 하나의 층입니다. 따라서 tf.keras.layers.Layer
로 재작성합니다. 자세한 내용은 이 문서를 참고하세요.
일반적인 패턴은 다음과 같습니다:
__init__
에서 층에 필요한 매개변수를 입력 받습니다.build
메서드에서 변수를 만듭니다.call
메서드에서 연산을 실행하고 결과를 반환합니다.
In [ ]:
# 모델에 추가하기 위해 사용자 정의 층을 만듭니다.
class CustomLayer(tf.keras.layers.Layer):
def __init__(self, *args, **kwargs):
super(CustomLayer, self).__init__(*args, **kwargs)
def build(self, input_shape):
self.w = self.add_weight(
shape=input_shape[1:],
dtype=tf.float32,
initializer=tf.keras.initializers.ones(),
regularizer=tf.keras.regularizers.l2(0.02),
trainable=True)
# call 메서드가 그래프 모드에서 사용되면
# training 변수는 텐서가 됩니다.
@tf.function
def call(self, inputs, training=None):
if training:
return inputs + self.w
else:
return inputs + self.w * 0.5
In [ ]:
custom_layer = CustomLayer()
print(custom_layer([1]).numpy())
print(custom_layer([1], training=True).numpy())
In [ ]:
train_data = tf.ones(shape=(1, 28, 28, 1))
test_data = tf.ones(shape=(1, 28, 28, 1))
# 사용자 정의 층을 포함한 모델을 만듭니다.
model = tf.keras.Sequential([
CustomLayer(input_shape=(28, 28, 1)),
tf.keras.layers.Conv2D(32, 3, activation='relu'),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Flatten(),
])
train_out = model(train_data, training=True)
test_out = model(test_data, training=False)
노트:
클래스 상속으로 만든 케라스 모델과 층은 v1 그래프(연산간의 의존성이 자동으로 제어되지 않습니다)와 즉시 실행 모드 양쪽에서 실행될 수 있어야 합니다.
tf.function()
으로 call()
메서드를 감쌉니다.call
메서드에 training
매개변수를 추가하는 것을 잊지 마세요.
tf.Tensor
가 됩니다.self.add_weight()
를 사용하여 생성자 메서드나 def build()
메서드에서 모델 변수를 만듭니다.
build
메서드에서 입력 크기를 참조할 수 있으므로 적절한 크기의 가중치를 만들 수 있습니다.tf.keras.layers.Layer.add_weight
를 사용하면 케라스가 변수와 규제 손실을 관리할 수 있습니다.사용자 정의 층 안에 tf.Tensors
객체를 포함하지 마세요.
tf.function
이나 즉시 실행 모드에서 모두 텐서가 만들어지지만 이 텐서들의 동작 방식은 다릅니다.tf.Variable
을 사용하세요. 변수는 양쪽 방식에 모두 사용할 수 있습니다.tf.Tensors
는 중간 값을 저장하기 위한 용도로만 사용합니다.예전 텐서플로 1.x 코드는 Slim 라이브러리를 많이 사용합니다. 이 라이브러리는 텐서플로 1.x의 tf.contrib.layers
로 패키지되어 있습니다. contrib
모듈은 더 이상 텐서플로 2.0에서 지원하지 않고 tf.compat.v1
에도 포함되지 않습니다. Slim을 사용한 코드를 TF 2.0으로 변환하는 것은 v1.layers
를 사용한 코드를 변경하는 것보다 더 어렵습니다. 사실 Slim 코드는 v1.layers
로 먼저 변환하고 그 다음 케라스로 변환하는 것이 좋습니다.
arg_scopes
를 삭제하세요. 모든 매개변수는 명시적으로 설정되어야 합니다.normalizer_fn
과 activation_fn
를 사용해야 한다면 분리하여 각각 하나의 층으로 만드세요.v1.layers
는 매개변수 이름과 기본값이 다릅니다.tf.keras.applications
나 TFHub를 확인해 보세요.일부 tf.contrib
층은 텐서플로 내부에 포함되지 못했지만 TF 애드온(add-on) 패키지로 옮겨졌습니다.
여러 가지 방법으로 tf.keras
모델에 데이터를 주입할 수 있습니다. 파이썬 제너레이터(generator)와 넘파이 배열을 입력으로 사용할 수 있습니다.
tf.data
패키지를 사용하여 모델에 데이터를 주입하는 것이 권장되는 방법입니다. 이 패키지는 데이터 조작을 위한 고성능 클래스들을 포함하고 있습니다.
tf.queue
는 데이터 구조로만 지원되고 입력 파이프라인으로는 지원되지 않습니다.
텐서플로 데이터셋(Datasets) 패키지(tfds
)는 tf.data.Dataset
객체로 정의된 데이터셋을 적재하기 위한 유틸리티가 포함되어 있습니다.
예를 들어 tfds
를 사용하여 MNIST 데이터셋을 적재하는 코드는 다음과 같습니다:
In [ ]:
datasets, info = tfds.load(name='mnist', with_info=True, as_supervised=True)
mnist_train, mnist_test = datasets['train'], datasets['test']
그 다음 훈련용 데이터를 준비합니다:
In [ ]:
BUFFER_SIZE = 10 # 실전 코드에서는 더 큰 값을 사용합니다.
BATCH_SIZE = 64
NUM_EPOCHS = 5
def scale(image, label):
image = tf.cast(image, tf.float32)
image /= 255
return image, label
간단한 예제를 위해 5개의 배치만 반환하도록 데이터셋을 자릅니다:
In [ ]:
train_data = mnist_train.map(scale).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
test_data = mnist_test.map(scale).batch(BATCH_SIZE)
STEPS_PER_EPOCH = 5
train_data = train_data.take(STEPS_PER_EPOCH)
test_data = test_data.take(STEPS_PER_EPOCH)
In [ ]:
image_batch, label_batch = next(iter(train_data))
훈련 과정을 세부적으로 제어할 필요가 없다면 케라스의 내장 메서드인 fit
, evaluate
, predict
를 사용하는 것이 좋습니다. 이 메서드들은 모델 구현(Sequential, 함수형 API, 클래스 상속)에 상관없이 일관된 훈련 인터페이스를 제공합니다.
이 메서드들의 장점은 다음과 같습니다:
tf.data.Datasets
을 사용할 수 있습니다.tf.distribute
을 지원합니다.tf.keras.callbacks.TensorBoard
와 같은 콜백(callback)이나 사용자 정의 콜백을 지원합니다.Dataset
을 사용하여 모델을 훈련하는 예제는 다음과 같습니다. (자세한 작동 방식은 튜토리얼을 참고하세요.)
In [ ]:
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(32, 3, activation='relu',
kernel_regularizer=tf.keras.regularizers.l2(0.02),
input_shape=(28, 28, 1)),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Flatten(),
tf.keras.layers.Dropout(0.1),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Dense(10)
])
# 사용자 정의 층이 없는 모델입니다.
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(train_data, epochs=NUM_EPOCHS)
loss, acc = model.evaluate(test_data)
print("손실 {}, 정확도 {}".format(loss, acc))
케라스 모델의 훈련 스텝(step)이 좋지만 그 외 다른 것을 더 제어하려면 자신만의 데이터 반복 루프를 만들고 tf.keras.model.train_on_batch
메서드를 사용해 보세요.
기억할 점: 많은 것을 tf.keras.Callback
으로 구현할 수 있습니다.
이 메서드는 앞에서 언급한 메서드의 장점을 많이 가지고 있고 사용자가 바깥쪽 루프를 제어할 수 있습니다.
훈련하는 동안 성능을 확인하기 위해 tf.keras.model.test_on_batch
나 tf.keras.Model.evaluate
메서드를 사용할 수도 있습니다.
노트: train_on_batch
와 test_on_batch
는 기본적으로 하나의 배치에 대한 손실과 측정값을 반환합니다. reset_metrics=False
를 전달하면 누적된 측정값을 반환합니다. 이 때는 누적된 측정값을 적절하게 초기화해 주어야 합니다. AUC
와 같은 일부 지표는 reset_metrics=False
를 설정해야 올바르게 계산됩니다.
앞의 모델을 계속 사용합니다:
In [ ]:
# 사용자 정의 층이 없는 모델입니다.
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
for epoch in range(NUM_EPOCHS):
# 누적된 측정값을 초기화합니다.
model.reset_metrics()
for image_batch, label_batch in train_data:
result = model.train_on_batch(image_batch, label_batch)
metrics_names = model.metrics_names
print("훈련: ",
"{}: {:.3f}".format(metrics_names[0], result[0]),
"{}: {:.3f}".format(metrics_names[1], result[1]))
for image_batch, label_batch in test_data:
result = model.test_on_batch(image_batch, label_batch,
# return accumulated metrics
reset_metrics=False)
metrics_names = model.metrics_names
print("\n평가: ",
"{}: {:.3f}".format(metrics_names[0], result[0]),
"{}: {:.3f}".format(metrics_names[1], result[1]))
자유도를 높이고 제어를 더 하려면 다음 세 단계를 사용해 자신만의 훈련 루프를 구현할 수 있습니다:
tf.data.Dataset
을 반복합니다.tf.GradientTape
을 사용하여 그래디언트를 계산합니다.tf.keras.optimizer
를 사용하여 모델의 가중치 변수를 업데이트합니다.기억할 점:
call
메서드에는 항상 training
매개변수를 포함하세요.training
매개변수를 올바르게 지정했는지 확인하세요.v1에 비해 단순해진 것:
tf.function
안에서도 연산은 즉시 실행 모드처럼 실행됩니다.
In [ ]:
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(32, 3, activation='relu',
kernel_regularizer=tf.keras.regularizers.l2(0.02),
input_shape=(28, 28, 1)),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Flatten(),
tf.keras.layers.Dropout(0.1),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Dense(10)
])
optimizer = tf.keras.optimizers.Adam(0.001)
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
@tf.function
def train_step(inputs, labels):
with tf.GradientTape() as tape:
predictions = model(inputs, training=True)
regularization_loss = tf.math.add_n(model.losses)
pred_loss = loss_fn(labels, predictions)
total_loss = pred_loss + regularization_loss
gradients = tape.gradient(total_loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
for epoch in range(NUM_EPOCHS):
for inputs, labels in train_data:
train_step(inputs, labels)
print("마지막 에포크", epoch)
In [ ]:
cce = tf.keras.losses.CategoricalCrossentropy(from_logits=True)
cce([[1, 0]], [[-1.0,3.0]]).numpy()
측정 객체는 다음과 같은 메서드를 가집니다:
update_state()
— 새로운 측정값을 추가합니다.result()
— 누적된 측정 결과를 얻습니다.reset_states()
— 모든 측정 내용을 지웁니다.이 객체는 호출 가능합니다. update_state
메서드처럼 새로운 측정값과 함께 호출하면 상태를 업데이트하고 새로운 측정 결과를 반환합니다.
측정 변수를 수동으로 초기화할 필요가 없습니다. 텐서플로 2.0은 자동으로 의존성을 관리하기 때문에 어떤 경우에도 신경 쓸 필요가 없습니다.
다음은 측정 객체를 사용하여 사용자 정의 훈련 루프 안에서 평균 손실을 관리하는 코드입니다.
In [ ]:
# 측정 객체를 만듭니다.
loss_metric = tf.keras.metrics.Mean(name='train_loss')
accuracy_metric = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')
@tf.function
def train_step(inputs, labels):
with tf.GradientTape() as tape:
predictions = model(inputs, training=True)
regularization_loss = tf.math.add_n(model.losses)
pred_loss = loss_fn(labels, predictions)
total_loss = pred_loss + regularization_loss
gradients = tape.gradient(total_loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
# 측정값을 업데이트합니다.
loss_metric.update_state(total_loss)
accuracy_metric.update_state(labels, predictions)
for epoch in range(NUM_EPOCHS):
# 측정값을 초기화합니다.
loss_metric.reset_states()
accuracy_metric.reset_states()
for inputs, labels in train_data:
train_step(inputs, labels)
# 측정 결과를 얻습니다.
mean_loss = loss_metric.result()
mean_accuracy = accuracy_metric.result()
print('에포크: ', epoch)
print(' 손실: {:.3f}'.format(mean_loss))
print(' 정확도: {:.3f}'.format(mean_accuracy))
텐서플로 2.0에서 케라스 모델은 지표 이름을 더 일관성있게 처리합니다.
지표를 문자열로 전달하면 정확히 같은 문자열이 지표의 name
으로 사용됩니다. model.fit
메서드가 반환하는 히스토리(history) 객체와 keras.callbacks
로 전달하는 로그에 나타나는 이름이 지표로 전달한 문자열이 됩니다.
In [ ]:
model.compile(
optimizer = tf.keras.optimizers.Adam(0.001),
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics = ['acc', 'accuracy', tf.keras.metrics.SparseCategoricalAccuracy(name="my_accuracy")])
history = model.fit(train_data)
In [ ]:
history.history.keys()
이전 버전은 이와 다르게 metrics=["accuracy"]
를 전달하면 dict_keys(['loss', 'acc'])
가 됩니다.
v1.train.AdamOptimizer
나 v1.train.GradientDescentOptimizer
같은 v1.train
에 있는 옵티마이저는 tf.keras.optimizers
에 있는 것과 동일합니다.
v1.train
을 keras.optimizers
로 바꾸기다음은 옵티마이저를 바꿀 때 유념해야 할 내용입니다:
1e-8
에서 1e-7
로 바뀌었습니다(대부분의 경우 큰 차이가 없습니다).v1.train.GradientDescentOptimizer
는 tf.keras.optimizers.SGD
로 바꿀 수 있습니다. v1.train.MomentumOptimizer
는 모멘텀 매개변수를 사용하는 SGD
옵티마이저로 바꿀 수 있습니다: tf.keras.optimizers.SGD(..., momentum=...)
.v1.train.AdamOptimizer
는 tf.keras.optimizers.Adam
로 바꿀 수 있습니다. beta1
과 beta2
매개변수는 beta_1
과 beta_2
로 이름이 바뀌었습니다.v1.train.RMSPropOptimizer
는 tf.keras.optimizers.RMSprop
로 바꿀 수 있습니다. decay
매개변수는 rho
로 이름이 바뀌었습니다.v1.train.AdadeltaOptimizer
는 tf.keras.optimizers.Adadelta
로 바꿀 수 있습니다.tf.train.AdagradOptimizer
는 tf.keras.optimizers.Adagrad
로 바꿀 수 있습니다.tf.train.FtrlOptimizer
는 tf.keras.optimizers.Ftrl
로 바꿀 수 있습니다. accum_name
과 linear_name
매개변수는 삭제되었습니다.tf.contrib.AdamaxOptimizer
와 tf.contrib.NadamOptimizer
는 tf.keras.optimizers.Adamax
와 tf.keras.optimizers.Nadam
로 바꿀 수 있습니다. beta1
, beta2
매개변수는 beta_1
, beta_2
로 바뀌었습니다.tf.keras.optimizers
의 새로운 기본값주의: 만약 모델이 수렴하는데 변화가 있다면 학습률 기본값을 확인해 보세요.
optimizers.SGD
, optimizers.Adam
, optimizers.RMSprop
기본값은 그대로입니다..
학습률 기본값이 바뀐 경우는 다음과 같습니다:
optimizers.Adagrad
는 0.01에서 0.001로 바뀌었습니다.optimizers.Adadelta
는 1.0에서 0.001로 바뀌었습니다.optimizers.Adamax
는 0.002에서 0.001로 바뀌었습니다.optimizers.Nadam
은 0.002에서 0.001로 바뀌었습니다.텐서플로 2는 텐서보드(TensorBoard) 시각화를 위해 서머리(summary) 데이터를 작성하는데 사용하는 tf.summary
API에 큰 변화가있습니다. 새로운 tf.summary
에 대한 개괄 소개는 TF 2 API를 사용한 시작하기 튜토리얼와 텐서보드 TF 2 이전 가이드를 참고하세요.
텐서플로 2.0은 객체 기반의 체크포인트를 사용합니다.
이전 이름 기반 스타일의 체크포인트도 여전히 복원할 수 있지만 주의가 필요합니다. 코드 변환 과정 때문에 변수 이름이 바뀔 수 있지만 해결 방법이 있습니다.
가장 간단한 방법은 새로운 모델의 이름과 체크포인트에 있는 이름을 나열해 보는 것입니다:
name
매개변수를 가집니다.name
매개변수를 가집니다. 이 값은 변수 이름의 접두어로 사용됩니다.v1.name_scope
함수를 변수 이름의 접두어를 지정하는데 사용할 수 있습니다. 이 함수는 tf.variable_scope
와는 매우 다릅니다. 이름에만 영향을 미치며 변수를 추적하거나 재사용을 관장하지 않습니다.이것이 주어진 상황에 잘 맞지 않는다면 v1.train.init_from_checkpoint
함수를 시도해 보세요. 이 함수는 assignment_map
매개변수로 예전 이름과 새로운 이름을 매핑할 수 있습니다.
노트: 지연 적재가 되는 객체 기반 체크포인트와는 달리 이름 기반 체크포인트는 함수가 호출될 때 모든 변수가 만들어 집니다. 일부 모델은 build
메서드를 호출하거나 배치 데이터에서 모델을 실행할 때까지 변수 생성을 지연합니다.
텐서플로 추정기(Estimator) 저장소에는 텐서플로 1.X의 추정기에서 만든 체크포인트를 2.0으로 업그레이드하는 변환 도구가 포함되어 있습니다. 비슷한 경우를 위한 도구를 만드는 방법을 보여주는 사례입니다.
원본 Graph.pb
파일을 텐서플로 2.0으로 업그레이드하는 쉬운 방법은 없습니다. 이 파일을 생성하는 코드를 업그레이드하는 것이 좋습니다.
하지만 "동결된 그래프"(변수가 상수로 바뀐 tf.Graph
)라면 v1.wrap_function
를 사용해 concrete_function
로 변환하는 것이 가능합니다:
In [ ]:
def wrap_frozen_graph(graph_def, inputs, outputs):
def _imports_graph_def():
tf.compat.v1.import_graph_def(graph_def, name="")
wrapped_import = tf.compat.v1.wrap_function(_imports_graph_def, [])
import_graph = wrapped_import.graph
return wrapped_import.prune(
tf.nest.map_structure(import_graph.as_graph_element, inputs),
tf.nest.map_structure(import_graph.as_graph_element, outputs))
예를 들어 2016년 Inception v1의 동결된 그래프입니다:
In [ ]:
path = tf.keras.utils.get_file(
'inception_v1_2016_08_28_frozen.pb',
'http://storage.googleapis.com/download.tensorflow.org/models/inception_v1_2016_08_28_frozen.pb.tar.gz',
untar=True)
tf.GraphDef
를 로드합니다:
In [ ]:
graph_def = tf.compat.v1.GraphDef()
loaded = graph_def.ParseFromString(open(path,'rb').read())
concrete_function
로 감쌉니다:
In [ ]:
inception_func = wrap_frozen_graph(
graph_def, inputs='input:0',
outputs='InceptionV1/InceptionV1/Mixed_3b/Branch_1/Conv2d_0a_1x1/Relu:0')
텐서를 입력으로 전달합니다:
In [ ]:
input_img = tf.ones([1,224,224,3], dtype=tf.float32)
inception_func(input_img).shape
In [ ]:
# 추정기 input_fn을 정의합니다.
def input_fn():
datasets, info = tfds.load(name='mnist', with_info=True, as_supervised=True)
mnist_train, mnist_test = datasets['train'], datasets['test']
BUFFER_SIZE = 10000
BATCH_SIZE = 64
def scale(image, label):
image = tf.cast(image, tf.float32)
image /= 255
return image, label[..., tf.newaxis]
train_data = mnist_train.map(scale).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
return train_data.repeat()
# 훈련과 평가 스펙을 정의합니다.
train_spec = tf.estimator.TrainSpec(input_fn=input_fn,
max_steps=STEPS_PER_EPOCH * NUM_EPOCHS)
eval_spec = tf.estimator.EvalSpec(input_fn=input_fn,
steps=STEPS_PER_EPOCH)
텐서플로 2.0에서 추정기를 구성하는 방법은 조금 다릅니다.
케라스를 사용하여 모델을 정의하는 것을 권장합니다. 그 다음 tf.keras.model_to_estimator
유틸리티를 사용하여 모델을 추정기로 바꾸세요. 다음 코드는 추정기를 만들고 훈련할 때 이 유틸리티를 사용하는 방법을 보여 줍니다.
In [ ]:
def make_model():
return tf.keras.Sequential([
tf.keras.layers.Conv2D(32, 3, activation='relu',
kernel_regularizer=tf.keras.regularizers.l2(0.02),
input_shape=(28, 28, 1)),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Flatten(),
tf.keras.layers.Dropout(0.1),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.Dense(10)
])
In [ ]:
model = make_model()
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
estimator = tf.keras.estimator.model_to_estimator(
keras_model = model
)
tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)
노트: 케라스에서는 가중치가 적용된 지표를 지원하지 않습니다. model_to_estimator
를 사용해 추정기 API의 가중 지표로 변경할 수 없습니다. add_metrics
함수를 사용해 추정기 스펙(spec)에 직접 이런 지표를 만들어야 합니다.
사용자 정의 model_fn
을 최소한의 변경만으로 TF 2.0과 사용하려면 tf.compat.v1
아래의 optimizers
와 metrics
을 사용할 수 있습니다.
사용자 정의 model_fn
에 케라스 모델을 사용하는 것은 사용자 정의 훈련 루프에 사용하는 것과 비슷합니다:
mode
매개변수에 기초하여 training
상태를 적절하게 지정하세요.trainable_variables
를 명시적으로 전달하세요.사용자 정의 루프와 큰 차이점은 다음과 같습니다:
model.losses
를 사용하는 대신 tf.keras.Model.get_losses_for
사용하여 손실을 추출하세요.tf.keras.Model.get_updates_for
를 사용하여 모델의 업데이트 값을 추출하세요.노트: "업데이트(update)"는 각 배치가 끝난 후에 모델에 적용해야 할 변화량입니다. 예를 들면 tf.keras.layers.BatchNormalization
층에서 평균과 분산의 이동 평균(moving average)이 있습니다.
다음은 사용자 정의 model_fn
으로부터 추정기를 만드는 코드로 이런 개념을 잘 보여 줍니다.
In [ ]:
def my_model_fn(features, labels, mode):
model = make_model()
optimizer = tf.compat.v1.train.AdamOptimizer()
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
training = (mode == tf.estimator.ModeKeys.TRAIN)
predictions = model(features, training=training)
if mode == tf.estimator.ModeKeys.PREDICT:
return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)
reg_losses = model.get_losses_for(None) + model.get_losses_for(features)
total_loss=loss_fn(labels, predictions) + tf.math.add_n(reg_losses)
accuracy = tf.compat.v1.metrics.accuracy(labels=labels,
predictions=tf.math.argmax(predictions, axis=1),
name='acc_op')
update_ops = model.get_updates_for(None) + model.get_updates_for(features)
minimize_op = optimizer.minimize(
total_loss,
var_list=model.trainable_variables,
global_step=tf.compat.v1.train.get_or_create_global_step())
train_op = tf.group(minimize_op, update_ops)
return tf.estimator.EstimatorSpec(
mode=mode,
predictions=predictions,
loss=total_loss,
train_op=train_op, eval_metric_ops={'accuracy': accuracy})
# 추정기를 만들고 훈련합니다.
estimator = tf.estimator.Estimator(model_fn=my_model_fn)
tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)
model_fn
만들기사용자 정의 model_fn
에서 TF 1.x API를 모두 제거하고 TF 2.0으로 업그레이드하려면 옵티마이저와 지표를 tf.keras.optimizers
와 tf.keras.metrics
로 업데이트해야 합니다.
위에서 언급한 최소한의 변경외에도 사용자 정의 model_fn
에서 업그레이드해야 할 것이 있습니다:
v1.train.Optimizer
대신에 tf.keras.optimizers
을 사용하세요.tf.keras.optimizers
에 모델의 trainable_variables
을 명시적으로 전달하세요.train_op/minimize_op
을 계산하려면,Tensor
이면 Optimizer.get_updates()
을 사용하세요. 반환되는 리스트의 첫 번째 원소가 train_op/minimize_op
입니다. train_op/minimize_op
객체를 얻기 위해 Optimizer.minimize()
를 사용하세요.tf.compat.v1.metrics
대신에 tf.keras.metrics
를 사용하세요.위의 my_model_fn
를 2.0으로 이전한 코드는 다음과 같습니다:
In [ ]:
def my_model_fn(features, labels, mode):
model = make_model()
training = (mode == tf.estimator.ModeKeys.TRAIN)
loss_obj = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
predictions = model(features, training=training)
# 조건이 없는 손실(None 부분)과
# 입력 조건이 있는 손실(features 부분)을 얻습니다.
reg_losses = model.get_losses_for(None) + model.get_losses_for(features)
total_loss=loss_obj(labels, predictions) + tf.math.add_n(reg_losses)
# tf.keras.metrics로 업그레이드 합니다.
accuracy_obj = tf.keras.metrics.Accuracy(name='acc_obj')
accuracy = accuracy_obj.update_state(
y_true=labels, y_pred=tf.math.argmax(predictions, axis=1))
train_op = None
if training:
# tf.keras.optimizers로 업그레이드합니다.
optimizer = tf.keras.optimizers.Adam()
# tf.compat.v1.train.global_step을 올바르게 증가시키기 위해
# 수동으로 tf.compat.v1.global_step 변수를 optimizer.iterations에 할당합니다.
# SessionRunHooks이 global_step에 의존하기 때문에
# 이 할당문은 추정기에 지정된 모든 `tf.train.SessionRunHook`에 필수적입니다.
optimizer.iterations = tf.compat.v1.train.get_or_create_global_step()
# 조건이 없는 손실(None 부분)과
# 입력 조건이 있는 손실(features 부분)을 얻습니다.
update_ops = model.get_updates_for(None) + model.get_updates_for(features)
# minimize_op을 계산합니다.
minimize_op = optimizer.get_updates(
total_loss,
model.trainable_variables)[0]
train_op = tf.group(minimize_op, *update_ops)
return tf.estimator.EstimatorSpec(
mode=mode,
predictions=predictions,
loss=total_loss,
train_op=train_op,
eval_metric_ops={'Accuracy': accuracy_obj})
# 추정기를 만들고 훈련합니다.
estimator = tf.estimator.Estimator(model_fn=my_model_fn)
tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)
tf.estimator.DNN*
, tf.estimator.Linear*
, tf.estimator.DNNLinearCombined*
모듈 아래에 있는 프리메이드 추정기(premade estimator)는 계속 텐서플로 2.0 API를 지원합니다. 하지만 일부 매개변수가 바뀌었습니다:
input_layer_partitioner
: 2.0에서 삭제되었습니다.loss_reduction
: tf.compat.v1.losses.Reduction
대신에 tf.keras.losses.Reduction
로 업데이트합니다. 기본값이 tf.compat.v1.losses.Reduction.SUM
에서 tf.keras.losses.Reduction.SUM_OVER_BATCH_SIZE
로 바뀌었습니다.optimizer
, dnn_optimizer
, linear_optimizer
: 이 매개변수는 tf.compat.v1.train.Optimizer
대신에 tf.keras.optimizers
로 업데이트되었습니다.위 변경 사항을 반영하려면:
Distribution Strategy
는 TF 2.0을 자동으로 대응하므로 input_layer_partitioner
에 대해 이전이 필요없습니다.loss_reduction
의 경우 지원되는 옵션을 tf.keras.losses.Reduction
확인해 보세요..optimizer
매개변수의 경우, optimizer
, dnn_optimizer
, linear_optimizer
매개변수를 전달하지 않거나 optimizer
매개변수를 string
으로 지정했다면 아무것도 바꿀 필요가 없습니다. 기본적으로 tf.keras.optimizers
를 사용합니다. 그외의 경우 tf.compat.v1.train.Optimizer
를 이에 상응하는 tf.keras.optimizers
로 바꾸어야 합니다.keras.optimizers
로 이전하면 TF 1.X로 저장한 체크포인트를 사용할 수 없습니다.
체크포인트에 저장하는 tf.keras.optimizers
변수가 다르기 때문입니다.
Tf 2.0으로 이전한 후에 예전 체크포인트를 사용하려면 체크포인트 변환기를 사용하세요.
In [ ]:
! curl -O https://raw.githubusercontent.com/tensorflow/estimator/master/tensorflow_estimator/python/estimator/tools/checkpoint_converter.py
이 스크립트는 도움말을 제공합니다:
In [ ]:
! python checkpoint_converter.py -h
다음 코드는 텐서플로 1.x와 텐서플로 2.0의 차이점을 보여줍니다.
In [ ]:
# TensorShape 객체를 만들고 인덱스를 참조합니다.
i = 0
shape = tf.TensorShape([16, None, 256])
shape
TF 1.x에서는 다음과 같이 사용합니다:
value = shape[i].value
TF 2.0에서는 다음과 같이 사용합니다:
In [ ]:
value = shape[i]
value
TF 1.x에서는 다음과 같이 사용합니다:
for dim in shape:
value = dim.value
print(value)
TF 2.0에서는 다음과 같이 사용합니다:
In [ ]:
for value in shape:
print(value)
TF 1.x에서는 다음과 같이 사용합니다(다른 Dimension 메서드를 사용할 때도):
dim = shape[i]
dim.assert_is_compatible_with(other_dim)
TF 2.0에서는 다음과 같이 사용합니다:
In [ ]:
other_dim = 16
Dimension = tf.compat.v1.Dimension
if shape.rank is None:
dim = Dimension(None)
else:
dim = shape.dims[i]
dim.is_compatible_with(other_dim) # 다른 Dimension 메서드도 동일
In [ ]:
shape = tf.TensorShape(None)
if shape:
dim = shape.dims[i]
dim.is_compatible_with(other_dim) # 다른 Dimension 메서드도 동일
랭크(rank)를 알 수 있다면 tf.TensorShape
의 불리언 값은 True
가 됩니다. 그렇지 않으면 False
입니다.
In [ ]:
print(bool(tf.TensorShape([]))) # 스칼라
print(bool(tf.TensorShape([0]))) # 길이 0인 벡터
print(bool(tf.TensorShape([1]))) # 길이 1인 벡터
print(bool(tf.TensorShape([None]))) # 길이를 알 수 없는 벡터
print(bool(tf.TensorShape([1, 10, 100]))) # 3D 텐서
print(bool(tf.TensorShape([None, None, None]))) # 크기를 모르는 3D 텐서
print()
print(bool(tf.TensorShape(None))) # 랭크를 알 수 없는 텐서
tf.colocate_with
삭제: 텐서플로의 장치 배치 알고리즘이 크게 향상되었습니다. 더 이상 이 연산이 필요하지 않습니다. 혹시 성능 저하가 발견된다면 [버그를 신고해 주세요]please file a bug.
v1.ConfigProto
를 동일한 tf.config
함수로 교체합니다.