GANの安定化の有効手法「Spectral Normalization」を実装する

GANの安定化の非常に大きなブレイクスルーである「Spectral Normalization」を使ったGAN（SNGAN）を実装してみました。

## 公式
**論文**
Takeru Miyato, Toshiki Kataoka, Masanori Koyama, Yuichi Yoshida. *Spectral Normalization for Generative Adversarial Networks*. ICLR2018. [OpenReview](https://openreview.net/forum?id=B1QRgziT-)
[Arxiv version](https://arxiv.org/abs/1802.05957)

書いたのは日本人でうち2人はPFNの方です。あまり知られていませんが、GANの安定化の研究に大きく貢献したのは実は日本人なのです。

**デモ**
https://www.youtube.com/watch?v=q3yy5Fxs7Lc
https://www.youtube.com/watch?v=83D_3WXpPjQ

**Chainerによる実装**
https://github.com/pfnet-research/sngan_projection

## 自分の実装・ポスト
**PyTorchによる実装**
https://github.com/koshian2/SNGAN

**より詳しい内容（日本語で自分が書いたもの）**
https://blog.shikoan.com/sngan/
※とにかく長いので時間あるときに読んでください。この投稿は相当端折ったものです。

## Spectral Normalizationのやっていること
### 前提（WGANの背景）
Spectral Normのやっていることを知るには、WGANの理論的な背景を知っていたほうがわかりやすいです。WGANでは「GANの安定性/出力画像の質の向上には、Dの損失関数の**連続性**が重要。$\theta\to P_\theta$のマッピングが滑らかであるほどよい」というのが大きな背景にあります。

その上で、WGANではDのスペクトルに**リプシッツ連続**という強い制約を置きます。このリプシッツ連続がGANの安定性を語る上でのキーワードとなっています。

WGANではリプシッツ連続を実践する手法の1つとして、損失関数にWasserstein距離を使っています（ここは今回大事ではないので省略）。

### Spectral Normの考え方
「結局大事なのってリプシッツ連続であって、何らかの別の方法で制約をおければ、別にWasserstein距離でなくてもいいよね」というのがSpectral Normの根幹の考え方です。実際WGAN-GP（WGANの改良版）はBackpropを2回走らせないといけないので計算が遅かったりします。

その上で、Spectral Normでは**DのBatch NormをSpectral Normという特別なNormalizationレイヤーに置き換える**ということで、この制約を実現しています。論文のGAN（SNGAN）では、損失関数にHinge Lossを使っていたりと、WGANの研究をかなり踏襲してはいるものの、Wasserstein距離からは既に離れているのが特徴です。SA(Self attention)GANや、BigGANといったモダンなGANではSpectral Normを使いつつHinge Lossを損失関数として使っています。Wasserstein距離にこだわらないのが最近のトレンドです。

Spectral Normって具体的にどういうNormalizationかというと、係数行列の**特異値分解**を使ったNormalizationです。

![sngan_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/7c7c9a1a-414c-6a20-6d5f-e31826bfcf19.png)

この$u_l^TW^lv_l$というのが特異値分解です。ただし、そのままSVDなどの関数をかませると遅いので、Power Iterationというアルゴリズムでニューラルネットの中で高速に計算できるようにしています。具体的になにやっているか気になる場合は、[コードを見る](https://github.com/ajbrock/BigGAN-PyTorch/blob/master/TFHub/biggan_v1.py#L27)のが速いと思います。

[PyTorch公式でも実装がある](https://pytorch.org/docs/stable/_modules/torch/nn/utils/spectral_norm.html)ので、これを使うのもありです。

## アニメ顔を生成してみる
[AnimeFace Character Dataset](http://www.nurs.or.jp/~nagadomi/animeface-character-dataset/)をSNGANで生成してみます。

![sngan_anime.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/7ba3b01e-521c-a2f8-92d8-cbf166191c06.png)

SNGANはACGANのようにラベルを加味して生成できるのでそれで生成してみました（Unconditionalなケースも見たい場合は巻末のリンクから別記事を見てください）

**20 epoch**
![epoch_020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/fb715487-1544-f19c-f3f7-fe44d8dd0ffb.png)

大方の輪郭はできています。

**100 epoch**
![epoch_100.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/c8dada63-9920-c7a0-760d-857843d6c963.png)

顔っぽくなってきました。

**500 epoch**
![epoch_500.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/830e8920-103d-674a-c51c-18c9a4329029.png)

細かい輪郭ができてきましたが、まだ線がぐにょぐにょですね

**900 epoch**
![epoch_900.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/12d53bd2-8507-c218-1ab2-33aec79f79dc.png)

とてもいい感じになってきました。背景がぼやけるのは仕方ないです。

**1100 epoch（完成）**
![epoch_1100.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/74abb278-412d-b25b-576b-cfb96bff7050.png)

1100エポック回したのはGを約5万回アップデートするためです。SNGANではアップデート回数をG:D=1:5にしたほうがうまくいきやすい（Spectral NormのぶんDの学習が進みづらい）ので、どうしても訓練時間は長めになってしまいます。訓練時間はRTX 2080Tiを1枚で丸一日程度でした。

### 潜在空間の補間
ACGANの場合は、クラス単位でモード崩壊が置きてしまいましたがどう変わっているでしょうか？

**ACGANの場合**
![inter_models.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/1d427388-a4c2-716c-86c2-84568dcbad3d.png)
クラス単位で1つの画像しか出力できない＝モード崩壊が起こっています

**SNGAN**
![](https://raw.githubusercontent.com/koshian2/SNGAN/master/sampling_interpolation/anime_case1.png)

（左：サンプリング、右：補間です）。まずクォリティが全然違いますし、補間をすると潜在空間の乱数を動かすにつれて**表情や顔の向きが変化している**のがわかります。

Anime Face Datasetはキャラ別に176枚のクラス、14490枚の画像からなります。つまり**1クラス平均82枚**しかありません。これでもそこそこGANとして形になってしまうのがSNGANのすごいところです。

## 花の生成をしてみる
同様に[Oxford Flower Dataset](http://www.robots.ox.ac.uk/~vgg/data/flowers/)という花のデータセットをSNGANしてみます。

![sngan_flower.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/511761ac-db69-3230-c483-798c49d20422.png)

これは8189枚の画像、花の種類別に102のクラスからなります。1クラスあたりの枚数はAnime Faceとほぼ同じです。

花の生成で使ったモデルはAnimeFaceのものとほぼ同じですが（そしてAnimeFaceのモデルは論文のImageNetのモデルを出力解像度だけいじったもの）、Dの計算量を減らすためにDの最初にstride=2のConvを入れました。花では5万回Gをアップデートさせるために1900 epoch回しますが、Dの計算量削減のおかげで、GPU1枚で18時間程度で計算できます。

**20 epoch**
![epoch_020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/961a37db-8e51-aaf2-3c6a-ec23dd22b8b6.png)


**100 epoch**
![epoch_100.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/aeb07cc1-037e-493b-183d-1b8b819d4d58.png)
模様っぽい形はできてきました


**500 epoch**
![epoch_500.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/aff141bc-9a67-f3b6-b575-e34fe735018f.png)
花びらの輪郭はそれっぽい感じになってきました

**1000 epoch**
![epoch_1000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/fe02c566-55b6-47d6-9c0b-9e6140a9f667.png)
だいぶそれっぽい形にはなってきましたが、一部難しい花があるようです

**1900 epoch（完成）**
![epoch_1900.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232088/73c50c24-5613-32a0-a7a8-7c79472cbb10.png)
ちょっと花の構成がおかしいもの（花びらがバラバラになっているもの）がありますが、おそらくデータ数が足りなかったのでしょう。

### 潜在空間の補間
![](https://raw.githubusercontent.com/koshian2/SNGAN/master/sampling_interpolation/flower_case1.png)

クラス単位でモード崩壊していないので、GAN自体はうまく行っています。出力のクォリティを上げるには、データ数を増やすかより最新のGANを使う必要がありそうです。

## まとめ
**Spectral Normalizationすごい**。DのBatch Normを置き換えればいいだければわかりやすい。しかも、データ数が少なくてクラス数が多いケースでもモード崩壊しないので、相当有力な手法でしょう。SNGANで相当綺麗になるのが確認できたので、最新形を使えばどれだけいくのか夢がありますね。

次はこの発展型のSAGAN（Self attention GAN）いってみよう。

## SNGANのより詳しい内容
もっと泥臭い内容が見たければこちら。論文の再実装や追加の実験結果などを掲載しています。
https://blog.shikoan.com/sngan/