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

Вот задача: обучить сети, которые могут принимать двоичное или десятичное число от 0 до 9, прибавлять к нему 1 или умножать на 2 и возвращать двоичное или десятичное число от 0 до 9.

Наивным подходом было бы тренировать каждый путь отдельно, без переплетающихся сетей. Это самый простой и быстрый способ выполнить это задание, но это не поможет нам учиться!

Давайте ограничимся обучением только 2 модулей кодировщика (двоичного и десятичного), 2 модулей математических операций (плюс 1 и умножить на 2) и 2 модулей декодера (двоичного и десятичного). Мы можем составить части вместе во время выполнения.

Вот как может выглядеть один путь через нашу модель:

Но обучение только этому одному пути не поможет нам повторно использовать модули. Нам нужно иметь возможность повторно использовать кодировщики, математические операции и декодеры. Мы можем решить это, переплетая и обучая сети следующим образом:

Здесь все довольно типично; в основном это будет много плотных слоев, за исключением тех, которые выбирают модули. В программировании вы можете называть их case-выражениями. В электротехнике их можно назвать мультиплексорами. В TensorFlow мы называем их операциями tf.where, tf.cond и tf.case.

В этом примере нам не нужны tf.cond или tf.case, потому что они плохо работают с партиями.

tf.где

tf.where позволяет нам выбирать между двумя ветвями для каждого примера в пакете. На простом Python это может выглядеть так:

def where(tests, as, bs):
    result = []
    for test, a, b in zip(tests, as, bs):
        val = a if test else b
        result.append(val)
    return result

О производительности и семантике исполнения (круто!)

Помните, что TensorFlow использует модель потока данных, но она не обязательно ленивая. Во время выполнения должны выполняться обе ветви (читай: тензоры) в tf.where. С tf.where мы отбрасываем половину вычисленных значений. Это займет некоторое количество циклов процессора, но, учитывая характер графических процессоров (SIMD хорош, ветвление плохо), это, вероятно, самый быстрый способ. Если вам нужно ленивое выполнение вместо нетерпеливого, попробуйте вместо этого tf.cond или tf.case и откажитесь от простой пакетной обработки.

Пример

Вот как мы можем выбирать между ветвью «плюс 1» и ветвью «умножить 2»:

math_output = tf.where(use_plus1, plus1_output, times2_output)
# use_plus1 is a boolean of shape [batch_size]
# plus1_output has batch_size as it's first dim
# times2_output has the same shape as plus1_output

Контроль веток

Для каждого обучающего примера нам нужно сообщить нашей сети:

  1. какой энкодер использовать
  2. какую математическую операцию использовать

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

Выход и потери

Нет причин не обучать каждый декодер каждому обучающему примеру. Мы можем объединить их потери, просто сложив их.

total_loss = binary_loss + digit_loss

К счастью, градиенты для каждого декодера останутся прежними. Частная производная для потерь обоих декодеров равна 1. С некоторыми оптимизаторами все может пойти наперекосяк, когда одна потеря намного выше другой, но в этом примере это работает отлично.

Собираем все вместе

Проверьте код! Это коротко, мило и доступно для копирования.

Я ищу работу! февраль 2018

Я в поиске работы! У меня большой опыт в области машинного обучения, функционального программирования, музыки и предпринимательства. Я ищу место, где можно применить свои навыки, предпочтительно в Лос-Анджелесе или других местах за пределами залива.