読者です 読者をやめる 読者になる 読者になる

rkgkpyrk

^q^

GANとDCGANの論文を読んだ。

今週はGenerative Adversarial Networks(以下GAN)とDeep Convolutional GAN(以下DCGAN)を何となく読んだので、その辺の概要をまとめておく。個人的な備忘録の側面が強いので、あまり色んな人が読むことは想定しておらず、ある程度前提知識を要求している気がするし、参考になるかわからないけれど読む人は参考程度に。もとの論文がいつでも一番正確なはず(GANDCGAN)。そもそも既に多くの人が解説記事を書いているし、多くの人にとってこの記事はあまり価値を持たないんじゃないかなと思う*1

GANとDCGANの位置づけ*2

GANの目的は与えられた学習データから生成モデルを推定すること。つまりは訓練データから学習することによって、確率分布に基づく乱数を吐くが如く、それっぽいデータを吐くようなサンプラーをつくることができるということだろう。たとえばパラメタ集合{\Theta}によって定まる何らかの確率分布{p(x;\Theta)}を定義して、訓練データ集合{X}に対して、最尤推定などをして{p(x;\hat{\Theta})}(ここで{\hat{\Theta}}は尤度を最大化するパラメタ集合)あたりをサンプラーとして使おうみたいなことがまず考えられるのかなと思う。

当然サンプラーの表現力は{p(x;\Theta)}の構成方法に大きく依存していて*3、例えば画像のような超絶複雑な何かをいい感じに表現するためには、統計の教科書に乗ってるような、よく知られた確率分布なんかを{p(x;\Theta)}としてそのまま使っても表現力が全然たりなくて、『じゃあそこに多層パーセプトロンとかを組み込んじゃえば超複雑な生成モデルが構成できるのでは?』なんて考える人がいてもおかしくはないだろう。変分オートエンコーダあたりはまさにそういう感じで、GAN(とDCGAN)も概ねこういう考え方に沿っているのかなと思う。

Adversarialな問題設定

ちなみに英語力の無い僕は、Adversarialという単語がわからなかった。反対の、敵対する、対立するなどの意味を持つらしい。 さて、まず目的関数を眺めてみる。

{V(D,G)=\rm{E}_{ {x} \sim p_{data}(x)} \lbrack log D(x) \rbrack + E_{ {z} \sim p_{z}(z)} \lbrack log (1-D(G(z))) \rbrack }

ここで、 p_{data}(x)はデータを生成する分布で xは訓練データのサンプルと見て良いと思う*4 p_{z}(z)は何らかの確率分布( p_{data}(x)の事前分布としての役割っぽい)、関数DGは多層パーセプトロンによって構成される関数。ということでこれらは多くのパラメータを含み、これらのパラメータを動かしていって上述の目的関数をどうにかしようという話。最終的には適当な乱数をp_z(z)から生成し、多層パーセプトロンGにブチ込むと学習データらしきもの(たとえば画像)が出てくる。そういう仕組みらしい。

目的関数がわかったので、これを最大化したいのか最小化したいのか、GANのユニークなところはたぶんここで、以下のような問題設定になっている。

{\underset{G}{\min} \underset{D}{\max} V(D,G)}

Dに関しては目的関数を最大化、一方でGに関しては最小化しようとする。何故か対立していて、これがたぶんAdversarialとつく所以。 DGはDiscriminative modelとGenerative modelを表していてるとのことで、論文を眺めていたとき、後者はともかく前者の意味がさっぱりわからなかった。Weblioで調べてみるなどしても、『慎重な判断を表現するさま』『微細に区別することができる』などと書いてあって、何を判断したり区別するんだよ感がすごい。というわけでまず{D}のお気持ちを考えることにした。

Dは何をしたいのか

先程の目的関数、何となく既視感があるなと思ったらベルヌーイ分布の対数尤度のように見えてきた。そう聞くとなんとなくdiscriminativeな感じがしてくる。つまりは2値分類。たとえば、 {x_1,\ldots,x_n \sim p_{data}(x)}{z_1,\ldots ,z_m \sim p_z(z)} (もちろんどちらもi.i.d)とするとnmが十分大きければ、

{V(D,G) \simeq \sum_{i=1}^{n} log(D(x_i)) + \sum_{j=1}^{m} log(1-D ( G (z_j) ) )}

と書ける。ここまで言及してこなかったけど、Dの出力は1との減算が行われていることから分かる通りスカラー値で、もしこのDの値域が(0,1)なら、D(x_i)は1、D(G(z_j))は0にできるだけ近づくようなDにしてあげれば、目的関数を最大化できそうだ。でも何か論文にはアクティベーション関数はmaxout使うって書いてあって、そういう値域の縛りは無さそうだった*5。でもまぁいずれにせよD(x_i)は大きな値、D(z_j)は小さな値を返すDを選ぶのが良さそうだ。ここまでの話を一言でまとめると、つまりはDxG(z)を上手に見分ける1次元の特徴量を吐く関数になろうとしているようだ。

じゃあGは何をしたいのか

前述の通り、先程の関数を最大化するということは、xG(z)を上手に見分けられるようにすることに対応する。一方でGはこれを最小化しようとする。つまりは何とかしてDを騙して、xと判別がつかないものをzから生成しようとするのがGのようだ。こういう回りくどいことをすることで、過学習を避けてるのかなと思った*6

一方でDを騙そうとしているGに基づいて生成されるサンプルの分布p_gが、p_{data}(x)の良い近似にそもそもなるのだろうかというのは自然な疑問だけれど、Gに関して{p_g=p_{data}}となるときが大域的最適解であり、またこのときに限ることの証明がついていた*7

アルゴリズムについて細かい話

基本はDG交互にミニバッチでSGD的な反復法を行う(論文にはmomentum使ったって書いてあった)。 パラメータkというのがあって、Dk回更新してからGを1回更新するみたいなことが書いてあったのだが、k=1でやったって書いてあって意義があまりわからなかった。

あとは最初のうちはGの更新の際、log(1-D(G(z)))がサチりやすいから、これを最小化するのではなく、log(D(G(z)))を最大化するのが良いとあった。たぶん情報落ち対策*8

DCGANにおける改善点

DCGANは、要は畳み込みネットワークをGANで構成するとなかなか上手く行かないから、以下のことをすると安定するよ、という話。

GANが言うこと聞かなくて試行錯誤したんだろうね。

*1:そもそも意義のある情報を書いているブログではないはず。

*2:僕の中での位置づけで、世間の話はよくわからない。

*3:もちろん最尤推定というフレームワーク自体を採用するかどうかも問題。

*4:こういう書き方は個人的にあまり見ない気がしたが、僕自身のそういう感覚自体もあまり信用できない。

*5:sigmoid来てくれ頼むーって思いながら読んでた。

*6:もちろん何の根拠もなくてきとうなことを言っている。

*7:SGD等の反復法で何が大域的最適解だよ、みたいなところはある。

*8:1との減算対策。