{"id":2378,"date":"2019-07-18T19:00:49","date_gmt":"2019-07-18T19:00:49","guid":{"rendered":"https:\/\/www.aiproblog.com\/index.php\/2019\/07\/18\/how-to-develop-an-auxiliary-classifier-gan-ac-gan-from-scratch-with-keras\/"},"modified":"2019-07-18T19:00:49","modified_gmt":"2019-07-18T19:00:49","slug":"how-to-develop-an-auxiliary-classifier-gan-ac-gan-from-scratch-with-keras","status":"publish","type":"post","link":"https:\/\/www.aiproblog.com\/index.php\/2019\/07\/18\/how-to-develop-an-auxiliary-classifier-gan-ac-gan-from-scratch-with-keras\/","title":{"rendered":"How to Develop an Auxiliary Classifier GAN (AC-GAN) From Scratch with Keras"},"content":{"rendered":"<p>Author: Jason Brownlee<\/p>\n<div>\n<p>Generative Adversarial Networks, or GANs, are an architecture for training generative models, such as deep convolutional neural networks for generating images.<\/p>\n<p>The conditional generative adversarial network, or cGAN for short, is a type of GAN that involves the conditional generation of images by a generator model. Image generation can be conditional on a class label, if available, allowing the targeted generated of images of a given type.<\/p>\n<p>The Auxiliary Classifier GAN, or AC-GAN for short, is an extension of the conditional GAN that changes the discriminator to predict the class label of a given image rather than receive it as input. It has the effect of stabilizing the training process and allowing the generation of large high-quality images whilst learning a representation in the latent space that is independent of the class label.<\/p>\n<p>In this tutorial, you will discover how to develop an auxiliary classifier generative adversarial network for generating photographs of clothing.<\/p>\n<p>After completing this tutorial, you will know:<\/p>\n<ul>\n<li>The auxiliary classifier GAN is a type of conditional GAN that requires that the discriminator predict the class label of a given image.<\/li>\n<li>How to develop generator, discriminator, and composite models for the AC-GAN.<\/li>\n<li>How to train, evaluate, and use an AC-GAN to generate photographs of clothing from the Fashion-MNIST dataset.<\/li>\n<\/ul>\n<p>Discover how to develop DCGANs, conditional GANs, Pix2Pix, CycleGANs, and more with Keras <a href=\"https:\/\/machinelearningmastery.com\/generative_adversarial_networks\/\" rel=\"nofollow\">in my new GANs book<\/a>, with 29 step-by-step tutorials and full source code.<\/p>\n<p>Let\u2019s get started.<\/p>\n<div id=\"attachment_8242\" style=\"width: 650px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8242\" class=\"wp-image-8242 size-full\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2019\/07\/How-to-Develop-an-Auxiliary-Classifier-GAN-AC-GAN-From-Scratch-with-Keras.jpg\" alt=\"How to Develop an Auxiliary Classifier GAN (AC-GAN) From Scratch with Keras\" width=\"640\" height=\"640\" srcset=\"http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/07\/How-to-Develop-an-Auxiliary-Classifier-GAN-AC-GAN-From-Scratch-with-Keras.jpg 640w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/07\/How-to-Develop-an-Auxiliary-Classifier-GAN-AC-GAN-From-Scratch-with-Keras-150x150.jpg 150w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/07\/How-to-Develop-an-Auxiliary-Classifier-GAN-AC-GAN-From-Scratch-with-Keras-300x300.jpg 300w\" sizes=\"(max-width: 640px) 100vw, 640px\"><\/p>\n<p id=\"caption-attachment-8242\" class=\"wp-caption-text\">How to Develop an Auxiliary Classifier GAN (AC-GAN) From Scratch with Keras<br \/>Photo by <a href=\"https:\/\/www.flickr.com\/photos\/81994612@N06\/28779669191\">ebbe ostebo<\/a>, some rights reserved.<\/p>\n<\/div>\n<h2>Tutorial Overview<\/h2>\n<p>This tutorial is divided into five parts; they are:<\/p>\n<ol>\n<li>Auxiliary Classifier Generative Adversarial Networks<\/li>\n<li>Fashion-MNIST Clothing Photograph Dataset<\/li>\n<li>How to Define AC-GAN Models<\/li>\n<li>How to Develop an AC-GAN for Fashion-MNIST<\/li>\n<li>How to Generate Items of Clothing With the AC-GAN<\/li>\n<\/ol>\n<h2>Auxiliary Classifier Generative Adversarial Networks<\/h2>\n<p>The generative adversarial network is an architecture for training a generative model, typically deep convolutional neural networks for generating image.<\/p>\n<p>The architecture is comprised of both a generator model that takes random points from a latent space as input and generates images, and a discriminator for classifying images as either real (from the dataset) or fake (generate). Both models are then trained simultaneously in a zero-sum game.<\/p>\n<p>A conditional GAN, cGAN or CGAN for short, is an extension of the GAN architecture that adds structure to the latent space. The training of the GAN model is changed so that the generator is provided both with a point in the latent space and a class label as input, and attempts to generate an image for that class. The discriminator is provided with both an image and the class label and must classify whether the image is real or fake as before.<\/p>\n<p>The addition of the class as input makes the image generation process, and image classification process, conditional on the class label, hence the name. The effect is both a more stable training process and a resulting generator model that can be used to generate images of a given specific type, e.g. for a class label.<\/p>\n<p>The Auxiliary Classifier GAN, or AC-GAN for short, is a further extension of the GAN architecture building upon the CGAN extension. It was introduced by <a href=\"http:\/\/www.augustusodena.com\/\">Augustus Odena<\/a>, et al. from Google Brain in the 2016 paper titled \u201c<a href=\"https:\/\/arxiv.org\/abs\/1610.09585\">Conditional Image Synthesis with Auxiliary Classifier GANs<\/a>.\u201d<\/p>\n<p>As with the conditional GAN, the generator model in the AC-GAN is provided both with a point in the latent space and the class label as input, e.g. the image generation process is conditional.<\/p>\n<p>The main difference is in the discriminator model, which is only provided with the image as input, unlike the conditional GAN that is provided with the image and class label as input. The discriminator model must then predict whether the given image is real or fake as before, and must also predict the class label of the image.<\/p>\n<blockquote>\n<p>\u2026 the model [\u2026] is class conditional, but with an auxiliary decoder that is tasked with reconstructing class labels.<\/p>\n<\/blockquote>\n<p>\u2014 <a href=\"https:\/\/arxiv.org\/abs\/1610.09585\">Conditional Image Synthesis With Auxiliary Classifier GANs<\/a>, 2016.<\/p>\n<p>The architecture is described in such a way that the discriminator and auxiliary classifier may be considered separate models that share model weights. In practice, the discriminator and auxiliary classifier can be implemented as a single neural network model with two outputs.<\/p>\n<p>The first output is a single probability via the sigmoid activation function that indicates the \u201c<em>realness<\/em>\u201d of the input image and is optimized using binary cross entropy like a normal GAN discriminator model.<\/p>\n<p>The second output is a probability of the image belonging to each class via the softmax activation function, like any given multi-class classification neural network model, and is optimized using categorical cross entropy.<\/p>\n<p>To summarize:<\/p>\n<h4>Generator Model:<\/h4>\n<ul>\n<li><strong>Input<\/strong>: Random point from the latent space, and the class label.<\/li>\n<li><strong>Output<\/strong>: Generated image.<\/li>\n<\/ul>\n<h4>Discriminator Model:<\/h4>\n<ul>\n<li><strong>Input<\/strong>: Image.<\/li>\n<li><strong>Output<\/strong>: Probability that the provided image is real, probability of the image belonging to each known class.<\/li>\n<\/ul>\n<p>The plot below summarizes the inputs and outputs of a range of conditional GANs, including the AC-GAN, providing some context for the differences.<\/p>\n<div id=\"attachment_8234\" style=\"width: 1034px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8234\" class=\"size-large wp-image-8234\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2019\/05\/Summary-of-the-Differences-Between-the-Conditional-GAN-Semi-Supervised-GAN-InfoGAN-and-AC-GAN-1024x423.png\" alt=\"Summary of the Differences Between the Conditional GAN, Semi-Supervised GAN, InfoGAN and AC-GAN\" width=\"1024\" height=\"423\" srcset=\"http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Summary-of-the-Differences-Between-the-Conditional-GAN-Semi-Supervised-GAN-InfoGAN-and-AC-GAN-1024x423.png 1024w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Summary-of-the-Differences-Between-the-Conditional-GAN-Semi-Supervised-GAN-InfoGAN-and-AC-GAN-300x124.png 300w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Summary-of-the-Differences-Between-the-Conditional-GAN-Semi-Supervised-GAN-InfoGAN-and-AC-GAN-768x317.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\"><\/p>\n<p id=\"caption-attachment-8234\" class=\"wp-caption-text\">Summary of the Differences Between the Conditional GAN, Semi-Supervised GAN, InfoGAN, and AC-GAN.<br \/>Taken from: Version of Conditional Image Synthesis With Auxiliary Classifier GANs.<\/p>\n<\/div>\n<p>The discriminator seeks to maximize the probability of correctly classifying real and fake images (LS) and correctly predicting the class label (LC) of a real or fake image (e.g. LS + LC). The generator seeks to minimize the ability of the discriminator to discriminate real and fake images whilst also maximizing the ability of the discriminator predicting the class label of real and fake images (e.g. LC \u2013 LS).<\/p>\n<blockquote>\n<p>The objective function has two parts: the log-likelihood of the correct source, LS, and the log-likelihood of the correct class, LC. [\u2026] D is trained to maximize LS + LC while G is trained to maximize LC \u2212 LS.<\/p>\n<\/blockquote>\n<p>\u2014 <a href=\"https:\/\/arxiv.org\/abs\/1610.09585\">Conditional Image Synthesis With Auxiliary Classifier GANs<\/a>, 2016.<\/p>\n<p>The resulting generator learns a latent space representation that is independent of the class label, unlike the conditional GAN.<\/p>\n<blockquote>\n<p>AC-GANs learn a representation for z that is independent of class label.<\/p>\n<\/blockquote>\n<p>\u2014 <a href=\"https:\/\/arxiv.org\/abs\/1610.09585\">Conditional Image Synthesis With Auxiliary Classifier GANs<\/a>, 2016.<\/p>\n<p>The effect of changing the conditional GAN in this way is both a more stable training process and the ability of the model to generate higher quality images with a larger size than had been previously possible, e.g. 128\u00d7128 pixels.<\/p>\n<blockquote>\n<p>\u2026 we demonstrate that adding more structure to the GAN latent space along with a specialized cost function results in higher quality samples. [\u2026] Importantly, we demonstrate quantitatively that our high resolution samples are not just naive resizings of low resolution samples.<\/p>\n<\/blockquote>\n<p>\u2014 <a href=\"https:\/\/arxiv.org\/abs\/1610.09585\">Conditional Image Synthesis With Auxiliary Classifier GANs<\/a>, 2016.<\/p>\n<div class=\"woo-sc-hr\"><\/div>\n<p><center><\/p>\n<h3>Want to Develop GANs from Scratch?<\/h3>\n<p>Take my free 7-day email crash course now (with sample code).<\/p>\n<p>Click to sign-up and also get a free PDF Ebook version of the course.<\/p>\n<p><a href=\"https:\/\/machinelearningmastery.lpages.co\/leadbox\/162526e1b172a2%3A164f8be4f346dc\/5926953912500224\/\" target=\"_blank\" style=\"background: rgb(255, 206, 10); color: rgb(255, 255, 255); text-decoration: none; font-family: Helvetica, Arial, sans-serif; font-weight: bold; font-size: 16px; line-height: 20px; padding: 10px; display: inline-block; max-width: 300px; border-radius: 5px; text-shadow: rgba(0, 0, 0, 0.25) 0px -1px 1px; box-shadow: rgba(255, 255, 255, 0.5) 0px 1px 3px inset, rgba(0, 0, 0, 0.5) 0px 1px 3px;\" rel=\"noopener noreferrer\">Download Your FREE Mini-Course<\/a><script data-leadbox=\"162526e1b172a2:164f8be4f346dc\" data-url=\"https:\/\/machinelearningmastery.lpages.co\/leadbox\/162526e1b172a2%3A164f8be4f346dc\/5926953912500224\/\" data-config=\"%7B%7D\" type=\"text\/javascript\" src=\"https:\/\/machinelearningmastery.lpages.co\/leadbox-1562872266.js\"><\/script><\/p>\n<p><\/center><\/p>\n<div class=\"woo-sc-hr\"><\/div>\n<h2>Fashion-MNIST Clothing Photograph Dataset<\/h2>\n<p>The <a href=\"https:\/\/machinelearningmastery.com\/how-to-develop-a-cnn-from-scratch-for-fashion-mnist-clothing-classification\/\">Fashion-MNIST dataset<\/a> is proposed as a more challenging replacement dataset for the MNIST handwritten digit dataset.<\/p>\n<p>It is a dataset comprised of 60,000 small square 28\u00d728 pixel grayscale images of items of 10 types of clothing, such as shoes, t-shirts, dresses, and more.<\/p>\n<p>Keras provides access to the Fashion-MNIST dataset via the <a href=\"https:\/\/keras.io\/datasets\/#fashion-mnist-database-of-fashion-articles\">fashion_mnist.load_dataset() function<\/a>. It returns two tuples, one with the input and output elements for the standard training dataset, and another with the input and output elements for the standard test dataset.<\/p>\n<p>The example below loads the dataset and summarizes the shape of the loaded dataset.<\/p>\n<p><strong>Note<\/strong>: the first time you load the dataset, Keras will automatically download a compressed version of the images and save them under your home directory in <em>~\/.keras\/datasets\/<\/em>. The download is fast as the dataset is only about 25 megabytes in its compressed form.<\/p>\n<pre class=\"crayon-plain-tag\"># example of loading the fashion_mnist dataset\r\nfrom keras.datasets.fashion_mnist import load_data\r\n# load the images into memory\r\n(trainX, trainy), (testX, testy) = load_data()\r\n# summarize the shape of the dataset\r\nprint('Train', trainX.shape, trainy.shape)\r\nprint('Test', testX.shape, testy.shape)<\/pre>\n<p>Running the example loads the dataset and prints the shape of the input and output components of the train and test splits of images.<\/p>\n<p>We can see that there are 60K examples in the training set and 10K in the test set and that each image is a square of 28 by 28 pixels.<\/p>\n<pre class=\"crayon-plain-tag\">Train (60000, 28, 28) (60000,)\r\nTest (10000, 28, 28) (10000,)<\/pre>\n<p>The images are grayscale with a black background (0 pixel value) and the items of clothing in white ( pixel values near 255). This means if the images were plotted, they would be mostly black with a white item of clothing in the middle.<\/p>\n<p>We can plot some of the images from the training dataset using the matplotlib library with the <a href=\"https:\/\/matplotlib.org\/api\/_as_gen\/matplotlib.pyplot.imshow.html\">imshow() function<\/a> and specify the color map via the \u2018<em>cmap<\/em>\u2018 argument as \u2018<em>gray<\/em>\u2018 to show the pixel values correctly.<\/p>\n<pre class=\"crayon-plain-tag\"># plot raw pixel data\r\npyplot.imshow(trainX[i], cmap='gray')<\/pre>\n<p>Alternately, the images are easier to review when we reverse the colors and plot the background as white and the clothing in black.<\/p>\n<p>They are easier to view as most of the image is now white with the area of interest in black. This can be achieved using a reverse grayscale color map, as follows:<\/p>\n<pre class=\"crayon-plain-tag\"># plot raw pixel data\r\npyplot.imshow(trainX[i], cmap='gray_r')<\/pre>\n<p>The example below plots the first 100 images from the training dataset in a 10 by 10 square.<\/p>\n<pre class=\"crayon-plain-tag\"># example of loading the fashion_mnist dataset\r\nfrom keras.datasets.fashion_mnist import load_data\r\nfrom matplotlib import pyplot\r\n# load the images into memory\r\n(trainX, trainy), (testX, testy) = load_data()\r\n# plot images from the training dataset\r\nfor i in range(100):\r\n\t# define subplot\r\n\tpyplot.subplot(10, 10, 1 + i)\r\n\t# turn off axis\r\n\tpyplot.axis('off')\r\n\t# plot raw pixel data\r\n\tpyplot.imshow(trainX[i], cmap='gray_r')\r\npyplot.show()<\/pre>\n<p>Running the example creates a figure with a plot of 100 images from the MNIST training dataset, arranged in a 10\u00d710 square.<\/p>\n<div id=\"attachment_8163\" style=\"width: 1034px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8163\" class=\"size-large wp-image-8163\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-First-100-Items-of-Clothing-From-the-Fashion-MNIST-Dataset-1024x768.png\" alt=\"Plot of the First 100 Items of Clothing From the Fashion MNIST Dataset.\" width=\"1024\" height=\"768\" srcset=\"http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-First-100-Items-of-Clothing-From-the-Fashion-MNIST-Dataset-1024x768.png 1024w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-First-100-Items-of-Clothing-From-the-Fashion-MNIST-Dataset-300x225.png 300w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-First-100-Items-of-Clothing-From-the-Fashion-MNIST-Dataset-768x576.png 768w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-First-100-Items-of-Clothing-From-the-Fashion-MNIST-Dataset.png 1280w\" sizes=\"(max-width: 1024px) 100vw, 1024px\"><\/p>\n<p id=\"caption-attachment-8163\" class=\"wp-caption-text\">Plot of the First 100 Items of Clothing From the Fashion MNIST Dataset.<\/p>\n<\/div>\n<p>We will use the images in the training dataset as the basis for training a Generative Adversarial Network.<\/p>\n<p>Specifically, the generator model will learn how to generate new plausible items of clothing, using a discriminator that will try to distinguish between real images from the Fashion MNIST training dataset and new images output by the generator model, and predict the class label for each.<\/p>\n<p>This is a relatively simple problem that does not require a sophisticated generator or discriminator model, although it does require the generation of a grayscale output image.<\/p>\n<h2>How to Define AC-GAN Models<\/h2>\n<p>In this section, we will develop the generator, discriminator, and composite models for the AC-GAN.<\/p>\n<p>The appendix of the AC-GAN paper provides suggestions for generator and discriminator configurations that we will use as inspiration. The table below summarizes these suggestions for the CIFAR-10 dataset, taken from the paper.<\/p>\n<div id=\"attachment_8235\" style=\"width: 1034px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8235\" class=\"size-large wp-image-8235\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2019\/05\/AC-GAN-Generator-and-Discriminator-Model-Configuration-Suggestions-1024x645.png\" alt=\"AC-GAN Generator and Discriminator Model Configuration Suggestions\" width=\"1024\" height=\"645\" srcset=\"http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/AC-GAN-Generator-and-Discriminator-Model-Configuration-Suggestions-1024x645.png 1024w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/AC-GAN-Generator-and-Discriminator-Model-Configuration-Suggestions-300x189.png 300w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/AC-GAN-Generator-and-Discriminator-Model-Configuration-Suggestions-768x484.png 768w\" sizes=\"(max-width: 1024px) 100vw, 1024px\"><\/p>\n<p id=\"caption-attachment-8235\" class=\"wp-caption-text\">AC-GAN Generator and Discriminator Model Configuration Suggestions.<br \/>Take from: Conditional Image Synthesis With Auxiliary Classifier GANs.<\/p>\n<\/div>\n<h3>AC-GAN Discriminator Model<\/h3>\n<p>Let\u2019s start with the discriminator model.<\/p>\n<p>The discriminator model must take as input an image and predict both the probability of the \u2018<em>realness<\/em>\u2018 of the image and the probability of the image belonging to each of the given classes.<\/p>\n<p>The input images will have the shape 28x28x1 and there are 10 classes for the items of clothing in the Fashion MNIST dataset.<\/p>\n<p>The model can be defined as per the DCGAN architecture. That is, using Gaussian weight initialization, batch normalization, LeakyReLU, Dropout, and a 2\u00d72 stride for downsampling instead of pooling layers.<\/p>\n<p>For example, below is the bulk of the discriminator model defined using the <a href=\"https:\/\/machinelearningmastery.com\/keras-functional-api-deep-learning\/\">Keras functional API<\/a>.<\/p>\n<pre class=\"crayon-plain-tag\">...\r\n# weight initialization\r\ninit = RandomNormal(stddev=0.02)\r\n# image input\r\nin_image = Input(shape=in_shape)\r\n# downsample to 14x14\r\nfe = Conv2D(32, (3,3), strides=(2,2), padding='same', kernel_initializer=init)(in_image)\r\nfe = LeakyReLU(alpha=0.2)(fe)\r\nfe = Dropout(0.5)(fe)\r\n# normal\r\nfe = Conv2D(64, (3,3), padding='same', kernel_initializer=init)(fe)\r\nfe = BatchNormalization()(fe)\r\nfe = LeakyReLU(alpha=0.2)(fe)\r\nfe = Dropout(0.5)(fe)\r\n# downsample to 7x7\r\nfe = Conv2D(128, (3,3), strides=(2,2), padding='same', kernel_initializer=init)(fe)\r\nfe = BatchNormalization()(fe)\r\nfe = LeakyReLU(alpha=0.2)(fe)\r\nfe = Dropout(0.5)(fe)\r\n# normal\r\nfe = Conv2D(256, (3,3), padding='same', kernel_initializer=init)(fe)\r\nfe = BatchNormalization()(fe)\r\nfe = LeakyReLU(alpha=0.2)(fe)\r\nfe = Dropout(0.5)(fe)\r\n# flatten feature maps\r\nfe = Flatten()(fe)\r\n...<\/pre>\n<p>The main difference is that the model has two output layers.<\/p>\n<p>The first is a single node with the sigmoid activation for predicting the real-ness of the image.<\/p>\n<pre class=\"crayon-plain-tag\">...\r\n# real\/fake output\r\nout1 = Dense(1, activation='sigmoid')(fe)<\/pre>\n<p>The second is multiple nodes, one for each class, using the softmax activation function to predict the class label of the given image.<\/p>\n<pre class=\"crayon-plain-tag\">...\r\n# class label output\r\nout2 = Dense(n_classes, activation='softmax')(fe)<\/pre>\n<p>We can then construct the image with a single input and two outputs.<\/p>\n<pre class=\"crayon-plain-tag\">...\r\n# define model\r\nmodel = Model(in_image, [out1, out2])<\/pre>\n<p>The model must be trained with two <a href=\"https:\/\/machinelearningmastery.com\/how-to-choose-loss-functions-when-training-deep-learning-neural-networks\/\">loss functions<\/a>, binary cross entropy for the first output layer, and categorical cross-entropy loss for the second output layer.<\/p>\n<p>Rather than comparing a one hot encoding of the class labels to the second output layer, as we might do normally, we can compare the integer class labels directly. We can achieve this automatically using the sparse categorical cross-entropy loss function. This will have the identical effect of the categorical cross-entropy but avoids the step of having to manually one hot encode the target labels.<\/p>\n<p>When compiling the model, we can inform Keras to use the two different loss functions for the two output layers by specifying a list of function names as strings; for example:<\/p>\n<pre class=\"crayon-plain-tag\">loss=['binary_crossentropy', 'sparse_categorical_crossentropy']<\/pre>\n<p>The model is fit using the <a href=\"https:\/\/machinelearningmastery.com\/adam-optimization-algorithm-for-deep-learning\/\">Adam version of stochastic gradient descent<\/a> with a small learning rate and modest momentum, as is recommended for DCGANs.<\/p>\n<pre class=\"crayon-plain-tag\">...\r\n# compile model\r\nopt = Adam(lr=0.0002, beta_1=0.5)\r\nmodel.compile(loss=['binary_crossentropy', 'sparse_categorical_crossentropy'], optimizer=opt)<\/pre>\n<p>Tying this together, the define_discriminator() function will define and compile the discriminator model for the AC-GAN.<\/p>\n<p>The shape of the input images and the number of classes are parameterized and set with defaults, allowing them to be easily changed for your own project in the future.<\/p>\n<pre class=\"crayon-plain-tag\"># define the standalone discriminator model\r\ndef define_discriminator(in_shape=(28,28,1), n_classes=10):\r\n\t# weight initialization\r\n\tinit = RandomNormal(stddev=0.02)\r\n\t# image input\r\n\tin_image = Input(shape=in_shape)\r\n\t# downsample to 14x14\r\n\tfe = Conv2D(32, (3,3), strides=(2,2), padding='same', kernel_initializer=init)(in_image)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# normal\r\n\tfe = Conv2D(64, (3,3), padding='same', kernel_initializer=init)(fe)\r\n\tfe = BatchNormalization()(fe)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# downsample to 7x7\r\n\tfe = Conv2D(128, (3,3), strides=(2,2), padding='same', kernel_initializer=init)(fe)\r\n\tfe = BatchNormalization()(fe)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# normal\r\n\tfe = Conv2D(256, (3,3), padding='same', kernel_initializer=init)(fe)\r\n\tfe = BatchNormalization()(fe)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# flatten feature maps\r\n\tfe = Flatten()(fe)\r\n\t# real\/fake output\r\n\tout1 = Dense(1, activation='sigmoid')(fe)\r\n\t# class label output\r\n\tout2 = Dense(n_classes, activation='softmax')(fe)\r\n\t# define model\r\n\tmodel = Model(in_image, [out1, out2])\r\n\t# compile model\r\n\topt = Adam(lr=0.0002, beta_1=0.5)\r\n\tmodel.compile(loss=['binary_crossentropy', 'sparse_categorical_crossentropy'], optimizer=opt)\r\n\treturn model<\/pre>\n<p>We can define and summarize this model.<\/p>\n<p>The complete example is listed below.<\/p>\n<pre class=\"crayon-plain-tag\"># example of defining the discriminator model\r\nfrom keras.models import Model\r\nfrom keras.layers import Input\r\nfrom keras.layers import Dense\r\nfrom keras.layers import Conv2D\r\nfrom keras.layers import LeakyReLU\r\nfrom keras.layers import Dropout\r\nfrom keras.layers import Flatten\r\nfrom keras.layers import BatchNormalization\r\nfrom keras.initializers import RandomNormal\r\nfrom keras.optimizers import Adam\r\nfrom keras.utils.vis_utils import plot_model\r\n\r\n# define the standalone discriminator model\r\ndef define_discriminator(in_shape=(28,28,1), n_classes=10):\r\n\t# weight initialization\r\n\tinit = RandomNormal(stddev=0.02)\r\n\t# image input\r\n\tin_image = Input(shape=in_shape)\r\n\t# downsample to 14x14\r\n\tfe = Conv2D(32, (3,3), strides=(2,2), padding='same', kernel_initializer=init)(in_image)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# normal\r\n\tfe = Conv2D(64, (3,3), padding='same', kernel_initializer=init)(fe)\r\n\tfe = BatchNormalization()(fe)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# downsample to 7x7\r\n\tfe = Conv2D(128, (3,3), strides=(2,2), padding='same', kernel_initializer=init)(fe)\r\n\tfe = BatchNormalization()(fe)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# normal\r\n\tfe = Conv2D(256, (3,3), padding='same', kernel_initializer=init)(fe)\r\n\tfe = BatchNormalization()(fe)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# flatten feature maps\r\n\tfe = Flatten()(fe)\r\n\t# real\/fake output\r\n\tout1 = Dense(1, activation='sigmoid')(fe)\r\n\t# class label output\r\n\tout2 = Dense(n_classes, activation='softmax')(fe)\r\n\t# define model\r\n\tmodel = Model(in_image, [out1, out2])\r\n\t# compile model\r\n\topt = Adam(lr=0.0002, beta_1=0.5)\r\n\tmodel.compile(loss=['binary_crossentropy', 'sparse_categorical_crossentropy'], optimizer=opt)\r\n\treturn model\r\n\r\n# define the discriminator model\r\nmodel = define_discriminator()\r\n# summarize the model\r\nmodel.summary()\r\n# plot the model\r\nplot_model(model, to_file='discriminator_plot.png', show_shapes=True, show_layer_names=True)<\/pre>\n<p>Running the example first prints a summary of the model.<\/p>\n<p>This confirms the expected shape of the input images and the two output layers, although the linear organization does make the two separate output layers clear.<\/p>\n<pre class=\"crayon-plain-tag\">__________________________________________________________________________________________________\r\nLayer (type)                    Output Shape         Param #     Connected to\r\n==================================================================================================\r\ninput_1 (InputLayer)            (None, 28, 28, 1)    0\r\n__________________________________________________________________________________________________\r\nconv2d_1 (Conv2D)               (None, 14, 14, 32)   320         input_1[0][0]\r\n__________________________________________________________________________________________________\r\nleaky_re_lu_1 (LeakyReLU)       (None, 14, 14, 32)   0           conv2d_1[0][0]\r\n__________________________________________________________________________________________________\r\ndropout_1 (Dropout)             (None, 14, 14, 32)   0           leaky_re_lu_1[0][0]\r\n__________________________________________________________________________________________________\r\nconv2d_2 (Conv2D)               (None, 14, 14, 64)   18496       dropout_1[0][0]\r\n__________________________________________________________________________________________________\r\nbatch_normalization_1 (BatchNor (None, 14, 14, 64)   256         conv2d_2[0][0]\r\n__________________________________________________________________________________________________\r\nleaky_re_lu_2 (LeakyReLU)       (None, 14, 14, 64)   0           batch_normalization_1[0][0]\r\n__________________________________________________________________________________________________\r\ndropout_2 (Dropout)             (None, 14, 14, 64)   0           leaky_re_lu_2[0][0]\r\n__________________________________________________________________________________________________\r\nconv2d_3 (Conv2D)               (None, 7, 7, 128)    73856       dropout_2[0][0]\r\n__________________________________________________________________________________________________\r\nbatch_normalization_2 (BatchNor (None, 7, 7, 128)    512         conv2d_3[0][0]\r\n__________________________________________________________________________________________________\r\nleaky_re_lu_3 (LeakyReLU)       (None, 7, 7, 128)    0           batch_normalization_2[0][0]\r\n__________________________________________________________________________________________________\r\ndropout_3 (Dropout)             (None, 7, 7, 128)    0           leaky_re_lu_3[0][0]\r\n__________________________________________________________________________________________________\r\nconv2d_4 (Conv2D)               (None, 7, 7, 256)    295168      dropout_3[0][0]\r\n__________________________________________________________________________________________________\r\nbatch_normalization_3 (BatchNor (None, 7, 7, 256)    1024        conv2d_4[0][0]\r\n__________________________________________________________________________________________________\r\nleaky_re_lu_4 (LeakyReLU)       (None, 7, 7, 256)    0           batch_normalization_3[0][0]\r\n__________________________________________________________________________________________________\r\ndropout_4 (Dropout)             (None, 7, 7, 256)    0           leaky_re_lu_4[0][0]\r\n__________________________________________________________________________________________________\r\nflatten_1 (Flatten)             (None, 12544)        0           dropout_4[0][0]\r\n__________________________________________________________________________________________________\r\ndense_1 (Dense)                 (None, 1)            12545       flatten_1[0][0]\r\n__________________________________________________________________________________________________\r\ndense_2 (Dense)                 (None, 10)           125450      flatten_1[0][0]\r\n==================================================================================================\r\nTotal params: 527,627\r\nTrainable params: 526,731\r\nNon-trainable params: 896\r\n__________________________________________________________________________________________________<\/pre>\n<p>A plot of the model is also created, showing the linear processing of the input image and the two clear output layers.<\/p>\n<div id=\"attachment_8236\" style=\"width: 390px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8236\" class=\"size-large wp-image-8236\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-Discriminator-Model-for-the-Auxiliary-Classifier-GAN-380x1024.png\" alt=\"Plot of the Discriminator Model for the Auxiliary Classifier GAN\" width=\"380\" height=\"1024\" srcset=\"http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-Discriminator-Model-for-the-Auxiliary-Classifier-GAN-380x1024.png 380w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-Discriminator-Model-for-the-Auxiliary-Classifier-GAN-111x300.png 111w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-Discriminator-Model-for-the-Auxiliary-Classifier-GAN.png 725w\" sizes=\"(max-width: 380px) 100vw, 380px\"><\/p>\n<p id=\"caption-attachment-8236\" class=\"wp-caption-text\">Plot of the Discriminator Model for the Auxiliary Classifier GAN<\/p>\n<\/div>\n<p>Now that we have defined our AC-GAN discriminator model, we can develop the generator model.<\/p>\n<h3>AC-GAN Generator Model<\/h3>\n<p>The generator model must take a random point from the latent space as input, and the class label, then output a generated grayscale image with the shape 28x28x1.<\/p>\n<p>The AC-GAN paper describes the AC-GAN generator model taking a vector input that is a concatenation of the point in latent space (100 dimensions) and the one hot encoded class label (10 dimensions) that is 110 dimensions.<\/p>\n<p>An alternative approach that has proven effective and is now generally recommended is to interpret the class label as an additional channel or feature map early in the generator model.<\/p>\n<p>This can be achieved by using a learned embedding with an arbitrary number of dimensions (e.g. 50), the output of which can be interpreted by a fully connected layer with a linear activation resulting in one additional 7\u00d77 feature map.<\/p>\n<pre class=\"crayon-plain-tag\">...\r\n# label input\r\nin_label = Input(shape=(1,))\r\n# embedding for categorical input\r\nli = Embedding(n_classes, 50)(in_label)\r\n# linear multiplication\r\nn_nodes = 7 * 7\r\nli = Dense(n_nodes, kernel_initializer=init)(li)\r\n# reshape to additional channel\r\nli = Reshape((7, 7, 1))(li)<\/pre>\n<p>The point in latent space can be interpreted by a fully connected layer with sufficient activations to create multiple 7\u00d77 feature maps, in this case, 384, and provide the basis for a low-resolution version of our output image.<\/p>\n<p>The 7\u00d77 single feature map interpretation of the class label can then be channel-wise concatenated, resulting in 385 feature maps.<\/p>\n<pre class=\"crayon-plain-tag\">...\r\n# image generator input\r\nin_lat = Input(shape=(latent_dim,))\r\n# foundation for 7x7 image\r\nn_nodes = 384 * 7 * 7\r\ngen = Dense(n_nodes, kernel_initializer=init)(in_lat)\r\ngen = Activation('relu')(gen)\r\ngen = Reshape((7, 7, 384))(gen)\r\n# merge image gen and label input\r\nmerge = Concatenate()([gen, li])<\/pre>\n<p>These feature maps can then go through the process of two transpose convolutional layers to upsample the 7\u00d77 feature maps first to 14\u00d714 pixels, and then finally to 28\u00d728 features, quadrupling the area of the feature maps with each upscaling step.<\/p>\n<p>The output of the generator is a single feature map or grayscale image with the shape 28\u00d728 and pixel values in the range [-1, 1] given the choice of a tanh activation function. We use <a href=\"https:\/\/machinelearningmastery.com\/rectified-linear-activation-function-for-deep-learning-neural-networks\/\">ReLU activation<\/a> for the upscaling layers instead of LeakyReLU given the suggestion the AC-GAN paper.<\/p>\n<pre class=\"crayon-plain-tag\"># upsample to 14x14\r\ngen = Conv2DTranspose(192, (5,5), strides=(2,2), padding='same', kernel_initializer=init)(merge)\r\ngen = BatchNormalization()(gen)\r\ngen = Activation('relu')(gen)\r\n# upsample to 28x28\r\ngen = Conv2DTranspose(1, (5,5), strides=(2,2), padding='same', kernel_initializer=init)(gen)\r\nout_layer = Activation('tanh')(gen)<\/pre>\n<p>We can tie all of this together and into the define_generator() function defined below that will create and return the generator model for the AC-GAN.<\/p>\n<p>The model is intentionally not compiled as it is not trained directly; instead, it is trained via the discriminator model.<\/p>\n<pre class=\"crayon-plain-tag\"># define the standalone generator model\r\ndef define_generator(latent_dim, n_classes=10):\r\n\t# weight initialization\r\n\tinit = RandomNormal(stddev=0.02)\r\n\t# label input\r\n\tin_label = Input(shape=(1,))\r\n\t# embedding for categorical input\r\n\tli = Embedding(n_classes, 50)(in_label)\r\n\t# linear multiplication\r\n\tn_nodes = 7 * 7\r\n\tli = Dense(n_nodes, kernel_initializer=init)(li)\r\n\t# reshape to additional channel\r\n\tli = Reshape((7, 7, 1))(li)\r\n\t# image generator input\r\n\tin_lat = Input(shape=(latent_dim,))\r\n\t# foundation for 7x7 image\r\n\tn_nodes = 384 * 7 * 7\r\n\tgen = Dense(n_nodes, kernel_initializer=init)(in_lat)\r\n\tgen = Activation('relu')(gen)\r\n\tgen = Reshape((7, 7, 384))(gen)\r\n\t# merge image gen and label input\r\n\tmerge = Concatenate()([gen, li])\r\n\t# upsample to 14x14\r\n\tgen = Conv2DTranspose(192, (5,5), strides=(2,2), padding='same', kernel_initializer=init)(merge)\r\n\tgen = BatchNormalization()(gen)\r\n\tgen = Activation('relu')(gen)\r\n\t# upsample to 28x28\r\n\tgen = Conv2DTranspose(1, (5,5), strides=(2,2), padding='same', kernel_initializer=init)(gen)\r\n\tout_layer = Activation('tanh')(gen)\r\n\t# define model\r\n\tmodel = Model([in_lat, in_label], out_layer)\r\n\treturn model<\/pre>\n<p>We can create this model and summarize and plot its structure.<\/p>\n<p>The complete example is listed below.<\/p>\n<pre class=\"crayon-plain-tag\"># example of defining the generator model\r\nfrom keras.models import Model\r\nfrom keras.layers import Input\r\nfrom keras.layers import Dense\r\nfrom keras.layers import Reshape\r\nfrom keras.layers import Conv2DTranspose\r\nfrom keras.layers import Embedding\r\nfrom keras.layers import Concatenate\r\nfrom keras.layers import Activation\r\nfrom keras.layers import BatchNormalization\r\nfrom keras.initializers import RandomNormal\r\nfrom keras.utils.vis_utils import plot_model\r\n\r\n# define the standalone generator model\r\ndef define_generator(latent_dim, n_classes=10):\r\n\t# weight initialization\r\n\tinit = RandomNormal(stddev=0.02)\r\n\t# label input\r\n\tin_label = Input(shape=(1,))\r\n\t# embedding for categorical input\r\n\tli = Embedding(n_classes, 50)(in_label)\r\n\t# linear multiplication\r\n\tn_nodes = 7 * 7\r\n\tli = Dense(n_nodes, kernel_initializer=init)(li)\r\n\t# reshape to additional channel\r\n\tli = Reshape((7, 7, 1))(li)\r\n\t# image generator input\r\n\tin_lat = Input(shape=(latent_dim,))\r\n\t# foundation for 7x7 image\r\n\tn_nodes = 384 * 7 * 7\r\n\tgen = Dense(n_nodes, kernel_initializer=init)(in_lat)\r\n\tgen = Activation('relu')(gen)\r\n\tgen = Reshape((7, 7, 384))(gen)\r\n\t# merge image gen and label input\r\n\tmerge = Concatenate()([gen, li])\r\n\t# upsample to 14x14\r\n\tgen = Conv2DTranspose(192, (5,5), strides=(2,2), padding='same', kernel_initializer=init)(merge)\r\n\tgen = BatchNormalization()(gen)\r\n\tgen = Activation('relu')(gen)\r\n\t# upsample to 28x28\r\n\tgen = Conv2DTranspose(1, (5,5), strides=(2,2), padding='same', kernel_initializer=init)(gen)\r\n\tout_layer = Activation('tanh')(gen)\r\n\t# define model\r\n\tmodel = Model([in_lat, in_label], out_layer)\r\n\treturn model\r\n\r\n# define the size of the latent space\r\nlatent_dim = 100\r\n# define the generator model\r\nmodel = define_generator(latent_dim)\r\n# summarize the model\r\nmodel.summary()\r\n# plot the model\r\nplot_model(model, to_file='generator_plot.png', show_shapes=True, show_layer_names=True)<\/pre>\n<p>Running the example first prints a summary of the layers and their output shape in the model.<\/p>\n<p>We can confirm that the latent dimension input is 100 dimensions and that the class label input is a single integer. We can also confirm that the output of the <a href=\"https:\/\/machinelearningmastery.com\/what-are-word-embeddings\/\">embedding<\/a> class label is correctly concatenated as an additional channel resulting in 385 7\u00d77 feature maps prior to the transpose convolutional layers.<\/p>\n<p>The summary also confirms the expected output shape of a single grayscale 28\u00d728 image.<\/p>\n<pre class=\"crayon-plain-tag\">__________________________________________________________________________________________________\r\nLayer (type)                    Output Shape         Param #     Connected to\r\n==================================================================================================\r\ninput_2 (InputLayer)            (None, 100)          0\r\n__________________________________________________________________________________________________\r\ninput_1 (InputLayer)            (None, 1)            0\r\n__________________________________________________________________________________________________\r\ndense_2 (Dense)                 (None, 18816)        1900416     input_2[0][0]\r\n__________________________________________________________________________________________________\r\nembedding_1 (Embedding)         (None, 1, 50)        500         input_1[0][0]\r\n__________________________________________________________________________________________________\r\nactivation_1 (Activation)       (None, 18816)        0           dense_2[0][0]\r\n__________________________________________________________________________________________________\r\ndense_1 (Dense)                 (None, 1, 49)        2499        embedding_1[0][0]\r\n__________________________________________________________________________________________________\r\nreshape_2 (Reshape)             (None, 7, 7, 384)    0           activation_1[0][0]\r\n__________________________________________________________________________________________________\r\nreshape_1 (Reshape)             (None, 7, 7, 1)      0           dense_1[0][0]\r\n__________________________________________________________________________________________________\r\nconcatenate_1 (Concatenate)     (None, 7, 7, 385)    0           reshape_2[0][0]\r\n                                                                 reshape_1[0][0]\r\n__________________________________________________________________________________________________\r\nconv2d_transpose_1 (Conv2DTrans (None, 14, 14, 192)  1848192     concatenate_1[0][0]\r\n__________________________________________________________________________________________________\r\nbatch_normalization_1 (BatchNor (None, 14, 14, 192)  768         conv2d_transpose_1[0][0]\r\n__________________________________________________________________________________________________\r\nactivation_2 (Activation)       (None, 14, 14, 192)  0           batch_normalization_1[0][0]\r\n__________________________________________________________________________________________________\r\nconv2d_transpose_2 (Conv2DTrans (None, 28, 28, 1)    4801        activation_2[0][0]\r\n__________________________________________________________________________________________________\r\nactivation_3 (Activation)       (None, 28, 28, 1)    0           conv2d_transpose_2[0][0]\r\n==================================================================================================\r\nTotal params: 3,757,176\r\nTrainable params: 3,756,792\r\nNon-trainable params: 384\r\n__________________________________________________________________________________________________<\/pre>\n<p>A plot of the network is also created summarizing the input and output shapes for each layer.<\/p>\n<p>The plot confirms the two inputs to the network and the correct concatenation of the inputs.<\/p>\n<div id=\"attachment_8237\" style=\"width: 807px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8237\" class=\"size-large wp-image-8237\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-Generator-Model-for-the-Auxiliary-Classifier-GAN-797x1024.png\" alt=\"Plot of the Generator Model for the Auxiliary Classifier GAN\" width=\"797\" height=\"1024\" srcset=\"http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-Generator-Model-for-the-Auxiliary-Classifier-GAN-797x1024.png 797w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-Generator-Model-for-the-Auxiliary-Classifier-GAN-233x300.png 233w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-Generator-Model-for-the-Auxiliary-Classifier-GAN-768x987.png 768w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Plot-of-the-Generator-Model-for-the-Auxiliary-Classifier-GAN.png 832w\" sizes=\"(max-width: 797px) 100vw, 797px\"><\/p>\n<p id=\"caption-attachment-8237\" class=\"wp-caption-text\">Plot of the Generator Model for the Auxiliary Classifier GAN<\/p>\n<\/div>\n<p>Now that we have defined the generator model, we can show how it might be fit.<\/p>\n<h3>AC-GAN Composite Model<\/h3>\n<p>The generator model is not updated directly; instead, it is updated via the discriminator model.<\/p>\n<p>This can be achieved by creating a composite model that stacks the generator model on top of the discriminator model.<\/p>\n<p>The input to this composite model is the input to the generator model, namely a random point from the latent space and a class label. The generator model is connected directly to the discriminator model, which takes the generated image directly as input. Finally, the discriminator model predicts both the realness of the generated image and the class label. As such, the composite model is optimized using two loss functions, one for each output of the discriminator model.<\/p>\n<p>The discriminator model is updated in a standalone manner using real and fake examples, and we will review how to do this in the next section. Therefore, we do not want to update the discriminator model when updating (training) the composite model; we only want to use this composite model to update the weights of the generator model.<\/p>\n<p>This can be achieved by setting the layers of the discriminator as not trainable prior to compiling the composite model. This only has an effect on the layer weights when viewed or used by the composite model and prevents them from being updated when the composite model is updated.<\/p>\n<p>The <em>define_gan()<\/em> function below implements this, taking the already defined generator and discriminator models as input and defining a new composite model that can be used to update the generator model only.<\/p>\n<pre class=\"crayon-plain-tag\"># define the combined generator and discriminator model, for updating the generator\r\ndef define_gan(g_model, d_model):\r\n\t# make weights in the discriminator not trainable\r\n\td_model.trainable = False\r\n\t# connect the outputs of the generator to the inputs of the discriminator\r\n\tgan_output = d_model(g_model.output)\r\n\t# define gan model as taking noise and label and outputting real\/fake and label outputs\r\n\tmodel = Model(g_model.input, gan_output)\r\n\t# compile model\r\n\topt = Adam(lr=0.0002, beta_1=0.5)\r\n\tmodel.compile(loss=['binary_crossentropy', 'sparse_categorical_crossentropy'], optimizer=opt)\r\n\treturn model<\/pre>\n<p>Now that we have defined the models used in the AC-GAN, we can fit them on the Fashion-MNIST dataset.<\/p>\n<h2>How to Develop an AC-GAN for Fashion-MNIST<\/h2>\n<p>The first step is to load and prepare the Fashion MNIST dataset.<\/p>\n<p>We only require the images in the training dataset. The images are black and white, therefore we must add an additional channel dimension to transform them to be three dimensional, as expected by the convolutional layers of our models. Finally, the <a href=\"https:\/\/machinelearningmastery.com\/how-to-manually-scale-image-pixel-data-for-deep-learning\/\">pixel values must be scaled<\/a> to the range [-1,1] to match the output of the generator model.<\/p>\n<p>The <em>load_real_samples()<\/em> function below implements this, returning the loaded and scaled Fashion MNIST training dataset ready for modeling.<\/p>\n<pre class=\"crayon-plain-tag\"># load images\r\ndef load_real_samples():\r\n\t# load dataset\r\n\t(trainX, trainy), (_, _) = load_data()\r\n\t# expand to 3d, e.g. add channels\r\n\tX = expand_dims(trainX, axis=-1)\r\n\t# convert from ints to floats\r\n\tX = X.astype('float32')\r\n\t# scale from [0,255] to [-1,1]\r\n\tX = (X - 127.5) \/ 127.5\r\n\tprint(X.shape, trainy.shape)\r\n\treturn [X, trainy]<\/pre>\n<p>We will require one batch (or a half batch) of real images from the dataset each update to the GAN model. A simple way to achieve this is to select a random sample of images from the dataset each time.<\/p>\n<p>The <em>generate_real_samples()<\/em> function below implements this, taking the prepared dataset as an argument, selecting and returning a random sample of Fashion MNIST images and clothing class labels.<\/p>\n<p>The \u201c<em>dataset<\/em>\u201d argument provided to the function is a list comprised of the images and class labels as returned from the <em>load_real_samples()<\/em> function. The function also returns their corresponding class label for the discriminator, specifically class=1 indicating that they are real images.<\/p>\n<pre class=\"crayon-plain-tag\"># select real samples\r\ndef generate_real_samples(dataset, n_samples):\r\n\t# split into images and labels\r\n\timages, labels = dataset\r\n\t# choose random instances\r\n\tix = randint(0, images.shape[0], n_samples)\r\n\t# select images and labels\r\n\tX, labels = images[ix], labels[ix]\r\n\t# generate class labels\r\n\ty = ones((n_samples, 1))\r\n\treturn [X, labels], y<\/pre>\n<p>Next, we need inputs for the generator model.<\/p>\n<p>These are random points from the latent space, specifically <a href=\"https:\/\/machinelearningmastery.com\/how-to-generate-random-numbers-in-python\/\">Gaussian distributed random variables<\/a>.<\/p>\n<p>The <em>generate_latent_points()<\/em> function implements this, taking the size of the latent space as an argument and the number of points required, and returning them as a batch of input samples for the generator model. The function also returns randomly selected integers in [0,9] inclusively for the 10 class labels in the Fashion-MNIST dataset.<\/p>\n<pre class=\"crayon-plain-tag\"># generate points in latent space as input for the generator\r\ndef generate_latent_points(latent_dim, n_samples, n_classes=10):\r\n\t# generate points in the latent space\r\n\tx_input = randn(latent_dim * n_samples)\r\n\t# reshape into a batch of inputs for the network\r\n\tz_input = x_input.reshape(n_samples, latent_dim)\r\n\t# generate labels\r\n\tlabels = randint(0, n_classes, n_samples)\r\n\treturn [z_input, labels]<\/pre>\n<p>Next, we need to use the points in the latent space and clothing class labels as input to the generator in order to generate new images.<\/p>\n<p>The <em>generate_fake_samples()<\/em> function below implements this, taking the generator model and size of the latent space as arguments, then generating points in the latent space and using them as input to the generator model.<\/p>\n<p>The function returns the generated images, their corresponding clothing class label, and their discriminator class label, specifically class=0 to indicate they are fake or generated.<\/p>\n<pre class=\"crayon-plain-tag\"># use the generator to generate n fake examples, with class labels\r\ndef generate_fake_samples(generator, latent_dim, n_samples):\r\n\t# generate points in latent space\r\n\tz_input, labels_input = generate_latent_points(latent_dim, n_samples)\r\n\t# predict outputs\r\n\timages = generator.predict([z_input, labels_input])\r\n\t# create class labels\r\n\ty = zeros((n_samples, 1))\r\n\treturn [images, labels_input], y<\/pre>\n<p>There are no reliable ways to determine when to stop training a GAN; instead, images can be subjectively inspected in order to choose a final model.<\/p>\n<p>Therefore, we can periodically generate a sample of images using the generator model and save the generator model to file for later use. The <em>summarize_performance()<\/em> function below implements this, generating 100 images, plotting them, and saving the plot and the generator to file with a filename that includes the training \u201c<em>step<\/em>\u201d number.<\/p>\n<pre class=\"crayon-plain-tag\"># generate samples and save as a plot and save the model\r\ndef summarize_performance(step, g_model, latent_dim, n_samples=100):\r\n\t# prepare fake examples\r\n\t[X, _], _ = generate_fake_samples(g_model, latent_dim, n_samples)\r\n\t# scale from [-1,1] to [0,1]\r\n\tX = (X + 1) \/ 2.0\r\n\t# plot images\r\n\tfor i in range(100):\r\n\t\t# define subplot\r\n\t\tpyplot.subplot(10, 10, 1 + i)\r\n\t\t# turn off axis\r\n\t\tpyplot.axis('off')\r\n\t\t# plot raw pixel data\r\n\t\tpyplot.imshow(X[i, :, :, 0], cmap='gray_r')\r\n\t# save plot to file\r\n\tfilename1 = 'generated_plot_%04d.png' % (step+1)\r\n\tpyplot.savefig(filename1)\r\n\tpyplot.close()\r\n\t# save the generator model\r\n\tfilename2 = 'model_%04d.h5' % (step+1)\r\n\tg_model.save(filename2)\r\n\tprint('>Saved: %s and %s' % (filename1, filename2))<\/pre>\n<p>We are now ready to fit the GAN models.<\/p>\n<p>The model is fit for 100 training epochs, which is arbitrary, as the model begins generating plausible items of clothing after perhaps 20 epochs. A batch size of 64 samples is used, and each training epoch involves 60,000\/64, or about 937, batches of real and fake samples and updates to the model. The <em>summarize_performance()<\/em> function is called every 10 epochs, or every (937 * 10) 9,370 training steps.<\/p>\n<p>For a given training step, first the discriminator model is updated for a half batch of real samples, then a half batch of fake samples, together forming one batch of weight updates. The generator is then updated via the combined GAN model. Importantly, the class label is set to 1, or real, for the fake samples. This has the effect of updating the generator toward getting better at generating real samples on the next batch.<\/p>\n<p>Note, the discriminator and composite model return three loss values from the call to the <em>train_on_batch()<\/em> function. The first value is the sum of the loss values and can be ignored, whereas the second value is the loss for the real\/fake output layer and the third value is the loss for the clothing label classification.<\/p>\n<p>The <em>train()<\/em> function below implements this, taking the defined models, dataset, and size of the latent dimension as arguments and parameterizing the number of epochs and batch size with default arguments. The generator model is saved at the end of training.<\/p>\n<pre class=\"crayon-plain-tag\"># train the generator and discriminator\r\ndef train(g_model, d_model, gan_model, dataset, latent_dim, n_epochs=100, n_batch=64):\r\n\t# calculate the number of batches per training epoch\r\n\tbat_per_epo = int(dataset[0].shape[0] \/ n_batch)\r\n\t# calculate the number of training iterations\r\n\tn_steps = bat_per_epo * n_epochs\r\n\t# calculate the size of half a batch of samples\r\n\thalf_batch = int(n_batch \/ 2)\r\n\t# manually enumerate epochs\r\n\tfor i in range(n_steps):\r\n\t\t# get randomly selected 'real' samples\r\n\t\t[X_real, labels_real], y_real = generate_real_samples(dataset, half_batch)\r\n\t\t# update discriminator model weights\r\n\t\t_,d_r1,d_r2 = d_model.train_on_batch(X_real, [y_real, labels_real])\r\n\t\t# generate 'fake' examples\r\n\t\t[X_fake, labels_fake], y_fake = generate_fake_samples(g_model, latent_dim, half_batch)\r\n\t\t# update discriminator model weights\r\n\t\t_,d_f,d_f2 = d_model.train_on_batch(X_fake, [y_fake, labels_fake])\r\n\t\t# prepare points in latent space as input for the generator\r\n\t\t[z_input, z_labels] = generate_latent_points(latent_dim, n_batch)\r\n\t\t# create inverted labels for the fake samples\r\n\t\ty_gan = ones((n_batch, 1))\r\n\t\t# update the generator via the discriminator's error\r\n\t\t_,g_1,g_2 = gan_model.train_on_batch([z_input, z_labels], [y_gan, z_labels])\r\n\t\t# summarize loss on this batch\r\n\t\tprint('>%d, dr[%.3f,%.3f], df[%.3f,%.3f], g[%.3f,%.3f]' % (i+1, d_r1,d_r2, d_f,d_f2, g_1,g_2))\r\n\t\t# evaluate the model performance every 'epoch'\r\n\t\tif (i+1) % (bat_per_epo * 10) == 0:\r\n\t\t\tsummarize_performance(i, g_model, latent_dim)<\/pre>\n<p>We can then define the size of the latent space, define all three models, and train them on the loaded fashion MNIST dataset.<\/p>\n<pre class=\"crayon-plain-tag\"># size of the latent space\r\nlatent_dim = 100\r\n# create the discriminator\r\ndiscriminator = define_discriminator()\r\n# create the generator\r\ngenerator = define_generator(latent_dim)\r\n# create the gan\r\ngan_model = define_gan(generator, discriminator)\r\n# load image data\r\ndataset = load_real_samples()\r\n# train model\r\ntrain(generator, discriminator, gan_model, dataset, latent_dim)<\/pre>\n<p>Tying all of this together, the complete example is listed below.<\/p>\n<pre class=\"crayon-plain-tag\"># example of fitting an auxiliary classifier gan (ac-gan) on fashion mnsit\r\nfrom numpy import zeros\r\nfrom numpy import ones\r\nfrom numpy import expand_dims\r\nfrom numpy.random import randn\r\nfrom numpy.random import randint\r\nfrom keras.datasets.fashion_mnist import load_data\r\nfrom keras.optimizers import Adam\r\nfrom keras.models import Model\r\nfrom keras.layers import Input\r\nfrom keras.layers import Dense\r\nfrom keras.layers import Reshape\r\nfrom keras.layers import Flatten\r\nfrom keras.layers import Conv2D\r\nfrom keras.layers import Conv2DTranspose\r\nfrom keras.layers import LeakyReLU\r\nfrom keras.layers import BatchNormalization\r\nfrom keras.layers import Dropout\r\nfrom keras.layers import Embedding\r\nfrom keras.layers import Activation\r\nfrom keras.layers import Concatenate\r\nfrom keras.initializers import RandomNormal\r\nfrom matplotlib import pyplot\r\n\r\n# define the standalone discriminator model\r\ndef define_discriminator(in_shape=(28,28,1), n_classes=10):\r\n\t# weight initialization\r\n\tinit = RandomNormal(stddev=0.02)\r\n\t# image input\r\n\tin_image = Input(shape=in_shape)\r\n\t# downsample to 14x14\r\n\tfe = Conv2D(32, (3,3), strides=(2,2), padding='same', kernel_initializer=init)(in_image)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# normal\r\n\tfe = Conv2D(64, (3,3), padding='same', kernel_initializer=init)(fe)\r\n\tfe = BatchNormalization()(fe)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# downsample to 7x7\r\n\tfe = Conv2D(128, (3,3), strides=(2,2), padding='same', kernel_initializer=init)(fe)\r\n\tfe = BatchNormalization()(fe)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# normal\r\n\tfe = Conv2D(256, (3,3), padding='same', kernel_initializer=init)(fe)\r\n\tfe = BatchNormalization()(fe)\r\n\tfe = LeakyReLU(alpha=0.2)(fe)\r\n\tfe = Dropout(0.5)(fe)\r\n\t# flatten feature maps\r\n\tfe = Flatten()(fe)\r\n\t# real\/fake output\r\n\tout1 = Dense(1, activation='sigmoid')(fe)\r\n\t# class label output\r\n\tout2 = Dense(n_classes, activation='softmax')(fe)\r\n\t# define model\r\n\tmodel = Model(in_image, [out1, out2])\r\n\t# compile model\r\n\topt = Adam(lr=0.0002, beta_1=0.5)\r\n\tmodel.compile(loss=['binary_crossentropy', 'sparse_categorical_crossentropy'], optimizer=opt)\r\n\treturn model\r\n\r\n# define the standalone generator model\r\ndef define_generator(latent_dim, n_classes=10):\r\n\t# weight initialization\r\n\tinit = RandomNormal(stddev=0.02)\r\n\t# label input\r\n\tin_label = Input(shape=(1,))\r\n\t# embedding for categorical input\r\n\tli = Embedding(n_classes, 50)(in_label)\r\n\t# linear multiplication\r\n\tn_nodes = 7 * 7\r\n\tli = Dense(n_nodes, kernel_initializer=init)(li)\r\n\t# reshape to additional channel\r\n\tli = Reshape((7, 7, 1))(li)\r\n\t# image generator input\r\n\tin_lat = Input(shape=(latent_dim,))\r\n\t# foundation for 7x7 image\r\n\tn_nodes = 384 * 7 * 7\r\n\tgen = Dense(n_nodes, kernel_initializer=init)(in_lat)\r\n\tgen = Activation('relu')(gen)\r\n\tgen = Reshape((7, 7, 384))(gen)\r\n\t# merge image gen and label input\r\n\tmerge = Concatenate()([gen, li])\r\n\t# upsample to 14x14\r\n\tgen = Conv2DTranspose(192, (5,5), strides=(2,2), padding='same', kernel_initializer=init)(merge)\r\n\tgen = BatchNormalization()(gen)\r\n\tgen = Activation('relu')(gen)\r\n\t# upsample to 28x28\r\n\tgen = Conv2DTranspose(1, (5,5), strides=(2,2), padding='same', kernel_initializer=init)(gen)\r\n\tout_layer = Activation('tanh')(gen)\r\n\t# define model\r\n\tmodel = Model([in_lat, in_label], out_layer)\r\n\treturn model\r\n\r\n# define the combined generator and discriminator model, for updating the generator\r\ndef define_gan(g_model, d_model):\r\n\t# make weights in the discriminator not trainable\r\n\td_model.trainable = False\r\n\t# connect the outputs of the generator to the inputs of the discriminator\r\n\tgan_output = d_model(g_model.output)\r\n\t# define gan model as taking noise and label and outputting real\/fake and label outputs\r\n\tmodel = Model(g_model.input, gan_output)\r\n\t# compile model\r\n\topt = Adam(lr=0.0002, beta_1=0.5)\r\n\tmodel.compile(loss=['binary_crossentropy', 'sparse_categorical_crossentropy'], optimizer=opt)\r\n\treturn model\r\n\r\n# load images\r\ndef load_real_samples():\r\n\t# load dataset\r\n\t(trainX, trainy), (_, _) = load_data()\r\n\t# expand to 3d, e.g. add channels\r\n\tX = expand_dims(trainX, axis=-1)\r\n\t# convert from ints to floats\r\n\tX = X.astype('float32')\r\n\t# scale from [0,255] to [-1,1]\r\n\tX = (X - 127.5) \/ 127.5\r\n\tprint(X.shape, trainy.shape)\r\n\treturn [X, trainy]\r\n\r\n# select real samples\r\ndef generate_real_samples(dataset, n_samples):\r\n\t# split into images and labels\r\n\timages, labels = dataset\r\n\t# choose random instances\r\n\tix = randint(0, images.shape[0], n_samples)\r\n\t# select images and labels\r\n\tX, labels = images[ix], labels[ix]\r\n\t# generate class labels\r\n\ty = ones((n_samples, 1))\r\n\treturn [X, labels], y\r\n\r\n# generate points in latent space as input for the generator\r\ndef generate_latent_points(latent_dim, n_samples, n_classes=10):\r\n\t# generate points in the latent space\r\n\tx_input = randn(latent_dim * n_samples)\r\n\t# reshape into a batch of inputs for the network\r\n\tz_input = x_input.reshape(n_samples, latent_dim)\r\n\t# generate labels\r\n\tlabels = randint(0, n_classes, n_samples)\r\n\treturn [z_input, labels]\r\n\r\n# use the generator to generate n fake examples, with class labels\r\ndef generate_fake_samples(generator, latent_dim, n_samples):\r\n\t# generate points in latent space\r\n\tz_input, labels_input = generate_latent_points(latent_dim, n_samples)\r\n\t# predict outputs\r\n\timages = generator.predict([z_input, labels_input])\r\n\t# create class labels\r\n\ty = zeros((n_samples, 1))\r\n\treturn [images, labels_input], y\r\n\r\n# generate samples and save as a plot and save the model\r\ndef summarize_performance(step, g_model, latent_dim, n_samples=100):\r\n\t# prepare fake examples\r\n\t[X, _], _ = generate_fake_samples(g_model, latent_dim, n_samples)\r\n\t# scale from [-1,1] to [0,1]\r\n\tX = (X + 1) \/ 2.0\r\n\t# plot images\r\n\tfor i in range(100):\r\n\t\t# define subplot\r\n\t\tpyplot.subplot(10, 10, 1 + i)\r\n\t\t# turn off axis\r\n\t\tpyplot.axis('off')\r\n\t\t# plot raw pixel data\r\n\t\tpyplot.imshow(X[i, :, :, 0], cmap='gray_r')\r\n\t# save plot to file\r\n\tfilename1 = 'generated_plot_%04d.png' % (step+1)\r\n\tpyplot.savefig(filename1)\r\n\tpyplot.close()\r\n\t# save the generator model\r\n\tfilename2 = 'model_%04d.h5' % (step+1)\r\n\tg_model.save(filename2)\r\n\tprint('>Saved: %s and %s' % (filename1, filename2))\r\n\r\n# train the generator and discriminator\r\ndef train(g_model, d_model, gan_model, dataset, latent_dim, n_epochs=100, n_batch=64):\r\n\t# calculate the number of batches per training epoch\r\n\tbat_per_epo = int(dataset[0].shape[0] \/ n_batch)\r\n\t# calculate the number of training iterations\r\n\tn_steps = bat_per_epo * n_epochs\r\n\t# calculate the size of half a batch of samples\r\n\thalf_batch = int(n_batch \/ 2)\r\n\t# manually enumerate epochs\r\n\tfor i in range(n_steps):\r\n\t\t# get randomly selected 'real' samples\r\n\t\t[X_real, labels_real], y_real = generate_real_samples(dataset, half_batch)\r\n\t\t# update discriminator model weights\r\n\t\t_,d_r1,d_r2 = d_model.train_on_batch(X_real, [y_real, labels_real])\r\n\t\t# generate 'fake' examples\r\n\t\t[X_fake, labels_fake], y_fake = generate_fake_samples(g_model, latent_dim, half_batch)\r\n\t\t# update discriminator model weights\r\n\t\t_,d_f,d_f2 = d_model.train_on_batch(X_fake, [y_fake, labels_fake])\r\n\t\t# prepare points in latent space as input for the generator\r\n\t\t[z_input, z_labels] = generate_latent_points(latent_dim, n_batch)\r\n\t\t# create inverted labels for the fake samples\r\n\t\ty_gan = ones((n_batch, 1))\r\n\t\t# update the generator via the discriminator's error\r\n\t\t_,g_1,g_2 = gan_model.train_on_batch([z_input, z_labels], [y_gan, z_labels])\r\n\t\t# summarize loss on this batch\r\n\t\tprint('>%d, dr[%.3f,%.3f], df[%.3f,%.3f], g[%.3f,%.3f]' % (i+1, d_r1,d_r2, d_f,d_f2, g_1,g_2))\r\n\t\t# evaluate the model performance every 'epoch'\r\n\t\tif (i+1) % (bat_per_epo * 10) == 0:\r\n\t\t\tsummarize_performance(i, g_model, latent_dim)\r\n\r\n# size of the latent space\r\nlatent_dim = 100\r\n# create the discriminator\r\ndiscriminator = define_discriminator()\r\n# create the generator\r\ngenerator = define_generator(latent_dim)\r\n# create the gan\r\ngan_model = define_gan(generator, discriminator)\r\n# load image data\r\ndataset = load_real_samples()\r\n# train model\r\ntrain(generator, discriminator, gan_model, dataset, latent_dim)<\/pre>\n<p>Running the example may take some time, and GPU hardware is recommended, but not required.<\/p>\n<p><strong>Note<\/strong>: Given the stochastic nature of the training algorithm, your results may vary. Try running the example a few times.<\/p>\n<p>The loss is reported each training iteration, including the real\/fake and class loss for the discriminator on real examples (dr), the discriminator on fake examples (df), and the generator updated via the composite model when generating images (g).<\/p>\n<pre class=\"crayon-plain-tag\">>1, dr[0.934,2.967], df[1.310,3.006], g[0.878,3.368]\r\n>2, dr[0.711,2.836], df[0.939,3.262], g[0.947,2.751]\r\n>3, dr[0.649,2.980], df[1.001,3.147], g[0.844,3.226]\r\n>4, dr[0.732,3.435], df[0.823,3.715], g[1.048,3.292]\r\n>5, dr[0.860,3.076], df[0.591,2.799], g[1.123,3.313]\r\n...<\/pre>\n<p>A total of 10 sample images are generated and 10 models saved over the run.<\/p>\n<p>Plots of generated clothing after 10 iterations already look plausible.<\/p>\n<div id=\"attachment_8238\" style=\"width: 650px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8238\" class=\"size-full wp-image-8238\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2019\/05\/Example-of-AC-GAN-Generated-Items-of-Clothing-after-10-Epochs.png\" alt=\"Example of AC-GAN Generated Items of Clothing after 10 Epochs\" width=\"640\" height=\"480\" srcset=\"http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-AC-GAN-Generated-Items-of-Clothing-after-10-Epochs.png 640w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-AC-GAN-Generated-Items-of-Clothing-after-10-Epochs-300x225.png 300w\" sizes=\"(max-width: 640px) 100vw, 640px\"><\/p>\n<p id=\"caption-attachment-8238\" class=\"wp-caption-text\">Example of AC-GAN Generated Items of Clothing after 10 Epochs<\/p>\n<\/div>\n<p>The images remain reliable throughout the training process.<\/p>\n<div id=\"attachment_8239\" style=\"width: 650px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8239\" class=\"size-full wp-image-8239\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2019\/05\/Example-of-AC-GAN-Generated-Items-of-Clothing-after-100-Epochs.png\" alt=\"Example of AC-GAN Generated Items of Clothing After 100 Epochs\" width=\"640\" height=\"480\" srcset=\"http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-AC-GAN-Generated-Items-of-Clothing-after-100-Epochs.png 640w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-AC-GAN-Generated-Items-of-Clothing-after-100-Epochs-300x225.png 300w\" sizes=\"(max-width: 640px) 100vw, 640px\"><\/p>\n<p id=\"caption-attachment-8239\" class=\"wp-caption-text\">Example of AC-GAN Generated Items of Clothing After 100 Epochs<\/p>\n<\/div>\n<h2>How to Generate Items of Clothing With the AC-GAN<\/h2>\n<p>In this section, we can load a saved model and use it to generate new items of clothing that plausibly could have come from the Fashion-MNIST dataset.<\/p>\n<p>The AC-GAN technically does not conditionally generate images based on the class label, at least not in the same way as the conditional GAN.<\/p>\n<blockquote>\n<p>AC-GANs learn a representation for z that is independent of class label.<\/p>\n<\/blockquote>\n<p>\u2014 <a href=\"https:\/\/arxiv.org\/abs\/1610.09585\">Conditional Image Synthesis With Auxiliary Classifier GANs<\/a>, 2016.<\/p>\n<p>Nevertheless, if used in this way, the generated images mostly match the class label.<\/p>\n<p>The example below loads the model from the end of the run (any saved model would do), and generates 100 examples of class 7 (sneaker).<\/p>\n<pre class=\"crayon-plain-tag\"># example of loading the generator model and generating images\r\nfrom math import sqrt\r\nfrom numpy import asarray\r\nfrom numpy.random import randn\r\nfrom keras.models import load_model\r\nfrom matplotlib import pyplot\r\n\r\n# generate points in latent space as input for the generator\r\ndef generate_latent_points(latent_dim, n_samples, n_class):\r\n\t# generate points in the latent space\r\n\tx_input = randn(latent_dim * n_samples)\r\n\t# reshape into a batch of inputs for the network\r\n\tz_input = x_input.reshape(n_samples, latent_dim)\r\n\t# generate labels\r\n\tlabels = asarray([n_class for _ in range(n_samples)])\r\n\treturn [z_input, labels]\r\n\r\n# create and save a plot of generated images\r\ndef save_plot(examples, n_examples):\r\n\t# plot images\r\n\tfor i in range(n_examples):\r\n\t\t# define subplot\r\n\t\tpyplot.subplot(sqrt(n_examples), sqrt(n_examples), 1 + i)\r\n\t\t# turn off axis\r\n\t\tpyplot.axis('off')\r\n\t\t# plot raw pixel data\r\n\t\tpyplot.imshow(examples[i, :, :, 0], cmap='gray_r')\r\n\tpyplot.show()\r\n\r\n# load model\r\nmodel = load_model('model_93700.h5')\r\nlatent_dim = 100\r\nn_examples = 100 # must be a square\r\nn_class = 7 # sneaker\r\n# generate images\r\nlatent_points, labels = generate_latent_points(latent_dim, n_examples, n_class)\r\n# generate images\r\nX  = model.predict([latent_points, labels])\r\n# scale from [-1,1] to [0,1]\r\nX = (X + 1) \/ 2.0\r\n# plot the result\r\nsave_plot(X, n_examples)<\/pre>\n<p>Running the example, in this case, generates 100 very plausible photos of sneakers.<\/p>\n<div id=\"attachment_8240\" style=\"width: 1034px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8240\" class=\"size-large wp-image-8240\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2019\/05\/Example-of-100-Photos-of-Sneakers-Generated-by-an-AC-GAN-1024x768.png\" alt=\"Example of 100 Photos of Sneakers Generated by an AC-GAN\" width=\"1024\" height=\"768\" srcset=\"http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-100-Photos-of-Sneakers-Generated-by-an-AC-GAN-1024x768.png 1024w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-100-Photos-of-Sneakers-Generated-by-an-AC-GAN-300x225.png 300w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-100-Photos-of-Sneakers-Generated-by-an-AC-GAN-768x576.png 768w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-100-Photos-of-Sneakers-Generated-by-an-AC-GAN.png 1280w\" sizes=\"(max-width: 1024px) 100vw, 1024px\"><\/p>\n<p id=\"caption-attachment-8240\" class=\"wp-caption-text\">Example of 100 Photos of Sneakers Generated by an AC-GAN<\/p>\n<\/div>\n<p>It may be fun to experiment with other class values.<\/p>\n<p>For example, below are 100 generated coats (n_class = 4). Most of the images are coats, although there are a few pants in there, showing that the latent space is partially, but not completely, class-conditional.<\/p>\n<div id=\"attachment_8241\" style=\"width: 1034px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8241\" class=\"size-large wp-image-8241\" src=\"https:\/\/machinelearningmastery.com\/wp-content\/uploads\/2019\/05\/Example-of-100-Photos-of-Coats-Generated-by-an-AC-GAN-1024x768.png\" alt=\"Example of 100 Photos of Coats Generated by an AC-GAN\" width=\"1024\" height=\"768\" srcset=\"http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-100-Photos-of-Coats-Generated-by-an-AC-GAN-1024x768.png 1024w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-100-Photos-of-Coats-Generated-by-an-AC-GAN-300x225.png 300w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-100-Photos-of-Coats-Generated-by-an-AC-GAN-768x576.png 768w, http:\/\/3qeqpr26caki16dnhd19sv6by6v.wpengine.netdna-cdn.com\/wp-content\/uploads\/2019\/05\/Example-of-100-Photos-of-Coats-Generated-by-an-AC-GAN.png 1280w\" sizes=\"(max-width: 1024px) 100vw, 1024px\"><\/p>\n<p id=\"caption-attachment-8241\" class=\"wp-caption-text\">Example of 100 Photos of Coats Generated by an AC-GAN<\/p>\n<\/div>\n<h2>Extensions<\/h2>\n<p>This section lists some ideas for extending the tutorial that you may wish to explore.<\/p>\n<ul>\n<li><strong>Generate Images<\/strong>. Generate images for each clothing class and compare results across different saved models (e.g. epoch 10, 20, etc.).<\/li>\n<li><strong>Alternate Configuration<\/strong>. Update the configuration of the generator, discriminator, or both models to have more or less capacity and compare results.<\/li>\n<li><strong>CIFAR-10 Dataset<\/strong>. Update the example to train on the CIFAR-10 dataset and use model configuration described in the appendix of the paper.<\/li>\n<\/ul>\n<p>If you explore any of these extensions, I\u2019d love to know.<br \/>\nPost your findings in the comments below.<\/p>\n<h2>Further Reading<\/h2>\n<p>This section provides more resources on the topic if you are looking to go deeper.<\/p>\n<h3>Papers<\/h3>\n<ul>\n<li><a href=\"https:\/\/arxiv.org\/abs\/1610.09585\">Conditional Image Synthesis With Auxiliary Classifier GANs, 2016<\/a>.<\/li>\n<li><a href=\"https:\/\/openreview.net\/forum?id=rJXTf9Bxg\">Conditional Image Synthesis With Auxiliary Classifier GANs, Reviewer Comments<\/a>.<\/li>\n<li><a href=\"https:\/\/www.youtube.com\/watch?v=myP2TN0_MaE\">Conditional Image Synthesis with Auxiliary Classifier GANs, NIPS 2016, YouTube<\/a>.<\/li>\n<\/ul>\n<h3>API<\/h3>\n<ul>\n<li><a href=\"https:\/\/keras.io\/datasets\/\">Keras Datasets API.<\/a><\/li>\n<li><a href=\"https:\/\/keras.io\/models\/sequential\/\">Keras Sequential Model API<\/a><\/li>\n<li><a href=\"https:\/\/keras.io\/layers\/convolutional\/\">Keras Convolutional Layers API<\/a><\/li>\n<li><a href=\"https:\/\/keras.io\/getting-started\/faq\/#how-can-i-freeze-keras-layers\">How can I \u201cfreeze\u201d Keras layers?<\/a><\/li>\n<li><a href=\"https:\/\/matplotlib.org\/api\/\">MatplotLib API<\/a><\/li>\n<li><a href=\"https:\/\/docs.scipy.org\/doc\/numpy\/reference\/routines.random.html\">NumPy Random sampling (numpy.random) API<\/a><\/li>\n<li><a href=\"https:\/\/docs.scipy.org\/doc\/numpy\/reference\/routines.array-manipulation.html\">NumPy Array manipulation routines<\/a><\/li>\n<\/ul>\n<h3>Articles<\/h3>\n<ul>\n<li><a href=\"https:\/\/github.com\/soumith\/ganhacks\">How to Train a GAN? Tips and tricks to make GANs work<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/zalandoresearch\/fashion-mnist\">Fashion-MNIST Project, GitHub.<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/eriklindernoren\/Keras-GAN\/blob\/master\/acgan\/acgan.py\">AC-GAN, Keras GAN Project<\/a>.<\/li>\n<li><a href=\"https:\/\/github.com\/keras-team\/keras\/blob\/master\/examples\/mnist_acgan.py\">AC-GAN, Keras Example<\/a>.<\/li>\n<\/ul>\n<h2>Summary<\/h2>\n<p>In this tutorial, you discovered how to develop an auxiliary classifier generative adversarial network for generating photographs of clothing.<\/p>\n<p>Specifically, you learned:<\/p>\n<ul>\n<li>The auxiliary classifier GAN is a type of conditional GAN that requires that the discriminator predict the class label of a given image.<\/li>\n<li>How to develop generator, discriminator, and composite models for the AC-GAN.<\/li>\n<li>How to train, evaluate, and use an AC-GAN to generate photographs of clothing from the Fashion-MNIST dataset.<\/li>\n<\/ul>\n<p>Do you have any questions?<br \/>\nAsk your questions in the comments below and I will do my best to answer.<\/p>\n<p>The post <a rel=\"nofollow\" href=\"https:\/\/machinelearningmastery.com\/how-to-develop-an-auxiliary-classifier-gan-ac-gan-from-scratch-with-keras\/\">How to Develop an Auxiliary Classifier GAN (AC-GAN) From Scratch with Keras<\/a> appeared first on <a rel=\"nofollow\" href=\"https:\/\/machinelearningmastery.com\/\">Machine Learning Mastery<\/a>.<\/p>\n<\/div>\n<p><a href=\"https:\/\/machinelearningmastery.com\/how-to-develop-an-auxiliary-classifier-gan-ac-gan-from-scratch-with-keras\/\">Go to Source<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Author: Jason Brownlee Generative Adversarial Networks, or GANs, are an architecture for training generative models, such as deep convolutional neural networks for generating images. The [&hellip;] <span class=\"read-more-link\"><a class=\"read-more\" href=\"https:\/\/www.aiproblog.com\/index.php\/2019\/07\/18\/how-to-develop-an-auxiliary-classifier-gan-ac-gan-from-scratch-with-keras\/\">Read More<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":2379,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"footnotes":""},"categories":[24],"tags":[],"_links":{"self":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts\/2378"}],"collection":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/comments?post=2378"}],"version-history":[{"count":0,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/posts\/2378\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/media\/2379"}],"wp:attachment":[{"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/media?parent=2378"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/categories?post=2378"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.aiproblog.com\/index.php\/wp-json\/wp\/v2\/tags?post=2378"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}