GANの潜在空間に新垣結衣は住んでいるのか?

1.はじめに

 ご無沙汰しております、cedroです。ずいぶんと長い間、ブログの更新をお休みしていましたが、再開します。

 顔画像を学習したGANモデルは、潜在変数を変化させることによって、様々な顔画像を生成出来るようになります。学習に使った顔画像の要素を色々組み合わせることによって、学習に使わなかった画像も生成することが出来るわけです。言い換えれば、顔画像を学習したGANの潜在空間には無数の顔が分布してると言うことが出来ます。

 ということは、そのGANの潜在空間には、新垣結衣さんに似た顔もあるかもしれないと思ったのが、今回のテーマを考えたきっかけです。

 今回は、顔画像生成の最新モデルであるStyleGAN2を使って、「GANの潜在空間に新垣結衣は住んでいるのか?」というテーマを検証してみたいと思います。

2.StyleGAN2とは?

 まず、StyleGANについて簡単におさらいします。

 左側のMapping network は8層の全結合層で、入力の潜在変数z(1, 512)を中間出力の潜在変数w(18, 512)にマッピングします。

 右側のSynthesis network は9層で、入力は定数(4×4×512)、そこへ先程の潜在変数w(18, 512)とノイズが各層に入ります。潜在変数wは、各層に2本づつ入っているので、合計9×2=18となるわけです。そして、生成する画像の解像度を、4×4, 8×8, 16×16と順次上げて行き、最終的に1024×1024にします。

 潜在変数wは主な画像生成をコントロールし、ノイズは細部の特徴(髪の流れ, 肌質など)をコントロールします。

StyleGAN2は、このStyleGANの改良版なので、改良点を簡単に説明すると、

  1. データ正規化の手法を見直し、特徴的な水滴状パターンの発生を防止
  2. Progressive growingをやめ、頻出特徴を生成してしまう不具合を防止
  3. 潜在空間の知覚的な滑らかさを示す指標をモデルに組み込み画像品質を向上

3.新垣結衣の顔をどうみつけるか?

 主な画像生成をコントロールするのは、潜在変数z(1, 512)あるいは潜在変数w(18, 512)です。潜在変数wの方が明らかに表現力が豊なので、潜在変数wの探索を行うことにします。

 アルゴリズムは、Synthesis networkの9個ある各層において、潜在変数wを適当に初期化して inital image を出力し、target image (新垣結衣の画像)との差をロスとします。そして、ロスが出来るだけ小さくなるような潜在変数wを見つけるものです。

 そのために、target image は9種類の解像度の画像(4×4〜1024×1024)を用意します。

 今回使用するコードは、NVIDIAが公開しているStyleGAN2の公式コードを元に、Google Colabで動かすような形にしてGithubに上げてありますので、それに沿って説明して行きます。自分で動かしてみたい方は、この「リンク」をクリックし表示されたノートブックの先頭にある「Colab on Web」ボタンをクリックすると動かせます。

4.セットアップ

 最初に、tensorflow1.15.0 を動かすために必要な cuda10.0 をインストールします(2022.10よりgoogle colab からcuda10.0が削除されたため)。

 まず、githubよりコードを取得します。

 次に、使用するクラスと関数を定義します。コードの記載は省略しますが、定義する内容だけをメモしておきます。

  • class LandmarksDetector: 顔のランドマーク(目、鼻、口、顎など)検出クラス
  • def image_align(): 顔画像の切り出し関数
  • def unpack_bz2(): bz2ファイルを解凍する関数
  • def project_real_images(): マルチ解像度データセットに出来るだけ似た画像を生成する潜在変数を探索する関数
  • def generate_gif(): 2つの潜在変数からGIF動画を生成する関数
  • def display_pic(): フォルダー内の画像ファイルを表示する関数
  • def display(): 潜在変数の画像を表示する関数

 そして、最後に顔画像を切り出すモデルの読み込みを行います。

5.顔画像の切り出し

 sample/picに用意した下記5つの画像から、顔画像を切り出します。

 普通はOpenCVでやるわけですが、StyleGAN2が学習に使ったFFHQデータセットは、dlibを使いalign(顔が直立するように回転させる)して独自の設定の範囲を切り取って作成されています。なので、用意した画像から顔画像を切り出す場合も同様な処理を行います。では、下記のコードを実行します。

 sample/pic にある各画像について、顔のランドマーク(目、鼻、口、顎など)を検出し、その位置を合わせて顔画像を切り出し、1024×1024にリサイズしてmy/picに保存しています。

 目、鼻、口、顎の位置が大体合っていることが分かると思います。これが大体合っていないと上手く潜在変数の探索が出来ないのでご注意下さい。

6.マルチ解像度データセットの作成

 切り取った顔画像を9種類の解像度(4×4, 8×8, 16×16,…., 1024×1024)の画像に変換し、Tensorflowの仕様であるマルチ解像度の画像TFRecordで保存するコードです。

 dataset_tool.pyを使って、my/picにある顔画像からマルチ解像度の画像TFRecordを作成し、my/datasetへ保存します。

 ちなみに、マルチ解像度の画像TFRecordはこんな形をしています。

7.新垣結衣を生成する潜在変数を探索する

 それでは、マルチ解像度のデータセットに登録された新垣結衣さんの顔を生成する潜在変数を探索します。

 探索途中の生成画像は my/real_imagesフォルダーに順次保存されます。探索した潜在変数はvec_synで返って来ます探索試行回数の設定値は300です。変更したい場合はprojector.py の self.num_stepsの値をエディター等で変更して下さい。では、コードを実行します。

 上段がターゲット画像、下段が探索した潜在変数が生成した画像です。なぜか順番が変わってしまいました。ターゲットに対する再現性は、まずまずですが、もう一味足らないということで70点くらいの出来でしょうか。

 検証結果は、「GANの潜在空間には、新垣結衣に良く似た人が住んでいた」です。

 探索した潜在変数vec_synをNumpyのバイナリファイルで保存する場合は、下記のコードを実行します。

 ちなみに、このファイルの容量は18×512の行列が5つだけなので、たった188KBです。ふと考えてみると、この情報だけから1024×1024ピクセルのカラー画像を5枚生成出来るのは凄いことですね。

 保存したNumpyのバイナリファイルを読み込みには、下記のコードを実行します。

8.顔画像のトランジション

 上手く顔画像を生成できる潜在変数を見つけることが出来ると、興味深いことが出来ます。例えば、顔画像Aを生成する潜在変数Aと顔画像Bを生成する潜在変数Bを見つけたとします。

 潜在変数Aと潜在変数Bの各要素の差をn等分したものをCとし、潜在変数を A+C*1, A+C*2, …., A+C*nと変化させながら顔画像を生成すると、顔画像Aから顔画像Bへスムーズへ画像を変化させることが出来ます。

それでは、8.で見つけた潜在変数を使って、やってみましょう。generate_gif ( 潜在変数名, リスト形式で順番指定 )で実行します。

 潜在変数の1番目→0番目→3番目と少しづつ変化させた結果です。見つけた潜在変数の間にも顔画像がきれいに分布していることが分かります。

 作成したGIF動画は、my/gif/ に保存されます。anime.gif は 1024×1024 のサイズ、anime_256.gif は 256×256 のサイズです。

 このブログでは、新垣結衣さんを題材にしていますが、例えば自分の写っている写真を使えば自分の顔画像の潜在変数も簡単に取得することが出来ます。ぜひ、色々トライしてみて下さい。

 では、また。

(Twitterへの投稿)

13 件のコメント

  • Google Colabで上から順番に実行していくと、
    クラスと関数の定義のコード部分以下のエラーが出てしまうのですが原因はわかるでしょうか?

    —————————————————————————
    AttributeError Traceback (most recent call last)
    in ()
    10 import numpy as np
    11 import dnnlib
    —> 12 import dnnlib.tflib as tflib
    13 import re
    14 import projector

    7 frames
    /usr/local/lib/python3.7/dist-packages/tensorflow/contrib/cloud/python/ops/gen_bigquery_reader_ops.py in _InitOpDefLibrary(op_list_proto_bytes)
    275 op_list = _op_def_pb2.OpList()
    276 op_list.ParseFromString(op_list_proto_bytes)
    –> 277 _op_def_registry.register_op_list(op_list)
    278 op_def_lib = _op_def_library.OpDefLibrary()
    279 op_def_lib.add_op_list(op_list)

    AttributeError: module ‘tensorflow.python.framework.op_def_registry’ has no attribute ‘register_op_list’

    • muraさん
      コメントありがとうござます。
      久しぶりに自分でも動かしたら確かにエラーが出ました。
      コードは修正済みです。
      お楽しみ下さい。

    • けんさん
      コメントありがとうございます。
      はい、可能です。
      自分の用意した画像を sample/pic にアップロードするだけです(PCからドラッグ&ドロップで行えます)。
      あとは、そのまま順に実行すれば潜在変数が取得できます。

      • 返信ありがとうございます
        self.num_stepsを編集しても、探索回数が増えないのですがなぜでしょうか。

        • けんさん

          コメントありがとうございます。
          projector.py は「— クラスと関数の定義 —-」のところで読み込むので、もう一度「—- クラスと関数の定義 —-」を実行して下さい。そうすると、新たな self.num_steps に変わります。

  • 上段がターゲット画像、下段が探索した潜在変数が生成した画像です。なぜか順番が変わってしまいました。

    これを解決できました。

    データセット作成時に

    !python dataset_tool.py create_from_images ./my/dataset ./my/pic –shuffle 0

    とすることでシャッフルされなくなり、上段と下段が一致します。

    詳しくは

    https://qiita.com/yuki_2020/items/510fe7a5754691199a10

    こちらに書いておきました。

    • おっ、これはナイスな情報ありがとうございました!
      デフォルトではデータがシャッフルされるんで順番が変わったわけですね。

  • コメント失礼いたします。

    2022年12月3日時点で、Google Colabで上から実行していくと「クラスと関数の定義」のところで以下のようなエラーが発生してしまったのですが、解決策はありますか?
    おそらくtensorflowの1.xがうまくダウンロードできていないのが原因だと思うのですが、、

    ModuleNotFoundError Traceback (most recent call last)
    in
    11 import numpy as np
    12 import dnnlib
    —> 13 import dnnlib.tflib as tflib
    14 import re
    15 import projector

    2 frames
    /content/stylegan2/dnnlib/tflib/tfutil.py in
    14 import logging
    15 logging.getLogger(‘tensorflow’).setLevel(logging.ERROR)
    —> 16 import tensorflow.contrib # requires TensorFlow 1.x!
    17 tf.contrib = tensorflow.contrib
    18

    ModuleNotFoundError: No module named ‘tensorflow.contrib’

    • ぽっぽさん

      12/1にgoogle colabのpythonバージョンが3.7から3.8にアップグレードされました。
      これに伴いtensorflow1.xはgoogle colabでは動作しなくなりました。
      colabのpythonを3.7にすることは不可能ではありませんが、面倒くさいです。
      長らくtensorflow1.xを使っているコードをアップデートして来ましたが、そろそろサヨナラする時期になったようです。

      このブログでご紹介したstyleGAN2の反転技術がその後どう進歩して行ったかは、本ブログでもいくつか紹介していますので、そちらをご覧いただければと思います。
      なお、最新の「Hyperstyleで、高精度で編集しやすい反転を高速に行う」を見て頂ければ、GANの潜在空間に新垣結衣は確実に住んでいることが分かります。

      「Hyperstyleで、高精度で編集しやすい反転を高速に行う」http://cedro3.com/ai/hyperstyle/

      • ご回答ありがとうございます。

        そうなのですね。
        私は現在大学の方で卒研に取り組んでいるのですが、そこでStyleGAN2に関したものがテーマの研究を行っています。
        こちらのサイトがとても参考になっていたのでコードを動かしたいと思ったのですが、やはり難しいのでしょうか。

        Pythonを3.7に変更するところまではできたのですが、そこからpip install tensorflow==1.15.2を実行してもimportのときには2.x系が読み込まれてしまうという状況に陥っているので厳しいとは思うのですが、もし現在でも動かす方法があるのなら大変恐縮なのですが教えていただくことは可能でしょうか。

          • cedroさん
            ご回答ありがとうございます。

            ご提示いただいたサイトを参考にpython3.7のインストールをすることはできましたが、tensorflowのインストールが以下のように表示され、上手くいかないみたいです。
            度々の投稿で申し訳ございません。

            ___________________________________________________
            Installing collected packages: tensorflow
            Successfully installed tensorflow-1.15.0
            WARNING: Running pip as the ‘root’ user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
            ERROR:root:Internal Python error in the inspect module.
            Below is the traceback from this internal error.

            ERROR:root:Internal Python error in the inspect module.
            Below is the traceback from this internal error.

            ERROR:root:Internal Python error in the inspect module.
            Below is the traceback from this internal error.
            …(エラー内容が続きます)
            ___________________________________________________

  • cedro へ返信する コメントをキャンセル

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

    日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

    ABOUTこの記事をかいた人

    アバター

    ディープラーニング・エンジニアを趣味でやってます。E資格ホルダー。 好きなものは、膨大な凡ショットから生まれる奇跡の1枚、右肩上がりのワクワク感、暑い国の新たな価値観、何もしない南の島、コード通りに動くチップ、完璧なハーモニー、仲間とのバンド演奏、数えきれない流れ星。