1
$\begingroup$

Lets assume that we have a model model_A and we want to build up a backpropagation based on 3 different loss functions. The first loss (Loss_1) should be based on the output of model_A, Loss_2 and Loss_3 can come from something else. Think about it like a deviation from an unknown source, like in process-automation if you want to build up ur PID-controller. The easiest way is my approach down there, but it actually fails, because the graph isnt constructed the way i want, because X_realB and X_realC have no connection to model_A, and are ignored by keras.

Any ideas how i could use additional loss functions, without passing the values through model_A, but stil influencing the minimization problem?

def generator_model(model_A):

  model_A.trainable = True

# import
  X_realA = Input(shape=image_shape)
  X_realB = Input(shape=image_shape)
  X_realC = Input(shape=image_shape)

# generate Fake image
  Fake_A=model_A(X_realA)


  model = Model([X_realA],[Fake_A,X_realB ,X_realC])

  opt = Adam(lr=0.0002, beta_1=0.5)
  model.compile(loss=["mse","mse","mse"],loss_weights=[1,1,1], optimizer=opt)
  model.summary()
  return model

And as a second question: Is there a way, to use not differeniable elements in custom keras layers, like tf.unique (counting elements in tensors), between two models like:

# import
  X_realA = Input(shape=image_shape)

# generate Fake image
  Fake_A=model_A(X_realA)

  # counting the elements and reshape the tensor
  _,_,counts = keras.layers.Lambda(lambda x: tf.unique_with_counts(x))(Fake_A)
  new_Fake_A= keras.layers.Lambda(lambda x: tf.reshape(x,(something,something)))(counts)

  Fake_B=model_B(new_Fake_A)  

  model = Model([X_realA],[Fake_A,Fake_B])

But with this approach, the model is not working properly and isnt updating the weigths of model_A. I thougth maybe because tf.unique_count produces new tensors, which have no connection to the old ones, and there also are no gradients, but for that is the lambda.layer anyway. Any ideas how to tackle that problem?

$\endgroup$

2 Answers 2

1
$\begingroup$

Try constructing your model like so:

model = Model([X_realA, X_realB, X_realC], [Fake_A, X_realB , X_realC])

I have a hunch your code should work this way. However if you want to update modelA using some calculated loss from X_realB and X_realC that is not going to work. You see when you define the losses ["mse", "mse", "mse"] that means three different losses are calculated and then the nodes that contribute to that loss (/output) are updated by backpropagating. Your modelA network does not contribute to the losses calculated from X_realB, X_realC.

If you want to update modelA, I would recommend implementing a custom loss function, where additional losses are added to the loss calculated from your Fake_A output. If I understand you correctly, you have a model output, and some additional information about the environment the input measurement was taken in, and you want to use this additional information when calculating the loss from Fake_A. This is essentially additional information about the expected output, so I would put X_realB and X_realC into the annotation and handle it in the custom loss.

If you can provide more information about your use case maybe I can be of more help.


Edit 1:

In combined_loss you are adding constants to the loss calculated from Fake_A, so when taking the derivatives wrt. model parameters they zero out. This comes from the linearity of differentiation, where differentiating a summation is differentiating by parts. To put it simply in your case:

deriv_wrt_params(loss+12+34) = deriv_wrt_params(loss) + deriv_wrt_params(12) + deriv_wrt_params(34) = deriv_wrt_params(loss) + 0 + 0

Also because you are using MSE, your generator will learn to output only ones, since you are punishing values deviating from one:

loss0=keras.losses.mse(FakeA,FakeA_ones)

I recommend using binary crossentropy.
If these added values are not related to the traditional identity loss, generator loss and consistency loss, but come from a prior knowledge, you should use eg. multiplication or something like that so they affect the gradients as well, not just the loss.

If you want to implement CycleGAN with identity loss, consistency loss etc. you will have to implement a custom train loop to update the generators and discriminators separately. For this I recommend the official Tensorflow 2.1 CycleGAN tutorial, where they implement a CycleGAN from start to finish.

$\endgroup$
10
  • $\begingroup$ Answer section is intended for answers only & not comments. If you want to make a comment wait until you get the required reputation. $\endgroup$ Apr 21, 2020 at 17:39
  • $\begingroup$ @Andrea Ilona, thank you very much for your insigth, i dont see any other way for us to communicate if your and mine reputation is that low. $\endgroup$ Apr 21, 2020 at 17:45
  • $\begingroup$ I answered on your post $\endgroup$ Apr 21, 2020 at 17:45
  • $\begingroup$ Please clarify what you are trying to do in combined_loss by posting relevant code as an edit in your question. @Michael R. Chernick I will update my answer to be an actual answer if I get the clarifications needed from OP, or delete my post if I cannot. I apologize for this inconvenience. $\endgroup$
    – Pivot
    Apr 21, 2020 at 19:04
  • $\begingroup$ Updated and added some code $\endgroup$ Apr 21, 2020 at 20:10
0
$\begingroup$

Yes thats hardly it. I tried that approch model=Model([A,B,C],[FakeA,B,C]), as you said, it also works, but like you also mentioned, model_A isnt going to be updated. I tried something else in the past 2 days. Wrapping [FakeA,B,C] in a custom lambda-layer, to calculate combined loss (one value output of that custom layer). Than passing this loss, in a dummy custom loss-function, which just outputs the combined value of the lambda layer. Here is an example:

# import A,B,C and than pass A into Generator .... and after that:

combined_loss= Lambda(lambda x: combined_loss_func(x))([FakeA,B,C])

model=Model([A,B,C],[combined_loss],loss=dummy_loss)

def dummy_loss(y_pred,y_true):
  return y_pred

combined_loss could look like that:

def combined_loss_func(x):

  FakeA,B,C=x[0],x[1],x[2]

  # transform all inputs into one row-tensors
  shape=tf.shape(FakeA)
  FakeA=tf.reshape(FakeA,[1,shape[0]*shape[1]*shape[2]*shape[3]])   
  shape=tf.shape(B)
  B=tf.reshape(B,[1,shape[0]*shape[1]*shape[2]*shape[3]]) 
  shape=tf.shape(C)
  C=tf.reshape(C,[1,shape[0]*shape[1]*shape[2]*shape[3]]) 

  # build up a hypothetical ground truth
  FakeA_ones=tf.ones_like(FakeA)
  A_ones=tf.ones_like(A)
  B_ones=tf.ones_like(B)

  # calculate losses
  loss0=keras.losses.mse(FakeA,FakeA_ones)
  loss1=keras.losses.mse(A,A_ones)
  loss2=keras.losses.mse(B,B_ones)

  # sum them up
  summe=tf.math.add(loss0,loss1)
  summe=tf.math.add(summe,loss2)

  # average them
  avg=tf.math.truediv(summe,3.0)
  avg=tf.expand_dims(summe,axis=-1)

  return avg

If i now try, to set the FakeA loss to zero, now no backpropagation to modelA happens anymore, or at least nothing in the system changes anymore:

   # calculate losses
  loss0=keras.losses.mse(FakeA,FakeA_ones) * 0
  loss1=keras.losses.mse(A,A_ones)
  loss2=keras.losses.mse(B,B_ones)

First it seemes really good, but when i go now into the custom-function, and not use FakeA, which is the one and only tensor which passed through the generator. Than i stil get a value for my loss function, which seems to be rigth, but actually nothing is happening, my cycle Gan isnt improving at all, and all images passed though stil look the same, even after 100 epochs. Any ideas ?

Thank you for your reply @Andrea Ilona

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.