Hi there and welcome to this session in which we shall practically train our GAN to produce images like
this one here.
We'll start with the imports and then we'll move on to prepare our data.
The dataset we shall be using will be the celeb, a dataset which signifies celeb faces.
Attributes.
Dataset.
Now, this is over 200,000 images of celebrities with 40 binary attribute annotations.
Let's open up some of this here.
You could have some of these images open this file up.
And there we go.
You see we have this faces right here.
And so what we'll be doing will be to train our discriminator alongside with our generator, such that
our generator can generate images of faces which can be able or which can be realistic enough to be
able to fool the discriminator to think that they are actually real faces.
This notebook is provided by Jessica Lee on Kaggle and can be downloaded, so let's go straight away
to download this dataset and then start with our GAN modelling.
In order to download a dataset from Kaggle, we'll be needing this Kaggle dot JSON file right here.
Now this Kaggle dot JSON file can be gotten from Kaggle by getting into your account and then creating
a new token.
So once you have that, you will get right here and then click on copy command, which when you paste
our here you see you have Kaggle dataset download and you have the username of the person who uploaded
this dataset to Kaggle platform.
And then you have the dataset name right here.
But before carrying out this dataset download, we'll start by installing Kaggle.
We'll make this directory, we'll copy this Kaggle dot JSON into this directory.
Then we can now go ahead and download the dataset from this command API command which we downloaded
or which we copied rather, and then we can unzip this into some dataset folder or directory which we
specify.
So that said, let's simply run this cell and everything should move on.
Well, let's take this off.
As you can see, the dataset has been downloaded and now we're extracting the files into this dataset
folder right here.
Now that we have this successfully extracted into our dataset folder, as we could see, we specify
the batch size, the image shape and the learning rate.
Now from here, let's run the cell and then we move on to create our TensorFlow data dataset.
So here we have let's call this dataset and then we specify this path.
Now to get this path, you could click open right here and then you see if you click open, this is
going to take a while since we have 200,000 of different images here.
So let's just let that and then we copy this path is a path that you specify in your Now once you specify
that you have oops, you have a labeling mode which is known.
Um, we have the image size which was specified already.
We have the batch size.
Um, anyway, let's let, we could have here batch size, but it doesn't matter as we'll see shortly.
Anyway, we have that.
And then from here we run the cell, you see, you could have dataset, we get an error TensorFlow not
defined.
Let's run this.
Oops.
Um, that's fine.
Uh, next.
This.
We've run this already, and then we run this.
Now this should be fine.
Let's check out our dataset to use that.
So as you can see, we have 202,599 files belonging to one class and our dataset has been batch.
So we have a batch dataset.
You can see the shape right here.
We have 64 by 64 by three images.
Now, uh, the default is um, 256 by 256.
So if we, if we do not specify this, let's see what we get.
Let's take this off.
Um, take that off and run this again and check out on the image size you see here.
When we don't specify anything, you have 256 by 256.
Okay, let's get back and run this again.
Then we move on to preprocess our data.
So right here, what we're going to do is we're going to make sure this data lies between -1 and 1.
And so that's why we're having here the image divided by 127.5 minus one.
And so this means that any value we get between 0 and 255, let's say, for example, we have the value
255, we'll take 255 divided by 127.5, which is two then minus one, which gives us one.
So that's how we we pre-process this images.
And then after pre-processing, we're going to.
But because we need to reshuffle or rather because we need to drop the remainder.
So we on batch and then we use the batching of our TensorFlow data.
I then from here we carry out some prefetching for a more efficient way of loading the data.
Now from here you can visualize a single element in our dataset.
Here we go.
We have 4G in our train dataset.
Let's take a single element.
We could print out its shape, the shape.
And there we go.
Now we have the shape, which is 128 by 64, by 64 by three as expected.
And we could go ahead and visualize some elements here.
So let's visualize four elements.
Um, we could increase this definitely.
So we should visualize four elements of this for now.
Now here we have the subplot, um, plot in show and we could take off the axis.
So let's run that and then see what we get.
So here we have this, um, four different images.
Let's reduce this a little.
Now, uh, one thing we could do, too, is modify this here, this, um, value of our array.
Now, the reason why we want to modify this is because this value ranges between -1 and 1.
Whereas this bloodhound image here takes in values of range 0 to 1.
So we're going to modify this such that we move from negative one one to the range zero one.
And to do that, we need to take whatever value we have in this range, add one to it, and then divide
by two.
So let's take this off and get back here.
We just have plus one, then divided by two.
There we go.
Let's run that again.
And there we go.
You see, you have now the images are much clearer and you do not get the messages which we were getting
previously.
Now we'll go ahead with the modeling and we're going to use this same architecture presented in the
Dcgan paper.
So right here we have this 100 dimensional latent vector, and then this is projected and reshaped into
this 4x4 by 1024 tensor.
And then from here we apply the Upsampling that's actually the Conv 2D transpose to then get this other
vector right here.
Notice how we're getting from four by 4 to 8 by eight and then from here again repeat the same process,
16 by 16, 32 by 32, and then finally 64 by 64.
Also notice that while the size of the outputs keep increasing from 8 to 6, from 8 to 16, 32, 64,
the depth is reduced.
And so we go from 1024 to 512 to 256 to 128.
And finally we have three.
So we get back to the code and we specify our latent dimension, which is equal 100.
Let's rerun this cell right here.
There we go.
And then that should be fine.
And then we go ahead and build our generator.
So here we have our generator, which will build with a sequential model.
We have tf.keras sequential, and then we start to pass in our different layers.
So here we have our input layer input, which has a shape of the latent time or latent dimension.
So here we have latent dimension, there we go.
And that's fine.
So that's our first layer.
And then the next is our dense layer.
So this is our projection.
So we project this such that the output is having four times, four times the latent dream.
Number of art units.
So just as we had seen in the paper.
Now, once we have this, we move to the next layer, which is going to be the reshape layer.
So we go ahead and reshape such that because at this point we having letting them is a hundred.
So we having 16 times 100 as 16,100 outputs.
Now we reshape this such that it is a three dimensional tensor.
So here we have 4x4 by latent.
Latent dream.
And that is it.
There we go.
So this is our next layer.
Reshape from the reshape.
We go ahead to do the Conv 2D or the UPSAMPLING with the Conv 2d transpose or as in the paper, we have
come to the transpose and then we have 512 number of filters represent the number of output channels,
then the kernel size kernel size equal four.
Now if we get back to the paper here, let's get back to the paper.
You will see that the kernel size isn't necessarily exactly equal four, but one very important rule
to follow when picking out the kernel size is that the kernel size has to be divisible by the number
of strides.
So when we pick kernel size equal four, we could have the strides to be equal to.
And the reason why we generally want that the kernel size is divisible by the number of strides is simply
because of the.
Or quality of outputs will get generated by the generator when this isn't the case.
So, um, always ensure that we have the kernel size divisible by number of strides.
Now from here on we go on to apply batch normalization as suggested in the paper and also in the tips
and tricks.
Um, GitHub repo.
So here we have batch norm and then from uh, after the batch norm, we have our leaky Relu.
Now for the leaky relu we have, it takes in value of 0.2.
So here we have 0.2 and that's it for this first part.
So we have this first block here which you could see in the paper this very first conv layer.
Now, uh, once we have this here, it could be repeated again.
So we just, um, copy this and then paste it out.
But modifying this depth.
So here we have 256 and then again.
Oops.
Here, get back.
Um, and then here we paste this out.
And then here finally we have 128.
Okay, so we have that.
Uh, for now, we're not going to apply any dropout.
You could always feel free to apply that and see, um, the kind of results you will get.
So here we have this and then now we have that.
We already set that.
We have the let's get to the paper.
We have the first, the second and the third conv layer.
Now this final conv layer is to get an output which is like an image.
So we have 64 by 64 by three.
And so let's get back here.
Um, uh, we will have no leaky relu or what whatever.
Like that.
We'll just copy this out and this is out here.
Okay, so we have that, and now we just come to the transpose.
We have an activation, which is a.
So after the strides here, we specify the activation activation.
And this activation is, uh, um, tensor activation.
So we just have change and that's it.
Padding, um, equals same.
We also copy paste this out here.
So we have here we have padding same.
And right here we have padding same.
Okay, so that's it.
Uh, that should be it for the generator.
I guess with respect to what we had in the tips and tricks.
And also, um, right here, you can see we were told use relu activation and generator for all layers
except for the output which uses the tank.
We'll call this model the generator.
So we have our generator model, and then let's run that.
And the next thing we want to do is summarize this.
So let's get the summary.
We check this out here and you see we're getting this here instead of three.
So let's go ahead and modify this right here.
Uh, this should be three.
Let's run that again and get the summary.
Okay, so this is what we have then.
Now we can move ahead to our discriminator.
So instead of generator right here, we have discriminator.
And the input we're going to have here is going to be 64 by 64 by three.
So it's actually in shape.
So M shape.
Um, zero index.
M shape.
There we go by three and then instead of the conv 2d transpose layers we'll be using the conv 2d layer.
So here we have conv 2d and then the depth increases instead here instead of decreasing as we had with
the generator.
So here we go from 64.
So we start with 64 and then we move on to 128 and so on and so forth.
For now, let's take this off since we did with the Conv 2D and then again we have the kernel size which
is divisible by the number of strides we have.
The leaky relu as we've seen already in the tips and tricks.
Um, let's take this off here, but we'll make use of the batch norm still.
So let's, let's paste this out and then we have batch batch normalization.
We have the batch normalization.
There we go.
Um, here is come 2D, but we increase the depth.
So we have 128.
Now that we have this depth increase, we just simply copy this out and paste it for the next layers.
So for the next blocks, because we consider this to be a block and this is a block and this a block,
Um, now we move on to 256, um, batch norm Leaky Relu still and that's it.
Now for the final or for the last conv layer, let's just paste this out here.
Let's take this off.
Uh, we have this last conv layer right here.
We'll give it a depth of one.
And then given that our discriminator call, that our discriminator is a usual classifier which takes
in the 64 by 64 by three input and then outputs a single value, whether a one or a zero or a value
between 0 and 1 actually.
So it outputs a single value here.
So at this point, we should be thinking of using some dense layer and then specifying that this output
is going to have only one unit.
Okay.
So let's take this off.
And then now from here we could, um, flatten.
So we flatten, um, what we get is output from the conv 2d layer.
And then after flattening, we could, uh, have our dense layer one.
See, here we have just one output and then all the activation activation is sigmoid.
So that's it.
We call with a sigmoid.
With a sigmoid we have values or inputs from negative infinity to positive infinity, which have been
mapped in the range zero 0 to 1 actually.
And that's exactly what we need right here.
So we have that.
Um, that's fine.
Let's take this off now.
Now, before we move on, it should be noted that unlike the sigmoid, which maps values between 0 and
1, the tan function maps values between -1 and 0.
So this is from 0 to 1.
And then the tan maps values between -1 and 1.
And that's what we use in the final layer for the for the discriminator for the generator and now for
the discriminator, we're using the sigmoid.
Okay.
So we have that.
Understood.
Now we have here our discriminator.
Okay.
So we have that.
Let's run this cell and then finally we're going to have our summary.
Discriminator summary.
Let's run this and then see what we get.
There we go.
We have our summary.
Um, everything looks fine.
And now we could go ahead and start with our training.
And just like we had done previously, we're going to overwrite the training step.
So here we have our model, which we had built previously, where we override this train step right
here.
And with this we're able to make use of methods like the model.fit.
So here instead of the we have again, let's get back we have again model.
This GAN model is made of a discriminator, discriminator and a generator.
So let's replace the encoder and decoder by the discriminator and the generator respectively.
Then here we have our discriminator discriminator and generator, self generator and self discriminator.
We can modify the compile method.
Let's, uh, modify this compile method.
The compile method actually will take in the optimizer for the discriminator, the optimizer for the
generator, and then the loss function.
So we have, uh, the optimizer, let's say the optimizer, G Optimizer, and then the loss function.
So that's our compiler method.
And then.
We also go ahead and define our discriminator loss metric and our generator loss metric.
Um, we've taken all these three and then we've put this out here.
Okay, so that's it.
Now let's have our the loss metric and our loss metric.
And then from here we move on now to the training step for the training.
Let's recall that we have a discriminator, we have a generator and then this generator takes in fake
data.
Your takes in a vector, a fake this, this noise here and then generates fake data.
So takes noise out, takes noise, generates fake data, and then this fake data is then passed on to
the discriminator, which says whether it's a 1 or 0 or gives the value between 0 and 1.
Okay.
Uh, we also have our real data right here, which is also going to be passed into our discriminator
and it's also going to give a value between 0 and 1.
Now it should be noted that we will start with training the discriminator.
And when you're training the discriminator, we're going to freeze the generator.
That's we do not update these parameters.
So we're going to just update the weights of the discriminator, just like we had seen previously.
So the first thing we want to do here is to, um, get our noise.
The noise is random, normal.
So we have normal and then we specify since we have a normal distribution, we want to specify its shape.
Now, the shape of this here would be, um.
The shape will be our latent dream.
Now let's.
Let's have our latent dream, which we've defined already.
So here we will have latent dimension.
Now, given that we will be working in batches, we'll need to add the batch dimension.
So here we have batch size by latent dream.
Now to obtain the batch size, all we need to do here batch size is equal to F dot shape of our x batch
x batch and then we get the zeroed value.
Now from here we have the batch size, we have the noise, and then we're ready to feed this into a
generator and then obtain the fake data.
Then also make use of the real data, which is basically this here, because this is our real data.
Remember, our data set is made up of 200,000 different images of faces of celebrities.
And what we've done is we've broken this up into batches of 128.
So for every batch, we're going to take this X batch here, which is basically the real data.
And then we're also going to use make use of this noise to generate a fake data and then train our discriminator.
And so with that, we have our fake data or let's say fake images, fake images equals self generator,
which takes in the noise or let's just say some random noise.
Here we have random noise, some random noise vector actually.
Okay, so we have this now we have our fake images, thus we have this here now the next, and we also
have the real.
So we can now dive into training the discriminator.
Now, it should be noted that the discriminator loss function will take in the output from here.
That's the output from the real data and compare it with one and then taking the output from those fakes
and compare it with zero.
So we'll take the output from the real let's call this our.
And compare with one.
And then we take the output from the fixed, let's call it F and then compare it with zero.
So getting back into the code right here, let's change this.
Let's call this real images.
And here we have real images, real images.
There we go.
So here we want to have the predicted output or better still, let's say real predictions, real predictions.
So here are our real predictions here.
Now, the real predictions are gotten from taking in our discriminator discriminator.
And then, uh, this discriminator actually takes in, uh, real images.
So we're representing what's going on, Um, right here.
So we have this real which gets into our discriminator and then we output the real predictions, which
will then compare with the value one.
So that's it.
We can now go ahead and compute the loss.
So we'll have discriminator loss for real is equal our loss function, which we are going to pass in.
And then this loss function takes in the real predictions.
And once.
So yeah, yo will have both the real predictions and the real labels.
Or the real labels, as I've said already, are the ones.
So it's basically this one here.
Since we haven't batches of images, we have several ones of size, the batch size.
So here we have real um, we there we go.
We have real, um, labels TF ones and then we specify its size or better still, its shape.
So here we have the, the shape, which is batch size by one.
See that batch size by one.
And the reason why we have batch size by one is simply because we have an output which takes in just
one single value while this output will, uh, for all the real label will be equal one and we have
the batch size.
Okay, so we have that Now.
The next thing we want to have here are the fake labels.
Now the fake labels is going to be this zeros right here.
So we have oh, sorry, we have zeros and then we have batch size and one.
Okay, so that's it.
Now we have our real labels and we have our fake labels and we will take the real labels.
We'll take the real predictions.
That is, we take we get what the model thinks about a particular input.
That's all the classification.
And then compare it with the real labels.
The real labels is ones because we expect that the model should take in a real input and then know that
it is a real input and that and that means that it should output a one when it takes in real data.
And if it outputs a value different from one, then the loss is going to be greater than zero.
Whereas if it's exactly equal one, the loss is going to be zero.
And our aim here is to minimize this loss.
Now we have that and the next thing we want to do is repeat this, but this time around for the fake
predictions.
So here we have fake predictions.
Now the first step is we have real data getting into the discriminator and the other we have fake data
getting to the discriminator.
So here we have fake predictions.
And this time around it doesn't just take the real images, but it takes, uh, the generated images.
So we have self generator and it takes in, uh oh, noise actually.
So it should take in a random noise, which is this, uh, one right here.
So it takes in random noise and then it outputs fake images.
But since we've defined this already here, we could just make use of it here.
So here we have fake images and there we go.
So your here we have the discriminator, which takes in fake image and then gives us a fake prediction.
And we're going to compare this fake prediction with zero.
So we expect that the the fake predictions should be zero, if not the the loss is not going to be equal
to zero.
So here instead of real labels, we have fake labels.
So we comparing zero with what the model is going to predict or the output of the discriminator.
Uh, here we have discriminator of fake.
We have here fake.
Uh, that's it.
I think that's okay.
We have the same loss function actually is a binary cross entropy loss.
And once we have this now we could have, we could define the loss to be equal, the loss real plus
the loss fake.
Since that's basically a combination of this two losses right here.
Now, before we move on, if you recall in the tips and tricks, we saw the label smoothing.
Now here we have our labels, our real and our fake labels.
Let's separate this.
And now what we're going to do is instead of taking a one, we'll take values around one.
So we take we add plus, uh, 0.25 times some random value between -1 and 1.
So basically what we're saying here is we want to take, uh, this one and then add it plus a value
in the range of -0.25 and two five and 0.25.
So this means that now instead of having, uh, the label to be fixed at one, we'll have the label
to be between, uh, because one -0.25 is 0.75.
So it will be between 0.75 and 1.25 instead of just one.
So that's how what our label will be now and then for the Zeros, since we we wouldn't want to have
negative values, we'll take the zero plus some random value between 0 and 0.25.
So instead of zero, we'll have some random value between 0 and 0.25.
Okay, so that's said.
What we'll do now is we'll get right here.
We have tf random.
Uniform and then we specify the mean vile, which is negative one and then the max vile, which is one.
Now specifying this means we're going from -1 to 1 and then multiplying by 0.25 means we're going from
-0.25 to 0.25.
So that's basically it.
And then also we specify its shape.
So we have, um, the batch size by one.
There we go.
Now we're just going to copy this out and then paste this right here.
So we don't want to have negative numbers, so we start from zero instead.
So here we have 0 to 1, But uh, by default, the values are already from 0 to 1.
So we could take this off.
Okay, so that's it?
Uh, everything looks fine.
I think everything is done for our discriminator.
Um, now we have this.
Let's take this off.
Uh, we have our loss, and that's it.
Okay, so that's basically it.
We now move on to our partial derivatives here.
We take in our loss, and then we're going to update, um, the discriminator.
So here we have this discriminator dot trainable weights.
Then, uh, for the optimizer is the optimizer which we had specified already right here.
So here we have instead of this optimizer, we have our D optimizer, which we're going to specify.
So we have the optimizer, uh, takes in the partial derivatives and our trainable weights.
Again, here we are training only the discriminator.
So we have discriminator.
That's it.
So here we go.
We have our model, our gan, the discriminator, the trainable weights, and we repeat the same process
here.
Now, uh, this is self.
Okay, so that's it.
Uh, this should be fine now.
And then the next step we do is we're going to do this same, but for the generator.
So what we do now is we're going to again sample some noise, uh, random noise here.
We'll copy this code out and then paste it out after this.
Okay.
There we go.
So we have this random noise right here.
Random noise.
And then from this random noise.
We have our fake images, um, self generator, and it takes in the random noise.
Okay, now we're going to have the same again.
So we're going to make use of the gradient tape as we've done already with the discriminator.
So paste that out here.
And then here we have we'll be working inside with the generator.
Um, this is discriminator, discriminator.
Let's go back discriminator.
Okay, so as we've said, this is, let's, let's just comment this.
Let's write out, uh, generator and then right here, um, we have the discrete discriminator.
Okay, so that's the discriminator.
And now for the generator.
So as we were saying, we have this fake images, we have our random noise and we made use of the random
noise to generate the fake image.
But this time around, we want to fool the discriminator.
So instead of expecting our fake labels to be zeros as we had here this time around, our fake labels
will be ones and we obviously not have anything to do with the real data.
So let's take this off and then get back here.
Uh, now we have the fake labels, uh, which, which is equal one, and it happens that they've actually
been flipped.
So let's, let's, uh, get down here and then paste this out here.
So here we have flipped, flipped fake labels which are actually ones instead of zeros.
Remember, we've seen this already.
So we have that and we're not going to do any label smoothing right here.
So we have that.
And the next thing we want to do is start with our recording of the gradients.
Now here, let's take this off.
We have our fake predictions.
We have, um, here we go.
Discriminator takes in the fake images and that's it.
Now here we have the flipped, flipped fake labels, and then we have our fake predictions.
Fake predictions.
I guess if we we should have had we should have made an error here.
This is actually fake fake predictions.
We're comparing the fake predictions with the fake labels.
And then here we're comparing the fake predictions with the flipped fake labels.
So that's it.
Um, that should be fine.
Here we have G.
So this is our loss.
G loss.
And there's nothing like loss, fake or loss real.
We just have G loss and that should be it.
Um, partial derivatives.
So generator That's it.
Generator.
And then here we have updating our generator.
So we're making we're not updating the parameters of the discriminator here.
Now here we have G Optimizer.
That's it.
Okay, so, um, if that's okay, we have now to update the different states.
So we have the loss metric, we update the state and we pass in the loss and then we repeat the same
for the loss.
That's the generator loss.
So here we have G and then here we have G.
Then for our loss, we have G loss, um, G loss, metric result metric.
That's it.
And then here we have our D loss south.
The loss metric and then results.
Okay, let's now run the cell.
And normally everything should work fine.
Now we move on to define the number of epochs.
So we're going to work for 20 epochs.
And then let's get back here.
We define our again, which is again, we've just defined and then it takes in the discriminator and
the generator which we defined already.
Uh, from here we go ahead and compile the model.
So we have again compile and then we specify the optimizer, the Optimizer optimizer, which is, uh,
the Adam Optimizer optimizers, and then Adam.
Now this Adam Optimizer will be or with a learning rate learning rate, which we specified already at
the beginning learning rate equal.
Two times ten to the negative four, which is specified at the beginning.
So we have a learning rate and then beta one, beta one equals 0.5.
Now we repeat the same for the generator.
Let's get back here.
Copy that and then paste it out here.
So this is for our generator.
Now.
Now we notice that there's no, there's no major difference.
Actually, there's no difference.
We just use the same optimizer.
Now we have that.
The next thing we want to do is pass in our loss function.
So we have your loss function which is equal, uh, binary cross entropy loss.
So we have losses, dot binary, cross entropy, and that's it.
So we have this set.
We could run this now and then go ahead to, uh, train our model by calling on again, dot fit method
or just model.fit method.
So we have history equal again, dot fit and then we pass in our train data set.
Uh, we'll start with, say, uh, ten or let's, let's take just 100 elements first and then here the
number of epochs equal our epochs, which we've defined already.
And then we have some callback.
So we'll make use of this callback.
And you already see the advantage of, uh, overriding the Trainstep method as now we could just, um,
define our callback and then pass it in here and the job is done.
So we will have this callback which is going to show us the generated images at the end of an epoch.
So, uh, let's call it show image.
And it's going to take in our latent dream.
Okay, so that's it.
Everything looks fine.
Now, the next thing we want to do is define this show image callback right here.
Now, here, let's.
Let's define this callback just above here.
So we have our show image callback, and then we get the latent dimension and then we specify an epoch.
And so at the end of every epoch, we are going to, uh, run this code right here.
Now, what's going on here is simple.
We have our model and then we have the generator.
We take in some random noise, we pass it into our generator, and then we try to see what the model
is generating.
So this means that, uh, when when we training or initially we have some output or some fake data generated
by the generator and then after an epoch, we want to see what the model is generating as we keep on
training our our whole or complete model.
So this, this is very important as we could already be able to debug and understand what's going on.
So this means that in a case where the model is saved, for example, uh, generating the same kinds
of output or generating some outputs which um, are clearly not the type of output we will expect to
get or whose distribution is very far away from that of the real data.
Then we will have to take some measures.
So it's very important to work with these kinds of callbacks as the already permit us to debug our whole
model training process.
So that said, we just have to specify the figure size here.
And then what we're doing is we we're having this different subplots because we see we have 64.
We could you could reduce this or you could increase.
This depends on you.
You could just pick whatever you want to pick here.
So you could generate a certain number of, um, images.
Um, here n equals six.
So we generating six by six, that's 36 images.
So we could change this to 36.
And then now for each and every subplot, we're going to show the image.
See this out?
This out comes from here.
So it's from the generator and that's it.
Now we're going to save this figure in some in some directory, which we could, uh, visualize.
So let's modify this.
Let's take here to be 36.
Um, that's fine.
Let's run this.
We have that show image.
That's fine.
Um.
Everything looks fine now.
Let's go ahead and start with the training so we could even reduce this to just ten so that we could
be able to notice any errors quickly and then now train on the full dataset.
We're getting this error unexpected keyword argument.
Meanwhile this actually mean let's get back here without the underscore.
So this is it.
Here you have this mean vowel and here we have max vowel.
Now you could feel free to check out the documentation and you should find the exact syntax.
So let's run this again and then start with the training.
Oh, we're getting this errors where we're told that no, no gradients are provided for any variable.
And when you look at this, you'll notice that we have come to the transposes.
Uh, this means that most probably this error is coming from the generator.
So let's get back here and make sure everything is okay.
Uh, one thing we notice here already is that this should be g.
So.
Yeah, it should be G And what do we have again here?
Um, yeah, it looks fine.
So let's run this again and then see what we get.
We still get this error, which again shows that it's coming from the generator.
We get back here and what do we notice?
We notice here that we get this fake images out of the scope of our gradient tape.
So what we have to do is instead directly call this in here.
So we we we want to update the parameters of the generator.
So it has to be in this gradient tape scope and not outside.
So let's take this off.
Take that off.
And one question you may be asking yourself is, why is it possible to just do this here that's have
this out but not in the generator?
And the simple answer is for the discriminator.
This generator isn't updated, so it doesn't matter if it's in here or not.
Whereas for the generator it actually matters.
So we we have to make sure it's in here.
So that's it.
Let's run this again and then see what we get.
So that's it.
Training has started.
We told no such file or directory generated and all of that.
Anyway, let's, let's make, uh, this directory generated and then we run this again.
There we go.
Things seem to be working fine.
We see that struggling to generate some images.
Let's open up our generator here.
You see, we have the different files.
Let's check out on, say, the 18th output.
Uh, you see this already?
So you see it's struggling already to produce.
You see this image here?
This one here looks already like so.
A human face, though it's still struggling a lot.
So we have that.
Let's.
Let's check out on, say, the 19th.
Oh, there we go.
Okay, so that's it.
Let's close this.
Hopefully there's no issue with connection.
Uh, let's get back up.
And then if those images aren't very visible, you can retrain and go for or take many more samples.
So here we're dealing with, uh, 100.
We take a hundred.
And then after, let's, uh, check this out.
After six epochs, you see the kind of results we get.
You see the images we get already look like humans.
Now let's modify this code.
Let's stop the training, and then let's modify the code such that we do not flip the labels.
Um, there we go.
We could get back to this flip fake labels, and then instead of once, we just take the fake labels.
So let's, let's take zeros.
So if we have this, we're going to compare it with when we actually do the flipped labeling.
So let's run this, um, let's, uh, rerun the cells and then we start with the training.
After nine epochs.
We can now check out those generated images.
Let's let's open up the zero, open up the third the feet and then say the eight.
Um, let's see what we get here.
See?
It is.
Our generators experiencing vanishing gradients, and that's why we're getting these kinds of horrible
outputs.
So let's again stop the training.
Let's stop the training, and then let's get back to our our models.
What we'll do now is instead of using the leaky Relu, we'll just use the relu.
So let's, let's change this activation and set it to relu.
There we go.
Let's simply paste this out everywhere and then take off the leaky relu and then see the kind of output
we would get in case we, we, we used the, the, the, the relu itself instead of the leaky relu.
Um, let's take that off.
Everything looks fine.
Now.
Let's get back into the generator and then repeat the same.
So space spaces out here.
Relu Activation.
Um, take off.
Leaky Relu, take off leaky relu, And then rerun this again.
Um, this should be fine.
Now let's run that and then see what we get.
Okay.
Now, that training has been going on for a while now.
We could open up the zeroth, let's say open up the second, the fifth, the seventh, um, and then
the 10th so we could look at this.
Now, what do you notice?
You see we're getting practically nothing as output right here.
And that's simply because we using the relu instead of the leaky Relu.
And as you can see, Sparsity isn't great for generating images, so you have to be very careful with
that.
Now, let's stop this training.
Um, let's stop the training and then, uh, get back to what we had before.
Um, we have this.
Yeah, this should be fine.
Uh, no discriminator.
And then right here, we get back again and.
Oops, let's get back.
Then we restart the training again and then see what we get.
Now, that training has been going on for a while.
Let's go ahead and open this up.
One.
Let's zero take out two, for example, five, um, seven and say eight.
So let's check out what the model is outputting.
You see, Um, there we go.
We can see that with the normal Relu It isn't doing that bad either.
So in this specific example, using the leaky Relu isn't maybe, uh, that necessary?
Okay, so now we've looked at the effect of not using this leaky relu.
Um, you could also take off the batchnorm and see how that affects the kinds of outputs you get from
the generator.
Um, here, we're getting an error.
Let's run this again.
Yeah, okay, that's fine.
Now, let's go ahead and, uh, restart the training, but this time around with the full data set.
So let's take this off and then start with the training.
After training for over 20 epochs, you are the kinds of outputs we shall be getting in our deep learning
for Image Generation course, we delve deep into how to create even much higher quality outputs like
this one, for example, which was created with a diffusion model, or this other one which we created
with a pro gan.
That's it for this section on image generation with the variational autoencoders and the generative
adversarial neural networks.