#python #tensorflow #machine-learning #keras #tensorflow2.0
#python #тензорный поток #машинное обучение #keras #tensorflow2.0
Вопрос:
У меня есть модель, написанная с использованием API подклассов модели tensorflow 2
. Он содержит пользовательский слой. Проблема в том, что на пользовательском уровне мне нужно отправить номер канала входного тензора на Conv2D
уровень во время выполнения. Пожалуйста, посмотрите код ниже:
Пользовательский слой
import tensorflow as tf
class AuxNet(tf.keras.layers.Layer):
def __init__(self, ratio=8):
super(AuxNet, self).__init__()
self.ratio = ratio
self.avg = tf.keras.layers.GlobalAveragePooling2D()
self.max = tf.keras.layers.GlobalMaxPooling2D()
def call(self, inputs):
avg = self.avg(inputs)
max = self.max(inputs)
avg = tf.keras.layers.Reshape((1, 1, avg.shape[1]))(avg)
max = tf.keras.layers.Reshape((1, 1, max.shape[1]))(max)
# ALERT ---------------------
input_shape = inputs.get_shape().as_list()
_, h, w, channels = input_shape
conv1a = tf.keras.layers.Conv2D(channels, kernel_size=1,
strides=1, padding='same',use_bias=True,
activation=tf.nn.relu)(avg)
conv1b = tf.keras.layers.Conv2D(channels, kernel_size=1, strides=1,
padding='same',use_bias=True,
activation=tf.nn.relu)(max)
return tf.nn.sigmoid(conv1a conv1b)
Вся модель
class Net(tf.keras.Model):
def __init__(self, dim):
super(Net, self).__init__()
self.base = tf.keras.layers.Conv2D(124, 3, 1)
self.gap = tf.keras.layers.GlobalAveragePooling2D()
self.aux = AuxNet() # init the custom layer
self.dense = tf.keras.layers.Dense(128, activation=tf.nn.relu)
self.out = tf.keras.layers.Dense(10, activation='softmax')
def call(self, input_tensor, training=False):
x = self.base(input_tensor)
# Using custom layer on the input tensor
aux = self.aux(x)*x
x = self.gap(aux)
x = self.dense(x)
return self.out(x)
Как вы можете видеть, AuxNet
классы содержат Conv2D
слой с размером фильтра channel
его входных данных. И входные данные — это не что иное, как входные данные класса модели, the Net
. При инициализации пользовательского слоя в классе модели я не смог установить номер канала его Conv2D
слоя. Итак, то, что я сделал здесь, я вычисляю номер канала для этого Conv2D
в call
методе AuxNet
слоя, что, я считаю, является плохой практикой.
Эта проблема приводит к проблеме времени выполнения. Я не смог скомпилировать Model
класс в графическом режиме, но принудительно включить режим ожидания.
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Dropout
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()
# train set / data
x_train = x_train.astype('float32') / 255
# train set / target
y_train = tf.keras.utils.to_categorical(y_train, num_classes=10)
model = Net((32, 32, 3))
tf.config.run_functions_eagerly(True) # < ----------------
model.compile(
loss = tf.keras.losses.CategoricalCrossentropy(),
metrics = tf.keras.metrics.CategoricalAccuracy(),
optimizer = tf.keras.optimizers.Adam())
# fit
model.fit(x_train, y_train, batch_size=128, epochs=1)
Это работает, но очень медленное обучение. Однако без этого возникает следующая ошибка,
Ошибка значения: функция, оформленная tf.function, пыталась создать переменные при не первом вызове.
Какой-либо обходной путь для отсутствия необходимости включать нетерпеливый режим? Как я могу эффективно передать требуемый аргумент этому пользовательскому слою? В этом случае мне не нужно вычислять глубину канала в call
методе.
Комментарии:
1. Не уверен, что вы пытаетесь здесь сделать. В нынешнем виде ваша модель создает новые слои свертки при каждом ее вызове (т. Е. При каждой итерации обучения), что не имеет смысла и делает невозможным обучение (поскольку переменные сбрасываются каждый раз). Я думаю, вы, возможно, захотите прочитать о том, как создавать пользовательские слои, например, здесь или здесь . В частности, посмотрите, как переменные создаются в
build
методе.2. Это игрушечная демонстрация основной задачи. Тем не менее, спасибо за указатель. Я внимательно прочитаю документ.
![]()
3. @xdurch0 Это решает. Спасибо. : xD
Ответ №1:
Мне нужно было посмотреть, как определить встроенные слои внутри пользовательского слоя. Рекомендуется, чтобы все слои инициализировали __init__
метод. Но нам нужна channel
была глубина неизвестного тензора, и на основе этого значения filters
будет установлено число. Однако в build
методе мы можем сделать это легко.
class AuxNet(tf.keras.layers.Layer):
def __init__(self, ratio=8):
super(AuxNet, self).__init__()
self.ratio = ratio
self.avg = tf.keras.layers.GlobalAveragePooling2D()
self.max = tf.keras.layers.GlobalMaxPooling2D()
def build(self, input_shape):
self.conv1 = tf.keras.layers.Conv2D(input_shape[-1],
kernel_size=1, strides=1, padding='same',
use_bias=True, activation=tf.nn.relu)
self.conv2 = tf.keras.layers.Conv2D(input_shape[-1],
kernel_size=1, strides=1, padding='same',
use_bias=True, activation=tf.nn.relu)
super(AuxNet, self).build(input_shape)
def call(self, inputs):
avg = self.avg(inputs)
max = self.max(inputs)
avg = tf.keras.layers.Reshape((1, 1, avg.shape[1]))(avg)
max = tf.keras.layers.Reshape((1, 1, max.shape[1]))(max)
conv1a = self.conv1(avg)
conv1b = self.conv2(max)
return tf.nn.sigmoid(conv1a conv1b)