GANコードで知る(1)


先に紹介したGANの概念と内容に基づき,Tensorflow 2を用いてMNIST画像を生成するモデルを学習し,コード中の内容を解析し実践する.
この練習では、次の成果物を生成できます.


本明細書で作成したコードは、Tensorflowが提供する2つのチュートリアルを参照しています.
深層合成積生成敵対神経ネットワーク|TensorFlow Core
この文書のコードは、次のリンクで表示できます.
Colab-MNISTジェネレータ

実習環境

  • Google Colab(GPU)
  • Tensorflow 2.0
  • 必要なライブラリのインストールとImport

    !pip install tensorflow-gpu==2.0.0-rc1
    !pip install imageio
    tensorflow-gpu
    実験室でGPUを用いたテンソル流
    imageio
    GIFイメージを作成するためのライブラリ
    # python 표준 라이브러리
    import os
    import time
    import glob
    
    # 오픈소스 라이브러리
    import tensorflow as tf
    from tensorflow.keras import layers
    import imageio
    import matplotlib.pyplot as plt
    import numpy as np
    import PIL
    
    # Colab Notebook Library
    from IPython import display
    glob
    Shellが使用するルールに従って、リポジトリは指定したモードに一致するすべてのパス名を検索します.指定したパスにFileNameをインポートしてデータセットを構成します.
    matplotlib.pyplot
    正式文書https://matplotlib.org/stable/users/index.html
    この練習では、Collabを使用して、セルの結果として画像を出力します.
    PIL
    Pillow、Python画像処理ライブラリ
    IPython.display
    Shell出力制御用モジュール

    データセットの準備

    (train_images, train_labels), (_, _) = tf.keras.datasets.mnist.load_data()
    Tensorflowが提供するAPIデータセットにはMNISTデータセットがある.
    https://www.tensorflow.org/api_docs/python/tf/keras/datasets/mnist
    関数を一度に呼び出し、MNISTデータセットを一度に呼び出し、複数の割当てを使用してNumpy配列データをtrain_images(60000,28,28)に割り当てることができます.
    現在、train_imagesは60000個のMNISTデータを格納している.
    train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
    train_images = (train_images - 127.5) / 127.5 # 이미지를 [-1, 1]로 정규화합니다.
    tray imagesの各画像はInt Type画素からなる階調画像で、現在の値は0~255です.
    GANの構成においては、経験上、0〜1または−1〜1の値を用いることが、0〜255の値を用いることよりも高い性能を有している.
    これは,訓練中にloss値が0から1の間にあるか,あるいはDiscrimiatorモデルが出力0から1の間の判別値に近いためである可能性がある.
    したがって、train images内部要素のデータ型をfloat32型に変更し、0~255の値を-1~1の値に計算します.
    BUFFER_SIZE = 60000
    BATCH_SIZE = 256
    # 데이터 배치를 만들고 섞습니다.
    train_dataset = tf.data.Dataset.from_tensor_slices(train_images).shuffle(BUFFER_SIZE).batch(BATCH_SIZE)
    Tensorflowは、使いやすいように複数のAPIを提供しています.関数の1つとして、from_tensor_slicesのタイプで返されるデータは、BatchDatasetメソッドを使用して各Epochsでデータを混合し、指定されたBatch Sizeで自動的に分割して、重複可能なオブジェクトを作成することができる.
    もちろん、トレーニング中に繰り返し文を使用して6万個のデータを1つずつ繰り返したり、Epochsごとにデータの順序を混合したりすることができます.

    モデルの構成


    Generator

    def make_generator_model():
        model = tf.keras.Sequential()
        model.add(layers.Dense(128, activation='relu', input_shape=(100,)))
        model.add(layers.Dense(256, activation='relu'))
        model.add(layers.Dense(512, activation='relu'))
        model.add(layers.Dense(28*28*1, activation='tanh'))
        model.add(layers.Reshape((28, 28, 1)))
    
        return model
    作成したコードの構造を下図に示します.

    入力として定数ベクトルzのみを受け入れ,「完全接続ニューラルネットワーク」(Dense Layer,完全接続ニューラルネットワーク)を積層し,出力を784ニューロンに拡張した.
    Reluを使用して関数をアクティブにし、最後のレイヤでTanhを使用して関数をアクティブにして、新しく実行された値を-1~1の間で決定します.Tanhを使用して関数をアクティブにする理由は、Train Data setを-1から1の値に前処理するため、次に考慮してください.

    Discriminator

    def make_discriminator_model():
        model = tf.keras.Sequential()
        model.add(layers.Flatten())
        model.add(layers.Dense(512, activation='relu'))
        model.add(layers.Dense(256, activation='relu'))
        model.add(layers.Dense(128, activation='relu'))
        model.add(layers.Dense(1))
    
        return model
    作成したコードの構造を下図に示します.

    これは前のGeneratorとはほぼ逆です.入力された28 x 28サイズのデータを1次元ベクトルに変換し、「応答レイヤ」を使用して徐々に縮小します.
    最後の層までRelu活性化関数を通過すると、0未満のデータは0に非常に近くなり、Inputは−1〜1の間の画像として定義されるので、最終値も近いと予想される.
    最後のレイヤの値は、0に近い値を学習して出力し、入力画像が本物であれば1に近い値を出力し、入力画像が偽物であれば0に近い値を出力します.
    generator = make_generator_model()
    discriminator = make_discriminator_model()
    定義された関数を実行し、グローバルScopeに存在する各変数に割り当てます.

    Loss FunctionとOptimizerの定義

    # 이 메서드는 크로스 엔트로피 손실함수 (cross entropy loss)를 계산하기 위해 헬퍼 (helper) 함수를 반환합니다.
    cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)
    https://www.tensorflow.org/api_docs/python/tf/keras/losses/BinaryCrossentropy?hl=en
    発電機とDistributorの損失を計算する場合、Distributorの出力値に基づきます.したがって、Distributorの出力は0または1に収束しなければならないため、shuffleが採用される.
    def generator_loss(fake_output):
        return cross_entropy(tf.ones_like(fake_output), fake_output)
    Generatorの目的はDistributorにGeneratorが生成した画像を1(リアル画像)と判別させることである.したがって、ジェネレータの損失は、Distributorによって決定された画像を入力として受信し、その値の1から1までの距離から損失を決定する.
    def discriminator_loss(real_output, fake_output):
        real_loss = cross_entropy(tf.ones_like(real_output), real_output)
        fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
        total_loss = real_loss + fake_loss
        return total_loss
    Distributorの目的は,ジェネレータが生成した画像を0(偽)と判別し,真画像を1(真)と判別することである.したがって,Distributorの損失は2つの損失の合計によって決まる.
  • Generatorが生成する画像の判別値が0からどのくらい離れているか
  • .
  • 実画像の判別値が1からどのくらい離れているか
  • .
    上記2つの損失を計算し、マージした値をDistributorの損失に戻します.
    generator_optimizer = tf.keras.optimizers.Adam(1e-4)
    discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)
    どちらの機種も、Adam Optimizer Optimizerを使用しています.

    トレーニングコースの定義

    EPOCHS = 300
    noise_dim = 100
    num_examples_to_generate = 16
    
    # 이 시드를 시간이 지나도 재활용하겠습니다. 
    # (GIF 애니메이션에서 진전 내용을 시각화하는데 쉽기 때문입니다.) 
    seed = tf.random.normal([num_examples_to_generate, noise_dim])
    トレーニングに必要なパラメータを定義します.
    EPOCHS
    経験的には、このモデルは300〜500の間のEPOCHS値に適用される.
    noise_dim
    発電機入力として用いられる定数ベクトルの大きさを100と定義する.
    num_examples_to_generator
    これは訓練に直接影響する変数ではない.
    トレーニング中にジェネレータで生成された画像を中間でチェックします.これは、チェックする画像の数を定義する変数です.
    seed
    これは訓練に直接影響する変数ではない.
    上のnum example to generatorの数に従って定数Vectorを作成し、Generatorに作成をコマンドします.
    # `tf.function`이 어떻게 사용되는지 주목해 주세요.
    # 이 데코레이터는 함수를 "컴파일"합니다.
    @tf.function
    def train_step(images):
        noise = tf.random.normal([BATCH_SIZE, noise_dim])
    
        with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
          generated_images = generator(noise, training=True)
    
          real_output = discriminator(images, training=True)
          fake_output = discriminator(generated_images, training=True)
    
          gen_loss = generator_loss(fake_output)
          disc_loss = discriminator_loss(real_output, fake_output)
    
        gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
        gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
    
        generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
        discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))

    @tf.ファンクションレコーダ


    https://www.tensorflow.org/api_docs/python/tf/function?hl=en#args_1
    Tensorflow2.0正式な文書をチェックするときは、次の操作を行います.BinaryCrossentropyTensorflow 1のバージョンではsessionが使用され、コンパイル中に発生したさまざまな問題を解決し、使いやすくするための機能が追加されています.上記のリンク例は理解しやすいです.
    内部宣言のTensorflow Graphを作成し、TensorFlow 그래프를 호출 가능한 함수를 컴파일합니다.などを可能にします.
    Tensorflow 2の勉強も必要かもしれません.

    train step関数解析

    Loss 기록, 가중치 기록 및 업데이트ジェネレータ用のnoiseを作成します.Noiseのshapeは(BATCH SIZE,noise dim)であり,本明細書では(256100)サイズのランダム値である.
    tf.GradientTape
    https://www.tensorflow.org/api_docs/python/tf/GradientTape
    じどうびぶんえんざんきろくnoise = tf.random.normal([BATCH_SIZE, noise_dim])gen tape,disc tapeにtfを加える.GradientTape()リソースを割り当てます.
    構文で使用されるtf.変数タイプの変数は自動的に監視されます.(generator、gen loss、discrificator、disc lossはここで提供する)
    文が出ている場合、tf.GradientTapeのリソースの一部は割り当て解除されますが、構築中にtf変数を監視するリソースは無効になると推定されます.
    generated_images = generator(noise, training=True)
    
    real_output = discriminator(images, training=True)
    fake_output = discriminator(generated_images, training=True)
    
    gen_loss = generator_loss(fake_output)
    disc_loss = discriminator_loss(real_output, fake_output)
    生成器を使用して偽画像を生成し、Distributorによって偽画像と真画像をそれぞれ識別します.Distributorの出力値で損失を計算します.
    gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
    gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
    gen_tape.勾配(y,x)関数を用いて微分値(傾斜)を求めた.
    generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
    discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
    ジェネレータとDistributorの重みは、前の勾配に基づいて更新されます.

    列関数

    def train(dataset, epochs):
      for epoch in range(epochs):
        start = time.time()
    
        for image_batch in dataset:
          train_step(image_batch)
    
        # GIF를 위한 이미지를 바로 생성합니다.
        display.clear_output(wait=True)
        generate_and_save_images(generator,
                                 epoch + 1,
                                 seed)
    
        # print (' 에포크 {} 에서 걸린 시간은 {} 초 입니다'.format(epoch +1, time.time()-start))
        print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))
    
      # 마지막 에포크가 끝난 후 생성합니다.
      display.clear_output(wait=True)
      generate_and_save_images(generator,
                               epochs,
                               seed)
    データセット,epochをパラメータとし,epochと同じ訓練を繰り返す.各epochは3つの動作を繰り返す.
  • データセットは小ロットに分けて訓練するwith tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:関数
  • である.
    関数train_stepは、
  • 発電機によって生成された16個の画像を記憶し、ユニットに出力する結果である.
  • 各カレンダにかかる時間は
  • である.
    def generate_and_save_images(model, epoch, test_input):
      # `training`이 False로 맞춰진 것을 주목하세요.
      # 이렇게 하면 (배치정규화를 포함하여) 모든 층들이 추론 모드로 실행됩니다. 
      predictions = model(test_input, training=False)
    
      fig = plt.figure(figsize=(4,4))
    
      for i in range(predictions.shape[0]):
          plt.subplot(4, 4, i+1)
          plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap='gray')
          plt.axis('off')
    
      plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))
      plt.show()
    matplotライブラリを使用してジェネレータで生成した16個の画像をマージして保存し、ユニット結果として出力します.

    トレーニング

    %%time
    train(train_dataset, EPOCHS)
    訓練を始める.各epochは、ジェネレータが作成した例と実行時間を表示できます.

    GIFの作成

    anim_file = 'gan.gif'
    
    with imageio.get_writer(anim_file, mode='I') as writer:
      filenames = glob.glob('image*.png')
      filenames = sorted(filenames)
      last = -1
      for i,filename in enumerate(filenames):
        frame = 2*(i**0.5)
        if round(frame) > round(last):
          last = frame
        else:
          continue
        image = imageio.imread(filename)
        writer.append_data(image)
      image = imageio.imread(filename)
      writer.append_data(image)
    
    import IPython
    if IPython.version_info > (6,2,0,''):
      display.Image(filename=anim_file)
    トレーニング中に作成した画像をimageio Libraryで連続的に貼り付けてgifファイルを作成します.
    try:
      from google.colab import files
    except ImportError:
      pass
    else:
      files.download(anim_file)
    上記のコードを使用してgifファイルをダウンロードできます.

    の最後の部分