function output = ImageGenerationExperiment_training1(params,monitor)

output.generator = [];
output.discriminator = [];
output.executionEnvironment = "auto";

monitor.Status = "Loading Data";

url = "http://download.tensorflow.org/example_images/flower_photos.tgz";
downloadFolder = tempdir;
filename = fullfile(downloadFolder,"flower_dataset.tgz");

imageFolder = fullfile(downloadFolder,"flower_photos");
if ~exist(imageFolder,"dir")
    websave(filename,url);
    untar(filename,downloadFolder)
end

datasetFolder = fullfile(imageFolder);
imdsTrain = imageDatastore(datasetFolder, ...
    IncludeSubfolders=true);

augmenter = imageDataAugmenter(RandXReflection=true);
augimdsTrain = augmentedImageDatastore([64 64],imdsTrain, ...
    DataAugmentation=augmenter);

monitor.Status = "Creating Generator";

filterSize = 5;
numFilters = 64;
numLatentInputs = 100;
projectionSize = [4 4 512];

layersGenerator = [
    featureInputLayer(numLatentInputs,Name="in")
    projectAndReshapeLayer(projectionSize,numLatentInputs,Name="proj")
    transposedConv2dLayer(filterSize,4*numFilters,Name="tconv1")
    batchNormalizationLayer(Name="bnorm1")
    reluLayer(Name="relu1")
    transposedConv2dLayer(filterSize,2*numFilters,Stride=2,Cropping="same",Name="tconv2")
    batchNormalizationLayer(Name="bnorm2")
    reluLayer(Name="relu2")
    transposedConv2dLayer(filterSize,numFilters,Stride=2,Cropping="same",Name="tconv3")
    batchNormalizationLayer(Name="bnorm3")
    reluLayer(Name="relu3")
    transposedConv2dLayer(filterSize,3,Stride=2,Cropping="same",Name="tconv4")
    tanhLayer(Name="tanh")];

lgraphGenerator = layerGraph(layersGenerator);
output.generator = dlnetwork(lgraphGenerator);

monitor.Status = "Creating Discriminator";

filterSize = 5;
numFilters = 64;
inputSize = [64 64 3];
dropoutProb = params.dropoutProb;
scale = 0.2;

layersDiscriminator = [
    imageInputLayer(inputSize,Normalization="none",Name="in")
    dropoutLayer(dropoutProb,Name="dropout")
    convolution2dLayer(filterSize,numFilters,Stride=2,Padding="same",Name="conv1")
    leakyReluLayer(scale,Name="lrelu1")
    convolution2dLayer(filterSize,2*numFilters,Stride=2,Padding="same",Name="conv2")
    batchNormalizationLayer(Name="bn2")
    leakyReluLayer(scale,Name="lrelu2")
    convolution2dLayer(filterSize,4*numFilters,Stride=2,Padding="same",Name="conv3")
    batchNormalizationLayer(Name="bn3")
    leakyReluLayer(scale,Name="lrelu3")
    convolution2dLayer(filterSize,8*numFilters,Stride=2,Padding="same",Name="conv4")
    batchNormalizationLayer(Name="bn4")
    leakyReluLayer(scale,Name="lrelu4")
    convolution2dLayer(4,1,Name="conv5")];

lgraphDiscriminator = layerGraph(layersDiscriminator);
output.discriminator = dlnetwork(lgraphDiscriminator);

numEpochs = 50;
miniBatchSize = 128;
initialLearnRate = 0.0002;
gradientDecayFactor = 0.5;
squaredGradientDecayFactor = 0.999;
trailingAvgGenerator = [];
trailingAvgSqGenerator = [];
trailingAvgDiscriminator = [];
trailingAvgSqDiscriminator = [];
flipFactor = params.flipFactor;

monitor.Metrics = ["scoreGenerator","scoreDiscriminator","scoreCombined"];
monitor.XLabel = "Iteration";
groupSubPlot(monitor,"Combined Score","scoreCombined");
groupSubPlot(monitor,"Generator and Discriminator Scores", ...
    ["scoreGenerator","scoreDiscriminator"]);
monitor.Status = "Training";

augimdsTrain.MiniBatchSize = miniBatchSize;
mbq = minibatchqueue(augimdsTrain,...
    MiniBatchSize=miniBatchSize,...
    PartialMiniBatch="discard",...
    MiniBatchFcn=@preprocessMiniBatch,...
    MiniBatchFormat="SSCB",...
    OutputEnvironment=output.executionEnvironment);

iteration = 0;
for epoch = 1:numEpochs
    shuffle(mbq);
    while hasdata(mbq)
        iteration = iteration + 1;
        dlX = next(mbq);
        
        Z = randn(numLatentInputs,miniBatchSize,"single");
        dlZ = dlarray(Z,"CB");
        
        if (output.executionEnvironment == "auto" && canUseGPU) || ...
                output.executionEnvironment == "gpu"
            dlZ = gpuArray(dlZ);
        end
        
        [gradientsGenerator,gradientsDiscriminator,stateGenerator,scoreGenerator,scoreDiscriminator] = ...
            dlfeval(@modelGradients,output.generator,output.discriminator,dlX,dlZ,flipFactor);
        output.generator.State = stateGenerator;
        
        [output.discriminator,trailingAvgDiscriminator,trailingAvgSqDiscriminator] = ...
            adamupdate(output.discriminator,gradientsDiscriminator, ...
            trailingAvgDiscriminator,trailingAvgSqDiscriminator,iteration, ...
            initialLearnRate,gradientDecayFactor,squaredGradientDecayFactor);
        
        [output.generator,trailingAvgGenerator,trailingAvgSqGenerator] = ...
            adamupdate(output.generator,gradientsGenerator, ...
            trailingAvgGenerator,trailingAvgSqGenerator,iteration, ...
            initialLearnRate,gradientDecayFactor,squaredGradientDecayFactor);
        
        scoreGeneratorValue = ...
            double(gather(extractdata(scoreGenerator)));
        scoreDiscriminatorValue = ...
            double(gather(extractdata(scoreDiscriminator)));
        scoreCombinedValue = 1-2*max(abs(scoreDiscriminatorValue-0.5),abs(scoreGeneratorValue-0.5));
        
        recordMetrics(monitor,iteration, ...
            scoreGenerator=scoreGeneratorValue, ...
            scoreDiscriminator=scoreDiscriminatorValue, ...
            scoreCombined=scoreCombinedValue);
        
        if monitor.Stop || isnan(scoreGeneratorValue) || isnan(scoreDiscriminatorValue)
            return;
        end
    end
    monitor.Progress = (epoch/numEpochs)*100;
end
end