0

Having some experience in developing neural networks in Keras, I decided to write a non-standard GAN, which you can't really call as such. The point is that the discriminator is a ready-made neural network that perfectly predicts the quality of a face image, and I wanted my network to increase the resolution of 48 by 48 images by 4 times (made them 96 by 96). My regular GAN with a trainable discriminator simply interpolated the image to a larger space with a loss of quality, as if you simply enlarged the image on a computer. It is not so important for me that the image is a complete copy, so the idea with a ready-made discriminator seemed good to me.

Here is the code of train step function:

@tf.function
def train_step(images_x, images_y):

  with tf.GradientTape() as gen_tape:
    generated_images = generator(images_x, training=True).numpy()

    #Integrating ready-made network, which returns numpy array with shape (batch_size, 1) and contains scalars representing the number in range of 0-1
    fake_output = K.constant(face_detector.estimate(generated_images)) #K = tensorflow backend

    gen_loss = generator_loss(fake_output)

  gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)

  generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))

  return gen_loss

common train function:

def train(dataset_x, dataset_y, epochs):
  history = []
  MAX_PRINT_LABEL = 10
  th = BUFFER_SIZE // (BATCH_SIZE * MAX_PRINT_LABEL)

  for epoch in range(1, epochs + 1):
    print(f'{epoch}/{EPOCHS}: ', end='')

    start = time.time()
    n = 0

    gen_loss_epoch = 0
    l = len(dataset_x)
    for o in range(l):
      tf.config.run_functions_eagerly(True) #I'm suspecting the issue is related to this, but i can't do it without running eagerly (train_step doesn't work)
      gen_loss = train_step(dataset_x[o], dataset_y[o]) 
      tf.config.run_functions_eagerly(False)
      print(gen_loss, disc_loss)
      gen_loss_epoch += K.mean(gen_loss)
      if (n % th == 0): print('=', end='')
      n += 1
    generator.save("generator.h5")


    history += [gen_loss_epoch / n]
    

  return history

generator structure and other

cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)

hidden_dim = 2


def dropout_and_batch():
  return Dropout(0.3)(BatchNormalization())

img_height = 96
img_width = 96
img_channels = 3  
latent_dim = 2  
input_img = Input(shape=(48, 48, 3))
x = Conv2D(32, 3, padding='same', activation='relu')(input_img)
x = Conv2D(32, 3, padding='same', activation='relu')(x)
x = Conv2D(32, 3, padding='same', activation='relu')(x)

x = Flatten()(input_img)
z_mean = Dense(latent_dim)(x)
z_log_var = Dense(latent_dim)(x)
@keras.saving.register_keras_serializable()
def sampling(args):
    z_mean, z_log_var = args
    epsilon = tf.random.normal([batch_size, latent_dim], 0, 1, tf.float32)
    return z_mean + tf.exp(0.5 * z_log_var) * epsilon 
z = Lambda(sampling, output_shape=(latent_dim,))([z_mean, 
 z_log_var])


decoder_input = Input(shape=(latent_dim,))
x = Dense(img_width*img_height*3, activation='relu')(decoder_input)
x = Reshape((img_width, img_height, 3))(x)
x = Conv2D(32, 3, padding='same', activation='relu')(x)
x = Conv2D(32, 3, padding='same', activation='relu')(x)
decoded = Conv2D(3, 3, padding='same', activation='sigmoid')(x)

encoder = Model(input_img, [z_mean, z_log_var, z])
decoder = Model(decoder_input, decoded)

generator = Model(input_img, decoder(encoder(input_img)[2]))

custom loss function:

def generator_loss(fake_output):
  loss = cross_entropy(tf.ones_like(fake_output), fake_output)
  return loss

loading dataset:

for path in glob("faces/*.jpg"):
  img = Image.open(path)
  y_train.append(np.asarray(img.resize((96, 96))))
  if y_train[-1].shape != (96, 96, 3):
    del y_train[-1]
    continue
  x_train.append(np.asarray(img.resize((48, 48))))
x_train = np.asarray(x_train)/255
y_train = np.asarray(y_train)/255

BUFFER_SIZE = x_train.shape[0]
BATCH_SIZE = 100

BUFFER_SIZE = BUFFER_SIZE // BATCH_SIZE * BATCH_SIZE
x_train = x_train[:BUFFER_SIZE]
y_train = y_train[:BUFFER_SIZE]
print(x_train.shape, y_train.shape)


train_dataset_x = list(tf.data.Dataset.from_tensor_slices(x_train).shuffle(BUFFER_SIZE).batch(BATCH_SIZE))
train_dataset_y = list(tf.data.Dataset.from_tensor_slices(y_train).shuffle(BUFFER_SIZE).batch(BATCH_SIZE))

I have had this error that I still can't solve on my own and with Gemini for a long time now: ValueError: No gradients provided for any variable (it points to a line generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables)))

5
  • Your code nowhere defines face_detector. If this is not a TF model, this will not work. Commented Nov 17, 2024 at 18:16
  • yes, it isn't a TF model, but it would be strange if there's no way to integrate non-TF models to TF-model training Commented Nov 19, 2024 at 8:25
  • Well what is it? You did not specify this anywhere. You need to backpropagate through the model for training to compute the gradients, TF doesn't know how to compute the gradient for some non-TF model. The only way would be to define a tf.custom_gradient for the face detector. Or implement it in Tensorflow/Keras. Commented Nov 19, 2024 at 10:23
  • i don't want to train face detector, i just want TF to ignore it and let it do its work Commented Nov 19, 2024 at 10:27
  • But your GAN loss is based on your face detector... so to compute the gradients of the loss with respect to the GAN variables, you need to backpropagate through the face detector. Which TF does not know how to do if it is not defined in terms of TF operations. If you want it to "ignore" the face detector, the solution is to not use it like this. Commented Nov 19, 2024 at 18:03

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.