이전 튜토리얼에서는 머신러닝을 위한 기본 구성 요소인 자동 미분(automatic differentiation)을 위한 텐서플로 API를 알아보았습니다. 이번 튜토리얼에서는 이전 튜토리얼에서 소개되었던 텐서플로의 기본 요소를 사용하여 간단한 머신러닝을 수행해보겠습니다.
텐서플로우는 반복되는 코드를 줄이기 위해 유용한 추상화를 제공하는 고수준의 신경망(neural network) API인 tf.keras
를 포함하고 있습니다. 신경망을 다룰 때 이러한 고수준의 API를 사용하는 것을 추천합니다. 이번 짧은 튜토리얼에서는 탄탄한 기초를 기르기 위해 기본적인 요소만으로 신경망을 훈련시켜 보겠습니다.
import warnings
warnings.filterwarnings(action='ignore')
import tensorflow as tf
텐서플로우의 텐서(Tensor)는 상태가 없고, 변경이 불가능한(immutable) 객체입니다. 그러나 머신러닝 모델은 상태가 변경될(stateful) 필요가 있습니다. 예를 들어, 모델 학습에서 예측을 계산하기 위한 동일한 코드는 시간이 지남에 따라 다르게(희망하건대 더 낮은 손실로 가는 방향으로)동작해야 합니다. 이 연산 과정을 통해 변화되어야 하는 상태를 표현하기 위해 명령형 프로그래밍 언어인 파이썬을 사용 할 수 있습니다.
# 파이썬 구문 사용
x = tf.zeros([10, 10])
x += 2 # 이것은 x = x + 2와 같습니다.
print(x)
텐서플로우는 변경이 불가능한 객체인 텐서의 상태를 변경할 수 있는 연산자가 내장되어 있으며, 이러한 연산자는 상태를 표현하기 위한 저수준의 파이썬 표현보다 사용하기 좋습니다. 예를 들어, 모델의 가중치를 나타내기 위해서는 텐서플로우 변수를 사용하는 것이 편하고 효율적입니다.
텐서플로우 변수는 값을 저장하는 객체로 텐서플로우 연산에 사용될 때 저장된 값을 읽어올 것입니다. tf.assign_sub
, tf.scatter_update
등은 텐서플로우 변수에 저장되있는 값을 조작하는 연산자입니다.
v = tf.Variable(1.0)
print(v.numpy())
# 값을 재할당합니다.
v.assign(3.0)
print(v.numpy())
# tf.square()와 같은 텐서플로 연산에 선언한 `v`를 사용하고 재할당합니다.
v.assign(tf.square(v))
print(v.numpy())
변수를 사용한 연산은 그래디언트가 계산될 때 자동적으로 추적됩니다. 임베딩(embedding)을 나타내는 변수의 경우 기본적으로 희소 텐서(sparse tensor)를 사용하여 업데이트됩니다. 이는 연산과 메모리에 더욱 효율적입니다.
또한 변수를 사용하는 것은 코드를 읽는 독자에게 상태가 변경될 수 있다는 것을 알려주는 손쉬운 방법입니다.
지금까지 간단한 모델을 구축하고 학습시키기 위해 —Tensor
, GradientTape
, Variable
— 와 같은 몇가지 개념을 설명했습니다. 이는 일반적으로 다음의 과정을 포함합니다.
이번 튜토리얼에서는 선형 모델의 간단한 예제를 살펴보겠습니다:
f(x) = x * W + b
모델은 W
와 b
두 변수를 가지고 있는 선형모델이며, 잘 학습된 모델이 W = 3.0
and b = 2.0
의 값을 갖도록 합성 데이터를 만들겠습니다.
W
는 가중치, b
는 편향을 의미합니다.변수와 연산을 캡슐화하기 위해 간단한 클래스를 정의해봅시다.
class Model(object):
def __init__(self):
# 가중치와 편향 변수를 각각 (5.0, 0.0)으로 초기화 합니다.
# 실제로는 임의의 값으로 초기화 되어야합니다.
self.W = tf.Variable(5.0)
self.b = tf.Variable(0.0)
def __call__(self, x):
# 모델 정의
return self.W * x + self.b
model = Model()
print(model(3.0).numpy())
손실 함수는 주어진 입력에 대한 모델의 출력이 원하는 출력과 얼마나 일치하는지를 측정합니다. 이번 튜토리얼에서는 평균 제곱 오차(mean square error) 를 적용한 손실 함수를 사용하겠습니다.
평균 제곱 오차(mean square error) : 오차의 제곱에 대해 평균을 취한 값으로, 계산하는 수식은 다음과 같습니다.
$\frac { 1 }{ n } \sum { i=1 }^{ n }{ ( { Y }{ i }-\hat { { Y }_{ i } }) } ^{ 2 }$
${ Y }{ i }$ 는 실제 y 값을 의미하며, $\hat { { Y }{ i }}$ 는 예측값을 의미합니다.
def loss(predicted_y, desired_y):
return tf.reduce_mean(tf.square(desired_y - predicted_y))
약간의 잡음(noise)와 훈련 데이터를 합칩니다.
TRUE_W = 3.0
TRUE_b = 2.0
NUM_EXAMPLES = 1000
# tf.random.normal 메소드를 사용한 훈련 데이터 생성
inputs = tf.random.normal(shape=[NUM_EXAMPLES])
noise = tf.random.normal(shape=[NUM_EXAMPLES])
outputs = inputs * TRUE_W + TRUE_b + noise
print(outputs)
모델을 훈련시키기 전에, 모델의 현재 상태를 시각화합시다. 모델의 예측을 빨간색으로, 훈련 데이터를 파란색으로 구성합니다.
import matplotlib.pyplot as plt
%matplotlib inline
# 실제 훈련 데이터
plt.scatter(inputs, outputs, c='b')
# 모델 예측
plt.scatter(inputs, model(inputs), c='r')
plt.show()
print('현재 손실: '),
print(loss(model(inputs), outputs).numpy())
이제 네트워크와 훈련 데이터가 준비되었습니다. 모델의 변수(가중치 W
와 편향 b
)를 업데이트하기 위해 훈련 데이터를 사용하여 훈련시켜 봅시다. 그리고 경사 하강법(gradient descent) 을 사용하여 손실을 감소시킵니다. 경사 하강법에는 여러가지 방법이 있는데, tf.train.Optimizer
에 구현되어 있습니다. 이미 구현되어 있는 경사 하강법을 사용하는 방법이 가장 간단하기 때문에 가장 많이 사용됩니다.
그러나 이번 튜토리얼에서는 구현되어 있는 방법을 사용하지 않고, 기본적인 방법을 구현하겠습니다.
def train(model, inputs, outputs, learning_rate):
with tf.GradientTape() as t:
current_loss = loss(model(inputs), outputs)
dW, db = t.gradient(current_loss, [model.W, model.b])
model.W.assign_sub(learning_rate * dW)
model.b.assign_sub(learning_rate * db)
마지막으로, 훈련 데이터를 반복적으로 실행하고, 변수 W
와 b
의 변화 과정을 확인합니다.
model = Model()
# 도식화를 위해 W값과 b값의 변화를 저장합니다.
Ws, bs = [], []
epochs = range(10)
for epoch in epochs:
Ws.append(model.W.numpy())
bs.append(model.b.numpy())
current_loss = loss(model(inputs), outputs)
train(model, inputs, outputs, learning_rate=0.1)
print('에포크 %2d: W=%1.2f b=%1.2f, 손실=%2.5f' %
(epoch, Ws[-1], bs[-1], current_loss))
# 저장된 값들을 도식화합니다.
plt.plot(epochs, Ws, 'r',
epochs, bs, 'b')
plt.plot([TRUE_W] * len(epochs), 'r--',
[TRUE_b] * len(epochs), 'b--')
plt.legend(['W', 'b', 'true W', 'true_b'])
plt.show()
#@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.