brunch

You can make anything
by writing

C.S.Lewis

by gnugeun Jul 29. 2017

텐서플로우(TensorFlow) 시작하기 - 2

파이썬도 시작하기

앞서 작성한 글에 이어 https://www.tensorflow.org/get_started/get_started 페이지 번역 및 실습을 따라 해보려고 한다.


tf.train API

머신러닝에 대해 완벽히 논의하는 건 이번 튜토리얼의 범위는 아니다 라고 첫머리에 나온다. 텐서플로우 사용법에 집중하다는 얘기 같다.


텐서플로우는 optimizer라는 걸 제공한다.

optimizer는 손실 함수를 최소화하기 위해 조금씩 variable을 변경한다.

가장 간단한 optimizer는 gradient descent 다.

gradient descent에 대해서 잘 모른다면
홍콩과기대 김성훈 교수님의 아래 강의를 듣는 게 가장 이해가 빠르다.
https://www.youtube.com/watch?v=TxIVr-nk1so


gradient=경사, descent=내려감, 경사를 따라 내려가는 알고리즘. 기울기를 생각하면 된다.

optimizer는 각각의 variable 들에 기대되는 값과 손실의 derivative(도함수)의 정도에 따라 variable의 값을 수정한다.

번역하기가 어려워서 제대로 못한 거 같다. 말이 조금 어려운데 위 강의를 들었다면 바로 이해가 될 것이다. 도함수(=원래 함수를 미분하여 얻은 함수)는 어떤 점에서의 기울기다.  캡처 화면에서 보이는 U자형 그래프를  때, 현재 지점에서 기울기가 음수면 오른쪽으로, 양수면 왼쪽으로 변수 값을 변경시키면서 cost가 최소가 되는 지점, U자형 그래프의 하단 꼭짓점을 찾겠다는 이야기다.  


일반적으로 미분을 직접 하는 건 크게 중요한 작업도 아니면서 실수를 하기도 쉽다. 그래서 텐서플로우는 도함수를 자동으로 만들어준다. tf.gradients 호출하면 된다. 수학에 정말 자신 없는 나 같은 사람들에겐 희소식이다. optimizer가 이걸 해준다. 아래 예시를 보자.

optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
드디어 무언가 머신러닝스러운 코드가 나왔다.

파이썬은 들여 쓰기(indent)를 잘 해야 다.

for문으로 돌리고 싶은 명령은 들여 쓰기로 표시해주어야 한다. 또한 for 문 작성 완료 후 엔터를 쳐줘야 다음 명령문으로 갈 수 있다. 위 캡처 화면 보시면 것에 내가 익숙지 않아 발생한 SyntaxError가 보인다.


드디어 어느 정도 훈련이 들어간 머신러닝을 해보았다. 아직 간단한 선형 회귀라 텐서플로우 코어 코드가 대단히 필요하진 않았다. 하지만 모델에 데이터를 넣기 위한 복잡한 모델들과 메소드들은 좀 더 코드가 필요할 것이다.  그래서 텐서플로우는 더 높은 레벨의 추상화를 제공한다. 공통 패턴, 구조, 기능을 위해.  

다음 섹션에서 이 추상화를 어떻게 이용하는지 배워보자.


Complete program

지금까지 만든 훈련 가능한 선형 회귀 모델 코드다.

import numpy as np
import tensorflow as tf

# Model parameters
W = tf.Variable([. 3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# training data
x_train = [1,2,3,4]
y_train = [0,-1,-2,-3]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
  sess.run(train, {x:x_train, y:y_train})

# evaluate training accuracy
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x:x_train, y:y_train})
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))

위에서부터 쭉 훑어보면

1. numpy와 tensorflow를 import 하고

2. W와 b는 float32 형 Variable로 선언한다. 각각 0.3과 -0.3 으로 초기값을 설정해준다. (여기서 바로 초기화되진 않는다.)

3. x를 float32 형 placeholder로 선언한다.

4. linear_model 이란 변수는 W * x + b로 선언한다. 변수명에서 선형 회귀를 사용하겠다는 걸 알 수 있다.

5. y를 x와 같이 선언한다.

6. 손실 함수를 정의한다. 여기선 linear_model과 y 의 오차를 제곱한 뒤 reduce_sum 한다. 

reduce_sum은 텐서의 차원들을 탐색하며 개체들의 총합을 계산하는 함수다. 옵션에 따라 열 단위로, 행단 위로 등이 가능하다. 아래 홈페이지에 나온 예시를 보면 이해가 빠르다. 0이 열 단위로, 1위 행단 위다.
# 'x' is [[1, 1, 1]
#         [1, 1, 1]]
tf.reduce_sum(x) ==> 6
tf.reduce_sum(x, 0) ==> [2, 2, 2]
tf.reduce_sum(x, 1) ==> [3, 3]
tf.reduce_sum(x, 1, keep_dims=True) ==> [[3], [3]]
tf.reduce_sum(x, [0, 1]) ==> 6
최소 제곱 법
가우스가 말하는 최소제곱법은 무엇인지 알아보자. 관찰이나 실험으로 얻은 적은 수의 자료를 분석하여 그 상황을 설명하기 위해서는 자료를 잘 표현할 수 있는 방정식을 예측해야 한다. 세레스의 궤도를 연구했던 여러 학자들 중에서 가우스만이 그 위치를 알 수 있었던 것은 그가 예측한 식이 그만큼 적합했다는 것이다.  
[네이버 지식백과] 최소제곱법 - 세레스 찾기 (수학산책)
http://terms.naver.com/entry.nhn?docId=3569970&cid=58944&categoryId=58970

7. optimizer로 GradientDescentOptimizer를 선택한다. 괄호 안 0.01은 learning rate다. 김성훈 교수님 강의 중 2차 함수 그래프에서 cost 최솟값을 찾기 위해 이동하는 단위라고 생각하면 된다.

8. loss를 최소화하는 train을 선언한다.

9. x와 y placeholder에 넣어줄 값을 선언한다.

10. init 변수를 통해 variable들 초기화 준비한다.

11. 세션을 생성하고 초기화시킨다.

12. for를 통해 1000번 훈련시킨다.

13. 훈련 결과를 확인한다.

실행하면 다음과 같은 값이 나온다.

하다가 졸아서 맨 위에ㅣㅣㅣㅣㅣㅣㅣㅣ 이 찍혔다.

loss가 매우 작은 것에 주목하자. 0에 가깝다. 모델이 랜덤 값으로 초기화되기 때문에 실제 각자 환경에서 실행해본 결과는 정확하게 같진 않을 수 있다.


모델이 조금 더 복잡해졌지만, 여전히 텐서 보드로 시각화가 가능하다.

https://www.tensorflow.org/get_started/get_started


tf.contrib.learn

tf.contrib.learn 은 머신러닝의 역학을 단순화할 수 있는 하이레벨 텐서 플로우 라이브러리다.

많은 공통 모델정의되어 있다.

아래를 포함한다.

running training loops

running evaluation loops

managing data sets

managing feeding


기본 사용법

tf.contrib.learn을 이용하면 위에서 실습해본 선형 회귀 프로그램이 얼마나 간단해지는지 비교해보라.

import tensorflow as tf
# NumPy is often used to load, manipulate and preprocess data.
import numpy as np

# Declare list of features. We only have one real-valued feature. There are many
# other types of columns that are more complicated and useful.
features = [tf.contrib.layers.real_valued_column("x", dimension=1)]

# An estimator is the front end to invoke training (fitting) and evaluation
# (inference). There are many predefined types like linear regression,
# logistic regression, linear classification, logistic classification, and
# many neural network classifiers and regressors. The following code
# provides an estimator that does linear regression.
estimator = tf.contrib.learn.LinearRegressor(feature_columns=features)

# TensorFlow provides many helper methods to read and set up data sets.
# Here we use two data sets: one for training and one for evaluation
# We have to tell the function how many batches
# of data (num_epochs) we want and how big each batch should be.
x_train = np.array([1., 2., 3., 4.])
y_train = np.array([0., -1., -2., -3.])
x_eval = np.array([2., 5., 8., 1.])
y_eval = np.array([-1.01, -4.1, -7, 0.])
input_fn = tf.contrib.learn.io.numpy_input_fn({"x":x_train}, y_train,
                                              batch_size=4,
                                              num_epochs=1000)
eval_input_fn = tf.contrib.learn.io.numpy_input_fn(
    {"x":x_eval}, y_eval, batch_size=4, num_epochs=1000)

# We can invoke 1000 training steps by invoking the  method and passing the
# training data set.
estimator.fit(input_fn=input_fn, steps=1000)

# Here we evaluate how well our model did.
train_loss = estimator.evaluate(input_fn=input_fn)
eval_loss = estimator.evaluate(input_fn=eval_input_fn)
print("train loss: %r"% train_loss)
print("eval loss: %r"% eval_loss)


tf.contrib.learn 사용하기 전 코드와 비교해보자.


주석을 제외한 코드는 19줄 vs 15줄로 4줄 차이 난다.


1. 일단 import 문은 같다.

2. 원래 코드에 있던 x, y variable 구문이 사라졌다.

대신 tf.contrib.layers.real_valued_column  이란 구문이 나타난다.

홈페이지에 layers는 뉴럴 네트워크(neural network) layers, regularizers, summaries를 만들기 위한 명령어 들을 제공한다고 나와있다.

그중에서 지금 사용되는 feature columns는 data를 모델에 매핑시키는 메커니즘을 제공하는 명령이라고 한다. feature columns 안의 여러 종류 중 하나가 real_valued_column으로 빽빽한 숫자 data를 위한 _RealValuedColumn라는 걸 만든다고 쓰여있다.

정확히 이해는 안 가지만 다음 코드인

estimator = tf.contrib.learn.LinearRegressor(feature_columns=features)

와 연관시켜 생각해보면 LinearRegressor는 y=wx + b라는 식일 테니 x에 넣을 숫자 값을 이런 형식으로 제공하기 위한 단계라고 생각하고 넘어갔다.

처음에는 잘 이해가 되지 않았던 estimator(추정 법칙)란 단어도 코드를 보니 내가 머신러닝에 적용할 방식이라고 생각하면 될 거 같다. 이번 코드에선 선형 회귀가 될 것이다.

3. 이전 코드에선 훈련과 평가를 같은 값으로 했었다. 이번 코드에선 평가를 다른 값으로 진행한다.

4. tf.contrib.learn.io.numpy_input_fn 를 이용해 input_fn 이란 걸 만든다.

이 부분도 정확한 내부 로직은 모르겠지만, 데이터 형태와 fit 함수의 input_fn의 제약조건, x와 y 값이 같이 필요, 를 채워주는 구문인 것 같다.

5. for문을 사용해 1000번 sess.run.. 을 반복했던 부분이 steps=1000을 준 estimator.fit(input_fn=input_fn, steps=1000)로 변경되었다.

fit 의 의미만 알면 깔끔하고 이해하기 쉽고 에러를 줄일 수 있는 코드가 된 거 같다.

6. 훈련 결과를 확인하기 위한 단계가 estimator.evlauate 호출로 변경되었다.

이전과 달리 eval을 위한 데이터를 별도로 준비하고 훈련용 데이터와 함께 결과를 확인한다.


실행결과는 아래와 같다.


gpu가 있음에도 cpu 버전 텐서플로우를 설치해놓으니 실행할 때마다 성능개선이 가능하다는 경고가 보인다.

eval 데이터의 loss는 train보다는 높지만 그래도 0에 가깝게 떨어진 걸 보니 제대로 learning 하고 있다고 볼 수 있다.


A custom model

tf.contrib.learn 명령어는 미리 정해진 모델들 외에도 사용할 수 있다.

텐서플로우에 내장되지 않은 커스텀 모델을 만들 때도 여전히 data set, feeding, training 등의 tf.contrib.learn 의 고레벨 추상화를 유지할 수 있다.

LinearRegressor와 동일한 모델을 낮은 레벨의 텐서플로우 API에 대한 지식을 사용하여 구현해보자.


tf.contrib.learn를 이용할 수 있는 커스텀 모델을 정의하기 위해, tf.contrib.learn.Estimator를 사용해야 한다.

tf.contrib.learn.LinearRegressor는 사실 tf.contrib.learn.Estimator의 서브클래스다. Estimator 서브 클래싱 대신, Estimator에 tf.contrib.learn이 어떻게 evaluate predictions, training steps, and loss를 해야 하는지 말해주는 model_fn 함수를 만들어보자.

아래가 코드다.

import numpy as np
import tensorflow as tf
# Declare list of features, we only have one real-valued feature
def model(features, labels, mode):
  # Build a linear model and predict values
  W = tf.get_variable("W", [1], dtype=tf.float64)
  b = tf.get_variable("b", [1], dtype=tf.float64)
  y = W*features['x'] + b
  # Loss sub-graph
  loss = tf.reduce_sum(tf.square(y - labels))
  # Training sub-graph
  global_step = tf.train.get_global_step()
  optimizer = tf.train.GradientDescentOptimizer(0.01)
  train = tf.group(optimizer.minimize(loss),
                   tf.assign_add(global_step, 1))
  # ModelFnOps connects subgraphs we built to the
  # appropriate functionality.
  return tf.contrib.learn.ModelFnOps(
      mode=mode, predictions=y,
      loss=loss,
      train_op=train)

estimator = tf.contrib.learn.Estimator(model_fn=model)
# define our data sets
x_train = np.array([1., 2., 3., 4.])
y_train = np.array([0., -1., -2., -3.])
x_eval = np.array([2., 5., 8., 1.])
y_eval = np.array([-1.01, -4.1, -7, 0.])
input_fn = tf.contrib.learn.io.numpy_input_fn({"x": x_train}, y_train, 4, num_epochs=1000)

# train
estimator.fit(input_fn=input_fn, steps=1000)
# Here we evaluate how well our model did.
train_loss = estimator.evaluate(input_fn=input_fn)
eval_loss = estimator.evaluate(input_fn=eval_input_fn)
print("train loss: %r"% train_loss)
print("eval loss: %r"% eval_loss)

위의 코드와는 차이점은

estimator = tf.contrib.learn.LinearRegressor(feature_columns=features)

대신

def model(features, labels, mode):

를 사용해 직접 모델을 정의했다는 점이다.

모델 내부는 그동안 배운 내용과 크게 다르지 않다.

한 가지 다른 점은 return문구에 새로 등장한 tf.contrib.learn.ModelFnOps  이다.

홈페이지에 따르면 model_fn으로부터 ops를 리턴한다고 쓰여있다.


포인트는 def model... 을 사용해도 tf.contrib.learn을 사용할 수 있다는 점이다.


결과 화면은 아래와 같다.

eval_loss가 0에 가깝다

참고로 위 코드는 그 위의 tf.contrib.learn 코드에서 사용됐던 아래 두 라인 없이 실행하면 에러 난다.

eval_input_fn = tf.contrib.learn.io.numpy_input_fn({"x":x_eval}, y_eval, batch_size=4, num_epochs=1000)

eval_loss = estimator.evaluate(input_fn=eval_input_fn)


Next steps

여기까지가 텐서플로우의 기본이라고 한다.

텐서플로우 홈페이지에선 두 가지 다음 단계를 제시한다.

    1. 비기너를 위한 MNIST for beginners

        https://www.tensorflow.org/get_started/mnist/beginners

    2. 전문가를 위한 Deep MNIST for experts

        https://www.tensorflow.org/get_started/mnist/pros

고민할 필요가 없다. 난 비기너니깐 1번을 보면 될 거 같다.


그런데 1번을 보기 전에 https://www.kaggle.com/ 에서 머신러닝 문제 하나 풀어보고 넘어가려고 한다.

kaggle은 어떤 문제가 주어지고 이 문제를 여러 팀이 경합하여 풀 수 있도록 플랫폼을 제공하는 사이트다.

문제는 기업이 내기도 하고 그 외 단체에서 내기도 하는 거 같다. 실제 현장에 적용해야 할 문제를 올리기도 하고 개념적인 문제도 올라온다.

주목해야 할 점은 상금이 걸린 문제가 있다는 것이다.

현재 가장 큰 상금은 $1,500,000 까지 걸려있다. 미국 국토안보부에서 올린 문제다.


현재 나 포함 4 명이 함께 공부하고 있는데,

kaggle에서 머신러닝 튜토리얼로 소개하고 있는 타이타닉 문제를 같이 풀어보기로 했다.

상금은 걸려있지 않지만 머신러닝을 공부하는데 큰 도움이 될 것 같다.


내용이 점점 어려워지고 있다.

여러 사이트 및 사람들의 조언을 들어본 결과 선형대수학을 다시 공부해야겠다.

선형대수학을 좀 알아야 머신러닝을 제대로 이해하고 적용할 수 있다고 한다.


그리고 사용하는 에디터도 윈도우 파워쉘에서 주피터로 변경하려고 한다.

파워쉘은 처음에 빠르고 간단하고 쉽게 시작해보려고 썼었는데,

같이 공부하는 사람에게 주피터를 추천받아서 갈아타려고 한다.


끝.

작가의 이전글 텐서플로우(TensorFlow) 시작하기 - 1
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari