Введение

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

Одним из таких подходов является tf.keras, который представляет собой реализацию TensorFlow спецификации Keras API. Keras превратился в высокоуровневый интерфейс для построения и обучения моделей в TensorFlow версии 2.0 и более поздних версиях.

Эти подходы включают в себя:

  1. Последовательный API
  2. Функциональный API
  3. Подклассы модели

Последовательный API

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

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

model =  keras.Sequential( [
                  layers.Dense(16, activation="relu", name="layer1"),
                  layers.Dense(8, activation="relu", name="layer2"), 
                  layers.Dense(4, name="layer3") ] )
model = keras.Sequential()
model.add(layers.Dense(16, activation="relu", name="layer1"))
model.add(layers.Dense(8, activation="relu", name="layer2"))
model.add(layers.Dense(4, name="layer3"))

Недостатки последовательного API

Последовательный API в Keras — это простой и удобный способ создания нейронных сетей, но он имеет некоторые недостатки:

  1. Ограниченная архитектура моделей: Sequential API разработан для линейных, последовательных моделей, в которых слои накладываются друг на друга по одному прямому пути. Это ограничивает его возможности создавать более сложные сетевые архитектуры, такие как модели с несколькими ветвями или модели с общими уровнями.
  2. Негибкость: Sequential API не обеспечивает гибкости для легкого повторного использования слоев. Добавив слой в модель, вы не сможете легко подключить его к нескольким частям сети или повторно использовать его по-разному в одной модели. Функциональный API лучше подходит для таких сценариев.
  3. Ограниченный доступ к промежуточным слоям. В некоторых сложных случаях использования вам может потребоваться доступ к промежуточным слоям или их результатам для различных целей, таких как извлечение признаков или реализация пользовательских функций потерь. Последовательный API не делает это так просто, как Функциональный API, где вы можете легко получить доступ к промежуточным выводам.
  4. Сложные модели становятся более сложными. Поскольку ваши модели становятся более сложными, вам может оказаться сложно выразить их, используя только последовательный API. Если вам нужно реализовать сложные соединения пропуска, общие слои или модели с несколькими входами и выходами, вам придется прибегнуть к функциональному API или API подклассов, которые предлагают большую гибкость.
  5. Отладка и визуализация. Отладка сложных моделей или визуализация архитектуры может оказаться более сложной задачей при использовании последовательного API по сравнению с функциональным API, где у вас есть четкий и явный график модели. Это может быть особенно сложно при работе с моделями, имеющими ответвления или пропускающие соединения.

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

Функциональный API

Этот подход дает вам возможность создавать модели нейронных сетей с множеством входов и выходов, а также возможность включать общие слои. Он не только включает в себя всю функциональность последовательного API, но также предлагает расширенный контроль над взаимодействием слоев друг с другом. Функциональный API использует концепцию Lego, в которой компоненты можно собирать для создания каркаса для вашей окончательной модели. Думайте об этих компонентах как о кубиках Lego, которые можно располагать и складывать в различных конфигурациях, чтобы определить архитектуру вашей модели. После создания этих компонентов их можно использовать многократно, обеспечивая преимущество повторного использования.

def CNN_block(x,filters, kernel_size=3):
    # Defining a function which returns an operation of Convolution, BatchNormalisation and Relu.
    # Here x is the input.
    x = layers.Conv2D(filters, kernel_size=3)(x)
    x = layers.BatchNormalization()(x)

    return tf.nn.relu(x)
def inception_block(x, filter1, filter2):
    # Defining a function which applies two CNN blocks. The output of both these blocks are then concatenated
    conv1 = CNN_block(x, filter1)
    conv2 = CNN_block(x, filter2)
    x = keras.layers.concatenate([conv1, conv2])

    return x

В этом контексте inception_block использует функциональность двух ветвей CNN_block. Эти выходные данные, исходящие из этих двойных ветвей, впоследствии объединяются с использованием двух экземпляров CNN_block, упорядоченных в виде списка и обрабатываемых через слой Layers.concatenate.

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

Теперь, когда все необходимые строительные блоки модели определены, можно приступить к построению ее архитектуры.

inputs = keras.Input(shape=(28, 28, 1))
x = CNN_block(inputs,32)
x = layers.MaxPooling2D(3)(x)
x = inception_block(x,32, 32)
x = inception_block(x,64, 64)
x = CNN_block(x,128)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(256, activation="relu")(x)
x = layers.Dropout(0.5)(x)
outputs = layers.Dense(10)(x)
model = keras.Model(inputs, outputs, name="final_model")
model.summary()

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

  • Сверточные слои: Conv1D, Conv2D и т. д.
  • Слои объединения: MaxPooling1D, MaxPooling2D и т. д.
  • Повторяющиеся слои: GRU, LSTM и т. д.
  • BatchNormalization, Dropout, Embedding и многие другие.

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

Подклассы модели

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

Создание подклассов модели TensorFlow — это продвинутый метод создания пользовательских архитектур нейронных сетей в TensorFlow, популярной среде глубокого обучения. При создании подклассов модели вместо использования предварительно определенных слоев и моделей, предоставляемых высокоуровневыми API-интерфейсами TensorFlow, такими как Keras, вы определяете свои собственные пользовательские слои и модели, создавая подклассы класса tf.keras.Model TensorFlow.

Вот базовый обзор того, как работает подклассификация модели:

  • Создание пользовательских слоев. Вы определяете свои пользовательские слои, создавая подкласс tf.keras.layers.Layer. Внутри метода вызова слоя вы указываете, как слой должен преобразовывать входные данные. Это дает вам полный контроль над поведением слоя.
  • Определите пользовательские модели: вы создаете собственные модели, создавая подкласс tf.keras.Model. В методе init модели вы создаете экземпляры своих пользовательских слоев, определяя архитектуру вашей нейронной сети. В методе вызова вы определяете прямой проход вашей модели, определяя, как данные проходят через слои.
  • Пользовательские циклы обучения. Благодаря созданию подклассов моделей у вас есть возможность реализовать собственные циклы обучения. Это означает, что вы можете определить, как ваша модель вычисляет градиенты, применяет алгоритмы оптимизации и обновляет свои веса во время обучения.

Создание пользовательских моделей

Метод вызова внутри модели фактически происходит из класса keras.engine.layer._Layer и наследуется классом модели.

Наше намерение — переопределить этот унаследованный метод вызова нашим собственным методом вызова.

Вот почему нет необходимости явно использовать model.call(). Когда мы вызываем экземпляр нашей модели, автоматически выполняется унаследованный метод вызова, который, в свою очередь, вызывает наш собственный метод вызова.

class MyModel2(keras.Model):  # model.fit, model.evalute, model.predict
    def __init__(self, num_classes=10):
        super().__init__()
        self.dense1 = layers.Dense(64)
        self.dense2 = layers.Dense(num_classes)

    def call(self, x):
        x = tf.nn.relu(self.dense1(x))
        return self.dense2(x)

    def model(self):
        x=keras.Input(shape=(28*28))
        return Model(inputs=[x],outputs=self.call(x))

Создание пользовательских слоев

В TensorFlow, если вы хотите создавать собственные слои, например собственный плотный слой, или если вы собираетесь создавать уникальные функции активации, такие как softmax или ReLU, у вас есть свобода сделать это.

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

Давайте рассмотрим, как концепция создания подклассов позволяет создавать новые и индивидуальные слои.

В Keras фундаментальным строительным блоком является класс Layer. Уровень состоит из двух основных компонентов: состояния, которое включает в себя «веса» слоя, и преобразования, которое сопоставляет входные данные с выходными, часто называемого «вызовом», представляющим прямой проход слоя.

Например, рассмотрим плотносвязный слой. Внутри этого слоя существует состояние, а именно переменные «w» и «b».

class custom_dense(keras.layers.Layer):
    def __init__(self, units, input_dim):
        super().__init__()
        self.w = self.add_weight(    # specifying this is a weight and trainable.
            name="w",
            shape=(input_dim, units),
            initializer="random_normal",
            trainable=True,
        )
        self.b = self.add_weight(
            name="b", shape=(units,), initializer="zeros", trainable=True
        )

    def call(self, inputs):
        return tf.matmul(inputs, self.w) + self.b

init() определяет все экземпляры пользовательских слоев, которые будут использоваться при построении модели. После того, как все экземпляры определены, вы можете создать метод call(), который переопределяет то, как должны происходить вычисления между экземплярами и другими уровнями.

class MyReLU(keras.layers.Layer):
    def __init__(self):
        super().__init__()

    def call(self, x):
        return tf.math.maximum(x, 0)

Создание наборов данных

Пользовательские наборы данных в TensorFlow служат нескольким важным целям:

  • Интеграция данных
  • Подготовка данных
  • Увеличение данных
  • Сложные входные конвейеры
  • Нестандартные форматы данных
  • Пользовательские источники данных
  • Специализированные задачи с данными
  • Исследования и эксперименты

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

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

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

API TensorFlow tf.data — ценный инструмент для создания и управления сложными конвейерами ввода. Это позволяет вам эффективно обрабатывать обширные наборы данных и выполнять сложные преобразования данных.

Чтобы проиллюстрировать этот процесс, давайте создадим набор данных, используя API tf.data, на примере набора данных MNIST.

  • API tf.data обеспечивает преобразование tf.data.Dataset.prefetch. Его можно использовать для отделения времени создания данных от времени их потребления».
  • Количество элементов для предварительной выборки должно быть равно (или, возможно, больше) количеству пакетов, использованных на одном этапе обучения.
  • Вы можете вручную настроить это значение или установить для него значение tf.data.AUTOTUNE, что предложит среде выполнения tf.data динамически настраивать значение во время выполнения.
AUTOTUNE = tf.data.experimental.AUTOTUNE

train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_dataset = (train_dataset.shuffle(buffer_size=len(y_train)).batch(32,drop_remainder=True).prefetch(buffer_size=AUTOTUNE))

test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))
test_dataset = (test_dataset.batch(32,drop_remainder=False).prefetch(buffer_size=AUTOTUNE))

Входной конвейер начинается с импорта данных и создания набора данных из данных, хранящихся в памяти. Для этого вы можете использовать tf.data.Dataset.from_tensor_slices(), который создает tf.data.Dataset — объект, элементами которого являются срезы переданных тензоров. Создав объект, вы можете преобразовать его, применяя к объекту набора данных различные операции. (например, Dataset.map() или Dataset.batch()).

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

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

Создание пользовательского цикла обучения

Самый интригующий аспект обучения модели заключается в создании индивидуального цикла обучения. Хотя метод fit() обеспечивает удобство обучения, важно понимать его внутреннюю работу.

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

Получите прогнозы от вашей модели.

  • Рассчитайте потери, сравнив эти прогнозы с целевыми значениями.
  • Выполняйте обратное распространение ошибки, отслеживая градиенты в ваших вычислениях.
  • Примените эти градиенты к параметрам вашей модели с помощью оптимизатора.

TensorFlow снабжает нас функциональностью GradientTape(), позволяющей тщательно отслеживать операции и облегчать вычисление градиента, тем самым обеспечивая точный контроль над каждой деталью процесса обучения.

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

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

Функция Gradient Tape предоставляет прямой доступ к отдельным градиентам внутри слоя. После того как вы записали соответствующие операции, вы можете использовать GradientTape.gradient(target,sources) для вычисления градиента конкретной цели (обычно потерь) по отношению к конкретным источникам (часто переменным модели).

Точно так же, как вы использовали возможности tf.keras.Model и tf.keras.layers.Layer для создания собственной модели и слоев, вы также хотите использовать удобные функции, предлагаемые методом fit(). Этот метод позволяет вам сохранить контроль над мелкими деталями и каждой операцией, сохраняя при этом абстракцию высокого уровня и простоту.

В процессе настройки метода fit() вам необходимо переопределить функцию train_step в вашем классе модели, аналогично тому, как мы переопределили метод call() для настройки прямого прохода.

Функция train_step автоматически вызывается, когда вы применяете метод fit() к своей модели. Следовательно, этот подход легко интегрирует настройку в рамках метода fit().

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

  • Создайте новый класс, создав подкласс keras.Model.
  • Переопределить метод train_step(self, data).
  • Возвращает словарь, содержащий имена метрик и их соответствующие значения.
class Custom_fit(keras.Model):
  def __init__(self, model):
    super().__init__()
    self.model = model

  def compile(self, optimizer, loss, metric):
    super(Custom_fit, self).compile()
    self.optimizer = optimizer
    self.loss = loss
    self.metric = metric

  def train_step(self,data):
    # Unpack the data
    x, y = data
    with tf.GradientTape() as tape:
      # Compute predictions
      y_pred = self.model(x, training=True)
      # Calculating loss
      loss = self.loss(y, y_pred)
    
    # Tracking gradients
    training_vars = self.trainable_variables
    gradients = tape.gradient(loss, training_vars)

    #Update optimizer & metrics
    self.optimizer.apply_gradients(zip(gradients, training_vars))
    self.metric.update_state(y, y_pred)

    return {"Train_loss for Custom_train": loss, "Train_accuracy for Custom_train": self.metric.result()}


  def test_step(self, data):
    # Unpack the data
    x, y = data
    # Compute predictions
    y_pred = self.model(x, training=False)
    # Calculating loss
    loss = self.loss(y, y_pred)
    #Update optimizer & metrics
    self.metric.update_state(y, y_pred)
    return {"Test_loss for Custom_test": loss, "Test_accuracy for Custom_test": self.metric.result()}

Краткое содержание

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

  • Tf.keras от TensorFlow предлагает широкий спектр встроенных слоев, включая сверточные, групповые, RNN и общие слои, такие как BatchNormalization, Dropout и Embedding.
  • Если эти встроенные слои не соответствуют вашим конкретным потребностям, у вас есть возможность расширить API, создав свои собственные слои. Все пользовательские слои должны наследовать от класса Layer и реализовывать метод вызова, который определяет вычисления слоя, а также метод построения для создания весов слоев (хотя это также можно сделать в методе init). .
  • Главной сущностью для обучения и сериализации является Модель. Модель, аналогичная слою, включает в себя дополнительные функции, предназначенные для обучения.

Ссылки: https://keras.io/guides/functional_api/

Блокнот с полным кодом: https://www.kaggle.com/code/sharanharsoor/custom-model-building-in-tensorflow-keras?scriptVersionId=141527796