ディープラーニングで櫟井唯ちゃんに喋ってもらう
この記事は ゆゆ式 Advent Calendar 2017 - Adventar 24 日目の記事です。
はじめに
前回、唯の画像を無限に生成することに(部分的に)成功した訳ですが、画像ができたら今度は声が欲しくなってきます。
そこで、 [1710.08969] Efficiently Trainable Text-to-Speech System Based on Deep Convolutional Networks with Guided Attention を chainer で実装して、唯の声で学習させてみました。
続きを読む櫟井唯ちゃんの画像を無限に生成する話りぴーと
joisino.hatenablog.com ▲昔の記事
前回の試みから二年以上経ちましたがまだゆゆ式二期は発表されません。(*1)
二期が発表されないためこの記事のタイトルものんのんびよりさんから拝借することになりました。
やはり今話題のディープラーニングでなんとかするしかなさそうです。
三行で説明して
Progressive Growing GAN (Tero Karras et. al. 2017) を chainer で実装して櫟井唯さん(ゆゆ式)の画像を生成しました。
こんな感じのができました。(下の方にスクロールするともっとたくさんあります。)
レポジトリです。
Progressive Growing GAN とは
浅いネットワークで小さい画像を生成・識別することからはじめ、段階的にネットワークと画像を大きくしていく手法です。太古に流行った AE の事前学習のような感じです。
こうすることで本来不安定な GAN の学習が安定します。
GAN の枠組みは基本的に WGAN-GP を使っています。
この論文では他にも GAN を安定させたり、多様性をもたせるためのいくつかの技術を提唱しているので紹介します。
バッチごとの統計
GAN の generator は教師データ 1 枚を覚えてそれだけ生成する過学習に陥りがちです。そこで、この論文では、 discriminator の中間層に、バッチ全体の統計量を用いることを提唱しています。
具体的には、
- その層の入力 (batch x channel x height x width 次元) について、各位置・チャネルごと (channel x height x width 箇所) に、バッチを通して(各 batch 個ある値についての)標準偏差を求める。
- それらの値 (channel x height x width 個ある) の平均 m を求める。
- m を height x width 個並べたものを(それぞれのバッチについて)入力に append して、出力とする(batch x (channel + 1) x height x width 次元)
というものです。こうすることで、データ 1 枚だけ覚えるのでは、分散が小さすぎて discriminator に見破られてしまい、結果として generator は多様な画像を生成するようになります。
論文では、この層を最終層付近に一箇所挿入しています。
論文によると、さらにリッチな統計量も試したが、このシンプルな方法が一番良かったとのことです。
論文では 1. で標準偏差を用いていましたが、後述の実験では、安定しなかったので分散を用いました。(chainer の F.sqrt は現状二回微分に対応していなくて、自分で書いたので何か間違えていたのかも)
初期化と動的補正
畳み込みの重みの初期化に He normal (平均 0 標準偏差 sqrt(2/N) の正規分布で初期化)を使うことが多いですが、この論文では標準正規分布で初期化し、畳み込むときに sqrt(2/N) 倍することを提唱しています。
こうすることで、様々な畳み込み層の学習が同じペースで進み安定するそうです。
ピクセルごとの正規化
generator の畳み込み層の出力の際に、毎回ピクセルごとに正規化をかけます。
つまり、出力は batch x channel x height x width 次元 あるわけですが、 batch x height x width 個の場所(各場所には channel 個値がある)について、標準偏差で割ってから出力します。
こうすることで値が発散するのを防ぐ効果があるそうです。
交互に学習
WGAN-GP では discriminator(critic) 5 回学習するごとに generator 1 回学習などとすることが一般的ですが、この論文では交互に学習 (n_critic = 1) させたそうです。
特に理由は書いてませんでした。
(これでもうまくいくから)簡単のため?
discriminator の出力にペナルティ
この論文では WGAN-GP の loss に加えて、 discriminator の出力値の二乗に比例する項を設けています。
これは、この論文では discriminator の最終層の活性化関数に恒等関数を使っていて、discriminator が強い時に出力がどんどん大きくなるのを防ぐためだと思います。
実験
実際に python 3.5.2
+ chainer 3.0.0
で実装して実験しました。(レポジトリは上の方に貼ってあります)
データは唯の画像 4077 枚です。(同じ・ほぼ同じ画像も多く含んでいます)
4x4 の画像からはじめて 256x256 の画像を出力するようにしました(論文では 1024x1024 までやっています)
学習にかかった時間は p3.2xlarge (GPU は Tesla V100) を使っておおよそ丸 2 日です。
生成は以下に載せるような gif アニメを一本作るくらいなら c5.large (GPU なし)で 1 分くらいでできます。
結果をどうぞ。
ギャラリー
割といい感じに生成できてるのから、雑に二枚の画像補完しただけやんけみたいなのまで様々です。
考察
体感的に他の GAN の手法に比べてかなり安定しているように思います。
論文のデモビデオほどはうまくいかなかったですが、思ったよりいい感じに生成できました。
論文のデモビデオでは、見たら分かるとおり、目や鼻や口の位置などをかなり丁寧に正規化しています(論文の appendix に正規化する手法も書いてあります)
今回はそこまで丁寧にデータを洗ったわけではないので、この辺をもっと丁寧にやるともっといい感じになりそうです。
データももっと必要そう。
そもそも唯の画像がそれほど大量には無いので、一般のアニメキャラクターの生成モデル作って唯でファインチューニングしたりすると良いかもしれません。
もっといい感じにしてまたどこかで発表できたらいいな。
おまけ
▲データに少しゆずこが混ざってしまったせいでできた合体画像
最後に
何か間違いや質問などがあればお願いします。
最後まで読んでいただきありがとうございました。
注
*1 OVA は発表されました!めでたい!!
参考文献
[1] Progressive Growing of GANs for Improved Quality, Stability, and Variation | Research
[2] GitHub - dhgrs/chainer-WGAN-GP: A Chainer implementation of WGAN-GP.
[3] GitHub - joisino/chainer-PGGAN: Progressive Growing of GANs implemented with chainer
Convolutional LSTM
大学の実験で必要になって実装したのでメモしておきます。
Convolutional LSTM の説明
名前で完全にネタバレしてる感が否めないですが、Convolutional LSTM とは、LSTM の結合を全結合から畳み込みに変更したものです。
例えば画像を RNN に食わすときに、位置情報が失われないので便利です。
動画の次フレームの予測や天気予報などに使えます。
具体的には、以下のような構造になっています。
x は要素ごとの掛け算、 * は畳み込みを表します。
通常の LSTM との差分を赤で書きました。といっても、一部の掛け算が畳み込みになっているだけですが。
peephole の部分だけ要素ごとの掛け算になっていることに注意してください。
実装
Convolutinoal LSTM と、それをビルディングブロックとして使って画像予測のネットワークを実装しました。
レポジトリです。
python3.5.2
+ chainer 3.0.0
で開発しました。
画像予測のネットワークは以下のような構造をしています。([1] より抜粋)
左側のネットワーク(Convolutional LSTM を 2 層並べたもの)に、入力となる画像(例えば 1 フレーム目, 2 フレーム目, 3 フレーム目)を流し込んでいって、データの表現を得ます。
次に、左側の LSTM たちの内部状態を右側の LSTM たちにそれぞれコピーします。
最後に、右側のネットワークにゼロを入力して、出てきた値を concat して、それらを 1x1 で畳み込んで出力を得ることを繰り返します(例えばこれが 4 フレーム目の予測画像, 5 フレーム目の予測画像, 6 フレーム目の予測画像になります)
右側のネットワークの二回目以降の入力は、前回の出力を入れるという実装もありそうです。論文を読んだ限り特に書いてなさそうだったので、今回はゼロを入力するようにしました。
実験
Moving MNIST [2] で実験しました。
計算資源の都合上論文の実装より小さめのネットワークです。
具体的には 2 -> 5x5 Conv -> 128 -> 5x5 Conv -> 64 -> 5x5 Conv -> 64 -> 1x1 Conv -> 2 (右側の forecasting network) という感じです。
以下は生成された画像たちです。
まあいいんじゃないでしょうか。
Loss です。
もう少しちゃんとチューニングする必要がありそうですかね。
だいたい 20 epoch = 8750 iteration = 140000 images の訓練を p2.xlarge (GPU は K80) 上で行って 22 時間くらいでした。
最後に
何か間違いや質問などがあればお願いします。
最後まで読んでいただきありがとうございました。
参考文献
[1] Xingjian Shi, et. al. Convolutional LSTM network: A machine learning approach for precipitation nowcasting. In NIPS, 2015
[2] Moving MNIST
神絵師がtwitterに上げた神絵を収集する
色んな人がやってそうだけど自分用に作ってみました。
リポジトリです。
使い方
動作例
詳しく
動作の流れは以下の通りです。
- twitter API を使って、リストから画像を含むツイートを抽出して保存する
- 保存した画像から二次元イラストを抽出する
- 抽出した画像を slack incoming-webhook をつかってチャンネルに流す
2 が難しいところです。
神絵師が画像を投稿したからといってそれが必ずしも神絵であるとは限りません。
例えば今回の場合、神絵師が食べた焼肉の写真は slack に流れてきてほしくありません。
▲流れてきてほしくないツイートのイメージ(このアカウントは神絵師ではありません)@wafrelka 少し遅くなりましたが、今日の昼ごはんは焼肉です!! pic.twitter.com/wGGmmxxF29
— 紫乃さん (@joisino_) 2016年11月5日
写真とイラストを区別するのは意外と難しかったです。(何か良いアルゴリズムがあったら教えてください)
ディープラーニングでなんとかしたりできるんでしょうか。
今回は OpenCV の Haar Cascade で nagadomi さんのアニメ顔検出器を使わせてもらいました。
これでアニメ顔が 1 箇所以上で検出されると、イラストという判定にしました。
体感 accuracy は 7 割くらいですが、 false negative はほとんど無く、slack に流れてくるのは神絵ばかりです。
今回の場合は、網羅的に収集したいというよりは、よさそうな画像をなんとなく眺めたい用途なので、これでよしとします。
今後の課題
ソシャゲガチャ問題
神絵師がソシャゲガチャの結果画像を上げることはよくあります。
今回の場合、ソシャゲガチャの結果画像は slack に流れてきてほしくないですが、アニメ顔が含まれるため防げません。
10 連ガチャの一覧結果はまだ例外でなんとかなりそうですが、特定の一枚の結果を上げている場合、その絵を描いたのも(別の)神絵師なので、弾くのはなかなかむずかしそうです。
最後に
何か間違いや質問などがあればお願いします。
最後まで読んでいただきありがとうございました。
ArtClass(IOI2013)をディープラーニングで解く
はじめに
IOI 2013 オーストラリア大会に Art Class という問題があります。
この問題は、画像データが与えられるのでその画像が
様式1(新造形主義の現代芸術)
様式2(印象派の風景画)
様式3(表現派のアクション・ペインティング)
様式4(カラーフィールド・ペインティング)
のいずれであるかを判定する問題です。
正答率が 0.9
以上になると満点が得られます。
IOI にしては珍しい機械学習的な問題であることと、ジャッジが壊れて結果が返ってこなくなったことなどで有名なので、知っている人も多いかもしれません。
問題文やデータは、 http://www.ioinformatics.org/locations/ioi13/contest/ から手に入ります。
普通の解法
例えば 3x3
と 65x65
の大きさの窓を作って分散を計算して、それらを使って手で決定木を作るなどすると解けます。
C++ での実装例
正答率は 99/102 = 0.97...
で満点が獲得できました。
ディープラーニングで解く
手で特徴量を作るのは面倒なのでディープラーニングでなんとかして欲しいですよね。
今回は VGG の pretrained model (with ImageNet) を使ってこの問題を解いてみました。(念の為に言っておくと、 IOI の本番では chainer も pretrained model も GPU も使うことはできません)
最終層だけ 4096 x 4
の FC 層に変えて学習しなおしました。
学習データが 4 * 9 = 36
枚しかないのでかなり厳しかったですが、augmentation(crop) してなんとか正答率 92/102 = 0.90...
で満点を獲得できました。
適当に作った決定木より精度低い。悲しい。
ImageNet で前学習したモデルを使ったのが筋が悪かったかもしれません。絵画的なデータで前学習したモデルがあれば、もっと楽に満点を達成できたと思います。
テストデータに対しても少しチューニングしたので、汎化性能はさらにもう少し低いと思います。体感 accuracy は 0.8
~ 0.85
くらいです。IOI 本番でもテストデータに対する正答率は見える(はずだった)のでよしとしましょう。
python + chainer での実装例
環境は python 3.5.2
+ chainer 2.1.0
です。
最後に
正直今回の場合、人力特徴量抽出とハイパーパラメータチューニングの面倒さは同じくらいでした。(本末転倒)
何か間違いや質問などがあればお願いします。
最後まで読んでいただきありがとうございました。
参考文献
画像や問題文は全て http://www.ioinformatics.org/locations/ioi13/contest/ からの引用です。