StarGANで顔イラストの表情変換

[Chainer Advent Calendar 2017](https://qiita.com/advent-calendar/2017/chainer)の8日目です。

<img width="600" src="https://qiita-image-store.s3.amazonaws.com/0/51673/2be2d2ac-3c9a-6181-5761-a9cf16e39ac2.png">
[ちえなちゃん](https://paintschainer-pr.preferred.tech/chiena-news/)の表情変化。テンプレートは[こちら](https://www.pixiv.net/member_illust.php?mode=medium&illust_id=2312727)を使用。

# はじめに
スタープラチナに殴られたようなイラストをGANで生成することを生きがいとしています。
ここ1~2ヶ月、GAN界を大きく進歩させる研究が続々と発表されました。[Imagenet画像を生成したり](https://openreview.net/forum?id=B1QRgziT-)、[1024x1024の解像度で生成ができるようになったり](https://openreview.net/forum?id=Hk99zCeAb)、[pix2pixの精度を上げたり](https://arxiv.org/abs/1711.11585)、[バリエーションを高めたり](https://arxiv.org/abs/1711.11586)などなど。
今回は約2週間前にarxivに投稿された[StarGAN](https://arxiv.org/abs/1711.09020)という手法をchainerで実装したいと思います。これはCycleGANのようなドメインペア間の画像変換を、多対多で変換できるように拡張した手法です。ちなみに著者らによる公式実装も[公開されています](https://github.com/yunjey/StarGAN)。

<img width="1036" alt="stargan_figure1" src="https://qiita-image-store.s3.amazonaws.com/0/51673/85c2877d-7d87-c1ef-9258-8b45c374e41a.png">
(論文の図から引用。顔の表情を変えたりすることができる。)

# StarGAN
まずはStarGANについて簡単に説明します。
いま異なるドメイン1, 2,...のデータセットがあるとき、ドメイン1に属するデータを、別のドメイン2のデータに変換するようなモデルを作りたいとします。ここで、もしデータセットの中に対応するデータが有るのならば、それを教師として教師付き学習ができます。白黒写真の着色や、地図を航空写真にするといったタスクは、このような対応する教師データを比較的簡単に作れる問題といえます。一方で、顔画像の性別の変更や、馬をシマウマにするといった問題になると対応するペアを用意することが難しくなってきます。
今年の春に提案された[CycleGAN](https://arxiv.org/abs/1703.10593)では、adversarial lossに加えて*cycle consistency loss*を用いることで、このような対応するペアを用意できないドメイン変換の問題を学習できるようにしました。ドメイン1をドメイン2にするネットワーク$G_{12}$とその逆を行う$G_{21}$を用意し、$G_{21}(G_{12}(x)) \sim x$かつ$G_{12}(G_{21}(y)) \sim y$となるようにLossを作ります。CycleGANの詳しい解説は[こちらの記事](https://qiita.com/itok_msi/items/b6b615bc28b1a720afd7)が参考になります。
しかし、このようなCycleGANをドメインの種類がたくさんあるようなデータに適用しようとすると、すべてのドメインペアに対して変換を行うGeneratorと、各ドメインのDiscriminatorを用意する必要があるという問題がありました(下図左)。StarGANでは、一つのネットワーク(Generator, Discriminator)で任意のドメイン間変換を担うことで、よりスマートにドメイン間変換を学習します(下図右)。今どのドメインに変換しようとしているのかをGeneratorの入力にconcatして加え、DiscriminatorはACGANのようにどのドメインの画像かの分類を行うように学習します。
<img width="400" alt="stargan_model" src="https://qiita-image-store.s3.amazonaws.com/0/51673/65b5fd51-3e2c-88d5-2a27-bd8bb8ae55ef.png">
(論文の図から引用)

複数のドメイン間変換を一つのネットワークで行うメリットは以下のようになります。
1. ドメイン数が多いときにモデルをたくさん作らなくていい
2. 1ドメインあたりのデータ数が少ないような場合に、別ドメインとパラメタを共有しているため、過学習を防ぐことができる
3. 異なるデータセットを混ぜることで、データ数を水増しできる(論文ではCelebAデータセット(約200000枚)とRaFDデータセット(約5000枚)を混ぜて実験している)

実際に、顔写真のデータセットでは、CycleGANなどよりもキレイな画像変換ができることが論文で示されています。一方で犬↔猫変換のようなパーツが大きく変わるものは苦手なのでは？(犬→猫と猫→犬はパラメタを共有する利点が少なそう)という直感が働きます。ちゃんと検証はできてないですが、この記事の実験でもその傾向が現れています。


# データ集め
Pixivに投稿された[表情練習](https://www.pixiv.net/member_illust.php?mode=medium&illust_id=2312727)、[表情変化50](https://www.pixiv.net/member_illust.php?mode=medium&illust_id=4482514)のテンプレートを使用している画像を使いました。各画像に対してパターンマッチで枠線を合わせて顔ごとに切り出し、さらにOpenCVで[アニメ顔検出](https://github.com/nagadomi/lbpcascade_animeface)をして顔が描かれていないと思われる画像を弾きます。実際のところ、このデータセットを作る作業が一番大変です。。。
これによって、「表情練習」から約18000枚、「表情変化50」から約3000枚の表情ラベル付き顔イラスト画像(解像度は200x240で固定)を用意できました。

# Chainerで実装
それではStarGANをchainerで実装していきます。
ディープラーニング界隈は進展が非常に速いので、あまり手法の実装に時間をかけていられません。そこでChainerはコードの再利用性に気を使って設計されており、多くのディープラーニングの手法は、Exampleなどにあるコードから
- データセット(dataset_mixin.DatasetMixinなどを継承して作る)
- ネットワーク(chainer.Chainを継承して作る)
- Updater(chainer.training.StandardUpdaterなどを継承して作る)
- Evaluator(extentionとして作る)
あたりをちょっと書き換えるだけで再現することができます。今回はこの前作ったProgressive growing of GANsの[実装](https://github.com/pfnet-research/chainer-gan-lib/tree/master/progressive)をコピペして実装をはじめました。

### データセット
今回は権利的に公開できませんが、画像数万枚程度のデータセットなら、numpy.savez_compressedなどでまとめておくのが楽です。[この例](https://github.com/pfnet-research/chainer-gan-lib/blob/master/common/dataset.py#L9)のように、get_example(i)でi番目のデータを返すようにします。cropなどの前処理もここに書くことが多いです。

### ネットワーク
多くの論文にはネットワークの構造がきちんと書いてあるので、それを書き起こしていく作業です。仕事や研究で使うときは、**まずは論文に忠実に再現する**ほうがいいです（自戒も込めて。。。）。そうしないと、うまく動かなかったときに何が悪いのかよくわからなくなります。。。
- weightの初期値
- Biasの有無
- downscale, upscaleの方法
- ResBlockの中身
- Lossを計算するときに平均を取っているのか、和を取っているのか
などは著者実装を見ないとわからなかったりもするので注意しましょう。

### Updater
updaterはchainer.training.StandardUpdaterを継承して実装します。update_core関数の中にforward-backward-updateの処理を書くことで、毎イテレーションごとにtrainerが呼び出してくれます。
StarGANではWGAN-GPを使っており、Discriminatorにgradient penalty lossが適用されます。これは出力を入力で微分した値の大きさが1に近づくようにするという項なので、微分の微分が必要になります。幸いchainer v3からbackward方向にも自動で計算グラフを作る機能が追加され、自分でbackwardの中身を書かなくても良くなりました。

```python3
y = f(x)
dydx, = chainer.grad([y], [x], enable_double_backprop=True)
```
このように書くことで、backward可能な形で微分を計算することができます。この機能は、[WGAN-GP](https://arxiv.org/abs/1704.00028)や、[MAML](https://arxiv.org/abs/1703.03400)などのmeta learningを始めとする最近の幾つかの手法で役に立ちます。


# 結果
では結果を見てみましょう。今日公開されたPaintsChainer公式キャラクター[ちえなちゃん](https://paintschainer-pr.preferred.tech/chiena-news/)を入力してみます。
<img width="200" src="https://qiita-image-store.s3.amazonaws.com/0/51673/50019c0b-fe93-fcc7-896b-6290b7544f90.png">
入力に使った画像

<img width="600" src="https://qiita-image-store.s3.amazonaws.com/0/51673/2be2d2ac-3c9a-6181-5761-a9cf16e39ac2.png">
表情練習12に対する結果。「笑顔」でちょっと口角を上げたり、「泣く」で涙を書いたり、「照れる」で頬を染めたりしているのがわかる。「嫌いな○○の前」で目のハイライトがちょっとなくなってるのが面白い。

<img width="600" src="https://qiita-image-store.s3.amazonaws.com/0/51673/0e9acbde-684a-5829-7cb0-278a3495f538.png">
表情練習50に対する結果。なぜか表情でなく色を変えられてしまった。データ数が少なすぎた(各表情に対して60枚程度)のだろうか？

自分では絵が描けないので[PGGAN](https://github.com/pfnet-research/chainer-gan-lib/tree/master/progressive)に生成してもらった画像に対しても適用してみた。
<img width="600" src="https://qiita-image-store.s3.amazonaws.com/0/51673/98627066-cfe9-0688-af8a-247313d69030.png">

<img width="600" src="https://qiita-image-store.s3.amazonaws.com/0/51673/d64f3387-7044-c7a4-75fb-9d1be0153247.png">

# 考察
表情変化12に対しては、比較的合理的な結果が出できたと思います。
しかしながら局所的な変化だけでごまかしている感もあり、「悲しみ」や「焦る」はよくわからないです。もう少しダイナミックに顔のパーツを変化することを期待していたのですが。。。
また、表情変化50の方は全然うまく行ってません。論文の主張では、数の多い表情変化12のデータが助けになって、数の少ないデータセットの学習もできるという印象でしたが、そううまくは行きませんでした。
原因としては、次のようなものが考えられます。
- 論文で使っていたCelebA, RaFDデータセットは顔の位置、大きさが揃っていたが、今回使ったイラストは顔に限定されているとはいえ位置や向き、大きさがばらばらで、また、画風（線画、ラフ、水彩、アニメ塗りなど）にも多様性があった
- 全体のデータ数が20000枚強で、論文の1/10程度しかなかった
- lossのバランスなどのパラメタが悪い/実装をミスっている

データ数を増やす、整形する、CelebAなどの論文で使っているデータセットで実装のミスがないか検証するなどして、リベンジしたいですね。

# おわりに
現在コードを準備中ですm(_ _)m近日中に公開します。。。