[튜토리얼1] 에스티메이터(Estimator) 사용해보기

이번 튜토리얼에서는 텐서플로우의 에스티메이터(Estimator) 를 사용하여 붓꽃(Iris) 데이터 분류 문제를 해결하는 방법을 설명합니다.

from __future__ import absolute_import, division, print_function, unicode_literals

import warnings
warnings.simplefilter('ignore')

import pandas as pd
import tensorflow as tf

목차

  1. 데이터셋
  2. 에스티메이터(Estimator)를 이용한 프로그래밍 개요
  3. 입력 함수 생성하기
  4. 피쳐 열 정의하기
  5. 에스티메이터(Estimator)의 인스턴스화
  6. 훈련하고, 평가하고, 예측하기
    • 6.1 모델 훈련시키기
    • 6.2 훈련된 모델 평가하기
    • 6.3 학습된 모델로 예측하기

1. 데이터셋

이 예제에서는 붓꽃 데이터를 사용하여 각각의 꽃받침과 꽃잎의 크기에 따라 세 개의 다른 종으로 분류하는 모델을 만들고 테스트합니다.

붓꽃(iris) 데이터셋에는 네 가지 피쳐과 하나의 레이블이 있습니다. 네 가지 피쳐는 개별 붓꽃의 식물학적 특성을 식별하는데 사용하고, 자세한 항목은 다음과 같습니다.

따라서 데이터 내에 존재하는 피쳐의 이름과 분류해야 하는 붓꽃의 종인 레이블은 다음과 같이 정의할 수 있습니다.

# 피쳐의 이름(꽃받침 길이, 꽃받침 너비, 꽃잎 길이, 꽃잎 너비)
CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth', 'Species']

# 분류해야 하는 붓꽃의 종()
SPECIES = ['Setosa', 'Versicolor', 'Virginica']

케라스(Keras)와 판다스(Pandas)를 사용하여 붓꽃 데이터셋을 다운로드합니다.

훈련을 위한 데이터셋과 테스트를 위한 데이터셋을 구분해야 합니다.

train_path = tf.keras.utils.get_file(
    "iris_training.csv", "https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv")
test_path = tf.keras.utils.get_file(
    "iris_test.csv", "https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv")

train = pd.read_csv(train_path, names=CSV_COLUMN_NAMES, header=0)
test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)

다운받은 데이터를 확인하면 4개의 실수 피쳐와 1개의 int32형의 레이블이 있는 것을 확인할 수 있습니다.

train.head()

각 데이터셋에 대해 레이블을 구분하여 예측할 수 있도록 모델을 훈련합니다.

# 레이블 열은 피쳐에서 제거합니다
train_y = train.pop('Species')
test_y = test.pop('Species')

train.head()

2. 에스티메이터(Estimator)를 이용한 프로그래밍 개요

이제 데이터가 설정되었으므로 텐서플로우 에스티메이터(Estimator)를 사용하여 모델을 정의할 수 있습니다. 에스티메이터(Estimator)는 tf.estimator.Estimator에서 파생된 클래스입니다. 텐서플로우는 일반적인 ML 알고리즘을 구현하기 위한 tf.estimator(예: LinearRegressor)의 집합(collection)을 제공합니다. 그 외에도 사용자가 지정하여 에스티메이터(Estimator)를 만들 수 있습니다. 이제 막 시작하는 분들은 이미 만들어진 에스티메이터(Estimator)를 사용하는 것이 좋습니다.

만들어진 에스티메이터(Estimator)를 가지고 텐서플로우 프로그램을 작성하려면 다음과 같은 작업을 수행해야 합니다:

이러한 작업들을 통해 어떻게 모델이 붓꽃 품종을 분류하는지 살펴보겠습니다.

3. 입력 함수 생성하기

입력 함수를 만들어 학습하고, 평가하고, 예측하기 위한 데이터를 적용시켜야 합니다.

입력 함수는 tf.data.Dataset 객체를 반환하는 함수로 아래와 같은 두 원소를 가진 튜플을 생성합니다:

간단한 방법으로 입력 함수의 형식을 살펴보겠습니다:

def input_evaluation_set():
    # 딕셔너리 생성
    features = {'SepalLength': np.array([6.4, 5.0]),
                'SepalWidth':  np.array([2.8, 2.3]),
                'PetalLength': np.array([5.6, 3.3]),
                'PetalWidth':  np.array([2.2, 1.0])}
    # 레이블 배열 생성
    labels = np.array([2, 1])
    
    return features, labels

이제 입력 함수를 통해 원하는 대로 features 딕셔너리와 label 목록을 생성할 수 있습니다.

하지만 이 방법보다 모든 종류의 데이터를 분석할 수 있는 텐서플로우의 Dataset API를 사용하는 것이 더 좋습니다.

일반적인 경우, Dataset API는 오류없이 이를 처리할 수 있습니다. 예를 들어, Dataset API를 사용하면 병렬적으로 대용량 파일 모음에서 레코드를 쉽게 읽어들일 수 있고, 이를 단일 스트림에 결합할 수 있습니다.

데이터를 판다스(pandas)로 불러오고 메모리 내 데이터로 입력 파이프라인을 구축해 이 예제를 간단히 만들어봅시다.

def input_fn(features, labels, training=True, batch_size=256):
    """훈련과 평가를 위한 입력 함수"""
    # 입력 데이터를 데이터셋으로 변환합니다.
    dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))

    # 훈련 모드라면 반복해서 섞습니다.
    if training:
        dataset = dataset.shuffle(1000).repeat()
    
    return dataset.batch(batch_size)

4. 피쳐 열 정의하기

피쳐 열은 모델에서 어떻게 피쳐 딕셔너리의 가공되지 않은(Raw) 입력 데이터를 사용하는 지에 대해 설명하는 객체입니다. 에스티메이터(Estimator) 모델을 만들 때 모델에 사용할 각 피쳐를 설명하는 피쳐 열의 목록을 이 모델에 전달합니다. tf.feature_column 모듈은 모델에 데이터를 나타내는 다양한 옵션들을 제공합니다.

붓꽃 데이터의 경우, 4개의 가공되지 않은(Raw) 피쳐는 숫자형(numeric) 값이기 때문에 에스티메이터(Estimator) 모델이 각 4개의 피쳐들을 32비트의 부동 소수점(floating-point) 값으로 나타내도록 하는 피쳐 열의 목록을 만들 것입니다.

피쳐 열을 생성하는 코드는 다음과 같습니다:

# 피쳐 열은 입력으로 사용하는 방법을 보여줍니다.
my_feature_columns = []
for key in train.keys():
    my_feature_columns.append(tf.feature_column.numeric_column(key=key))

이제 우리는 모델이 가공되지 않은(Raw) 피쳐를 나타내도록 할 수 있으므로 에스티메이터(Estimator)를 만들 수 있습니다.

(사실 피쳐 열은 이번 튜토리얼에서 사용되는 것보다 훨씬 정교하게 사용될 수 있으며, 4장 [튜토리얼1] 정형데이터 다루기에서 더 자세하게 다루겠습니다.)

5. 에스티메이터(Estimator)의 객체화

붓꽃 데이터 문제는 전형적인 분류 문제입니다. 텐서플로우는 다음과 같은 몇 가지 미리 만들어진 분류기 에스티메이터(Classifier Estimator)를 제공합니다.

붓꽃 데이터 문제에서는 tf.estimator.DNNClassifier가 가장 적절해보입니다. 이 에스티메이터(Estimator)를 객체화하는 방법은 아래와 같습니다.

# 각각 30개, 10개의 노드로 구성된 2층의 은닉 층을 쌓은 DNN을 생성합니다.
classifier = tf.estimator.DNNClassifier(
    feature_columns=my_feature_columns,
    # 두 개의 은닉 층의 노드의 수를 각각 30개와 10개로 설정합니다.
    hidden_units=[30, 10],
    # 모델은 3개의 클래스 중 하나를 선택해야 합니다. 
    n_classes=3)

6. 훈련하고, 평가하고, 예측하기

이제 에스티메이터(Estimator) 객체가 있으므로 메서드를 호출하여 다음 작업을 수행할 수 있습니다.

6.1 모델 훈련시키기

다음과 같이 에스티메이터(Estimator)의 train 메서드를 호출하여 모델을 훈련시킬 수 있습니다.

# 모델 훈련시키기
classifier.train(
    input_fn=lambda: input_fn(train, train_y, training=True),
    steps=5000)

6.2 훈련된 모델 평가하기

이제 모델이 훈련되었으므로 모델의 성능에 대한 통계를 얻을 수 있습니다. 다음 코드에서는 테스트 데이터에 대한 모델의 정확도를 측정합니다.

eval_result = classifier.evaluate(
    input_fn=lambda: input_fn(test, test_y, training=False))

print('\n테스트 셋 정확도: {accuracy:0.3f}\n'.format(**eval_result))

train 메서드를 호출하는 것과 달리, 테스트 셋의 정확도를 평가할 때는 steps 인수를 전달하지 않았습니다. 평가를 위한 input_fn은 오직 하나의 에포크(epoch)의 데이터만을 생성합니다.

eval_result 딕셔너리에는 average_loss(샘플당 평균 손실)와 loss(미니 배치당 평균 손실), 에스티메이터의 global_step(훈련 반복 횟수)도 포함되어 있습니다.

6.3 학습된 모델로 예측하기

좋은 평가 결과를 생성하는 모델로 훈련되었습니다. 이제 훈련된 모델을 사용하여 레이블이 없는 붓꽃의 종을 예측할 수 있습니다. 훈련시키고 평가했던 것과 마찬가지로, 하나의 함수를 호출하여 예측합니다:

# 모델에서 예측값을 생성합니다.
expected = ['Setosa', 'Versicolor', 'Virginica']
predict_x = {
    'SepalLength': [5.1, 5.9, 6.9],
    'SepalWidth': [3.3, 3.0, 3.1],
    'PetalLength': [1.7, 4.2, 5.4],
    'PetalWidth': [0.5, 1.5, 2.1],
}

def input_fn(features, batch_size=256):
    """예측을 위한 입력 함수"""
    # 입력값을 레이블이 없는 데이터셋으로 변환합니다.
    return tf.data.Dataset.from_tensor_slices(dict(features)).batch(batch_size)

predictions = classifier.predict(
    input_fn=lambda: input_fn(predict_x))

predict 메서드는 Python으로 반복될 수 있고(iterable), 만들 수 있는 각 샘플에 대한 예측 결과를 가진 딕셔너리를 반환합니다. 다음 코드는 일부 예측 결과와 그 확률을 보여줍니다:

for pred_dict, expec in zip(predictions, expected):
    class_id = pred_dict['class_ids'][0]
    probability = pred_dict['probabilities'][class_id]

    print('예측 결과 : "{}" ({:.1f}%), 실제 결과 : "{}"'.format(
        SPECIES[class_id], 100 * probability, expec))

Copyright 2019 The TensorFlow Authors.

#@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.