Main Content

このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。

条件付き敵対的生成ネットワーク (CGAN) の学習

この例では、条件付き敵対的生成ネットワーク (CGAN) に学習させてイメージを生成する方法を説明します。

敵対的生成ネットワーク (GAN) は深層学習ネットワークの一種で、入力された学習データと類似の特性をもつデータを生成できます。

GAN は一緒に学習を行う 2 つのネットワークで構成されています。

  1. ジェネレーター。ランダムな値で構成されるベクトルを入力として与えられ、学習データと同じ構造のデータを生成します。

  2. ディスクリミネーター — このネットワークは、学習データとジェネレーターにより生成されたデータの両方からの観測値を含むデータのバッチを与えられ、その観測値が "実データ" か "生成データ" かの分類を試みます。

"条件付き" 敵対的生成ネットワークは敵対的生成ネットワークの一種で、こちらも学習プロセス中にラベルを利用します。

  1. ジェネレーター。このネットワークは、ラベルと乱数の配列を入力として与えられ、同じラベルに対応する学習データの観測値と同じ構造のデータを生成します。

  2. ディスクリミネーター。学習データとジェネレーターにより生成されたデータの両方の観測値を含むラベル付きデータのバッチを与えられ、その観測値が "実データ" か "生成データ" かの分類を試みます。

条件付き GAN に学習させる場合は、両方のネットワークの学習を同時に行うことで両者のパフォーマンスを最大化します。

  • ジェネレーターに学習させて、ディスクリミネーターを "騙す" データを生成。

  • ディスクリミネーターに学習させて、実データと生成データを区別。

ジェネレーターのパフォーマンスを最大化するには、生成されたラベル付きデータが与えられたときのディスクリミネーターの損失を最大化します。つまり、ジェネレーターの目的はディスクリミネーターが "実データ" と分類するようなラベル付きデータを生成することです。

ディスクリミネーターのパフォーマンスを最大化するには、ラベル付きの実データと生成データ両方のバッチが与えられたときのディスクリミネーターの損失を最小化します。つまり、ディスクリミネーターの目的はジェネレーターに "騙されない" ことです。

この方法の理想的な結果は、入力ラベルごとに実データに類似するデータをジェネレーターに生成させ、ラベルごとの学習データの特性を表す強い特徴表現をディスクリミネーターに学習させることです。

学習データの読み込み

Flowers のデータセット [1] をダウンロードし、解凍します。

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')
    disp('Downloading Flowers data set (218 MB)...')
    websave(filename,url);
    untar(filename,downloadFolder)
end

写真のイメージ データストアを作成します。

datasetFolder = fullfile(imageFolder);

imds = imageDatastore(datasetFolder, ...
    'IncludeSubfolders',true, ...
    'LabelSource','foldernames');

クラス数を表示します。

classes = categories(imds.Labels);
numClasses = numel(classes)
numClasses = 5

データを拡張して水平方向にランダムに反転させ、イメージのサイズを 64 x 64 に変更します。

augmenter = imageDataAugmenter('RandXReflection',true);
augimds = augmentedImageDatastore([64 64],imds,'DataAugmentation',augmenter);

ジェネレーター ネットワークの定義

乱数値の 1 x 1 x 100 の配列と対応するラベルを与えられてイメージを生成する、次の 2 入力ネットワークを定義します。

このネットワークは、次を行います。

  • ノイズの 1 x 1 x 100 の配列を 4 x 4 x 1024 の配列に変換します。

  • カテゴリカル ラベルを埋め込みベクトルに変換して 4 行 4 列の配列に形状を変更します。

  • 2 つの入力から得られた結果のイメージをチャネルの次元に沿って連結します。出力は 4 x 4 x 1025 の配列です。

  • バッチ正規化と ReLU 層を用いた一連の転置畳み込み層を使用して、結果の配列を 64 x 64 x 3 の配列にスケール アップ。

このネットワーク アーキテクチャを層グラフとして定義し、次のネットワーク プロパティを指定します。

  • カテゴリカル入力では、50 の埋め込み次元を使用します。

  • 転置畳み込み層では、5 x 5 のフィルターを指定し、各層のフィルター数を減少させ、ストライドを 2 にし、出力を "同じく" トリミングするように設定します。

  • 最後の転置畳み込み層では、生成されたイメージの 3 つの RGB チャネルに対応する 3 つの 5 x 5 のフィルターを指定します。

  • ネットワークの最後に、tanh 層を追加。

ノイズ入力を投影して形状変更するには、この例にサポート ファイルとして添付されている、カスタム層 projectAndReshapeLayer を使用します。projectAndReshapeLayer オブジェクトは、全結合演算を使用して入力をスケール アップし、指定されたサイズに出力を形状変更します。

ラベルをネットワークに入力するには、imageInputLayer オブジェクトを使用し、イメージ サイズに 1 x 1 を指定します。ラベル入力を埋め込んで形状変更するには、この例にサポート ファイルとして添付されている、カスタム層 embedAndReshapeLayer を使用します。embedAndReshapeLayer オブジェクトは、埋め込みと全結合演算を使用して、カテゴリカル ラベルを指定サイズの 1 チャネルのイメージに変換します。

numLatentInputs = 100;
embeddingDimension = 50;
numFilters = 64;

filterSize = 5;
projectionSize = [4 4 1024];

layersGenerator = [
    imageInputLayer([1 1 numLatentInputs],'Normalization','none','Name','noise')
    projectAndReshapeLayer(projectionSize,numLatentInputs,'proj');
    concatenationLayer(3,2,'Name','cat');
    transposedConv2dLayer(filterSize,4*numFilters,'Name','tconv1')
    batchNormalizationLayer('Name','bn1')
    reluLayer('Name','relu1')
    transposedConv2dLayer(filterSize,2*numFilters,'Stride',2,'Cropping','same','Name','tconv2')
    batchNormalizationLayer('Name','bn2')
    reluLayer('Name','relu2')
    transposedConv2dLayer(filterSize,numFilters,'Stride',2,'Cropping','same','Name','tconv3')
    batchNormalizationLayer('Name','bn3')
    reluLayer('Name','relu3')
    transposedConv2dLayer(filterSize,3,'Stride',2,'Cropping','same','Name','tconv4')
    tanhLayer('Name','tanh')];

lgraphGenerator = layerGraph(layersGenerator);

layers = [
    imageInputLayer([1 1],'Name','labels','Normalization','none')
    embedAndReshapeLayer(projectionSize(1:2),embeddingDimension,numClasses,'emb')];

lgraphGenerator = addLayers(lgraphGenerator,layers);
lgraphGenerator = connectLayers(lgraphGenerator,'emb','cat/in2');

カスタム学習ループを使用してネットワークに学習させ、自動微分を有効にするには、層グラフを dlnetwork オブジェクトに変換します。

dlnetGenerator = dlnetwork(lgraphGenerator)
dlnetGenerator = 
  dlnetwork with properties:

         Layers: [16×1 nnet.cnn.layer.Layer]
    Connections: [15×2 table]
     Learnables: [19×3 table]
          State: [6×3 table]
     InputNames: {'noise'  'labels'}
    OutputNames: {'tanh'}

ディスクリミネーター ネットワークの定義

イメージのセットと対応するラベルを与えられて 64 x 64 の実イメージと生成イメージを分類する、次の 2 入力ネットワークを定義します。

64 x 64 x 1 のイメージと対応するラベルを入力として受け取り、バッチ正規化層と leaky ReLU 層をもつ一連の畳み込み層を使用してスカラーの予測スコアを出力するネットワークを作成します。ドロップアウトを使用して、入力イメージにノイズを追加します。

  • ドロップアウト層で、ドロップアウトの確率を 0.75 に設定。

  • 畳み込み層で、5 x 5 のフィルターを指定し、各層でフィルター数を増やす。また、ストライド 2 で出力の各エッジをパディングするように設定します。

  • leaky ReLU 層で、スケールを 0.2 に設定。

  • 最後の層で、4 x 4 のフィルターをもつ畳み込み層を設定します。

dropoutProb = 0.75;
numFilters = 64;
scale = 0.2;

inputSize = [64 64 3];
filterSize = 5;

layersDiscriminator = [
    imageInputLayer(inputSize,'Normalization','none','Name','images')
    dropoutLayer(dropoutProb,'Name','dropout')
    concatenationLayer(3,2,'Name','cat')
    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);

layers = [
    imageInputLayer([1 1],'Name','labels','Normalization','none')
    embedAndReshapeLayer(inputSize,embeddingDimension,numClasses,'emb')];

lgraphDiscriminator = addLayers(lgraphDiscriminator,layers);
lgraphDiscriminator = connectLayers(lgraphDiscriminator,'emb','cat/in2');

カスタム学習ループを使用してネットワークに学習させ、自動微分を有効にするには、層グラフを dlnetwork オブジェクトに変換します。

dlnetDiscriminator = dlnetwork(lgraphDiscriminator)
dlnetDiscriminator = 
  dlnetwork with properties:

         Layers: [17×1 nnet.cnn.layer.Layer]
    Connections: [16×2 table]
     Learnables: [19×3 table]
          State: [6×3 table]
     InputNames: {'images'  'labels'}
    OutputNames: {'conv5'}

モデル勾配と損失関数の定義

この例のモデル勾配関数セクションにリストされている関数 modelGradients を作成します。この関数は、ジェネレーターおよびディスクリミネーター ネットワーク、入力データのミニバッチ、およびランダムな値の配列を入力として受け取り、学習可能なネットワーク パラメーターについての損失の勾配と、生成されたイメージの配列を返します。

学習オプションの指定

ミニバッチ サイズを 128 として 500 エポック学習させます。

numEpochs = 500;
miniBatchSize = 128;
augimds.MiniBatchSize = miniBatchSize;

Adam 最適化のオプションを指定します。両方のネットワークで次を使用します。

  • 学習率 0.0002

  • 勾配の減衰係数 0.5

  • 2 乗勾配の減衰係数 0.999

learnRate = 0.0002;
gradientDecayFactor = 0.5;
squaredGradientDecayFactor = 0.999;

GPU が利用できる場合、GPU で学習を行います。GPU を使用するには、Parallel Computing Toolbox™、および Compute Capability 3.0 以上の CUDA® 対応 NVIDIA® GPU が必要です。

executionEnvironment = "auto";

100 回の反復ごとに学習の進行状況プロットを更新します。

validationFrequency = 100;

実イメージと生成イメージとを区別するディスクリミネーターの学習速度が速すぎる場合、ジェネレーターの学習に失敗する可能性があります。ディスクリミネーターとジェネレーターの学習バランスを改善するために、実イメージの一部のラベルをランダムに反転します。反転係数を 0.5 に指定します。

flipFactor = 0.5;

モデルの学習

カスタム学習ループを使用してモデルに学習させます。学習データ全体をループ処理し、各反復でネットワーク パラメーターを更新します。学習の進行状況を監視するには、ホールドアウトされたランダムな値の配列をジェネレーターに入力して得られた生成イメージのバッチと、ネットワーク スコアのプロットを表示します。

Adam オプティマイザーのパラメーターを初期化します。

velocityDiscriminator = [];
trailingAvgGenerator = [];
trailingAvgSqGenerator = [];
trailingAvgDiscriminator = [];
trailingAvgSqDiscriminator = [];

学習の進行状況のプロットを初期化します。Figure を作成して幅が 2 倍になるようサイズ変更します。

f = figure;
f.Position(3) = 2*f.Position(3);

生成されたイメージとスコア プロットのサブプロットを作成します。

imageAxes = subplot(1,2,1);
scoreAxes = subplot(1,2,2);

スコアのプロットのアニメーションの線を初期化します。

lineScoreGenerator = animatedline(scoreAxes,'Color',[0 0.447 0.741]);
lineScoreDiscriminator = animatedline(scoreAxes, 'Color', [0.85 0.325 0.098]);

プロットの外観をカスタマイズします。

legend('Generator','Discriminator');
ylim([0 1])
xlabel("Iteration")
ylabel("Score")
grid on

学習の進行状況を監視するには、乱数値の 1 x 1 x numLatentInputs の配列 25 個を含むホールドアウトされたバッチと、対応する 1 から 5 のラベル (クラスに対応) を 5 回繰り返した集合を作成します。ここで、ラベルは配列の 4 番目の次元にあります。

numValidationImagesPerClass = 5;
ZValidation = randn(1,1,numLatentInputs,numValidationImagesPerClass*numClasses,'single');
TValidation = single(repmat(1:numClasses,[1 numValidationImagesPerClass]));
TValidation = permute(TValidation,[1 3 4 2]);

データを dlarray オブジェクトに変換し、次元ラベル 'SSCB' (spatial、spatial、channel、batch) を指定します。

dlZValidation = dlarray(ZValidation, 'SSCB');
dlTValidation = dlarray(TValidation, 'SSCB');

GPU で学習する場合、データを gpuArray オブジェクトに変換。

if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu"
    dlZValidation = gpuArray(dlZValidation);
    dlTValidation = gpuArray(dlTValidation);
end

GAN に学習させます。各エポックについて、データをシャッフルしてデータのミニバッチをループで回します。

各ミニバッチで次を行います。

  • ディスクリミネーターへの入力とジェネレーターの出力を確実に一致させるために、実イメージを再スケーリングしてピクセルが取る値を範囲 [-1, 1] に収める。

  • 基となる型が singledlarray オブジェクトにイメージ データとラベルを変換し、次元ラベルを 'SSCB' (spatial、spatial、channel、batch) に指定。

  • ジェネレーター ネットワーク用の乱数値の配列を含む dlarray オブジェクトを生成。

  • GPU で学習する場合、データを gpuArray オブジェクトに変換。

  • 関数 dlfeval および modelGradients を使用してモデルの勾配を評価します。

  • 関数 adamupdate を使用してネットワーク パラメーターを更新。

  • 2 つのネットワークのスコアをプロット。

  • validationFrequency の反復がすべて終了した後で、ホールドアウトされた固定ジェネレーター入力の生成イメージのバッチを表示。

学習を行うのに時間がかかる場合があります。

iteration = 0;
start = tic;

% Loop over epochs.
for epoch = 1:numEpochs
    
    % Reset and shuffle datastore.
    reset(augimds);
    augimds = shuffle(augimds);
    
    % Loop over mini-batches.
    while hasdata(augimds)
        iteration = iteration + 1;
        
        % Read mini-batch of data and generate latent inputs for the
        % generator network.
        data = read(augimds);
        
        % Ignore last partial mini-batch of epoch.
        if size(data,1) < miniBatchSize
            continue
        end
        
        X = cat(4,data{:,1}{:});
        X = single(X);
        
        T = single(data.response);
        T = permute(T,[2 3 4 1]);
        
        Z = randn(1,1,numLatentInputs,miniBatchSize,'single');
        
        % Rescale the images in the range [-1 1].
        X = rescale(X,-1,1,'InputMin',0,'InputMax',255);
        
        % Convert mini-batch of data to dlarray and specify the dimension labels
        % 'SSCB' (spatial, spatial, channel, batch).
        dlX = dlarray(X, 'SSCB');
        dlZ = dlarray(Z, 'SSCB');
        dlT = dlarray(T, 'SSCB');
        
        % If training on a GPU, then convert data to gpuArray.
        if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu"
            dlX = gpuArray(dlX);
            dlZ = gpuArray(dlZ);
            dlT = gpuArray(dlT);
        end
        
        % Evaluate the model gradients and the generator state using
        % dlfeval and the modelGradients function listed at the end of the
        % example.
        [gradientsGenerator, gradientsDiscriminator, stateGenerator, scoreGenerator, scoreDiscriminator] = ...
            dlfeval(@modelGradients, dlnetGenerator, dlnetDiscriminator, dlX, dlT, dlZ, flipFactor);
        dlnetGenerator.State = stateGenerator;
        
        % Update the discriminator network parameters.
        [dlnetDiscriminator,trailingAvgDiscriminator,trailingAvgSqDiscriminator] = ...
            adamupdate(dlnetDiscriminator, gradientsDiscriminator, ...
            trailingAvgDiscriminator, trailingAvgSqDiscriminator, iteration, ...
            learnRate, gradientDecayFactor, squaredGradientDecayFactor);
        
        % Update the generator network parameters.
        [dlnetGenerator,trailingAvgGenerator,trailingAvgSqGenerator] = ...
            adamupdate(dlnetGenerator, gradientsGenerator, ...
            trailingAvgGenerator, trailingAvgSqGenerator, iteration, ...
            learnRate, gradientDecayFactor, squaredGradientDecayFactor);
        
        % Every validationFrequency iterations, display batch of generated images using the
        % held-out generator input.
        if mod(iteration,validationFrequency) == 0 || iteration == 1
            
            % Generate images using the held-out generator input.
            dlXGeneratedValidation = predict(dlnetGenerator,dlZValidation,dlTValidation);
            
            % Tile and rescale the images in the range [0 1].
            I = imtile(extractdata(dlXGeneratedValidation), ...
                'GridSize',[numValidationImagesPerClass numClasses]);
            I = rescale(I);
            
            % Display the images.
            subplot(1,2,1);
            image(imageAxes,I)
            xticklabels([]);
            yticklabels([]);
            title("Generated Images");
        end
        
        % Update the scores plot
        subplot(1,2,2)
        addpoints(lineScoreGenerator,iteration,...
            double(gather(extractdata(scoreGenerator))));
        
        addpoints(lineScoreDiscriminator,iteration,...
            double(gather(extractdata(scoreDiscriminator))));
        
        % Update the title with training progress information.
        D = duration(0,0,toc(start),'Format','hh:mm:ss');
        title(...
            "Epoch: " + epoch + ", " + ...
            "Iteration: " + iteration + ", " + ...
            "Elapsed: " + string(D))
        
        drawnow
    end
end

ここでは、ディスクリミネーターは生成イメージの中から実イメージを識別する強い特徴表現を学習しました。それに対し、ジェネレーターは実データのように見えるデータを生成できるように、同様に強い特徴表現を学習しました。各列は 1 つのクラスに対応します。

学習プロットは、ジェネレーターおよびディスクリミネーターのネットワークのスコアを示しています。ネットワークのスコアを解釈する方法の詳細については、GAN の学習過程の監視と一般的な故障モードの識別を参照してください。

新しいイメージの生成

特定のクラスの新しいイメージを生成するには、ジェネレーターに対して関数 predict を使用し、dlarray オブジェクトを指定します。このオブジェクトには、乱数値の 1 x 1 x numLatentInputs の配列のバッチと、目的のクラスに対応するラベルの配列が含まれています。データを dlarray オブジェクトに変換し、次元ラベル 'SSCB' (spatial、spatial、channel、batch) を指定します。GPU での予測のために、データを gpuArray に変換します。イメージを並べて表示するには関数 imtile を使用し、関数 rescale を使ってイメージを再スケーリングします。

1 番目のクラスに対応する、乱数値から成る 36 ベクトルの配列を作成します。

numObservationsNew = 36;
idxClass = 1;
Z = randn(1,1,numLatentInputs,numObservationsNew,'single');
T = repmat(single(idxClass),[1 1 1 numObservationsNew]);

データを次元ラベル 'SSCB' (spatial、spatial、channel、batch) 付きの dlarray オブジェクトに変換します。

dlZ = dlarray(Z,'SSCB');
dlT = dlarray(T,'SSCB');

GPU を使用してイメージを生成するには、データを gpuArray オブジェクトにも変換します。

if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu"
    dlZ = gpuArray(dlZ);
    dlT = gpuArray(dlT);
end

ジェネレーター ネットワークに対して関数 predict を使用し、イメージを生成します。

dlXGenerated = predict(dlnetGenerator,dlZ,dlT);

生成されたイメージをプロットに表示します。

figure
I = imtile(extractdata(dlXGenerated));
I = rescale(I);
imshow(I)
title("Class: " + classes(idxClass))

ここで、ジェネレーター ネットワークは指定のクラスで条件付けされたイメージを生成します。

モデル勾配関数

関数 modelGradients は、ジェネレーターおよびディスクリミネーターの dlnetwork オブジェクト dlnetGenerator および dlnetDiscriminator、入力データ dlX のミニバッチ、対応するラベル dlT、およびランダムな値の配列 dlZ を入力として受け取り、学習可能なネットワーク パラメーターについての損失の勾配、ジェネレーターの状態、およびネットワークのスコアを返します。

実イメージと生成イメージとを区別するディスクリミネーターの学習速度が速すぎる場合、ジェネレーターの学習に失敗する可能性があります。ディスクリミネーターとジェネレーターの学習バランスを改善するために、実イメージの一部のラベルをランダムに反転します。

function [gradientsGenerator, gradientsDiscriminator, stateGenerator, scoreGenerator, scoreDiscriminator] = ...
    modelGradients(dlnetGenerator, dlnetDiscriminator, dlX, dlT, dlZ, flipFactor)

% Calculate the predictions for real data with the discriminator network.
dlYPred = forward(dlnetDiscriminator, dlX, dlT);

% Calculate the predictions for generated data with the discriminator network.
[dlXGenerated,stateGenerator] = forward(dlnetGenerator, dlZ, dlT);
dlYPredGenerated = forward(dlnetDiscriminator, dlXGenerated, dlT);

% Calculate probabilities.
probGenerated = sigmoid(dlYPredGenerated);
probReal = sigmoid(dlYPred);

% Calculate the generator and discriminator scores
scoreGenerator = mean(probGenerated);
scoreDiscriminator = (mean(probReal) + mean(1-probGenerated)) / 2;

% Flip labels.
numObservations = size(dlYPred,4);
idx = randperm(numObservations,floor(flipFactor * numObservations));
probReal(:,:,:,idx) = 1 - probReal(:,:,:,idx);

% Calculate the GAN loss.
[lossGenerator, lossDiscriminator] = ganLoss(probReal, probGenerated);

% For each network, calculate the gradients with respect to the loss.
gradientsGenerator = dlgradient(lossGenerator, dlnetGenerator.Learnables,'RetainData',true);
gradientsDiscriminator = dlgradient(lossDiscriminator, dlnetDiscriminator.Learnables);

end

GAN の損失関数

ジェネレーターの目的はディスクリミネーターが "実データ" に分類するようなデータを生成することです。ジェネレーターが生成したイメージをディスクリミネーターが実データとして分類する確率を最大化するには、負の対数尤度関数を最小化します。

ディスクリミネーターの出力 Y が与えられた場合、次のようになります。

  • Yˆ=σ(Y) は、入力イメージが "実" クラスに属する確率です。

  • 1-Yˆ は、入力イメージが "生成" クラスに属している確率です。

シグモイド演算 σ は関数 modelGradients で行われる点に注意してください。ジェネレーターの損失関数は次の式で表されます。

lossGenerator=-mean(log(YˆGenerated)),

ここで、YˆGenerated は生成イメージに対するディスクリミネーターの出力確率を表しています。

ディスクリミネーターの目的はジェネレーターに "騙されない" ことです。ディスクリミネーターが実イメージと生成イメージを正しく区別する確率を最大化するには、対応する負の対数尤度関数の和を最小化します。ディスクリミネーターの損失関数は次の式で表されます。

lossDiscriminator=-mean(log(YˆReal))-mean(log(1-YˆGenerated)),

ここで、YˆReal は実イメージに対するディスクリミネーターの出力確率を表しています。

function [lossGenerator, lossDiscriminator] = ganLoss(scoresReal,scoresGenerated)

% Calculate losses for the discriminator network.
lossGenerated = -mean(log(1 - scoresGenerated));
lossReal = -mean(log(scoresReal));

% Combine the losses for the discriminator network.
lossDiscriminator = lossReal + lossGenerated;

% Calculate the loss for the generator network.
lossGenerator = -mean(log(scoresGenerated));

end

参考文献

参考

| | | | | |

関連するトピック