Input shape keras что это
Перейти к содержимому

Input shape keras что это

  • автор:

tf.keras.Input

Input() используется для создания экземпляра тензора Кераса.

View aliases

Main aliases

Сравнивать псевдонимы для миграции

См. Migration guide для более подробной информации.

tf.keras.Input( shape=None, batch_size=None, name=None, dtype=None, sparse=None, tensor=None, ragged=None, type_spec=None, **kwargs )

Тензор Кераса — это символический тензороподобный объект, который мы дополняем определенными атрибутами, которые позволяют нам построить модель Кераса, просто зная входные и выходные данные модели.

Например, если a , b и c являются тензорами Кераса, становится возможным сделать: model = Model(input=[a, b], output=c)

Args
shape Кортеж формы (integers), без размера пакета. Например, shape=(32,) указывает, что ожидаемые входные данные будут пакетами 32-мерных векторов. Элементами этого кортежа могут быть None; Элементы ‘None’ представляют размеры, форма которых неизвестна.
batch_size дополнительный размер партии static (integer).
name Необязательная строка имени слоя. Должно быть уникальным в модели (не используйте повторно одно и то же имя twice).. Если оно не указано, оно будет сгенерировано автоматически.
dtype Тип данных, ожидаемый на входе, в виде строки ( float32 , float64 , int32 . )
sparse boolean, указывающий, является ли создаваемый заполнитель разреженным. Только один из «рваных» и «разреженных» может быть True. Обратите внимание, что если sparse — это False, разреженные тензоры по-прежнему могут быть переданы на вход — они будут уплотнены со значением по умолчанию, равным 0.
tensor Необязательный существующий тензор для включения в слой Input . Если установлено, слой будет использовать tf.TypeSpec этого тензора, а не создавать новый тензор-заполнитель.
ragged boolean, указывающий, является ли создаваемый заполнитель рваным. Только один из «рваных» и «разреженных» может быть True. В этом случае значения ‘None’ в аргументе «форма» представляют неровные размеры. Дополнительные сведения о RaggedTensors, см. в this guide .
type_spec Объект tf.TypeSpec , из которого создается заполнитель ввода. При наличии все остальные аргументы, кроме имени, должны быть None.
**kwargs поддержка устаревших аргументов. Поддерживает batch_shape и batch_input_shape .
Returns
A tensor .
Example:
# это логистическая регрессия в Keras x = Input(shape=(32,)) y = Dense(16, activation='softmax')(x) model = Model(x, y)

Обратите внимание, что даже если активное выполнение включено, Input создает символический тензороподобный объект (i.e. a placeholder).. Этот символический тензороподобный объект можно использовать с операциями более низкого уровня TensorFlow, которые принимают тензоры в качестве входных данных, как таковые:

x = Input(shape=(32,)) y = tf.square(x) # Эта операция будет рассматриваться как слой model = Model(x, y)

Поведение (This не работает для TensorFlow APIs более высокого порядка, таких как поток управления и непосредственное наблюдение tf.GradientTape ).

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

Вход Keras также может создать заполнитель из произвольного tf.TypeSpec , e.g:

x = Input(type_spec=tf.RaggedTensorSpec(shape=[None, None], dtype=tf.float32, ragged_rank=1)) y = x.values model = Model(x, y)

При передаче произвольного tf.TypeSpec он должен представлять подпись всего пакета, а не только одного примера.

Raises
ValueError Если предоставлены оба sparse и ragged .
ValueError Если предоставлены оба shape и ( batch_input_shape или batch_shape ).
ValueError Если shape , tensor и type_spec — это None.
ValueError Если аргументы, кроме type_spec , равны non-None, а передается type_spec .
ValueError если указаны какие-либо нераспознанные параметры.

Слои Keras: параметры и свойства / keras 5

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

Слои Keras

Для определения или создания слоя Keras нужна следующая информация:

  • Форма ввода: для понимания структуры входящей информации
  • Количество: для определения количества узлов/нейронов в слое
  • Инициализатор: для определения весов каждого входа, что важно для выполнения вычислений
  • Активаторы: для преобразования входных данных в нелинейный формат, чтобы каждый нейрон мог обучаться эффективнее
  • Ограничители: для наложения ограничений на веса при оптимизации
  • Регуляторы: для применения штрафов к параметрам во время оптимизации

Различные слои в Keras

Основные слои Keras

Dense

Вычисляет вывод следующим образом:

output=activation(dot(input,kernel)+bias) 

Здесь activation — это активатор, а kernel — взвешенная матрица, применяемая к входящим тензорам. bias — это константа, помогающая настроить модель наилучшим образом.

Dense-слой получает информацию со всех узлов предыдущего слоя. Вот его аргументы и значения по умолчанию:

Dense(units, activation=NULL, use_bias=TRUE,kernel_initializer='glorot_uniform', bias_regularizer=NULL, activity_regularizer=NULL, kernel_constraint=NULL, bias_constrain=NULL) 
Activation

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

Activation(activation_function) 

Если функцию активации не передать, то будет выполнена линейная активация.

Dropout

Dropout применяется в нейронных сетях для решения проблемы переобучения. Для этого он случайным образом выбирает доли единиц и при каждом обновлении назначает им значение 0.

Dropout(rate, noise_shape=NULL, seed=NULL) 
Flatten

Flatten используется для конвертации входящих данных в меньшую размерность.

Например, входящий слой размерности (batch_size, 3,2) «выравнивается» для вывода размерности (batch_size, 6) . Он имеет следующие аргументы.

Flatten(data_format=None) 
Input

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

Input(shape, batch_shape, name, dtype, sparse=FALSE,tensor=NULL) 
Reshape

Изменить вывод под конкретную размерность

Reshape(target_shape) 

Выдаст следующий результат:

(batch_size,)+ target_shape 
Permute

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

Permute(dims) 
Lambda

Слои Lambda используются для создания дополнительных признаков слоев, которых нет в Keras.

Lambda(lambda_fun,output_shape=None, mask=None, arguments=None) 
Masking

Пропустить временной промежуток, если все признаки равны mask_value .

Masking(mask_value=0.0) 

Сверточные сети Keras

Conv1D и Conv2D

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

Conv1D(filters,kernel_size,strides=1, padding='valid', data_format='channels_last', dilation_rate=1, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None) Conv2D(filters,kernel_size,strides=(1,1) , padding='valid', data_format='channels_last', dilation_rate=(1,1) , activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None) 

Слои пулинга (подвыборки)

Они используются для уменьшения размера ввода и извлечения важной информации.

MaxPooling1D и MaxPooling2D
MaxPooling1D(pool_size=2, strides=None, padding='valid', data_format='channels_last') MaxPooling2D(pool_size=(2,2), strides=None, padding='valid', data_format='channels_last') 
AveragePooling1D и AveragePooling2D
AveragePooling1D(pool_size=2, strides=None, padding='valid', data_format='channels_last') AveragePooling1D(pool_size=(2,2), strides=None, padding='valid', data_format=None) 

Рекуррентный слой

Этот слой используется для вычисления последовательных данных — временного ряда или естественного языка.

SimpleRNN

Это полностью связанная RNN (рекуррентная нейронная сеть), где вывод подается обратно в качестве входящих данных

SimpleRNN(units, activation, use_bias, kernel_initializer, recurrent_initializer, bias_initializer, kernel_regularizer, recurrent_regularizer, bias_regularizer, activity_regularizer, kernel_constraint, recurrent_constraint, bias_constraint, dropout, recurrent_dropout, return_sequences, return_state) 
LSTM

Это увеличенная форма RNN с хранилищем для информации.

LSTM(units, activation , recurrent_activation, use_bias, kernel_initializer, recurrent_initializer, bias_initializer, unit_forget_bias, kernel_regularizer, recurrent_regularizer, bias_regularizer, activity_regularizer, kernel_constraint, recurrent_constraint, bias_constraint, dropout, recurrent_dropout, implementation, return_sequences, return_state) 

В Keras есть и другие слои, но чаще всего используются описанные выше.

Выводы

Этот материал посвящен концепции слоев в моделях Keras. Вы узнали о требованиях для построения слоя, а также об их типах: основные слои, сверточные слои, подвыборки, рекуррентные слои, а также их свойства и параметры.

Keras — введение в функциональное API

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

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

Давайте посмотрим, как это делается.В начале программы подключим необходимые модули:

import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' import tensorflow as tf from tensorflow.keras.layers import Dense, Input, Conv2D, MaxPooling2D, Flatten from tensorflow import keras from tensorflow.keras.datasets import cifar10 import matplotlib.pyplot as plt

И установим зерно датчика случайных чисел в 1, чтобы обеспечить воспроизводимость результатов:

tf.random.set_seed(1)

Затем, сформируем первыедва слоя так, как это мы обычно делали:

input = keras.Input(shape=(32, 32, 3)) x = Conv2D(32, 3, activation='relu')

А, далее, свяжем их между собой, следующим образом:

x = x(input)

Эта строчка и есть элемент функционального API пакета Keras. Но как это работает? Все очень просто. Смотрите, объект input – это тензор специального вида, имеющий типKerasTensor:

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

Конечно, эту связь можно описать короче, в виде:

x = Conv2D(32, 3, activation='relu')(input)

Здесь мы сразу создаем объект и выполняем связывание со слоем input. В итоге, вся структура нейронной сети может быть описана в виде:

input = keras.Input(shape=(32, 32, 3)) x = layers.Conv2D(32, 3, activation='relu')(input) x = layers.MaxPooling2D(2, padding='same')(x) x = layers.Conv2D(64, 3, activation='relu')(x) x = layers.MaxPooling2D(2, padding='same')(x) x = layers.Flatten()(x) x = layers.Dense(256, activation='relu')(x) x = layers.Dropout(0.5)(x) output = layers.Dense(10, activation='softmax')(x)

Но это еще не модель. Конечно, мы можем использовать сеть в таком виде, подавать на вход числовые данные и получать выходные значения, но нам бы хотелось получить полноценную модель, чтобы средствами Keras обучать ее и производить оценку эффективности. Для этого достаточно воспользоваться классом Model, о котором мы с вами уже говорили на предыдущих занятиях, и указать входы и выходы:

model = keras.Model(inputs=input, outputs=output)

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

model.summary()

Как видите, все было сделано верно. Возможно, у вас возникнет вопрос: а зачем было все так усложнять? Почему бы не воспользоваться классом Sequential? Это было бы проще. Все верно. Для последовательных структур так и следует поступать. Однако, на практике относительно часто встречаются и другие архитектуры, которые не удается описать последовательными моделями. Либо, сделать это сложнее. В таких ситуациях, как раз, и используется функциональный подход к проектированию архитектур сетей.

Обучение сверточной нейронной сети

Давайте теперь обучим полученную модель сверточной НС для классификации изображений БД CIFAR-10. Сразу отмечу, что современные нейросети достигают здесь точности около 96,5%, а человек – всего 94%. Посмотрим, что получится у нас.

В этой БД имеется 50 000 полноцветных изображений, размером 32×32 пикселов в обучающей выборке и 10 000 таких же полноцветных изображений в тестовой выборке. Все изображения разбиты на 10 классов (см. рисунок). Наша задача отнести предъявленный образец нужному классу.

Вначале, как всегда, загрузим набор данных и стандартизуем их:

(x_train, y_train), (x_test, y_test) = cifar10.load_data() x_train = x_train / 255 x_test = x_test / 255 y_train = keras.utils.to_categorical(y_train, 10) y_test = keras.utils.to_categorical(y_test, 10)

Затем, укажем оптимизатор и функцию потерь:

model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])

После этого, запустим процесс обучения:

model.fit(x_train, y_train, batch_size=64, epochs=20, validation_split=0.2)

В конце программы выполним оценку качества работы модели на тестовой выборке:

print(model.evaluate(x_test, y_test) )

После запуска программы увидим качество распознавания на обучающей выборке в 87%, а на тестовой в 72%, что в целом неплохо, учитывая сложность задачи и простоту архитектуры НС. Предлагаю вам в качестве практики попытаться улучшить качество распознавания изображений БД CIFAR-10.

Реализация сверточных слоев в Tensorflow

Так как этот курс посвящен не только Keras, но и Tensorflow, то я покажу пример реализации сверточных слоев на уровне Tensorflow. Делается относительно просто, но несколько сложнее, чем в Keras. Вначале определим вспомогательный класс слоя:

class TfConv2D(tf.Module): def __init__(self, kernel=(3, 3), channels=1, strides=(2, 2), padding='SAME', activate="relu"): super().__init__() self.kernel = kernel self.channels = channels self.strides = strides self.padding = padding self.activate = activate self.fl_init = False def __call__(self, x): if not self.fl_init: self.w = tf.random.truncated_normal((*self.kernel, x.shape[-1], self.channels), stddev=0.1) self.b = tf.zeros([self.channels], dtype=tf.float32) self.w = tf.Variable(self.w) self.b = tf.Variable(self.b) self.fl_init = True y = tf.nn.conv2d(x, self.w, strides=(1, *self.strides, 1), padding=self.padding) if self.activate == "relu": return tf.nn.relu(y) elif self.activate == "softmax": return tf.nn.softmax(y) return y

Я его взял из одного из предыдущих занятий и полносвязный слой заменил сверточным. Логика работы здесь следующая. Для сверток нужно задать размер фильтра (kernel), число выходных каналов (channels), смещение фильтров в плоскости входных каналов (strides) и режим формирования выходных каналов (padding). То есть, мы прописываем все те же параметры, что и в стандартном сверточном слое.

Затем, при первом вызове магического метода __call__() формируем наборы весовых коэффициентов размерностью:

[kernel_x,kernel_y, input_channels, output_channels]

И еще тензор коэффициентов для смещений (biases) размером:

После этого обращаемся к ветке tf.nn и вызываем функцию conv2d для применения фильтров к входному сигналу x. К полученному выходному тензору y применяем функцию активации и, таким образом, формируем выход сверточного слоя.

Использовать этот класс можно, следующим образом:

layer1 = TfConv2D((3, 3), 32) y = layer1(tf.expand_dims(x_test[0], axis=0)) print(y.shape)

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

Далее, можно применить операцию MaxPooling, например, так:

y = tf.nn.max_pool2d(y, ksize=(1, 2, 2, 1), strides=(1, 2, 2, 1), padding="SAME") print(y.shape)

Сформируется тензор yразмерностью:

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

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

Модели в функциональном API

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

Если вы не знаете, что такое автоэнкодер, для чего он нужен и как работает, то смотрите занятие по этой теме в курсе «Нейронные сети»:

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

enc_input = Input(shape=(28, 28, 1)) x = Conv2D(32, 3, activation='relu')(enc_input) x = MaxPooling2D(2, padding='same')(x) x = Conv2D(64, 3, activation='relu')(x) x = MaxPooling2D(2, padding='same')(x) x = Flatten()(x) enc_output = Dense(8, activation='linear')(x) encoder = keras.Model(enc_input, enc_output, name="encoder")

А, затем, модельдекодера:

dec_input = keras.Input(shape=(8,), name="encoded_img") x = Dense(7 * 7 * 8, activation='relu')(dec_input) x = keras.layers.Reshape((7, 7, 8))(x) x = Conv2DTranspose(64, 5, strides=(2, 2), activation="relu", padding='same')(x) x = keras.layers.BatchNormalization()(x) x = Conv2DTranspose(32, 5, strides=(2, 2), activation="linear", padding='same')(x) x = keras.layers.BatchNormalization()(x) dec_output = Conv2DTranspose(1, 3, activation="sigmoid", padding='same')(x) decoder = keras.Model(dec_input, dec_output, name="decoder")

Теперь, используя модели encoder и decoder можно на их основе сформировать общую архитектуру автоэнкодера. Воспользуемся для этого функциональным подходом и будем рассматривать каждую модель как один единый элемент (объект):

autoencoder_input = Input(shape=(28, 28, 1), name="img") x = encoder(autoencoder_input) autoencoder_output = decoder(x) autoencoder = keras.Model(autoencoder_input, autoencoder_output, name="autoencoder")

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

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

Давайте выполним этот шаг для БД изображений цифр MNIST. Вначале подготовим обучающую выборку:

(x_train, y_train), (x_test, y_test) = mnist.load_data() x_train = x_train.astype("float32") / 255.0 x_test = x_test.astype("float32") / 255.0 y_train = keras.utils.to_categorical(y_train, 10) y_test = keras.utils.to_categorical(y_test, 10)

Установим оптимизатор Adamдля автоэнкодера и потребуем минимум квадрата ошибки рассогласования между входным и выходным изображениями:

autoencoder.compile(optimizer='adam', loss='mean_squared_error')

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

autoencoder.fit(x_train, x_train, batch_size=32, epochs=1)

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

h = encoder.predict(tf.expand_dims(x_test[0], axis=0)) img = decoder.predict(h)

В результате должны получить изображение похожее на входное. Для этого отобразим исходное изображение и восстановленное:

plt.subplot(121) plt.imshow(x_test[0], cmap='gray') plt.subplot(122) plt.imshow(img.squeeze(), cmap='gray') plt.show()

Как видим, результаты получились достаточно близкими. Это показывает, что кодер и декодер действительно обучились через автоэнкодер.

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

Видео по теме

#1. Что такое Tensorflow? Примеры применения. Установка

#2. Тензоры tf.constant и tf.Variable. Индексирование и изменение формы

#3. Математические операции и функции над тензорами

#4. Реализация автоматического дифференцирования. Объект GradientTape

#5. Строим градиентные алгоритмы оптимизации Adam, RMSProp, Adagrad, Adadelta

#6. Делаем модель с помощью класса tf.Module. Пример обучения простой нейросети

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

#8. Декоратор tf.function для ускорения выполнения функций

#9. Введение в модели и слои бэкэнда Keras

#10. Keras — последовательная модель Sequential

#11. Keras — введение в функциональное API

#12. ResNet — революция глубокого обучения. Исчезающие и взрывающиеся градиенты

#13. Создаем ResNet подобную архитектуру для классификации изображений CIFAR-10

#14. Тонкая настройка и контроль процесса обучения через метод fit()

#15. Тонкая настройка обучения моделей через метод compile()

#16. Способы сохранения и загрузки моделей в Keras

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

Keras Functional API в TensorFlow

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

Введение

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

Он основан на том, что модель глубоко обучения обычно представляет собой ориентированный ациклический граф (DAG) слоев

Functional API — это набор инструментов для построения графа слоев.

Рассмотрим следующую модель:

(вход: 784-мерный вектор)

[Плотный слой (64 элемента, активация relu)]

[Плотный слой (64 элемента, активация relu)]

[Плотный слой (10 элементов, активация softmax)]

(выход: вероятностное распределение на 10 классов)

Это простой граф из 3 слоев.

Для построения этой модели с помощью Functional API, вам надо начать с создания входного узла:

from tensorflow import keras inputs = keras.Input(shape=(784,))

Здесь мы просто указываем размерность наших данных: 784-мерных векторов. Обратите внимание, что количество данных всегда опускается, мы указываем только размерность каждого элемента. Для ввода предназначенного для изображений размеров `(32, 32, 3)`, мы бы использовали:

img_inputs = keras.Input(shape=(32, 32, 3))

То, что возвращает inputs , содержит информацию о размерах и типе данных которые вы планируете передать в вашу модель:

inputs.shape
TensorShape([None, 784])
inputs.dtype
tf.float32

Вы создаете новый узел в графе слоев, вызывая слой на этом объекте inputs :

from tensorflow.keras import layers dense = layers.Dense(64, activation='relu') x = dense(inputs)

«Вызов слоя» аналогичен рисованию стрелки из «входных данных» в созданный нами слой. Мы «передаем» входные данные в dense слой, и мы получаем x .

Давайте добавим еще несколько слоев в наш граф слоев:

x = layers.Dense(64, activation='relu')(x) outputs = layers.Dense(10, activation='softmax')(x)

Сейчас мы можем создать Model указав его входы и выходы в графе слоев:

model = keras.Model(inputs=inputs, outputs=outputs)

Посмотрим еще раз полный процесс определения модели:

inputs = keras.Input(shape=(784,), name='img') x = layers.Dense(64, activation='relu')(inputs) x = layers.Dense(64, activation='relu')(x) outputs = layers.Dense(10, activation='softmax')(x) model = keras.Model(inputs=inputs, outputs=outputs, name='mnist_model')

Давайте посмотрим как выглядит сводка модели:

model.summary()
Model: "mnist_model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= img (InputLayer) [(None, 784)] 0 _________________________________________________________________ dense_3 (Dense) (None, 64) 50240 _________________________________________________________________ dense_4 (Dense) (None, 64) 4160 _________________________________________________________________ dense_5 (Dense) (None, 10) 650 ================================================================= Total params: 55,050 Trainable params: 55,050 Non-trainable params: 0 _________________________________________________________________

Мы также можем начертить модель в виде графа:

keras.utils.plot_model(model, 'my_first_model.png')

image

И опционально выведем размерности входа и выхода каждого слоя на построенном графе:

keras.utils.plot_model(model, 'my_first_model_with_shape_info.png', show_shapes=True)

image

Это изображение и код который мы написали идентичны. В версии кода, связывающие стрелки просто заменены операциями вызова.

«Граф слоев» это очень интуитивный ментальный образ для модели глубокого обучения, а Functional API это способ создания моделей которые близко отражают этот ментальный образ.

Обучение, оценка и вывод

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

Рассмотрим быструю демонстрацию.

Тут мы загружаем датасет изображений MNIST, преобразуем его в векторы, обучаем модель на данных (мониторя при этом качество работы на проверочной выборке), и наконец мы оцениваем нашу модель на тестовых данных:

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data() x_train = x_train.reshape(60000, 784).astype('float32') / 255 x_test = x_test.reshape(10000, 784).astype('float32') / 255 model.compile(loss='sparse_categorical_crossentropy', optimizer=keras.optimizers.RMSprop(), metrics=['accuracy']) history = model.fit(x_train, y_train, batch_size=64, epochs=5, validation_split=0.2) test_scores = model.evaluate(x_test, y_test, verbose=2) print('Test loss:', test_scores[0]) print('Test accuracy:', test_scores[1]) 

Сохранение и сериализация

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

Стандартным способом сохранения Functional модели является вызов model.save( ) позволяющий сохранить всю модель в один файл.

Позже вы можете восстановить ту же модель из этого файла, даже если у вас больше нет доступа к коду создавшему модель.

Этот файл включает:

  • Архитектуру модели
  • Значения весов модели (которые были получены во время обучения)
  • Конфигурация обучения модели (то что вы передавали в compile )
  • Оптимизатор и его состояние, если оно было (это позволяет возобновить обучение с того места, где вы остановились)
 model.save('path_to_my_model.h5') del model # Recreate the exact same model purely from the file: model = keras.models.load_model('path_to_my_model.h5') 

Использование одного и того же графа слоев для определения нескольких моделей

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

В приведенном ниже примере мы используем один и тот же стек слоев для создания двух моделей:
модель кодировщика (encoder) которая преобразует входные изображения в 16-мерные вектора, и сквозную модель автокодировщика (autoencoder) для обучения.

encoder_input = keras.Input(shape=(28, 28, 1), name='img') x = layers.Conv2D(16, 3, activation='relu')(encoder_input) x = layers.Conv2D(32, 3, activation='relu')(x) x = layers.MaxPooling2D(3)(x) x = layers.Conv2D(32, 3, activation='relu')(x) x = layers.Conv2D(16, 3, activation='relu')(x) encoder_output = layers.GlobalMaxPooling2D()(x) encoder = keras.Model(encoder_input, encoder_output, name='encoder') encoder.summary() x = layers.Reshape((4, 4, 1))(encoder_output) x = layers.Conv2DTranspose(16, 3, activation='relu')(x) x = layers.Conv2DTranspose(32, 3, activation='relu')(x) x = layers.UpSampling2D(3)(x) x = layers.Conv2DTranspose(16, 3, activation='relu')(x) decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x) autoencoder = keras.Model(encoder_input, decoder_output, name='autoencoder') autoencoder.summary() 

Обратите внимание, что мы делаем архитектуру декодирования строго симметричной архитектуре кодирования, так что мы получим размерность выходных данных такую же как и входных данных (28, 28, 1) . Обратным к слою Conv2D является слой Conv2DTranspose , а обратным к слою MaxPooling2D будет слой UpSampling2D .

Модели можно вызывать как слои

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

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

encoder_input = keras.Input(shape=(28, 28, 1), name='original_img') x = layers.Conv2D(16, 3, activation='relu')(encoder_input) x = layers.Conv2D(32, 3, activation='relu')(x) x = layers.MaxPooling2D(3)(x) x = layers.Conv2D(32, 3, activation='relu')(x) x = layers.Conv2D(16, 3, activation='relu')(x) encoder_output = layers.GlobalMaxPooling2D()(x) encoder = keras.Model(encoder_input, encoder_output, name='encoder') encoder.summary() decoder_input = keras.Input(shape=(16,), name='encoded_img') x = layers.Reshape((4, 4, 1))(decoder_input) x = layers.Conv2DTranspose(16, 3, activation='relu')(x) x = layers.Conv2DTranspose(32, 3, activation='relu')(x) x = layers.UpSampling2D(3)(x) x = layers.Conv2DTranspose(16, 3, activation='relu')(x) decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x) decoder = keras.Model(decoder_input, decoder_output, name='decoder') decoder.summary() autoencoder_input = keras.Input(shape=(28, 28, 1), name='img') encoded_img = encoder(autoencoder_input) decoded_img = decoder(encoded_img) autoencoder = keras.Model(autoencoder_input, decoded_img, name='autoencoder') autoencoder.summary()

Как вы видите, модель может быть вложена: модель может содержать подмодель (поскольку модель можно рассматривать как слой).

Распространенным вариантом использования вложения моделей является ensembling.

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

def get_model(): inputs = keras.Input(shape=(128,)) outputs = layers.Dense(1, activation='sigmoid')(inputs) return keras.Model(inputs, outputs) model1 = get_model() model2 = get_model() model3 = get_model() inputs = keras.Input(shape=(128,)) y1 = model1(inputs) y2 = model2(inputs) y3 = model3(inputs) outputs = layers.average([y1, y2, y3]) ensemble_model = keras.Model(inputs=inputs, outputs=outputs)

Манипулирование сложными топологиями графов

Модели с несколькими входами и выходами

Functional API упрощает манипуляции с несколькими входами и выходами. Это не может быть сделано с Sequential API.

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

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

У вашей модели будет 3 входа:

  • Заголовок заявки (текстовые входные данные)
  • Текстовое содержание заявки (текстовые входные данные)
  • Любые теги добавленные пользователем (категорийные входные данные)
  • Оценка приоритета между 0 и 1 (скалярный сигмоидный выход)
  • Отдел который должен обработать заявку (softmax выход относительно множества отделов)
num_tags = 12 # Количество различных тегов проблем num_words = 10000 # Размер словаря полученный в результате предобработки текстовых данных num_departments = 4 # Количество отделов для предсказаний title_input = keras.Input(shape=(None,), name='title') # Последовательность целых чисел переменной длины body_input = keras.Input(shape=(None,), name='body') # Последовательность целых чисел переменной длины tags_input = keras.Input(shape=(num_tags,), name='tags') # Бинарный вектор размера `num_tags` # Вложим каждое слово заголовка в 64-мерный вектор title_features = layers.Embedding(num_words, 64)(title_input) # Вложим каждое слово текста в 64-мерный вектор body_features = layers.Embedding(num_words, 64)(body_input) # Сокращаем последовательность вложенных слов заголовка до одного 128-мерного вектора title_features = layers.LSTM(128)(title_features) # Сокращаем последовательность вложенных слов заголовка до одного 32-мерного вектора body_features = layers.LSTM(32)(body_features) # Объединим все признаки в один вектор с помощью конкатенации x = layers.concatenate([title_features, body_features, tags_input]) # Добавим логистическую регрессию для прогнозирования приоритета по признакам priority_pred = layers.Dense(1, activation='sigmoid', name='priority')(x) # Добавим классификатор отделов прогнозирующий на признаках department_pred = layers.Dense(num_departments, activation='softmax', name='department')(x) # Создание сквозной модели, предсказывающей приоритет и отдел model = keras.Model(inputs=[title_input, body_input, tags_input], outputs=[priority_pred, department_pred])

Давайте начертим граф модели:

keras.utils.plot_model(model, 'multi_input_and_output_model.png', show_shapes=True)

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

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

model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss=['binary_crossentropy', 'categorical_crossentropy'], loss_weights=[1., 0.2])

Так как мы дали имена нашим выходным слоям, мы можем также указать функции потерь:

model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss=, loss_weights=[1., 0.2])

Мы можем обучить модель передавая списки массивов Numpy входных данных и меток:

import numpy as np # Dummy input data title_data = np.random.randint(num_words, size=(1280, 10)) body_data = np.random.randint(num_words, size=(1280, 100)) tags_data = np.random.randint(2, size=(1280, num_tags)).astype('float32') # Dummy target data priority_targets = np.random.random(size=(1280, 1)) dept_targets = np.random.randint(2, size=(1280, num_departments)) model.fit(, , epochs=2, batch_size=32) 

При вызове fit с объектом Dataset , должны возвращаться либо кортеж списков, таких как ([title_data, body_data, tags_data], [priority_targets, dept_targets]) , либо кортеж словарей (, ) .

Учебная resnet модель

В дополнение к моделям с несколькими входами и выходами, Functional API упрощает манипулирование топологиями с нелинейной связностью, то есть моделями, в которых слои не связаны последовательно. Такие модели также не может быть реализованы с помощью Sequential API (это видно из названия).

Распространенный пример использования этого — residual connections.

Давайте построим учебную ResNet модель для CIFAR10 чтобы продемонстрировать это.

inputs = keras.Input(shape=(32, 32, 3), name='img') x = layers.Conv2D(32, 3, activation='relu')(inputs) x = layers.Conv2D(64, 3, activation='relu')(x) block_1_output = layers.MaxPooling2D(3)(x) x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_1_output) x = layers.Conv2D(64, 3, activation='relu', padding='same')(x) block_2_output = layers.add([x, block_1_output]) x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_2_output) x = layers.Conv2D(64, 3, activation='relu', padding='same')(x) block_3_output = layers.add([x, block_2_output]) x = layers.Conv2D(64, 3, activation='relu')(block_3_output) x = layers.GlobalAveragePooling2D()(x) x = layers.Dense(256, activation='relu')(x) x = layers.Dropout(0.5)(x) outputs = layers.Dense(10, activation='softmax')(x) model = keras.Model(inputs, outputs, name='toy_resnet') model.summary()

Давайте начертим граф модели:

keras.utils.plot_model(model, 'mini_resnet.png', show_shapes=True)

(x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data() x_train = x_train.astype('float32') / 255. x_test = x_test.astype('float32') / 255. y_train = keras.utils.to_categorical(y_train, 10) y_test = keras.utils.to_categorical(y_test, 10) model.compile(optimizer=keras.optimizers.RMSprop(1e-3), loss='categorical_crossentropy', metrics=['acc']) model.fit(x_train, y_train, batch_size=64, epochs=1, validation_split=0.2)

Совместное использование слоев

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

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

Чтобы совместно использовать слой в Functional API, просто вызовите тот же экземпляр слоя несколько раз. Например, здесь слой Embedding используется совместно на двух текстовых входах:

# Вложения для 1000 различных слов в 128-мерные вектора shared_embedding = layers.Embedding(1000, 128) # Целочисленные последовательности переменной длины text_input_a = keras.Input(shape=(None,), dtype='int32') # Целочисленные последовательности переменной длины text_input_b = keras.Input(shape=(None,), dtype='int32') # Мы переиспользуем тот же слой для кодирования на обоих входах encoded_input_a = shared_embedding(text_input_a) encoded_input_b = shared_embedding(text_input_b)

Извлечение и повторное использование узлов в графе слоев

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

Это также означает, что мы можем получить доступ к активациям промежуточных слоев («узлов» в графе) и использовать их в других местах. Это чрезвычайно полезно для извлечения признаков, например!

Давайте посмотрим пример. Это модель VGG19 с весами предобученными на ImageNet:

from tensorflow.keras.applications import VGG19 vgg19 = VGG19()

И это промежуточные активации модели, полученные путем запроса к структуре данных графа:

features_list = [layer.output for layer in vgg19.layers]

Мы можем использовать эти признаки для создания новой модели извлечения признаков, которая возвращает значения активаций промежуточного уровня — и мы можем сделать все это в 3 строчки

feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list) img = np.random.random((1, 224, 224, 3)).astype('float32') extracted_features = feat_extraction_model(img)

Это удобно при реализации neural style transfer, как и в других случаях.

Расширение API при помощи написания кастомных слоев

tf.keras обладает широким набором встроенных слоев. Вот несколько примеров:

Сверточные слои: Conv1D , Conv2D , Conv3D , Conv2DTranspose , и т.д.
Слои пулинга: MaxPooling1D , MaxPooling2D , MaxPooling3D , AveragePooling1D , и т.д.
Слои RNN: GRU , LSTM , ConvLSTM2D , и т.д.
BatchNormalization , Dropout , Embedding , и т.д.

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

Все слои сабклассируют класс Layer и реализуют:

Метод call , определяющий вычисления выполняемые слоем.
Метод build , создающий веса слоя (заметим что это всего лишь стилевое соглашение; вы можете также создать веса в __init__ ).

Вот простая реализация Dense слоя:

class CustomDense(layers.Layer): def __init__(self, units=32): super(CustomDense, self).__init__() self.units = units def build(self, input_shape): self.w = self.add_weight(shape=(input_shape[-1], self.units), initializer='random_normal', trainable=True) self.b = self.add_weight(shape=(self.units,), initializer='random_normal', trainable=True) def call(self, inputs): return tf.matmul(inputs, self.w) + self.b inputs = keras.Input((4,)) outputs = CustomDense(10)(inputs) model = keras.Model(inputs, outputs)

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

class CustomDense(layers.Layer): def __init__(self, units=32): super(CustomDense, self).__init__() self.units = units def build(self, input_shape): self.w = self.add_weight(shape=(input_shape[-1], self.units), initializer='random_normal', trainable=True) self.b = self.add_weight(shape=(self.units,), initializer='random_normal', trainable=True) def call(self, inputs): return tf.matmul(inputs, self.w) + self.b def get_config(self): return inputs = keras.Input((4,)) outputs = CustomDense(10)(inputs) model = keras.Model(inputs, outputs) config = model.get_config() new_model = keras.Model.from_config( config, custom_objects=)

Опционально, вы также можете реализовать метод класса from_config (cls, config) , который отвечает за пересоздание экземпляра слоя, учитывая его словарь конфигурации. Реализация по умолчанию from_config выглядит так:

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

Когда использовать Functional API

Как определить когда лучше использовать Functional API для создания новой модели, или просто сабклассировать Model напрямую?

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

Однако, сабклассирование Model дает вам большую гибкость при создании моделей, которые не описываются легко в виде направленного ациклического графа слоев (например, вы не сможете реализовать Tree-RNN с Functional API, вам нужно сабклассировать напрямую Model ).

Cильные стороны Functional API:

Свойства перечисленные ниже являются все верными и для Sequential моделей (которые также являются структурами данных), но они верны для сабклассированных моделей (которые представляют собой код Python, а не структуры данных).

С Functional API получается более короткий код.

Нет super(MyClass, self).__init__(. ) , нет def call(self, . ): , и т.д.

inputs = keras.Input(shape=(32,)) x = layers.Dense(64, activation='relu')(inputs) outputs = layers.Dense(10)(x) mlp = keras.Model(inputs, outputs)

С сабклассированной версией:

class MLP(keras.Model): def __init__(self, **kwargs): super(MLP, self).__init__(**kwargs) self.dense_1 = layers.Dense(64, activation='relu') self.dense_2 = layers.Dense(10) def call(self, inputs): x = self.dense_1(inputs) return self.dense_2(x) # Создадим экземпляр модели. mlp = MLP() # Необходимо создать состояние модели. # У модели нет состояния пока она не была вызвана хотя бы раз. _ = mlp(tf.zeros((1, 32))) 
Ваша модель валидируется по ходу ее написания

В Functional API входные спецификации (shape и dtype) создаются заранее (через `Input`), и каждый раз, когда вы вызываете слой, слой проверяет, что спецификации переданные ему соответствует его предположениям, если это не так то вы получите полезное сообщение об ошибке.

Это гарантирует, что любая модель которую вы построите с Functional API запустится. Вся отладка (не относящаяся к отладке сходимости) будет происходить статично во время конструирования модели, а не во время выполнения. Это аналогично проверке типа в компиляторе.

Вашу Functional модель можно представить графически, а также она проверяема.

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

features_list = [layer.output for layer in vgg19.layers] feat_extraction_model = keras.Model(inputs=vgg19.input, outputs=features_list)

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

Слабые стороны Functional API

Он не поддерживает динамичные архитектуры.

Functional API обрабатывает модели как DAG слоев. Это справедливо для большинства архитектур глубокого обучения, но не для всех: например, рекурсивные сети или Tree RNN не соответствуют этому предположению и не могут быть реализованы в Functional API.

Иногда вам просто нужно написать все с нуля.

При написании продвинутых архитектур вы можете захотеть сделать то, что выходит за рамки «определения DAG слоев»: например, вы можете использовать несколько пользовательских методов обучения и вывода на экземпляре вашей модели. Это требует сабклассирования.

Сочетание и комбинирование различных стилей API

Важно отметить, что выбор между Functional API или сабклассированием Model не является бинарным решением, которое ограничивает вас одной категорией моделей. Все модели в API tf.keras могут взаимодействовать друг с другом, будь то Sequential модели, Functional модели или сабклассированные Models/Layers, написанные с нуля.

Вы всегда можете использовать Functional модель или Sequential модель как часть сабклассированного Model/Layer:

units = 32 timesteps = 10 input_dim = 5 # Define a Functional model inputs = keras.Input((None, units)) x = layers.GlobalAveragePooling1D()(inputs) outputs = layers.Dense(1, activation='sigmoid')(x) model = keras.Model(inputs, outputs) class CustomRNN(layers.Layer): def __init__(self): super(CustomRNN, self).__init__() self.units = units self.projection_1 = layers.Dense(units=units, activation='tanh') self.projection_2 = layers.Dense(units=units, activation='tanh') # Our previously-defined Functional model self.classifier = model def call(self, inputs): outputs = [] state = tf.zeros(shape=(inputs.shape[0], self.units)) for t in range(inputs.shape[1]): x = inputs[:, t, :] h = self.projection_1(x) y = h + self.projection_2(state) state = y outputs.append(y) features = tf.stack(outputs, axis=1) print(features.shape) return self.classifier(features) rnn_model = CustomRNN() _ = rnn_model(tf.zeros((1, timesteps, input_dim))) 

Обратно, вы можете использовать любой сабклассированный Layer или Model в Functional API в том случае если реализован метод call который соответствует одному из следующих паттернов:

call(self, inputs, **kwargs) где inputs это тензор или вложенная струтура тензоров (напр. список тензоров), и где **kwargs это нетензорные аргументы (не входные данные).
call(self, inputs, training=None, **kwargs) где training это булево значение показывающее в каком режиме должен вести себя слой, обучения или вывода.
call(self, inputs, mask=None, **kwargs) где mask это тензор булевой маски (полезно для RNN, например).
call(self, inputs, training=None, mask=None, **kwargs) — конечно вы можете иметь одновременно оба параметра определяющих поведение слоя.

В дополнение, если вы реализуете метод `get_config` на вашем пользовательском Layer или Model, Functional модели которые вы создадите с ним будут сериализуемы и клонируемы.

Далее приведем небольшой пример где мы используем кастомный RNN написанный с нуля Functional модели:

units = 32 timesteps = 10 input_dim = 5 batch_size = 16 class CustomRNN(layers.Layer): def __init__(self): super(CustomRNN, self).__init__() self.units = units self.projection_1 = layers.Dense(units=units, activation='tanh') self.projection_2 = layers.Dense(units=units, activation='tanh') self.classifier = layers.Dense(1, activation='sigmoid') def call(self, inputs): outputs = [] state = tf.zeros(shape=(inputs.shape[0], self.units)) for t in range(inputs.shape[1]): x = inputs[:, t, :] h = self.projection_1(x) y = h + self.projection_2(state) state = y outputs.append(y) features = tf.stack(outputs, axis=1) return self.classifier(features) # Заметьте что мы задаем статичный размер пакета для входных данных # аргументом `batch_shape`, потому что внутренние вычисления `CustomRNN` требуют # фиксированного размера пакета (когда мы создает нулевые тензоры `state`). inputs = keras.Input(batch_shape=(batch_size, timesteps, input_dim)) x = layers.Conv1D(32, 3)(inputs) outputs = CustomRNN()(x) model = keras.Model(inputs, outputs) rnn_model = CustomRNN() _ = rnn_model(tf.zeros((1, 10, 5))) 

Это завершает наше руководство по Functional API!

Теперь у вас под рукой мощный набор инструментов для построения моделей глубокого обучения.

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

  • tensorflow
  • functional api
  • keras
  • машинное обучение
  • нейронные сети
  • Python
  • Big Data
  • Машинное обучение
  • Искусственный интеллект
  • TensorFlow

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *