본문 바로가기
Tensorflow

16. Tensorflow 시작하기 - input function

by 대소니 2016. 11. 14.


이번에는 tf.contrib.learn 에 있는 input functions를 생성하는 방법에 대해서 알아보겠습니다.

input_fn을 사용하면 모델에 preprocess를 처리하거나 data를 feed 하는데 유용하게 사용할 수 있습니다.



Custom Input Pipelines with input_fn


tf.contrib.learn을 사용해서 neural network 학습을 진행할때 우리는 feature와 target data를 직접적으로 fit, evaluate, predict ops에 사용하였습니다.


tf.contrib.learn quickstart 의 예제를 통해서 보았던 방법인데 이러한 접근방식은 source data가 완벽하여 추가 보정같은 것이 필요하지 않을때만 사용이 가능합니다.

하지만, 대부분의 실데이터에서는 input fuction을 통해서 data를 preprocessing 하고 piping 하여 모델에 사용하는 것이더욱 적절합니다.



Anatomy of an input_fn


그러면 input function에 대해서 조금더 자세히 알아보겠습니다.

아래의 코드는 기본적인 input_fn에 구성과 같습니다.

먼저, data를 preprocessing 하기 위한 로직이 포함될 것입니다. 이것은 잘못된 정보를 수정하거나 feature scaling을 하기 위해서 사용되겠습니다.

그리고 input_fn은 반듯이 2가지의 결과물을 return 해주어야 합니다. 바로 feature와 label data 입니다. 이 데이터가 모델에 학습을 위해서 feed가 되기 때문이지요

두가지 모두 Tensor 형태가 되어야 합니다.


def my_input_fn()

    # Preprocess your data here...


    # ...then return 1) a mapping of feature columns to Tensors with

    # the corresponding feature data, and 2) a Tensor containing labels

    return feature_cols, labels



Converting Feature Data to Tensors


만약 우리의 input data가 pandas dataframes 이나 numpy arrays로 되어 있다면, 이것을 Tensor로 변환해주어야 합니다. 


연속적인 값의 데이터라면 쉽게 tf.constant를 사용해서 Tensor로 생성할 수 있습니다.


feature_column_data = [1, 2.4, 0, 9.9, 3, 120]

feature_tensor = tf.constant(feature_column_data)


혹은 sparse, categorical data 라면 SparseTensor로 생성하면 됩니다.


sparse_tensor = tf.SparseTensor(indices=[[0,1], [2,4]],

                                values=[6, 0.5],

                                shape=[3, 5])

>

[[0, 6, 0, 0, 0]

 [0, 0, 0, 0, 0]

 [0, 0, 0, 0, 0.5]]



Passing input_fn Data to Your Model


자 이렇게 생성한 input_fn 을 모델에 적용하기 위해서는 아주 간단하게 이름만 옵션에 지정해 주면 됩니다.


classifier.fit(input_fn=my_input_fn, steps=2000)


만약, input_fn 함수에 인자를 주어 사용하고 싶을 경우에는 직접적으로 사용하지 않고, 새로운 wrapper 함수를 만들어서 사용하는 방법이 있습니다.

input_fn 옵션에는 함수 이름이 지정이 되어야 하기 때문입니다. 이를 잘못 사용하게 되면 TypeError가 발생할 수 있습니다.


def my_input_function_training_set:

  my_input_function(training_set)


classifier.fit(input_fn=my_input_fn_training_set, steps=2000)


또는 Python에서 제공하는 functools.partial 함수를 사용할 수도 있고


classifier.fit(input_fn=functools.partial(my_input_function,

                                          data_set=training_set), steps=2000)


또는 lambda 식을 사용할 수도 있습니다.

아마도 lamdba를 이용하는 것이 가장 깔끔한것 같습니다.


classifier.fit(input_fn=lambda: my_input_fn(training_set), steps=2000)



fit 할때 외에도 evaluate 할 때도 마찬가지로 사용하면 됩니다.


classifier.evaluate(input_fn=lambda: my_input_fn(test_set), steps=2000)



A Neural Network Model for Boston House Values


이제 본격적으로 이번 튜토리얼에 대해서 알아보겠습니다.

이번 내용은 Boston housing data를 Neural Network 를 통해 학습을 시키고 적정한 집값을 예측해보도록 하겠습니다.

이 보스톤의 하우스 데이터는 9가지의 feature 정보들을 포함하고 있습니다.


이 데이터는 csv 파일로 되어 있고 아래의 경로에서 다운로드 받을 수 있습니다. 3가지 파일 모두를 받습니다. ( boston_train.csv, boston_test.csv, boston_predict.csv)

(https://www.tensorflow.org/versions/master/tutorials/input_fn/index.html#building-input-functions-with-tf-contrib-learn)



전체 소스는 아래와 같습니다.



from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import pandas as pd
import tensorflow as tf

tf.logging.set_verbosity(tf.logging.INFO)

COLUMNS = ["crim", "zn", "indus", "nox", "rm", "age",
"dis", "tax", "ptratio", "medv"]
FEATURES = ["crim", "zn", "indus", "nox", "rm",
"age", "dis", "tax", "ptratio"]
LABEL = "medv"


def input_fn(data_set):
feature_cols = {k: tf.constant(data_set[k].values) for k in FEATURES}
labels = tf.constant(data_set[LABEL].values)
return feature_cols, labels


def main(unused_argv):
# Load datasets
training_set = pd.read_csv("boston_train.csv", skipinitialspace=True,
skiprows=1, names=COLUMNS)
test_set = pd.read_csv("boston_test.csv", skipinitialspace=True,
skiprows=1, names=COLUMNS)

# Set of 6 examples for which to predict median house values
prediction_set = pd.read_csv("boston_predict.csv", skipinitialspace=True,
skiprows=1, names=COLUMNS)

# Feature cols
feature_cols = [tf.contrib.layers.real_valued_column(k)
for k in FEATURES]

# Build 2 layer fully connected DNN with 10, 10 units respectively.
regressor = tf.contrib.learn.DNNRegressor(
feature_columns=feature_cols, hidden_units=[10, 10])

# Fit
regressor.fit(input_fn=lambda: input_fn(training_set), steps=5000)

# Score accuracy
ev = regressor.evaluate(input_fn=lambda: input_fn(test_set), steps=1)
loss_score = ev["loss"]
print("Loss: {0:f}".format(loss_score))

# Print out predictions
y = regressor.predict(input_fn=lambda: input_fn(prediction_set))
print ("Predictions: {}".format(str(y)))

if __name__ == "__main__":
tf.app.run()



순서대로 소스를 살펴보면,

먼저 로그 레벨을 INFO로 지정해 줍니다.


그린후 이번 데이터에서 사용하게 될 컬럼들과 피쳐들과 결과값인 LABEL의 이름들을 지정해 줍니다.


이제 input_fn을 만들어 줍니다.

pandas dataframe으로 되어 있는 데이터를 Tensor로 변환해주어야 겠죠?

그리하여 두가지 features와 labels 데이터를 return해주도록 합니다. 아주 심플한 입력 함수가 완성이 되었습니다.


이제 main 함수를 살펴보면,

다운로드 받은 3개의 csv 파일을 read_csv 함수를 이용해서 로딩하도록 합니다.


다음으로 FeatureColumns 를 생성해줍니다. 이번 데이터에서 사용되는 features은 모두 연속적인 값을 갖는 data들이므로 real_valued_column() 함수를 사용하여 생성해 주도록 합니다. ( feature columns에 대한 내용은 선형모델 튜토리얼에서 살펴보았으니 자세한 내용은 이전 내용을 참고해주세요)


이렇게 하여 DNNRegressor에 feature_columns 항목에 지정을 해줍니다.


fit()  함수에서 lambda 식을 사용해서 input_fn 에 학습 데이터 셋을 feed 해줍니다.


evaluate() 함수에서도 input_fn 을 이용해서 test 데이터 셋을 feed 해줍니다.

그리고 loss 값을 출력하도록 되어 있습니다.


최종적으로 prediction 데이터 셋을 이용해서 예측값이 어떻게 나오는지 확인해줍니다.

정상적으로 결과가 나오면 다음과 같이 로그를 확인할 수 있게 됩니다.


...

INFO:tensorflow:Setting targets info to TensorSignature(dtype=tf.float64, shape=TensorShape([Dimension(400)]), is_sparse=False)

INFO:tensorflow:Transforming feature_column _RealValuedColumn(column_name='age', dimension=1, default_value=None, dtype=tf.float32, normalizer=None)

INFO:tensorflow:Transforming feature_column _RealValuedColumn(column_name='crim', dimension=1, default_value=None, dtype=tf.float32, normalizer=None)

INFO:tensorflow:Transforming feature_column _RealValuedColumn(column_name='dis', dimension=1, default_value=None, dtype=tf.float32, normalizer=None)

INFO:tensorflow:Transforming feature_column _RealValuedColumn(column_name='indus', dimension=1, default_value=None, dtype=tf.float32, normalizer=None)

INFO:tensorflow:Transforming feature_column _RealValuedColumn(column_name='nox', dimension=1, default_value=None, dtype=tf.float32, normalizer=None)

INFO:tensorflow:Transforming feature_column _RealValuedColumn(column_name='ptratio', dimension=1, default_value=None, dtype=tf.float32, normalizer=None)

INFO:tensorflow:Transforming feature_column _RealValuedColumn(column_name='rm', dimension=1, default_value=None, dtype=tf.float32, normalizer=None)

INFO:tensorflow:Transforming feature_column _RealValuedColumn(column_name='tax', dimension=1, default_value=None, dtype=tf.float32, normalizer=None)

INFO:tensorflow:Transforming feature_column _RealValuedColumn(column_name='zn', dimension=1, default_value=None, dtype=tf.float32, normalizer=None)

INFO:tensorflow:Create CheckpointSaverHook

INFO:tensorflow:loss = 354.563, step = 1

INFO:tensorflow:Saving checkpoints for 1 into /tmp/tmpRzagvu/model.ckpt.

INFO:tensorflow:loss = 105.581, step = 101

INFO:tensorflow:loss = 87.0334, step = 201

INFO:tensorflow:loss = 81.2485, step = 301

INFO:tensorflow:loss = 77.9607, step = 401

INFO:tensorflow:loss = 75.6769, step = 501

INFO:tensorflow:loss = 73.912, step = 601

...

INFO:tensorflow:loss = 34.7391, step = 4501

INFO:tensorflow:loss = 34.4925, step = 4601

INFO:tensorflow:loss = 34.006, step = 4701

INFO:tensorflow:loss = 33.6934, step = 4801

INFO:tensorflow:loss = 33.2854, step = 4901

INFO:tensorflow:Saving checkpoints for 5000 into /tmp/tmpRzagvu/model.ckpt.

INFO:tensorflow:Loss for final step: 33.2056.

...

INFO:tensorflow:Saving evaluation summary for 5000 step: loss = 19.4123

Loss: 19.412342

...

Predictions: [ 35.73047638  18.93321991  21.97756958  35.20219803  14.68127346

  20.57698059]



이렇게 input functions를 잘 활용하면 입력데이터를 전처리하거나 스케일링 해줄 수 있고 이렇게 구현하면 코드도 훨씬 간결하고 파워풀 해질 것 같습니다.



댓글