이 포스팅은 Tensorflow 2.0 시리즈 2 편 중 2 번째 글 입니다.
목차
Tensorflow 2.0 Tutorials의 Expert Start Code를 정리한다.
Code
# 파이썬 3에서 쓰던 문법을 파이썬 2에서 사용가능하게 해줌
from __future__ import absolute_import, division, print_function, unicode_literals
!pip install -q tensorflow-gpu==2.0.0-rc1 # q 옵션은 quiet 이다.. 한참찾았네..
import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten, Conv2D
from tensorflow.keras import Model
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
# 채널을 나타내는 차원을 추가합니다. mnist는 흑백이므로 하나만.
x_train = x_train[..., tf.newaxis]
x_test = x_test[..., tf.newaxis]
x_train.shape
(60000, 28, 28, 1)
# tf.data에 있는 slices를 사용하여 섞고, batch 단위로 나누자.
train_ds = tf.data.Dataset.from_tensor_slices(
(x_train, y_train)
).shuffle(10000).batch(32)
test_ds = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)
# 케라스(Keras)의 모델 서브클래싱(subclassing) API를 사용하여 tf.keras 모델을 만든다.
class MyModel(Model):
def __init__(self):
super(MyModel, self).__init__()
self.conv1 = Conv2D(32, 3, activation='relu')
self.flatten = Flatten()
self.d1 = Dense(128, activation='relu')
self.d2 = Dense(10, activation='relu')
def call(self, x):
x = self.conv1(x)
x = self.flatten(x)
x = self.d1(x)
return self.d2(x)
model = MyModel()
# 훈련에 사용할 optimizer와 loss function을 선택한다.
loss_object = tf.keras.losses.SparseCategoricalCrossentropy()
optimizer = tf.keras.optimizers.Adam()
# metric을 선택한다.
train_loss = tf.keras.metrics.Mean(name='train_loss')
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')
test_loss = tf.keras.metrics.Mean(name='test_loss')
test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')
# tf.GradientTape를 사용하여 모델을 훈련한다.
# 이것은 모든 작업을 기록한다는 의미에서 Tape를 사용한 듯하다.
@tf.function # decorator를 사용하여 진행한다.
def train_step(images, labels):
# with 문은 자주 사용하는 함수를 클래스로 묶은 것을 가져와서 사용할 수 있게한다.
# 재사용에 매우 유리하다.
# 작동 방식은, GradientTape라는 클래스 안에는 시작, 끝을 알리는 행위가 기록되어 있다.
# 우리가 하는 작업 이후에 이를 꼭 해줘야 한다면, with문을 사용할 경우 이를 무조건 보장한다.
# 보통 파일 읽기, 쓰기와 같은 작업에서 많이 사용한다고 보면 된다.
# 여기서는 GradientTape에서 with 문 아래에 있는 context의 내용을 실행하고,
# 모든 연산을 "기록"한다.
# 우리는 train data를 넣고, loss를 뽑는 작업 이외에 수행하는 것은 항상 같다.
# 따라서 with 문으로 설계하는 것이 맞다.
with tf.GradientTape() as tape:
predictions = model(images) # 예측 결과를 저장한다.
loss = loss_object(labels, predictions)
# 기록을 완료한뒤, 기록한 gradient를 뽑자. 기록된 tape에서 후진 자동 미분을 수행한다.
gradients = tape.gradient(loss, model.trainable_variables)
# 계산된 gradient를 적용한다.
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
# 현재 loss와 accuracy를 저장한다.
train_loss(loss)
train_accuracy(labels, predictions)
여기서 잠깐, trainable_variables은 실제로 model에 데이터가 들어갔을 때,
[
<tf.Variable 'my_model/conv2d/kernel:0' shape=(3, 3, 1, 32) dtype=float32>,
<tf.Variable 'my_model/conv2d/bias:0' shape=(32,) dtype=float32>,
<tf.Variable 'my_model/dense/kernel:0' shape=(21632, 128) dtype=float32>,
<tf.Variable 'my_model/dense/bias:0' shape=(128,) dtype=float32>,
<tf.Variable 'my_model/dense_1/kernel:0' shape=(128, 10) dtype=float32>,
<tf.Variable 'my_model/dense_1/bias:0' shape=(10,) dtype=float32>]
위와 같이 배열 형태로 들어가게 된다. 이러한 입력에 대해 loss를 보고 gradient를 계산한다. 업데이트도 마찬가지고 입력과 gradients가 주어져야 할 수 있기 때문에 zip의 형태로 들어간다. 아주 편리하다,,
# test에서도 만들어준다.
# 이경우는 gradient를 할필요가 없다.
@tf.function
def test_step(images, labels):
predictions = model(images)
t_loss = loss_object(labels, predictions)
test_loss(t_loss)
test_accuracy(labels, predictions)
EPOCHS = 5
for epoch in range(EPOCHS):
for images, labels in train_ds:
train_step(images, labels)
for test_images, test_labels in test_ds:
test_step(test_images, test_labels)
template = '에포크: {}, 손실: {}, 정확도: {}, 테스트 손실: {}, 테스트 정확도: {}'
print(template.format(epoch+1,
train_loss.result(),
train_accuracy.result()*100,
test_loss.result(),
test_accuracy.result()*100))
에포크: 1, 손실: 2.3113672733306885, 정확도: 9.899853706359863, 테스트 손실: 2.3025553226470947, 테스트 정확도: 9.800000190734863
에포크: 2, 손실: 2.3101234436035156, 정확도: 9.895792961120605, 테스트 손실: 2.3025572299957275, 테스트 정확도: 9.800000190734863
에포크: 3, 손실: 2.309101104736328, 정확도: 9.892754554748535, 테스트 손실: 2.3025825023651123, 테스트 정확도: 9.800000190734863
에포크: 4, 손실: 2.3081705570220947, 정확도: 9.890397071838379, 테스트 손실: 2.3026015758514404, 테스트 정확도: 9.800000190734863
에포크: 5, 손실: 2.307427406311035, 정확도: 9.88851261138916, 테스트 손실: 2.3026163578033447, 테스트 정확도: 9.800000190734863