понедельник, 8 октября 2018 г.

TensorFlow Estimators: задача классификации Iris

В этом посте мы покажем решение классической задачи классификации Iris с помощью TensorFlow.

Предпосылки

Для использования примеров кода в этом руководстве необходимо следующее:

  • Установить TensorFlow
  • Если TensorFlow установлен вместе virtualenv или Anaconda, тогда необходимо активировать TensorFlow окружение.
  • Установить или обновить pandas, используя следующую команду:
  • pip install pandas
    

Получение кода из примеров

Для получения примеров кода в этом руководстве выполните следующее:

  1. Склонируйте TensorFlow Models репозиторий с GitHub следующей командой:

    git clone https://github.com/tensorflow/models
    

  2. Смените директорию в этой ветке на место, содержащее примеры:

    cd models/samples/core/get_started/
    

Программа, описанная в этом руководстве - это premade_estimator.py. Эта программа использует iris_data.py для получения тренировочных данных.

Выполнение программы

TensorFlow программы можно запускать как любые другие Python программы. Например:

python premade_estimator.py

Программа должна вывести тренировочные логи, затем прогнозы относительно тестового набора. Например, первая линия следующего вывода показывает, что модель оценила с 99.6% вероятностью, что первый пример в тестовом наборе - это вид Setosa. Ввиду того, что тестовый набор ожидал Setosa, этот прогноз верен.

...
Prediction is "Setosa" (99.6%), expected "Setosa"

Prediction is "Versicolor" (99.8%), expected "Versicolor"

Prediction is "Virginica" (97.9%), expected "Virginica"

Если программа выдала ошибку вместо ответов, необходимо проверить следующие пункты:

  • Правильно ли установлен TensorFlow?
  • Используется ли правильная версия TensorFlow? (использован 1.9)
  • Активировано ли окружение, в котором установлен TensorFlow? (если использовался virtualenv или Anaconda)

Программный стек

Перед углублением в детали самой программы рассмотрим программное окружение. На следующем графике показано, что TensorFlow предоставляет программный стек, состоящий из нескольких API слоев:

Рекомендуется писать TensorFlow программы со следующими API:

  • Estimators, которые представляют полностью готовые модели. Estimator API предоставляет методы для тренировки модели, оценки аккуратности модели и вывода прогнозов.
  • Datasets для Estimators, которые строят потоки вводных данных. Dataset API имеет методы для загрузки и манипулирования данными, и передачи их в модель. Dataset API тесно связано с Estimators API.

Классификация ирисов: обзор

Пример программы в этом руководстве создает и тестирует модель, которая классифицирует цветы Ириса на три разных вида, основываясь на размере их чашелистиков и лепестков.

Слева направо Iris setosa, Iris versicolor, Iris virginica.

Набор данных

Iris набор данных сожержит 4 свойства и одну метку. Четыре свойства идентифицируют ботанические характеристики цветов ириса:

  • длина чашелистика
  • ширина чашелистика
  • длина лепестка
  • ширина лепестка

Наша модель будет представлять эти свойства как нецельночисловые 32-битовые данные.

Метка идентифицирует вид Iris. один из следующих:

  • Iris setosa (0)
  • Iris versicolor (1)
  • Iris virginica (2)

Наша модель будет представлять метку как цельночисловые 32-битовые данные.

Следующая таблица показывает три примера в наборе данных:

Алгоритм

Программа тренирует модель классификатор глубокой нейронной сети (Deep Neural Network (DNN)), имеющую следующую топологию:

  • 2 скрытых слоя
  • каждый скрытый слой имеет 10 узлов (нод)

Следующий график иллюстрирует свойства, скрытые слои, прогнозы (не все узлы в скрытых слоях показаны):

Вывод

Выполнение тренированной модели на непомеченном примере выводит три прогноза, а именно вероятность того, что этот цветок является одним из трех видов ирисов. Сумма этих выведенных прогнозов равна 1. Например, прогноз для немеченного примера может быть похожим на следующий:

  • 0.03 для Iris Setosa
  • 0.95 для Iris Versicolor
  • 0.02 для Iris Virginica

Этот прогноз показывает, что с 95% вероятностью переданный пример является Iris Versicolor.

Обзор программирования с Estimators

Estimator - это TensorFlow высокоуровневое представление завершенной модели. Он управляет деталями инициализации, логированием, сохранением и восстановлением, и многими другими свойствами, так чтобы можно было сконцентрироваться на разработке самой модели.

Любой Estimator - это производный класс от tf.estimator.Estimator. TensorFlow предоставляет коллекцию tf.estimator (например, LinearRegressor) для реализации алгоритмов машинного обучения. Кроме того можно писать собственные кастомные Estimators. В первую очередь рекомендуется использовать предсозданные Estimators.

Для написания TensorFlow программы, основанной на предсозданных Estimators необходимо выполнить следующие шаги:

  • Создать одну или несколько функций ввода.
  • Определить колонки свойств модели.
  • Создать инстанс Estimator, задав колонки свойств и различные гиперпараметры.
  • Вызвать один или несколько методов на Estimator объекте, передав подходящую функцию ввода как источник данных.

Рассмотрим как эти шаги реализованы для Iris классификации.

Создание функций ввода

Необходимо создать функции ввода, чтобы передать данные для тренировки, оценки, и прогнозирования.

Функция ввода - это функция, которая возвращает tf.data.Dataset объект, который выводит следующий двухэлементный тапл (tuple):

  • features - это Python словарь в котором:
    • Каждый ключ - это имя свойства.
    • Каждое значение - это массив, содержащий все значения свойства.
  • label - это массив, содержащий значения метки для каждого примера.

Для демонстрации формата функции ввода, вот простой пример:

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

Функция ввода может производить лист словаря свойств и метки любым предпочитаемым образом. Однако, рекомендуется использование TensorFlow Dataset API, которое может обрабатывать все виды данных. На высоком уровне Dataset API состоит из следующих классов:

Где отдельные участники это:

  • Dataset - основной класс, содержащий методы для создания и трансформирования датасетов. Также позволяет инициализировать датасет из данных в памяти, или из Python генератора.
  • TextLineDataset - читает строки из текстовых файлов.
  • TFRecordDataset - читает записи из TFRecord файлов.
  • FixedLengthRecordDataset - читает записи фиксированного размера из бинарных файлов.
  • Iterator - предоставляет путь для доступа к одному элементу набора данных за один раз.

Dataset API может обрабатывать большинство часто встречающихся случаев. Например, используя Dataset API, можно легко читать записи из большой коллекции файлов параллельно и объединять их в один поток.

Чтобы сохранить вещи простыми в этом примере загрузим данные с использованием pandas, и построим поток ввода из данных в памяти.

Вот функция ввода, использованная для тренировки в этой программе, которая доступна в iris_data.py:

def train_input_fn(features, labels, batch_size):
    """Функция ввода для тренировки"""
    # Преобразует вводы в Dataset.
    dataset = tf.data.Dataset.from_tensor_slices(
                                (dict(features), labels))

    # Перемешивание, повтор, и пакетизация примеров.
    return dataset.shuffle(1000).repeat().batch(batch_size)

Определение колонок свойств

Колонка свойства - это объект, описывающий как модель должна использовать сырые данные ввода из словаря свойств. При создании Estimator модели передается лист колонок свойств, который описывает каждое свойство, которое будет использовать модель. tf.feature_column модуль предоставляет много настроек для представления данных модели.

Для Iris примера, 4 сырых свойства - это числовые значения, поэтому строим лист колонок свойств информируя Estimator модель представлять каждое и четырех свойств как 32-битные нецельночисловые значения. Вот код для создание колонки свойств:

# Колонки свойств описывают как использовать ввод
my_feature_columns = []
for key in train_x.keys():
    my_feature_columns.append(tf.feature_column.numeric_column(key=key))

Колонки свойств могут быть намного более сложными, чем показано в примере выше.

Теперь, когда есть описание того как модель должна представлять сырые свойства, можно построить estimator.

Создание инстанса estimator

Iris задача - это классическая задача классификации. К счастью, TensorFlow предоставляет несколько предсозданных Estimators классификаторов, включая:

  • tf.estimator.DNNClassifier для глубоких моделей, которые выполняют мультиклассовую классификацию.
  • tf.estimator.DNNLinearCombinedClassifier для широких и глубоких моделей.
  • tf.estimator.LinearClassifier для классификаторов, основанных на линейных моделях.

Для Iris задачи, tf.estimator.DNNClassifier подходит лучше всего. Вот создание инстанса этого Estimator:

# Строим DNN с 2 скрытыми слоями и 10 узлами в каждом скрытом слое.
classifier = tf.estimator.DNNClassifier(
    feature_columns=my_feature_columns,
    # Два скрытых слоя из 10 узлов каждый.
    hidden_units=[10, 10],
    # Модель должна делать выбор из 3 классов.
    n_classes=3)

Тренировка, оценка, и прогнозирование.

Теперь, когда у нас есть Estimator объект, можно вызывать методы, выполняющие следующее:

  • Тренируют модель.
  • Оценивают тренированную модель.
  • Используют тренированную модель для выполнения прогнозов.

Тренировка модели.

Тренируем модель, вызывая тренировочный метод Estimator'а:

# Тренируем модель.
classifier.train(
    input_fn=lambda:iris_data.train_input_fn(
                            train_x, train_y, args.batch_size),
    steps=args.train_steps)

Здесь мы обернули input_fn вызов в лямбда для захвата аргументов, предоставляя функцию ввода, которая не принимает аргументов, как ожидает Estimator. Аргумент steps сообщает методу остановить тренировку после ряда тренировочных шагов.

Оценка тренированной модели.

Теперь модель натренирована и можно получить статистику ее производительности. Следующий код оценивает аккуратность тренированной модели на тестовых данных:

# Оцениваем модель.
eval_result = classifier.evaluate(
    input_fn=lambda:iris_data.eval_input_fn(
                                    test_x, test_y, args.batch_size))

print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result))

В отличие от вызова метода тренировки, мы не передаем steps аргумент для оценки. eval_input_fn выводит только единственную эпоху данных.

Выполнение кода выше выводит следующий результат:

Test set accuracy: 0.967

eval_result словарь также содержит average_loss (среднюю потерю на пример), loss (среднюю потрею на мини-пакет) и значение global_step estimator'а (количество тренировочных итераций, которые были проведены).

Выполнение прогнозов (вывод) на тренированной модели.

Теперь у нас есть тренированная модель, которая производит хорошие результаты. Можно использовать тренированную модель для прогнозирования видов цветов ириса, основываясь на немеченных измерениях. Как и с тренировкой и оценкой, выполняем прогнозирование, используя единственный вызов функции:

# Генерируем прогнозы на модели 
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],
}

predictions = classifier.predict(
    input_fn=lambda:iris_data.eval_input_fn(predict_x,
                                            batch_size=args.batch_size))

predict метод возвращает python итерируемый объект, выводящий словарь результатов прогноза для каждого примера. Следующий код печатает несколько прогнозов и их вероятности:

template = ('\nPrediction is "{}" ({:.1f}%), expected "{}"')

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

    print(template.format(iris_data.SPECIES[class_id],
                          100 * probability, expec))

Выполнение предыдущего кода дает следующий вывод:

...
Prediction is "Setosa" (99.6%), expected "Setosa"

Prediction is "Versicolor" (99.8%), expected "Versicolor"

Prediction is "Virginica" (97.9%), expected "Virginica"

Резюме

Предсозданные Estimators - это эффективный путь для быстрого создания стандартных моделей.