CycleGANを実装してみた（1）

# Linux上でCycleGANの実装をしてみた
今回は、CycleGANの実装をしてみました。
基本的に、githubで公開されているコードをもとに実装を行っていきます。
本ページでは、軽い論文の説明と実装をしていきます。
自分のデータセットを用いて適用する回は、次回やっていきたいと思います。

- CycleGANについて
- Linux上での実装

簡単ではありますが、上の２項目に従って説明します。

# CycleGANについて
論文：https://arxiv.org/pdf/1703.10593.pdf
こちらの論文に沿って説明をしていきます。
#### 導入
CycleGANは、画風変換を可能とするGenerative Adversarial Network (GAN)です。
<img width="400" alt="コメント 2020-05-23 144015.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/0dd1b455-e06a-eeaa-1806-8c7e241a2fe3.png">
上の図は、論文内記載のものですが、左のような画風変換（色塗り）をしたい場合は、**pix2pix**に代表されるような、入力と出力の画像のペアで用いる学習方法が採用されていました。
つまり、図のPairedに示されるような1対1の対応が必要となります。
一方で、右に表されるような、Unpairedの画風変換を可能とする手法も提案されています。
Unpairedの手法では、画風変換のタスクごとに、クラスラベル空間、画像特徴空間、ピクセル空間など、様々な距離空間を定義し、それらを用いて入力と出力の距離を近づける必要がありました。

そこで、**CycleGAN**は、1対1のペア画像を必要とせず、さらにタスクに合わせて学習方法を変える必要がない、そんな手法として提案されました。
<img width="700" alt="コメント 2020-05-23 145111.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/d75f8c22-c22b-a591-dba3-7a8ce1e1da56.png">
これが、**CycleGAN**によって変換された画像たちです。
風景などの写真が、世界的画家であるMonetやGoghの画風へと変換されています。
これは、**pix2pix**のようなペアを必要とする学習ではなし得ません。
なぜなら、Goghらが描いた風景の写真を撮るためには、タイムスリップしなければいけないからです。
そして、ZebraとHorseの変換、SummerとWinterの変換も可能としています。
タスクに沿って学習方法を変えることなく、**CycleGAN**では画風変換を学習可能となります。

----------
#### 目的関数
それを可能とするのが、**Cycle-consustency loss**の導入です。
これが本手法の肝となりますので、後ほどこちらについて説明します。
<img width="600" alt="コメント 2020-05-23 152841.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/b74e7c21-1978-7be3-a364-b7c715013dd0.png">
上の画像は、**Cycle-GAN**で用いられるlossを表したものになります。
まず、**(a)**は、**GAN**の一般的なlossである、**Adversarial loss**になります。
<img width="391" alt="コメント 2020-05-23 153449.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/f3bc89ca-ddb5-61db-d3a3-710c802c625d.png">
上式で定式化されますが、第1項では、Discriminatorは、本物のデータ*y*を本物と識別することを意味します。
第2項では、Generatorによって生成されたデータを偽物と識別することを意味します。
この**Adversarial loss**をDiscriminatorに対して最大化（正しく識別する）かつGeneratorに対して最小化（誤識別させる）ように学習を行います。
Discriminatorに関して、第1項の最大化の意味は、本物のデータ*y*の確率値が1（本物）と識別されることです。
また、第2項の最大化の意味は、*z*に対して*G( )*を用いて生成した偽物*G(z)*の確率値を0（偽物）と識別することです。
Generatorに関しては、その逆、ということになります。
Discriminatorが識別できないような*G()*を作ることを目的としています。
これらの最大化・最小化を行う際は、片方は固定します。
この最大化・最小化を交互に行うことで、学習を進めていきます。
これを、両方のドメインに対して、行います。
つまり、<img width="134" alt="コメント 2020-05-23 160615.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/be5f6f1c-6454-ec9c-6140-e4ded0610b8f.png">と<img width="135" alt="コメント 2020-05-23 160533.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/5cb2a163-436d-2f39-72c4-c9405cf63283.png">に対して、最適化を行っていくということです。

---------

続いて、**(b)**と**(c)**についてです。
こちらが、**Cycle-consustency loss**と呼ばれ、下式で表されます。
<img width="400" alt="コメント 2020-05-23 160243.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/f1ddbbbd-fbe5-7046-13df-f922a4b4cb48.png">
こちらの第1項では、*x*を*G( )*を用いて生成したデータ*G(x)*を*F( )*を用いて元のドメインに戻したデータ*F(G(x))*が、ちゃんと*x*になっているか、というものをL1ノルムを使って表しています。
第2項では、その逆をやっていますね。
考え方としては簡単です。

---------

最終的に、**(a)~(c)**を合わせて、
<img width="350" alt="コメント 2020-05-23 161325.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/46531c90-fa66-7f83-c7bd-0edabddb05e8.png">
こちらのように目的関数を設定します。
<img width="370" alt="コメント 2020-05-23 161539.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/bfa79385-89dd-98de-38de-16c308fb9ef8.png">
この最適化問題を解くことで、目的の*G*と*F*が学習できます。

-------
#### 実験結果
<img width="700" alt="コメント 2020-05-23 161951.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/b3d0765b-de82-259b-6219-6dd93457cfc3.png">
実験結果の一例です。
「うま」と「しまうま」の変換、風景写真における「夏」と「冬」の変換、「りんご」と「みかん」の変換など、様々なタスクにおいて、高精度な画風変換が実現していることが分かります。
<img width="227" alt="コメント 2020-05-23 162151.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/66c782e4-403d-e669-e5c4-5260fa6b5240.png">
これは、失敗例です。
プーチン大統領がしまうまになっちゃっていますね。
この様に、テクスチャの変換はうまくいくものの、形状を捉えた変換は難しい場合もあるようです。
物体検出手法の導入なんかで解決されていくのではと思います。

もっと結果を見たいという方は、論文をのぞいてみてください。

-------
# 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)**
<img width="616" alt="コメント 2020-05-23 164534.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/c6ba2c86-a4ab-3298-c465-05458d830f1b.png">

- **zebra2horse (BtoA)**
<img width="611" alt="コメント 2020-05-23 164735.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642312/93bf2250-0ab7-0c55-253f-e36ee0210bcb.png">
しっかりと変換ができています。すごいです。
以上がLinux上での実装となります。
次回は、自身のデータセットに適用してみたいと思います。

-------
# 参考資料
論文：https://arxiv.org/pdf/1703.10593.pdf
Github：https://github.com/xhujoy/CycleGAN-tensorflow