Linux上でCycleGANの実装をしてみた
今回は、CycleGANの実装をしてみました。
基本的に、githubで公開されているコードをもとに実装を行っていきます。
本ページでは、軽い論文の説明と実装をしていきます。
自分のデータセットを用いて適用する回は、次回やっていきたいと思います。
- CycleGANについて
- Linux上での実装
簡単ではありますが、上の2項目に従って説明します。
CycleGANについて
論文:https://arxiv.org/pdf/1703.10593.pdf
こちらの論文に沿って説明をしていきます。
導入
CycleGANは、画風変換を可能とするGenerative Adversarial Network (GAN)です。
上の図は、論文内記載のものですが、左のような画風変換(色塗り)をしたい場合は、pix2pixに代表されるような、入力と出力の画像のペアで用いる学習方法が採用されていました。
つまり、図のPairedに示されるような1対1の対応が必要となります。
一方で、右に表されるような、Unpairedの画風変換を可能とする手法も提案されています。
Unpairedの手法では、画風変換のタスクごとに、クラスラベル空間、画像特徴空間、ピクセル空間など、様々な距離空間を定義し、それらを用いて入力と出力の距離を近づける必要がありました。
そこで、CycleGANは、1対1のペア画像を必要とせず、さらにタスクに合わせて学習方法を変える必要がない、そんな手法として提案されました。
これが、CycleGANによって変換された画像たちです。
風景などの写真が、世界的画家であるMonetやGoghの画風へと変換されています。
これは、pix2pixのようなペアを必要とする学習ではなし得ません。
なぜなら、Goghらが描いた風景の写真を撮るためには、タイムスリップしなければいけないからです。
そして、ZebraとHorseの変換、SummerとWinterの変換も可能としています。
タスクに沿って学習方法を変えることなく、CycleGANでは画風変換を学習可能となります。
目的関数
それを可能とするのが、Cycle-consustency lossの導入です。
これが本手法の肝となりますので、後ほどこちらについて説明します。
上の画像は、Cycle-GANで用いられるlossを表したものになります。
まず、(a)は、GANの一般的なlossである、Adversarial lossになります。
上式で定式化されますが、第1項では、Discriminatorは、本物のデータyを本物と識別することを意味します。
第2項では、Generatorによって生成されたデータを偽物と識別することを意味します。
このAdversarial lossをDiscriminatorに対して最大化(正しく識別する)かつGeneratorに対して最小化(誤識別させる)ように学習を行います。
Discriminatorに関して、第1項の最大化の意味は、本物のデータyの確率値が1(本物)と識別されることです。
また、第2項の最大化の意味は、zに対して*G( )を用いて生成した偽物G(z)の確率値を0(偽物)と識別することです。
Generatorに関しては、その逆、ということになります。
Discriminatorが識別できないようなG()*を作ることを目的としています。
これらの最大化・最小化を行う際は、片方は固定します。
この最大化・最小化を交互に行うことで、学習を進めていきます。
これを、両方のドメインに対して、行います。
つまり、とに対して、最適化を行っていくということです。
続いて、**(b)と(c)**についてです。
こちらが、Cycle-consustency lossと呼ばれ、下式で表されます。
こちらの第1項では、xをG( )を用いて生成したデータG(x)をF( )を用いて元のドメインに戻したデータF(G(x))が、ちゃんとxになっているか、というものをL1ノルムを使って表しています。
第2項では、その逆をやっていますね。
考え方としては簡単です。
最終的に、**(a)~(c)**を合わせて、
こちらのように目的関数を設定します。
この最適化問題を解くことで、目的のGとFが学習できます。
実験結果
実験結果の一例です。 「うま」と「しまうま」の変換、風景写真における「夏」と「冬」の変換、「りんご」と「みかん」の変換など、様々なタスクにおいて、高精度な画風変換が実現していることが分かります。 これは、失敗例です。 プーチン大統領がしまうまになっちゃっていますね。 この様に、テクスチャの変換はうまくいくものの、形状を捉えた変換は難しい場合もあるようです。 物体検出手法の導入なんかで解決されていくのではと思います。もっと結果を見たいという方は、論文をのぞいてみてください。
Linux上での実装
公開コード
https://github.com/xhujoy/CycleGAN-tensorflow
実装環境
- Ubuntu 18.04 LTS
- Python 3.6
- PyTorch 0.4.0
- Tensorflow 1.4.0
- numpy 1.11.0
- scipy 0.17.0
- pillow 3.3.0
公開データセットで実装
まずは、任意のディレクトリにgitをクローンします。
続いて、CycleGAN-tensorflow/
のディレクトリに移動します。
今回は、論文でも使用されていたhorse2zebraのデータセットをダウンロードしていきます。
$ git clone https://github.com/xhujoy/CycleGAN-tensorflow
$ cd CycleGAN-tensorflow/
$ bash ./download_dataset.sh horse2zebra
学習
続いて、ダウンロードしたhorse2zebraのデータセットで学習を行っていきます。
$ CUDA_VISIBLE_DEVICES=0 python main.py --dataset_dir=horse2zebra
GPU指定の場合は、CUDA_VISIBLE_DEVICES=
で指定してください。
学習が始まります。
Epoch: [ 0] [ 0/1067] time: 14.2652
Epoch: [ 0] [ 1/1067] time: 16.9671
Epoch: [ 0] [ 2/1067] time: 17.6442
Epoch: [ 0] [ 3/1067] time: 18.3194
Epoch: [ 0] [ 4/1067] time: 19.0001
Epoch: [ 0] [ 5/1067] time: 19.6724
Epoch: [ 0] [ 6/1067] time: 20.3511
Epoch: [ 0] [ 7/1067] time: 21.0326
Epoch: [ 0] [ 8/1067] time: 21.7106
Epoch: [ 0] [ 9/1067] time: 22.3866
Epoch: [ 0] [ 10/1067] time: 23.0501
Epoch: [ 0] [ 11/1067] time: 23.7298
.
.
.
初期設定では、Epochは200回になっています。
こちらは自身が適用するデータセットに合わせて、変更しても良いと思います。
そこまで大きな差異を含む変換の学習ではないのならば、Epochを減らしてみてもいいかもしれません。
ここで注意なのですが、ダウンロードしたdatasets/horse2zebra/
ディレクトリの中にtestA/
、testB/
、trainA/
、trainB/
が存在し、それぞれのディレクトリ内には画像が入っています。
学習時であっても、testA/
またはtestB/
のいずれにもデータが入っていないと、以下のエラーを吐かれてしまいます。
ValueError: Cannot feed value of shape (1, 256, 256, 6) for Tensor 'real_A_and_B_images:0', which has shape '(?, 512, 512, 6)'
自分自身でデータセットを構築し、実装する際は注意が必要です。
テスト
テストは以下のコマンドで行います。
$ CUDA_VISIBLE_DEVICES=0 python main.py --dataset_dir=horse2zebra --phase=test --which_direction=AtoB
--which_direction=
のオプションで、AtoBかBtoAを指定します。
datasets/horse2zebra/testA
もしくはdatasets/horse2zebra/testB
内の画像が変換され、test/
に保存されていきます。
それぞれの画像にはわかりやすく、AtoB_
もしくはBtoA_
が記されます。
以下はテスト結果の一例です。
- horse2zebra (AtoB)
- zebra2horse (BtoA)
参考資料
論文:https://arxiv.org/pdf/1703.10593.pdf
Github:https://github.com/xhujoy/CycleGAN-tensorflow