Ошибка значения: функция, оформленная tf.function, пыталась создать переменные при не первом вызове

#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)