Показаны сообщения с ярлыком Keras. Показать все сообщения
Показаны сообщения с ярлыком Keras. Показать все сообщения

четверг, 20 декабря 2018 г.

TensorFlow: AutoGraph, примеры использования

AutoGraph помогает писать код сложных графов с использованием обычного Python. За кулисами AutoGraph автоматически преобразует ваш код в эквивалентный код графов TensorFlow. AutoGraph уже поддерживает большую часть языка Python, и этот охват продолжает расти.

Настройка

Импортируем TensorFlow, AutoGraph и вспомогательные модули:

from future import division, print_function, absolute_import

import tensorflow as tf
layers = tf.keras.layers
from tensorflow.contrib import autograph


import numpy as np
import matplotlib.pyplot as plt

Стремительное исполнение (eager execution) включено для демонстрационных целей, но AutoGraph работает в окружениях как стремительного, так и в графового исполнения:

tf.enable_eager_execution()

Примечание: Преобразованный код AutoGraph предназначен для запуска во время выполнения графа. Когда стремительное исполнение включено, используйте явные графы (как показано в этом примере) или tf.contrib.eager.defun.

Автоматическое преобразование Python потока управления

AutoGraph преобразует большую часть языка Python в эквивалентный код построения графов TensorFlow.

Примечание: В реальных приложениях пакетирование важно для повышения производительности. Лучший код для преобразования в AutoGraph - это код, в котором поток управления определяется на уровне batch. Если вы принимаете решения на индивидуальном уровне example, вы должны индексировать и группировать примеры, чтобы поддерживать производительность при применении логики потока управления.

AutoGraph преобразует функцию, например:

def square_if_positive(x):
  if x > 0:
    x = x * x
  else:
    x = 0.0
  return x

В функцию, которая использует построение графа:

print(autograph.to_code(square_if_positive))

from future import print_function
import tensorflow as tf

def tf__square_if_positive(x):
  try:
    with ag__.function_scope('square_if_positive'):

      def if_true():
        with ag__.function_scope('if_true'):
          x_1, = x,
          x_1 = x_1 * x_1
          return x_1,

      def if_false():
        with ag__.function_scope('if_false'):
          x_2, = x,
          x_2 = 0.0
          return x_2,
      x = ag__.utils.run_cond(tf.greater(x, 0), if_true, if_false)
      return x
  except:
    ag__.rewrite_graph_construction_error(ag_source_map__)



tf__square_if_positive.autograph_info__ = {}

Код написанный для стремительного исполнения может выполняться в tf.Graph с теми же результатами, но с преимуществами графового исполнения:

print('Eager results: %2.2f, %2.2f' % 
(square_if_positive(tf.constant(9.0)), 
 square_if_positive(tf.constant(-9.0))))

Eager results: 81.00, 0.00

Генерируем графовую версию и выполняем ее:

# -*- coding: utf-8 -*-

tf_square_if_positive = autograph.to_graph(square_if_positive)

with tf.Graph().as_default():  
  # Работает как обычная операция: принимает тензоры, 
  # возвращает тензоры.
  # Вы можете проверять граф, 
  # используя tf.get_default_graph().as_graph_def()
  g_out1 = tf_square_if_positive(tf.constant( 9.0))
  g_out2 = tf_square_if_positive(tf.constant(-9.0))
  with tf.Session() as sess:
    print('Graph results: %2.2f, %2.2f\n' % 
          (sess.run(g_out1), sess.run(g_out2)))

Graph results: 81.00, 0.00

AutoGraph поддерживает общие Python выражения, такие как while, for, if, break, и return, с поддержкой вложенности. Сравните эту функцию со сложной графовой версией, показанной в следующих блоках кода:

# Продолжаем в цикле
def sum_even(items):
  s = 0
  for c in items:
    if c % 2 > 0:
      continue
    s += c
  return s

print('Eager result: %d' % sum_even(tf.constant([10,12,15,20])))

tf_sum_even = autograph.to_graph(sum_even)

with tf.Graph().as_default(), tf.Session() as sess:
    print('Graph result: %d\n\n' % 
          sess.run(tf_sum_even(tf.constant([10,12,15,20]))))

Eager result: 42
Graph result: 42

print(autograph.to_code(sum_even))

from future import print_function
import tensorflow as tf

def tf__sum_even(items):
  try:
    with ag__.function_scope('sum_even'):
      s = 0

      def extra_test(s_2):
        with ag__.function_scope('extra_test'):
          return True

      def loop_body(loop_vars, s_2):
        with ag__.function_scope('loop_body'):
          c = loop_vars
          continue_ = tf.constant(False)

          def if_true():
            with ag__.function_scope('if_true'):
              continue__1, = continue_,
              continue__1 = tf.constant(True)
              return continue__1,

          def if_false():
            with ag__.function_scope('if_false'):
              return continue_,
          continue_ = ag__.utils.run_cond(tf.greater(c % 2, 0), 
                                          if_true, if_false)

          def if_true_1():
            with ag__.function_scope('if_true_1'):
              s_1, = s_2,
              s_1 += c
              return s_1,

          def if_false_1():
            with ag__.function_scope('if_false_1'):
              return s_2,
          s_2 = ag__.utils.run_cond(tf.logical_not(continue_), 
                                    if_true_1, if_false_1)
          return s_2,
      s = ag__.for_stmt(items, extra_test, loop_body, (s,))
      return s
  except:
    ag__.rewrite_graph_construction_error(ag_source_map__)



tf__sum_even.autograph_info__ = {}


Декоратор

Если вам не нужен простой доступ к изначальной Python функции, используйте convert декоратор:

@autograph.convert()
def fizzbuzz(i, n):
  while i < n:
    msg = ''
    if i % 3 == 0:
      msg += 'Fizz'
    if i % 5 == 0:
      msg += 'Buzz'
    if msg == '':
      msg = tf.as_string(i)
    print(msg)
    i += 1
  return i

with tf.Graph().as_default():
  final_i = fizzbuzz(tf.constant(10), tf.constant(16))
  # Работает как обычная операция: принимает тензоры, 
  # возвращает тензоры.
  # Вы можете проверять граф, используя 
  # tf.get_default_graph().as_graph_def()
  with tf.Session() as sess:
    sess.run(final_i)

Buzz
11
Fizz
13
14
FizzBuzz


Примеры

Продемонстрируем некоторые полезные свойства языка Python

Assert

AutoGraph автоматически преобразовывает Python assert выражение в эквивалентный tf.Assert код:

@autograph.convert()
def inverse(x):
  assert x != 0.0, 'Do not pass zero!'
  return 1.0 / x

with tf.Graph().as_default(), tf.Session() as sess:
  try:
    print(sess.run(inverse(tf.constant(0.0))))
  except tf.errors.InvalidArgumentError as e:
    print('Got error message:\n    %s' % e.message)

Got error message:
    assertion failed: [Do not pass zero!]
     [[node inverse/Assert/Assert (defined at /tmp/tmpzsz37apn.py:6)  
     = Assert[T=[DT_STRING], summarize=3, 
     _device="/job:localhost/replica:0/task:0/device:CPU:0"]
     (inverse/NotEqual, inverse/Assert/Assert/data_0)]]

Print

Используйте Python print функцию в графе:

@autograph.convert()
def count(n):
  i=0
  while i < n:
    print(i)
    i += 1
  return n
    
with tf.Graph().as_default(), tf.Session() as sess:
    sess.run(count(tf.constant(5)))

Lists (списки)

Добавляйте в списки (lists) в циклах (тензор list операции автоматически созданы):

@autograph.convert()
def arange(n):
  z = []
  # Мы запрашиваем dtype элемента списка
  autograph.set_element_type(z, tf.int32)
  
  for i in tf.range(n):
    z.append(i)

  # когда мы закончили со списком
  # складываем его в стек (как np.stack)
  return autograph.stack(z) 


with tf.Graph().as_default(), tf.Session() as sess:
    sess.run(arange(tf.constant(10)))

Вложенный поток контроля

@autograph.convert()
def nearest_odd_square(x):
  if x > 0:
    x = x * x
    if x % 2 == 0:
      x = x + 1
  return x

with tf.Graph().as_default():  
  with tf.Session() as sess:
    print(sess.run(nearest_odd_square(tf.constant(4))))
    print(sess.run(nearest_odd_square(tf.constant(5))))
    print(sess.run(nearest_odd_square(tf.constant(6))))

17
25
37

While цикл

@autograph.convert()
def square_until_stop(x, y):
  while x < y:
    x = x * x
  return x
    
with tf.Graph().as_default():  
  with tf.Session() as sess:
    print(sess.run(square_until_stop(tf.constant(4), tf.constant(100))))

256

For цикл

@autograph.convert()
def squares(nums):

  result = []
  autograph.set_element_type(result, tf.int64)

  for num in nums: 
    result.append(num * num)
    
  return autograph.stack(result)
    
with tf.Graph().as_default():  
  with tf.Session() as sess:
    print(sess.run(squares(tf.constant(np.arange(10)))))

[ 0  1  4  9 16 25 36 49 64 81]

Break

@autograph.convert()
def argwhere_cumsum(x, threshold):
  current_sum = 0.0
  idx = 0
  for i in tf.range(len(x)):
    idx = i
    if current_sum >= threshold:
      break
    current_sum += x[i]
  return idx

N = 10
with tf.Graph().as_default():  
  with tf.Session() as sess:
    idx = argwhere_cumsum(tf.ones(N), tf.constant(float(N/2)))
    print(sess.run(idx))

5


Взаимодействие с tf.Keras

Теперь когда вы увидели основы, давайте построим некоторые компоненты модели с помощью autograph.

Довольно просто интегрировать autograph с tf.keras.

Функции без состояния

Для функций без состояния, таких как collatz показанной ниже, простейший путь включить их в keras модель - обернуть их как слой, используя tf.keras.layers.Lambda.

import numpy as np

@autograph.convert()
def collatz(x):
  x = tf.reshape(x,())
  assert x > 0
  n = tf.convert_to_tensor((0,)) 
  while not tf.equal(x, 1):
    n += 1
    if tf.equal(x%2, 0):
      x = x // 2
    else:
      x = 3 * x + 1
      
  return n

with tf.Graph().as_default():
  model = tf.keras.Sequential([
    tf.keras.layers.Lambda(collatz, input_shape=(1,), output_shape=())
  ])
  
result = model.predict(np.array([6171]))
result

array([261], dtype=int32)

Пользовательские слои и модели

Простейший путь использовать AutoGraph с Keras слоями и моделями - это @autograph.convert() call метод.

Вот простой пример:

# K используется для проверки в каком мы режиме 
# - в тренировочном или тестовом.
K = tf.keras.backend

class StochasticNetworkDepth(tf.keras.Sequential):
  def init(self, pfirst=1.0, plast=0.5, *args,**kwargs):
    self.pfirst = pfirst
    self.plast = plast
    super().init(*args,**kwargs)
        
  def build(self,input_shape):
    super().build(input_shape.as_list())
    self.depth = len(self.layers)
    self.plims = np.linspace(self.pfirst, self.plast, 
                                          self.depth + 1)[:-1]
    
  @autograph.convert()
  def call(self, inputs):
    training = tf.cast(K.learning_phase(), dtype=bool)  
    if not training: 
      count = self.depth
      return super(StochasticNetworkDepth, self).call(inputs), count
    
    p = tf.random_uniform((self.depth,))
    
    keeps = (p <= self.plims)
    x = inputs
    
    count = tf.reduce_sum(tf.cast(keeps, tf.int32))
    for i in range(self.depth):
      if keeps[i]:
        x = self.layers[i](x)
      
    # возвращаем вывод последнего слоя и количество выполненных слоев
    return x, count

Попробуем модель на данных с формой как в MNIST наборе:

train_batch = np.random.randn(64, 28, 28, 1).astype(np.float32)

Построим простой стек conv слоев, в созданной нами модели:

with tf.Graph().as_default() as g:
  model = StochasticNetworkDepth(
        pfirst=1.0, plast=0.5)

  for n in range(20):
    model.add(
          layers.Conv2D(filters=16, activation=tf.nn.relu,
                        kernel_size=(3, 3), padding='same'))

  model.build(tf.TensorShape((None, None, None, 1)))
  
  init = tf.global_variables_initializer()

Теперь протестируем модель, чтобы убедиться, что она ведет себя так как ожидается в train и test режимах:

# Используем явную сессию здесь, таким образом мы можем установить
# train/test switch, и проверить количество слоев, 
# возвращаенное call методом
with tf.Session(graph=g) as sess:
  init.run()
 
  for phase, name in enumerate(['test','train']):
    K.set_learning_phase(phase)
    result, count = model(tf.convert_to_tensor(train_batch, 
                                               dtype=tf.float32))

    result1, count1 = sess.run((result, count))
    result2, count2 = sess.run((result, count))

    delta = (result1 - result2)
    print(name, "sum abs delta: ", abs(delta).mean())
    print("    layers 1st call: ", count1)
    print("    layers 2nd call: ", count2)
    print()

test sum abs delta:  0.0
    layers 1st call:  20
    layers 2nd call:  20

train sum abs delta:  0.0010305465
    layers 1st call:  14
    layers 2nd call:  17


Более сложный пример: внутриграфовый тренировочный цикл

В предыдущих примерах показано как AutoGraph может быть использован внутри Keras слоев и моделей. Keras модели могут также быть использованы в AutoGraph коде.

Ввиду того что написание потока контроля в AutoGraph является простым, то выполнение тренировочного цикла в TensorFlow графе должно быть также простым.

Этот пример показывает как натренировать простую Keras модель для MNIST с целым тренировочным процессом - загрузкой пакетов, вычислением градиентов, обновлением параметров, вычислением валидационной аккуратности, и повторением до схождения - все выполнено внутри графа.

Загрузка данных

(train_images, train_labels), (test_images, test_labels) = \
                                    tf.keras.datasets.mnist.load_data()

Downloading data from 
https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step

Определение модели

def mlp_model(input_shape):
  model = tf.keras.Sequential((
      tf.keras.layers.Dense(100, activation='relu', 
                            input_shape=input_shape),
      tf.keras.layers.Dense(100, activation='relu'),
      tf.keras.layers.Dense(10, activation='softmax')))
  model.build()
  return model


def predict(m, x, y):
  y_p = m(tf.reshape(x, (-1, 28 * 28)))
  losses = tf.keras.losses.categorical_crossentropy(y, y_p)
  l = tf.reduce_mean(losses)
  accuracies = tf.keras.metrics.categorical_accuracy(y, y_p)
  accuracy = tf.reduce_mean(accuracies)
  return l, accuracy


def fit(m, x, y, opt):
  l, accuracy = predict(m, x, y)
  # Autograph автоматически добавляет необходимые 
  # tf.control_dependencies здесь.
  # (Без них ничего не зависит от opt.minimize, 
  # поэтому ничего не выполняется.)
  # Это выполняется схожим образом с кодом с нетерпеливого исполнения.
  opt.minimize(l)
  return l, accuracy


def setup_mnist_data(is_training, batch_size):
  if is_training:
    ds = tf.data.Dataset.from_tensor_slices((train_images, 
                                             train_labels))
    ds = ds.shuffle(batch_size * 10)
  else:
    ds = tf.data.Dataset.from_tensor_slices((test_images, test_labels))

  ds = ds.repeat()
  ds = ds.batch(batch_size)
  return ds


def get_next_batch(ds):
  itr = ds.make_one_shot_iterator()
  image, label = itr.get_next()
  x = tf.to_float(image) / 255.0
  y = tf.one_hot(tf.squeeze(label), 10)
  return x, y 

Определение тренировочного цикла

# Используйте recursive = True 
# для того чтобы рекурсивно преобразовать функции вызванные этой.
@autograph.convert(recursive=True)
def train(train_ds, test_ds, hp):
  m = mlp_model((28 * 28,))
  opt = tf.train.AdamOptimizer(hp.learning_rate)
  
  # Мы сохраним наши потери в список. Для того чтобы AutoGraph
  # преобразовывал эти списки в их графовые эквиваленты,
  # нам необходимо определить тип элементов листов.
  train_losses = []
  autograph.set_element_type(train_losses, tf.float32)
  test_losses = []
  autograph.set_element_type(test_losses, tf.float32)
  train_accuracies = []
  autograph.set_element_type(train_accuracies, tf.float32)
  test_accuracies = []
  autograph.set_element_type(test_accuracies, tf.float32)
  
  # Этот тренировочный цикл целиком будет выполняться внутри графа.
  i = tf.constant(0)
  while i < hp.max_steps:
    train_x, train_y = get_next_batch(train_ds)
    test_x, test_y = get_next_batch(test_ds)
    step_train_loss, step_train_accuracy = fit(m, train_x, train_y, opt)
    step_test_loss, step_test_accuracy = predict(m, test_x, test_y)
    if i % (hp.max_steps // 10) == 0:
      print('Step', i, 'train loss:', step_train_loss, 'test loss:',
            step_test_loss, 'train accuracy:', step_train_accuracy,
            'test accuracy:', step_test_accuracy)
    train_losses.append(step_train_loss)
    test_losses.append(step_test_loss)
    train_accuracies.append(step_train_accuracy)
    test_accuracies.append(step_test_accuracy)
    i += 1
  
  # Мы записали значения наших потерь и аккуратностей
  # в список в графе с помощью AutoGraph.
  # Для того чтобы вернуть значения как Tensor, 
  # нам необходимо сложить их друг на друга перед их возвращением.
  return (autograph.stack(train_losses), 
          autograph.stack(test_losses),  
          autograph.stack(train_accuracies), 
          autograph.stack(test_accuracies))

Теперь построим граф и выполним тренировочный цикл:

with tf.Graph().as_default() as g:
  hp = tf.contrib.training.HParams(
      learning_rate=0.005,
      max_steps=500,
  )
  train_ds = setup_mnist_data(True, 50)
  test_ds = setup_mnist_data(False, 1000)
  (train_losses, test_losses, train_accuracies,
   test_accuracies) = train(train_ds, test_ds, hp)

  init = tf.global_variables_initializer()
  
with tf.Session(graph=g) as sess:
  sess.run(init)
  (train_losses, test_losses, train_accuracies,
   test_accuracies) = sess.run([train_losses, 
                                test_losses, 
                                train_accuracies,
                                test_accuracies])
  
plt.title('MNIST train/test losses')
plt.plot(train_losses, label='train loss')
plt.plot(test_losses, label='test loss')
plt.legend()
plt.xlabel('Training step')
plt.ylabel('Loss')
plt.show()
plt.title('MNIST train/test accuracies')
plt.plot(train_accuracies, label='train accuracy')
plt.plot(test_accuracies, label='test accuracy')
plt.legend(loc='lower right')
plt.xlabel('Training step')
plt.ylabel('Accuracy')
plt.show()

Step 0 train loss: 2.39437 test loss: 2.3646827 
train accuracy: 0.02 test accuracy: 0.036
Step 50 train loss: 0.32149887 test loss: 0.5104491 
train accuracy: 0.9 test accuracy: 0.839
Step 100 train loss: 0.33964032 test loss: 0.32525328 
train accuracy: 0.88 test accuracy: 0.898
Step 150 train loss: 0.27726996 test loss: 0.37781104 
train accuracy: 0.9 test accuracy: 0.867
Step 200 train loss: 0.18788506 test loss: 0.274848 
train accuracy: 0.94 test accuracy: 0.921
Step 250 train loss: 0.35899135 test loss: 0.26668844 
train accuracy: 0.86 test accuracy: 0.912
Step 300 train loss: 0.11612513 test loss: 0.24186224 
train accuracy: 0.98 test accuracy: 0.916
Step 350 train loss: 0.18267077 test loss: 0.25310495 
train accuracy: 0.96 test accuracy: 0.915
Step 400 train loss: 0.17388473 test loss: 0.18192373 
train accuracy: 0.96 test accuracy: 0.939
Step 450 train loss: 0.12562008 test loss: 0.19091153 
train accuracy: 0.96 test accuracy: 0.94


Читайте также другие статьи в блоге:

TensorFlow: стремительное исполнение (eager execution), базовое использование

TensorFlow: стремительное исполнение (eager execution), продвинутое использование

TensorFlow: Keras, базовое использование

пятница, 14 сентября 2018 г.

TensorFlow: Keras, сложные модели, сохранение, распределение

Создание сложных моделей

Функциональное (Functional) API

tf.keras.Sequential модель - это просто стек слоев, которые не могут представлять произвольные модели. Для построения моделей с комплексной топологией необходимо использовать Keras функциональное (functional) API, таких моделей как:

  • Модели с многими входами
  • Модели с многими выходами
  • Модели с общими слоями (тот же самый слой вызывается несколько раз)
  • Модели с непоследовательными потоками данных (например, остаточные соединения)

Построение модели с функциональным API:

  1. Вызывается экземпляр слоя и возвращается тензор.
  2. Вводные тензоры и выводные тензоры использованы для определения tf.keras.Model экземпляра.
  3. Эта модель тренируется так же как Sequential модель.

Следующий пример использует функциональное API для построения простой, полностью связанной сети:

# Выполняет тензор заполнитель
inputs = keras.Input(shape=(32,))  

# экземпляр слоя вызывается на тензоре и возвращает тензор
x = keras.layers.Dense(64, activation='relu')(inputs)
x = keras.layers.Dense(64, activation='relu')(x)
predictions = keras.layers.Dense(10, activation='softmax')(x)

# Воплощение модели с заданными вводами и выводами
model = keras.Model(inputs=inputs, outputs=predictions)

# Шаг компиляции определяет тренировочную конфигурацию
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Тренируем в течение 5 эпох
model.fit(data, labels, batch_size=32, epochs=5)

Создание подклассов модели

Построим полностью кастомизированную модель посредством создания подкласса tf.keras.Model и определения собственного прохода вперед. Создаем слои в init методе и устанавливаем их как атрибуты экземпляра класса. Определим следующий проход в методе вызова.

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

Ключевой момент: используйте правильное API для задания. Хотя создание подклассов модели предлагает гибкость, это несет цену высокой сложности и возможности пользовательской ошибки. Если возможно используйте функциональное API.

Следующий пример показывает подкласс tf.keras.Model, используя пользовательский проход вперед:

class MyModel(keras.Model):

  def init(self, num_classes=10):
    super(MyModel, self).init(name='my_model')
    self.num_classes = num_classes
    # Определяем слои здесь
    self.dense_1 = keras.layers.Dense(32, activation='relu')
    self.dense_2 = keras.layers.Dense(num_classes, activation='sigmoid')

  def call(self, inputs):
    # Определяем будущий проход здесь,
    # используя слои определенные ранее(в __init__).
    x = self.dense_1(inputs)
    return self.dense_2(x)

  def compute_output_shape(self, input_shape):
    # Необходимо переопределить эту функцию
    # чтобы использовать модель с подклассами
    # как часть модели функционального стиля.
    # В ином случае этот метод опционален.
    shape = tf.TensorShape(input_shape).as_list()
    shape[-1] = self.num_classes
    return tf.TensorShape(shape)


# Воплощаем модель с подклассами.
model = MyModel(num_classes=10)

# Шаг компиляции определяет тренировочную конфигурацию.
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Тренируем в течение 5 эпох.
model.fit(data, labels, batch_size=32, epochs=5)

Кастомные слои

Создаем кастомный слой созданием подкласса tf.keras.layers.Layer и имплементируя следующие методы:

  • build: Создает веса слоя. Добавляет веса с add_weight методом.
  • call: Задает будущий проход.
  • compute_output_shape: Определяет как вычислить выводную форму слоя при данной входной форме.

Опционально слой может быть сериализован имплеметацией get_config метода и from_config классового метода.

Вот пример кастомного слоя, который имплементирует matmul входа с матрицей ядра:

class MyLayer(keras.layers.Layer):

  def init(self, output_dim, **kwargs):
    self.output_dim = output_dim
    super(MyLayer, self).init(**kwargs)

  def build(self, input_shape):
    shape = tf.TensorShape((input_shape[1], self.output_dim))
    # Создаем переменную тренируемого веса для этого слоя.
    self.kernel = self.add_weight(name='kernel',
                                  shape=shape,
                                  initializer='uniform',
                                  trainable=True)
    super(MyLayer, self).build(input_shape)

  def call(self, inputs):
    return tf.matmul(inputs, self.kernel)

  def compute_output_shape(self, input_shape):
    shape = tf.TensorShape(input_shape).as_list()
    shape[-1] = self.output_dim
    return tf.TensorShape(shape)

  def get_config(self):
    base_config = super(MyLayer, self).get_config()
    base_config['output_dim'] = self.output_dim

  @classmethod
  def from_config(cls, config):
    return cls(**config)


# Создание слоя с использованием кастомного слоя
model = keras.Sequential([MyLayer(10),
                          keras.layers.Activation('softmax')])

# Шаг компиляции определяет тренировочную конфигурацию
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Тренируем в течение 5 эпох.
model.fit(data, targets, batch_size=32, epochs=5)

Колбеки (callbacks)

Колбек (callback) - это объект переданный модели для кастомизирования и расширения ее модели в ходе тренировки. Можно писать собственный кастомный колбек или использовать встроенный tf.keras.callbacks, который включает:

  • tf.keras.callbacks.ModelCheckpoint: Сохраняет контрольные точки модели с регулярными интервалами.
  • tf.keras.callbacks.LearningRateScheduler: Динамически изменяет скорость обучения.
  • tf.keras.callbacks.EarlyStopping: Прерывает тренировку, когда валидационная производительность перестает улучшаться.
  • tf.keras.callbacks.TensorBoard: Отображает поведение модели, используя TensorBoard.

Чтобы использовать tf.keras.callbacks.Callback передаем ее методу fit модели:

callbacks = [
  # Прерываем тренировку 
  # если val_loss перестает улучшаться в течение 2 эпох
  keras.callbacks.EarlyStopping(patience=2, monitor='val_loss'),
  # Записываем логи TensorBoard в ./logs директорию
  keras.callbacks.TensorBoard(log_dir='./logs')
]
model.fit(data, labels, batch_size=32, epochs=5, callbacks=callbacks,
          validation_data=(val_data, val_targets))

Сохранение и восстановление

Только веса

Сохраняем и загружаем веса модели, используя tf.keras.Model.save_weights:

# Сохраняем веса в TensorFlow Checkpoint файл
model.save_weights('./my_model')

# Восстанавливаем состояние модели,
# это требует модель с точно такой же архитектурой.
model.load_weights('my_model')

По умолчанию это сохраняет веса модели в файл формата контрольной точки TensorFlow. Веса могут быть также сохранены в Keras HDF5 формате (по умолчанию для мульти-бекенд имплементации Keras):

# Сохраняем веса в HDF5 файле
model.save_weights('my_model.h5', save_format='h5')

# Восстанавливаем состояние модели
model.load_weights('my_model.h5')

Сохранение и восстановление только конфигурации

Конфигурация модели может быть сохранена - это сериализация архитектуры модели без весов. Сохраненная конфигурация может пересоздать и инициализировать такую же модель, даже без кода, который определял изначальную модель. Keras поддерживает JSON и YAML форматы сериализации:

# Сериализация модели в JSON формате
json_string = model.to_json()

# Пересоздание модели (со свежей инициализацией)
fresh_model = keras.models.from_json(json_string)

# Сериализация модели в YAML формате
yaml_string = model.to_yaml()

# Пересоздание модели
fresh_model = keras.models.from_yaml(yaml_string)

Предостережение: модели с подклассами не сериализуемы, потому что их архитектура определена в Python коде в теле call метода.

Сохранение и восстановление целой модели

Целая модель может быть сохранена в файл, который содержит значения весов, конфигурацию модели, и конфигурацию оптимизатора. Это позволяет создавать контрольную точку модели и возобновлять тренировку позже - с того же самого места, где она была остановлена - без необходимости доступа к изначальному коду.

# Создаем обычную модель
model = keras.Sequential([
  keras.layers.Dense(10, activation='softmax', input_shape=(32,)),
  keras.layers.Dense(10, activation='softmax')
])
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.fit(data, targets, batch_size=32, epochs=5)


# Сохраняем целую модель в HDF5 файле
model.save('my_model.h5')

# Пересоздаем ту же самую модель, включая веса и оптимизатор.
model = keras.models.load_model('my_model.h5')

Нетерпеливое исполнение

Нетерпеливое исполнение (eager execution) - это императивное программное окружение, которое применяет операции немедленно. Это не требуется для Keras, но поддерживается tf.keras и полезно для проверки программ и отладки.

Все tf.keras API для построения моделей совместимы с нетерпеливым исполнением. И, хотя Sequential и фукнциональное API могут быть использованы, нетерпеливое исполнение имеет особые преимущества при создании подклассов моделей и построении кастомных слоев.

Распределение

Estimators (оценщики)

Estimators API используется для тренировки модели в распределенных окружениях. Это целевые методы использования, такие как распределенная тренировка на крупных наборах данных, которые могут экспортировать модель в производственную среду.

tf.keras.Model может быть тренирована с tf.estimator API преобразованием модели в tf.estimator.Estimator объект с tf.keras.estimator.model_to_estimator.

model = keras.Sequential([layers.Dense(10,activation='softmax'),
                          layers.Dense(10,activation='softmax')])

model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])

estimator = keras.estimator.model_to_estimator(model)

Исполнение на многих GPU

tf.keras модели могут выполняться на нескольких GPU, используя tf.contrib.distribute.DistributionStrategy. Это API предоставляет распределенную тренировку на нескольких GPU почти без изменений в существующем коде.

На данный момент tf.contrib.distribute.MirroredStrategy единственная поддерживаемая стратегия распределения. MirroredStrategy выполняет внутри-графовую репликацию с синхронной тренировкой, используя all-reduce на единственной машине. Чтобы использовать DistributionStrategy с Keras, преобразуем tf.keras.Model в tf.estimator.Estimator с tf.keras.estimator.model_to_estimator, затем тренируем как estimator.

Следующий пример распределяет tf.keras.Model по нескольким GPU на единственной машине.

Во-первых, определим простую модель:

model = keras.Sequential()
model.add(keras.layers.Dense(16, activation='relu', input_shape=(10,)))
model.add(keras.layers.Dense(1, activation='sigmoid'))

optimizer = tf.train.GradientDescentOptimizer(0.2)

model.compile(loss='binary_crossentropy', optimizer=optimizer)
model.summary()

Определим входной пайплайн. input_fn возвращает tf.data.Dataset объект, используемый чтобы распределить данные по многим устройствам - с каждым устройством, обрабатывающим часть входного пакета.

def input_fn():
  x = np.random.random((1024, 10))
  y = np.random.randint(2, size=(1024, 1))
  x = tf.cast(x, tf.float32)
  dataset = tf.data.Dataset.from_tensor_slices((x, y))
  dataset = dataset.repeat(10)
  dataset = dataset.batch(32)
  return dataset

Затем создаем tf.estimator.RunConfig и устанавливаем train_distribute аргумент к tf.contrib.distribute.MirroredStrategy экземпляру. При создании MirroredStrategy можно определить лист устройств или установить num_gpus аргумент. По умолчанию используются все доступные GPU:

strategy = tf.contrib.distribute.MirroredStrategy()
config = tf.estimator.RunConfig(train_distribute=strategy)

Преобразуем Keras модель в tf.estimator.Estimator экземпляр:

keras_estimator = keras.estimator.model_to_estimator(
  keras_model=model,
  config=config,
  model_dir='/tmp/model_dir')

Наконец, тренируем Estimator экземпляр предоставляя input_fn и steps аргументы:

keras_estimator.train(input_fn=input_fn, steps=10)

среда, 12 сентября 2018 г.

TensorFlow: Keras, базовое использование

Keras - это высокоуровневое API для построения и тренировки моделей глубокого обучения. Оно используется для быстрого прототипирования, углубленного исследования, и в производственной среде, и вот его три главных преимущества:

  • Дружественность к пользователю

    Keras имеет простой, согласованный интерфейс, оптимизированный для случаев общего применения. Он обеспечивает четкую и эффективную обратную связь для пользовательских ошибок.

  • Модулярный и компонуемый

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

  • Простой для расширения

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

Импорт tf.keras

tf.keras это TensorFlow реализация Keras API спецификации. Это высокоуровненое API для построения и тренировки моделей, которое включает первоклассную поддержку для TensorFlow специфичной функциональности, такой как нетерпеливое исполнение (eager execution), tf.data пайплайны, и Estimators. tf.keras делает TensorFlow проще для использования без жертвования гибкостью и производительностью.

Для начала импортируем tf.keras как часть создания TensorFlow программы:

import tensorflow as tf
from tensorflow import keras

tf.keras может исполнять любой Keras-совместимый код, но необходимо помнить:

  • Версия tf.keras в последнем TensorFlow релизе может быть не той же самой, что и последняя версия keras из PyPI. Проверяйте tf.keras.version.
  • При сохранении весов модели tf.keras по умолчанию использует формат контрольных точек. Передайте save_format='h5', чтобы использовать HDF5.

Создание простой модели

Последовательная модель (Sequential model)

В Keras для построения модели необходимо собрать слои. Модель - это (обычно) граф слоев. Наиболее общий тип модели - это стек слоев: tf.keras.Sequential модель.

Для построения простой, полностью связанной сети (то есть многослойного перцептрона):

model = keras.Sequential()
# Добавляем плотно-соединенный (densely-connected) слой 
# с 64 элементами в модели:
model.add(keras.layers.Dense(64, activation='relu'))
# Добавляем другой:
model.add(keras.layers.Dense(64, activation='relu'))
# Добавляем софтмакс слой с 10 выводными элементами:
model.add(keras.layers.Dense(10, activation='softmax'))

Конфигурирование слоев

Существует много tf.keras.layers доступных с некоторыми параметрами конструктора:

  • activation: Устанавливает функцию активации для слоя. Этот параметр определяется именем встроенной функции или как вызываемый объект. По умолчанию, активация не применяется.
  • kernel_initializer и bias_initializer: Инициализационные схемы, которые создают веса слоя (ядро и смещение). Этот параметр - это имя или вызываемый объект. По умолчанию равен "Glorot uniform" инициализатору.
  • kernel_regularizer и bias_regularizer: Регуляризационные схемы, которые применяют веса слоя (ядро и смещение), такие как L1 или L2 регуляризация. По умолчанию регуляризация не применяется.

Следующие экземпляры tf.keras.layers.Dense слоев используют аргументы конструктора:

# Создаем сигмоидный слой:
layers.Dense(64, activation='sigmoid')
# Или:
layers.Dense(64, activation=tf.sigmoid)

# Линейный слой с L1 регуляризацией фактора 0.01, 
# применяемой к матрице ядра:
layers.Dense(64, kernel_regularizer=keras.regularizers.l1(0.01))
# Линейный слой с L2 регуляризацией фактора 0.01, 
# применяемой к вектору смещения:
layers.Dense(64, bias_regularizer=keras.regularizers.l2(0.01))

# Линейный слой с ядром инициализированным 
# к случайной ортогональной матрице:
layers.Dense(64, kernel_initializer='orthogonal')
# Линейный слой с вектором смещения, инициализированным к 2.0s:
layers.Dense(64, bias_initializer=keras.initializers.constant(2.0))

tf.keras.Model.compile принимает три важных аргумента:

  • optimizer: Этот объект определяет тренировочную процедуру. Передаем этому экземпляры оптимизатора из модуля tf.train, такие как AdamOptimizer, RMSPropOptimizer, или GradientDescentOptimizer.
  • loss: Функция для минимизации потери в ходе оптимизации. Общие варианты включают среднюю квадратичную ошибку (mean square error (mse)), categorical_crossentropy, и binary_crossentropy. Функции потери определяется именем или передачей вызываемого объекта из модуля tf.keras.losses.
  • metrics: Используются для отображения тренировки. Это строковые имена или вызываемые объекты из модуля tf.keras.metrics.

Далее показаны несколько примеров конфигурирования модели для тренировки:

# Кофигурируем модель для регрессии со средней квадратичной ошибкой
model.compile(optimizer=tf.train.AdamOptimizer(0.01),
              loss='mse',       # средняя квадратичная ошибка
              metrics=['mae'])  # средняя абсолютная ошибка

# Конфигурируем модель для категорийной классификации
model.compile(optimizer=tf.train.RMSPropOptimizer(0.01),
              loss=keras.losses.categorical_crossentropy,
              metrics=[keras.metrics.categorical_accuracy])

Ввод NumPy данных

Для маленьких наборов данных используем NumPy массивы в памяти для тренировки и оценки модели. Модель обучается тренировочным данным, используя метод fit:

tf.keras.Model.fit принимает три важных аргумента:

  • epochs: Тренировка структурирована в эпохи. Эпоха - это одна итерация по всем входным данным (она выполняется маленькими пакетами).
  • batch_size: При передаче NumPy данных модель нарезает данные кусками в маленькие пакеты и итерирует по этим пакетам в течение тренировки. Это число определяет размер каждого пакета. Необходимо помнить, что последний пакет может быть меньше если общее количество примеров не делится целиком на размер пакета.
  • validation_data: При прототипировании модели необходимо проверять ее производительность на некоторых валидационных данных. Передача этого аргумента - пары из вводных данных и меток - позволяет модели показывать потерю и метрики в режиме работы модели для переданных данных в конце каждой эпохи.

Вот пример использования validation_data:

import numpy as np

data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

val_data = np.random.random((100, 32))
val_labels = np.random.random((100, 10))

model.fit(data, labels, epochs=10, batch_size=32,
          validation_data=(val_data, val_labels))

Ввод tf.data наборов данных

Используем Datasets API для больших наборов данных или тренировки на нескольких устройствах. Передаем tf.data.Dataset экземпляр fit методу:

# Создаем маленький экземпляр набора данных:
dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32)
dataset = dataset.repeat()

# Задаем `steps_per_epoch` при вызове `fit` на наборе данных.
model.fit(dataset, epochs=10, steps_per_epoch=30)

Здесь fit метод использует steps_per_epoch аргумент - это количество тренировочных шагов, которые выполняет модель до того как перейти к следующей эпохе. Ввиду того, что Dataset выводит данные пакетами, этот случай не требует batch_size.

Datasets также может быть использован для валидации:

dataset = tf.data.Dataset.from_tensor_slices((data, labels))
dataset = dataset.batch(32).repeat()

val_dataset = tf.data.Dataset.from_tensor_slices((val_data, val_labels))
val_dataset = val_dataset.batch(32).repeat()

model.fit(dataset, epochs=10, steps_per_epoch=30,
          validation_data=val_dataset,
          validation_steps=3)

Оценка и прогнозирование

tf.keras.Model.evaluate и tf.keras.Model.predict методы могут использовать NumPy данные и tf.data.Dataset.

Чтобы оценить потерю и метрики в рабочем режиме модели выполняем следующее:

model.evaluate(x, y, batch_size=32)

model.evaluate(dataset, steps=30)

А чтобы прогнозировать вывод последним слоем в рабочем режиме модели выполняем следующее:

model.predict(x, batch_size=32)

model.predict(dataset, steps=30)

В следующем посте мы рассмотрим другие аспекты использования TensorFlow Keras.