ABEJA Tech Blog

株式会社ABEJAのエンジニアたちがAI, IoT, Big Dataなど様々なテーマについて発信します

俺はSSHをしたくない ~ Fargate vs ECS+SpotFleet のPrice比較 ~

この記事は AWS Fargate Advent Calendar 2017 の9日目の記事です。

こんにちは。 service sshd stop 布教活動中の、ABEJAのサービス系インフラを管理しているインフラエンジニアの村主です。

今回AWSからFargateというコンテナマネージドサービスが発表されました。 Fargateの詳しい説明は AWS Fargate Advent Calendar 2017 の等の情報を参照いただければと思います。

タイトルでも書き、昔に こんな記事 を書きましたが、僕はSSHはしたくないし、極論言えばインフラも管理したくありません。

そこで今回のre:InventでFargateが発表されて、EC2やクラスタの面倒みない=SSHしなくてええやん!イェーイ!を夢見て、コスト面の試算を始めました。

CPUとメモリの組み合わせ

FargateはCPUとメモリをある程度柔軟に選択でき、約50通りの組み合わせができます。 CPUとメモリの組み合わせは以下の通り、CPUに合わせて利用できるメモリ量が決まってるようです。

CPU (vCPU) Memory Values (GB)
0.25 0.5, 1, 2
0.5 1, 2, 3, 4
1 Min. 2GB and Max. 8GB, in 1GB increments
2 Min. 4GB and Max. 16GB, in 1GB increments
4 Min. 8GB and Max. 30GB in 1GB increments

Fargateの料金

CPUとメモリ毎の利用料は以下になり、1ヶ月に換算すると概ね以下のような金額になります。

Resource Cost/Hour Cost/Month(744h)
CPU (1vCPU) $0.0506 $37.6464
Memory Values (1GB) $0.0127 $9.4488

オンデマンドとの比較(バージニア)

そしてオンデマンドとの比較をしてみます。(若干計算間違ってるかも。ご了承ください)

Type CPU MEM On-demand Cost(744h) Fargate Cost(744h)
t2.small 1vCPU 2GB $17.112 $56.544
m3.medium 1vCPU 3.75GB $49.848 $73.0794
m4.large 2vCPU 8GB $74.4 $150.8832

オンデマンドと比較すると高く見えますが、オンデマンドのCPU/MEMリソースを100%効率活用した場合の比較なので、 スケジューリングされたコンテナが上手くはまらなければ効率はもう少し落ちると思います。 また、AutoScalingとか入れてると70%や80%でAutoScale発動とか組んでると、さらに20-30%位の効率は落ちると思います。 それでも少し高いかもしれませんが、EC2やクラスタの面倒をみない分手間が減ります。

僕の立ち位置としては、社内のエンジニアにAWSやインフラのノウハウとトランスファーすることが多いので、 インフラの考慮する要素が減ると(例えば、EC2やクラスタの管理等が減ると)エンジニアが少ない労力で たくさんのインフラを管理することが出来るようになる(インフラすら意識しない感じかもしれないけど)ので、 学習コスト等を考えると非常に安いものかと思います。

この時点で積極的に使っていこうと思いました。そう、この時点では。

SpotFleetとの比較

弊社ではECSを使ってるサービスがあるので置き換えられるかな?と思いましたが、よく考えたらSpotFleetも利用しているため、 オンデマンドの価格より少し高いなら、SpotFleetと比較したらもっと高くなっちゃうのでは・・?夢への計画が・・。と思い恐る恐る計算してみました。

  • 注意点
    • 2017/12/7時点でのバージニアの価格です
    • 価格変動するため、以下の金額から増減することは大いにあります
    • あくまで超参考値としてください
    • t2.smallはSpotInstanceがないので省いています 最近tシリーズもSpotInstanceが利用出来るようになったようです!
Type CPU MEM On-demand Fargate SpotFleet
m3.medium 1vCPU 3.75GB $49.848 $73.0794 $5.8776
m4.large 2vCPU 8GB $74.4 $150.8832 $22.3944

愕然としました

参考値としてm3.mediumとm4.largeだけ算出してますが、概ね同じような価格差かと思います。 リソースの非効率分の割合の話や、SpotFleetは価格変動に伴う停止リスクがあるにしても、この差は無いだろう・・。何度も計算しましたが、結果は同じでした。

でも僕は諦めません。 SpotFleet使うまでは無いよね。とか、ここ落ちると流石に・・。という部分にはSpotFleetを使うのを躊躇するので、 そういう部分ではインフラの管理コストを削減するために積極的にFargateを採用して行きたいと思います。 僕は諦めません。

また、そもそもLambdaで無理やりやっているような処理をFargateに置き換える事の相性は良さそうなので、積極的に採用して行きたいと思います。

おすすめ

SpotFleetの金額は見ずに、何も考えずにFargate使う方が幸せになれます。

異空間への埋め込み!Poincare Embeddingsが拓く表現学習の新展開

ABEJAでResearcherしている白川です。

今回ご紹介するのは、Poincaré Embeddings [1]という手法です。その内容に驚愕し、個人的に調べたり実装したり勉強会でお話したりしていたところ、最近運良く自分の実装をredditで取り上げてもらえたので、これを機にその驚愕の内容を共有できればと思います。 正直、自分の中ではまだ煮詰まりきっていない技術なので、現況の共有はしますが、ところどころ私の憶測や展望、期待が入り混じっていることをご容赦ください。

www.reddit.com

Poincaré Embeddingsは大雑把に言えばword2vecを異空間で実現する技術で、双曲空間(Hyperbolic Space)という、おなじみのEuclide空間(2点$x,y$の間の距離を$\sqrt{(x_1 - y_1)^2 + (x_2 - y_2)^2 + … + (x_d - y_d)^2}$で測るいつもの空間)とは異なる空間でデータを表現します。それにより、従来手法(word2vecのようにEuclide空間へ埋め込む手法)では200次元くらいのベクター表現を要していたケースで、なんと5次元で遥かに高い精度を達成することに成功しています。

f:id:daynap1204:20170827142406p:plain Poincaré Embeddingsによる埋め込み精度の向上([1]から引用)

Poincaré空間に埋め込むことによるメリットを列挙しておきます。

  • Euclide空間への埋め込みに比べて、空間をexponentialに効率よく利用できる
  • それにより、十分精度の埋め込みを低次元で実現できる
  • データのもつ階層構造を自動的に抽出できる
  • 実装が簡単(ちょっとした補正をするだけです)
  • アイデアがわかりやすい
  • リーマン多様体上での機械学習の取っ掛かりとしてよい

この手法、アイデアは簡単ですがとても可能性を感じます(リーマン多様体上の機械学習とかは昔からちょいちょいあるので、やっと実用段階に来たかという感が強いですが)。 論文を読むと数学的なバックグラウンドが一見必要に見えますが、やってることは単純ですので、本記事でその雰囲気を噛み砕いてお伝えできればと思います。

なお、私はグラフ構造をDeep Learningで表現したり、グラフ構造を使って課題を解決することに興味を持っているのですが、本手法をそういったケースにうまく使えないか模索しています。とにかく、シンプルなアイデアなのでいろいろ想像が膨らんで楽しいです!

以下、勉強会で使ったslideもあるので、合わせてご参照ください。本稿で説明しきれていない内容もちょいちょい盛り込んであります。

www.slideshare.net

また、私の実装は下記にあります。本稿で紹介する実験の一部も再現できるので、ご興味あればお試しください。IssueやPRも大歓迎です。

github.com

まずはword2vecから復習していきましょう。

word2vecの復習

word2vecやskipgramをよくご存じの方は本節は飛ばしても大丈夫です。

word2vec[2]はテキスト中の単語の表現ベクターを求めるツールで、skipgramとcbow[3,4]という2つのモデルが実装されています。

もともと、Yoshua Bengioらにより、Deep Neural Networksを使って単語の表現ベクター(分散表現)を求めるという論文[5]が2001年(!)に出されていたのですが、この論文では、単語の表現ベクターをDeep Neural Networksを用いて求めるという仕組みになっていました。

Tomas Mikolovによるskipgram/cbowの発見の貢献は、Deep Neural Networksのような複雑でパワフルなモデルを使わなくとも、単語の「表現ベクター表」を直接最適化していけば良いことを実証したことにあります(冷静に考えれば表の最適化だけでよいことはわかります)。

さらにTomas Mikolovらは、Negative Samplingという仕組みを導入し、圧倒的な計算効率の向上も実現しています[6]。ここにおいて、NLP界隈でのword2vec祭が始まりました。よいモデルとよい実装がちゃんと公開されるというのは素晴らしいことですね!ちなみに私はシンプルで強力なword2vecが大好きです。

さて、word2vecですが、ここではNegative Samplingつきのskipgram(SGNS)の方だけ説明します(cbowもちょっとモデルが違いますがノリは一緒です)。 SGNSは、非常に単純なモデルで、単語の表現ベクター表を用意しておいて

  1. 文中に近接して出て来る単語対(positive samples)の表現ベクターは内積が大きい
  2. 乱択された単語対(negative samples)の表現ベクターは内積が小さい

となるように最適化を行います。具体的には、単語 $i$ の表現ベクターを $v_i$ とするとき $V={\{v_i\}}_{i \in I}$ が表現ベクター表ですね)、下記の目的関数を最大化します。

$\text{maximize}_{ \{v_i \}_i } \ \ \mathbb{E}_{(i,j) \sim P(i,j)} \Bigl[ \log \sigma ( \langle v_i, v_j \rangle ) \Bigr] + k \ \mathbb{E}_{(i,j) \sim P(i)P(j)} \Bigl[ \log 1-\sigma (\langle v_i, v_j \rangle) \Bigr]$

ここで、$\langle v_i, v_j \rangle$は単語$i,j$の表現ベクターの内積です。$P(i,j)$は単語対$(i,j)$が近接して現れる確率分布(下図の場合、注目している語onの左右に2単語以内に現れる単語を近接語としています)を表し、$P(i)$は単語$i$単独の頻度分布(unigram分布)です。また、$k$はpotive samplesとnegative samplesのバランスをコントロールする非負定数で(理論的には$k=1$が理想ですが、$k=5,10,20$などの値が実用上用いられます)、関数$\sigma(x)=1/(1+\exp(-x))$はシグモイド関数です($P(i)$を3/4乗したりという細かいチューニングが本当はあるのですが、本質的でないので無視しています)。

難しいことを抜きにしたら、positive samplesの出現確率を増加させ、negative samplesの出現確率を低下させるように単語の表現ベクターを求めていることになります。

f:id:daynap1204:20170827155338p:plain SkipGram with Negative Sampling (SGNS)

なお、SGNSはpointwise mutual information(PMI)を要素値にとる行列の分解になることが知られています(GANsのオリジナル論文のdiscriminatorの収束証明と同様の議論で容易に示せます)。

$$ \langle v_i, v_j \rangle = PMI(i,j) = log \frac{P(i,j)}{P(i)P(j)} $$

SGNSの素晴らしい点は、その計算効率です。テキストデータの上に窓(上図の左上の点線窓)を滑らせていきながら注目語(上図ならon)と周辺語(同cat, sat, the, mat)を取得していき(positive samples)、その一方で単語の頻度分布から単語をサンプリング(negative samples)してくれば、あとは確率的勾配降下法により容易に最適化できます。さらに、これにより得られる単語の表現ベクターは、よく知られているような正則性をもつことが実験的に知られています(linguistic regularity)。king-man = queen-womanというやつですね。

f:id:daynap1204:20170827161023p:plain

[6]より引用

SGNSですが、適用先はテキストデータだけにとどまりません。ここではグラフ構造への適用例として、LINE(Large-scale Information Network Embedding)[7]という手法を紹介します。なお、ここでグラフへの適用を紹介するのは、私の趣味もありますが、本題のpoincaré embeddingもグラフ構造(実際は木構造)に関係するからというのもあります。word2vecはグラフ構造へ何も考えずそのまま適用できるのになかなか日の目を見ないという状況を改善したいという気持ちもあります(もちろん常識的に適用している方もたくさんおられると思います)。

LINE - グラフへのSGNSの適用

LINEはグラフへSGNSを適用する手法です。具体的には、下記の対応関係にあります。

対象 LINE SGNS
埋め込まれるもの ノード 単語
P(i) ノードの次数に比例 単語の出現頻度に比例
P(i,j) グラフのエッジからの一様分布 窓から一様サンプリング

すなわち、下記のような学習になります。

  1. グラフからエッジを一様サンプリングして単語対を生成(positve samples)
  2. ノードを次数に比例する確率で2つサンプリングすることでノード対を生成(negative samples)

最適化するのもSGNSと同じ目的関数です。

$ \text{maximize}_{ \{v_i \}_i } \ \ \mathbb{E}_{(i,j) \sim P(i,j)} \Bigl[ \log \sigma (\langle v_i, v_j \rangle ) \Bigr] + k \ \mathbb{E}_{(i,j) \sim P(i)P(j)} \Bigl[ \log 1-\sigma (\langle v_i, v_j \rangle ) \Bigr]$

グラフにもそのまま適用できたのは、SGNSのシンプルさのおかげですね。

Poincaré Embeddings

ここからが本題です。SGNSはシンプルで素晴らしい。計算効率も非常に良い。これ以上言うことはないのではないかと思っていましたが、Poincaré Embeddingsはそこに待ったをかけました。

その埋め込み、最良ですか?

SGNSでは、二つの単語$i,j$の表現ベクター$v_i, v_j$をとり、その内積

$$ \langle v_i, v_j \rangle $$

でそれらの単語の共起の度合いを測りました(これが大きい方が共起しやすい)。

でもそれより良い測り方はないのでしょうか?

Poincaré Embeddings[1]では、Euclide空間よりも空間効率の良い双曲空間という歪んだ空間へデータ(ノードや単語)を埋め込み、その空間でそれらのデータ間の距離を測ることで、はるかに効率的な表現ベクターを獲得することに成功しました。

内積$\langle u,v \rangle$は $$ \langle u,v \rangle = ({\Vert u + v \Vert}^2 - {\Vert u \Vert}^2 + {\Vert v \Vert}^2)/2 $$ を満たすので、本質的にEulide空間における距離の測り方に依存しています。したがって、SGNSはEuclide空間への埋め込みモデルと考えることができます。 Deep Learningで扱われるたいていのモデルはEuclide空間で構成されていると考えて差し支えないと思います(もちろん例外はあります)。

Poincaré Embeddingsの場合、Euclide空間ではなく、双曲空間(Hyperbolic Space)で埋め込みを構成します。双曲空間ではEuclide空間と同様、各点はベクターとして表現されますが、Euclide空間とは異なり空間に歪みがあり、2点間の距離の測られ方が異なります。そのため、この歪みを利用することでEuclide空間よりも効率的で個性的な埋め込みが可能になります(要はEuclide空間はフラットすぎたんですね)。

双曲空間についてさらっておきます。

双曲空間(hyperbolic space)

双曲空間は数学的には非常に由緒正しい空間です。深掘りすると議論が尽きないので、ここでは美味しいところだけつまみ食いしましょう。 詳細については、wikipedia[8]や論文[9]を参照してください。書籍であれば、以下の本が非常にわかりやすく書かれています。

双曲幾何 (現代数学への入門)

双曲幾何 (現代数学への入門)

私は読んでいないですが、書店でちら見した感じでは、最近出た下記の本にも双曲空間が易しく解説されていました。

双曲空間にはいくつかの構成方法(モデル)がありますが、ここでは、オリジナル論文に従ってPoincaré Ball (Poincaré Disk) モデルを採用します。Poincaré Ballモデルでは、データは$d$次元の単位球$\mathbf{B}^d = \{x \in \mathbb{R}^d | \Vert x \Vert < 1 \}$ 内に表現されます。Poincaré Ballモデルの場合、この単位球$\mathbf{B}^d$の周縁部($\Vert x \Vert = 1$)に近づくに従って解像度が加速度的に高くなるようになっていて(外側に行くほど空間が爆発的に広がっているイメージです)、これが表現の効率性を生み出しています。

双曲空間については、エッシャーによる描画があります。

f:id:daynap1204:20170827165827j:plain

M.C. Escher, Circle Limit III, 1959

外側に行くほど空間が密に詰まっているのがわかると思います(逆に見れば、外側ほど空間が拡がっています)。

数学的にはこの空間の拡がり方の増加は、下記のように表されます。

$$ g_x = \Biggl(\frac{2}{1-{\Vert x \Vert }^2 } \Biggr)^2 g^E $$

この式の意味ですが、$g_x$がPoincaré Disc上での物差し(metric)、$g^E$がEuclide空間の物差しを表します。Poincaré Ball上の点$x$における空間の拡がり方は、同じ点のEuclide空間における拡がり方に比べて、$2/(1-{\Vert x \Vert}^2)$倍であることを表します。$\Vert x \Vert$が1に近づけば近づくほどこの倍数は大きくなっていくので、Poincaré Ballの周縁部へ行くほど空間が拡大されていくことになります。

では、Poincaré Ballの2点$x,y$の間の距離はどのようになるのでしょうか?数学的には、このような歪んだ空間における2点間の距離は、測地線(geodesic)という2点を結ぶ最短経路の長さで定義されます。Euclide空間における2点間の距離はその2点を結ぶ直線の長さで定義されましたが、測地線はEuclide空間における距離の概念を歪んだ空間(多様体)へ拡張したものです。

Poincaré Ballでは場所ごとに物差しの長さ異なるため、点$x$から点$y$へ至る道それぞれの途上で物差しは変化し続けることになります。そのため、それらの道のうちの最短のもの(測地線)がどんなものでその長さがいくらかを特定したり計算したりするのは難しそうな気がしますが、幸いなことにPoincaré Ballの場合、

  1. 2点$x,y$を結ぶ測地線は常にその2点を通り、単位球に直行する円弧になる
  2. 測地線の長さは明示的に計算可能で$d(x,y) = arccosh \Bigl(1 + 2 \frac{{\Vert x - y \Vert}^2}{(1-{\Vert x \Vert}^2)(1-{\Vert y \Vert}^2)} \Bigr)$

となることが知られています。ここで、$arccosh(\gamma)=\log \bigl(\gamma + \sqrt{\gamma-1}\sqrt{\gamma+1} \bigr)$は逆双曲線余弦関数と呼ばれる関数です。

f:id:daynap1204:20170827231134p:plain

[1]より引用

これだけでも嬉しいのですが、それだけでは終わりません。 双曲空間には木構造を自然な形で埋め込むことができるという特殊な性質が知られており、木構造を構成するノード間の距離(家系図で言うところの1親等、2親等というやつです)を保つように、適当な次元の双極空間へ埋め込むことができます[9]。雰囲気だけ理解することにすれば、この埋め込みは木のルートに近いノードをPoincaré Ballの中心近くに、リーフに近いノードをPoincaré Ballの周縁部の近くに配置します(上図の(b))。

この性質により、Poncaré Ballへ埋め込んだときに中心近くに配置されるデータはより抽象度が高く、周縁部に配置されるデータは具象度が高くなることが期待されます(あとで見るように、実際にそうなるところが驚愕すべき点です)。

勢いで書いてしまったので整理します。

  • 双曲空間は数学的に由緒正しい空間
  • 双曲空間はPoincaré Ballモデルを採用することで、単位球内に表現することができる

さらに、Poincaré Ballモデルは

  • 単位球の原点から周縁部に行くにつれ、空間が加速度的に拡がっていき、空間効率よくデータを埋め込むことができる
  • 2点間の距離は明示的に計算できる
  • 階層構造が末端ほど周縁部に配置されるように自然に埋め込まれることが知られている。

という素晴らしい性質を持ちます。

Poincaré Embeddingsの構成

Poincaré EmbeddingsはPoincaré Ballへデータを埋め込む手法です。skipgramではデータ対$(i,j)$の間の類似度を内積$\langle v_i, v_j \rangle$で表していましたが、Poincaré Embeddingsのオリジナル論文では、これを2点間の距離の符号を反転したもの$-d(v_i,v_j)$で表します。内積で表していたものを距離で表すことにしたので、厳密にはskipgramとPoincaré Embeddingは対応が崩れていますが、ここでは目を瞑っておきます(あとで触れるように、双曲空間で内積相当のものを定義して利用している論文も出ています[10])。

Poincaré Embeddingsをskipgramと同様に学習する場合、

  • 共起するデータ対(positive samples)に対しては距離を小さく
  • 乱択されたデータ対(negative samples)に対しては距離を大きく

という具合に最適化を行います。ただし、距離関数が非負値な関係上、距離$d(v_i,v_j)$をそのまま使わずに、負値もとれるように $$ \frac{d(v_i, v_j) - r}{t}$$ というような補正を入れます($r,t$はハイパーパラメータです)。ちょっと歯がゆい感じですが、致し方なし。より良い類似度/非類似度関数の定義はfuture worksを待ちましょう。

最適化はRiemannian SGDという歪んだ空間で実行可能なSGD(確率的勾配降下法)により行いますが、こちらの説明は下記のスライド1枚にかえさせていただきます。要点は簡単で、勾配降下する際に、物差しの長さ分の補正をするだけです。

f:id:daynap1204:20170828013331p:plain

オリジナル論文[1]における実験結果

お待ちかねの実験結果です。2種類の実験をしています。

  1. WordNet の名詞の階層構造の埋め込み
  2. グラフのリンク予測

それぞれ見ていきましょう。細かい詳細は割愛していますので、必要であればオリジナル論文[1]を参照してください。

WordNet の名詞の階層構造の埋め込み

こちらは最初に挙げた私の実装でも再現できるようにしてあります。実行方法の詳細はREADMEをご覧ください。

WordNet[10]は英語の概念辞書です。オリジナル論文では、ここから名詞のみを抽出し、その概念上の上位・下位関係を列挙したものをデータセットとしています(例えば犬は哺乳類なので、上位概念が哺乳類、下位概念が犬という具合です)。具体的には

下位語 上位語
apple_jelly.n.01 confiture.n.01
telephone_system.n.01 physical_entity.n.01
organizer.n.02 causal_agent.n.01
utmost.n.01 extent.n.02
tael.n.01 abstraction.n.06
financial_institution.n.01 organization.n.01
uruguayan.n.01 whole.n.02
holocentrus_ascensionis.n.01 vertebrate.n.01
bell_metal.n.01 substance.n.01
takeoff.n.02 happening.n.01

という感じのデータになると想像されます(論文に書いてなかったので私は実験結果の数値をもとに、想像しながら合わせ込みました…)。 このデータは名詞対の並びになっていて、左側に下位の語が、右側に上位の語が来るようにしてあります。".n"はその語が名詞であることを表し、".01"や".02"といった数字は、 同じ綴りでも意味が異なる語を区別するために付けられています。

この1行を1 positive sampleとし、negative samplesはこのデータ中の単語の出現頻度に比例した確率で2単語乱択することにより生成します。

上記データに対して、下記のような関数の最大化を行います。

$$ \mathcal{L}(\Theta) = \sum_{(i, j)\in \mathcal{D}} \log \frac{ \exp(-d(v_i, v_j))}{\sum_{j' \in \mathcal{N}(i)} \exp(-d(v_i, v_{j'}))}$$

ここで、$\Theta$はこのモデルのパラメータ全体を表し、$\mathcal{N}(i)$は、データ$i$に対して生成された$j$のnegative samplesで、基本的には出現頻度に比例した乱択で生成されますが、$(i, j')$がデータセット$\mathcal{D}$に存在している場合はそれを除外して再生成します。 上記関数の最大化は、positive sampleとnegative samplesが混ぜこぜに与えられたときに、どれがpositive sampleかを当てる確率を最大化することに相当します。

結果は最初にお見せしたとおり、Euclide空間での埋め込みとは段違いの精度を叩き出しています。

f:id:daynap1204:20170828015724p:plain

精度向上もすごいですが、個人的にとても興味深く感じるのは、その埋め込まれ方です。下記もオリジナル論文からの引用ですが、上位語ほど中心近くに配置されていることがわかると思います。正しく階層構造が抽出されていますね(もっともデータセットを作るところで作為的に(下位語、上位語)とならべているので、ちょっとズルいですが…)

f:id:daynap1204:20170828015924p:plain

自前のコードでも同様の埋め込みを再現できたので載せておきます(私のコードを実行していただければ再現可能です)。見やすさのため、データの距離関係を保つ変換(等長変換)を施して、哺乳類(mammal)を中心に配置しています。

f:id:daynap1204:20170828020209p:plain

グラフのリンク予測

これは、与えられた2ノードの間にエッジが存在するかを予測するタスクです。このタスクの場合、下記の確率モデルのクロスエントロピー最大化を行います。

$$ P(e(i,j)=1; \Theta) = \frac{1}{1 + \exp((d(i,j)-r)/t)} $$

ここで、$i,j$はグラフのノードで$e(i,j)$は、それらの間にエッジが存在しているとき$e(i,j)=1$、エッジが存在していないとき$e(i,j)=0$となる関数です。$\Theta$は同様にモデルのパラメータで、$d(i,j)-r)/t$は先に説明した負値をとれるように補正済みの距離関数です。 この確率モデルを最大化することにより、互いにエッジが貼られているノードほど近くに配置されるようになります。

この場合でもやはり、Euclide空間での埋め込みを上回る精度を達成しています。

f:id:daynap1204:20170828021136p:plain

余談: Poincaré Ballにおける"内積"

内積ではなくあえて"内積”としたのは、Poincaré Ballにおける理想的な内積の定義はとても難しいと感じているからです。

Poincaré Embeddingsの論文[1]が出て間もなく、Poincaré Ballにおいて"内積"を定義し、Poincaré Embeddingsで$-d(v_i, v_i)$と表されていた類似度関数をその"内積"で置き換えた論文[11]が登場しました(この頃、何か双曲空間での埋め込みを助長するようなトリガーがあったのでしょうか?)。先日開催されたKDD2017のworkshopで発表されたようです。

www.mlgworkshop.org

この論文ではPoincaré Ballにおける"内積"を以下のように定義しています。 2点$u,v$のPoincaré Ballにおける"内積"をEuclide空間の内積$\langle u, v \rangle$と区別するために$\langle \langle u, v \rangle \rangle$と表したとき

$$ \langle\langle u,v \rangle \rangle = d(o,u) \ d(o,v) \ cos \theta .$$

がその"内積"です。 ここで、$o$はPoincaré Ballの原点であり、$\theta$は2点u,vが原点oを中心に為す角の角度です。Poincaré Ballにおいて、原点ともう一点の間の測地線はその2点間を結ぶ直線になることが知られているので、$\theta$は$o$と$u$とを結ぶ測地線と$o$と$v$とを結ぶ測地線とがなす角と思うことができます。この観点から、この"内積"は、Euclide空間における内積の概念をPoincaré Ballへ拡張したものとみなすことができます。

実は私も$-d(v_i, v_j)$の代わりになる関数を探していて、同じような"内積"に思い至ったのですが、最近、以下の対応関係から、Poincaré Ballにおける"内積"の定義はPoincaré Ballの空間としての豊かさを有効活用できていないことに気づきました。

$$ \langle \langle u,v \rangle \rangle = <u', v'>, \ \ u' = \frac{d(o,u)}{\Vert u \Vert} u, \ \ v' = \frac{d(o,v)}{\Vert v \Vert} v $$

対応$u \rightarrow u',\ v \rightarrow v'$により、Poincaré BallとEuclide空間との間に1対1の対応関係ができてしまい、しかもこの対応関係は"内積"の値を保ちます。なので、せっかくPoincaré Ballで考えていたのに、Euclide空間で考えるのと同じ表現能力しか獲得できていません。

双曲空間に適切な類似度/非類似度を定義するのは今後の課題になりそうです。

おわりに

いかがだったでしょうか。Poincaré Embeddingsの可能性を少しでもお伝えできていれば幸いです。データをよりリッチな空間で表現するという方向性は従来からありましたが(kernel法もimplicitにそういうことをしていますね)、Poincaré Ballでの表現は実用上の最適解の一つかもしれません。

以前Graph Convlutionをご紹介したことがありますが[12]、Graph Convolutionがうまくいくのはグラフに局所性や再帰性、フラクタル性、階層構造があるからだと思います。これらの性質を考えるにつけ、Poincaré Embeddingsとの親和性を感じずにはいられません。双曲空間でDeep Learningが展開される将来に胸をときめかせつつ、本稿を終わりにしたいと思います。

宣伝

ABEJAでは先端技術に目ざといイケてるResearcherを募集しています!その他職種も大絶賛募集中です!!

ABEJAが発信する最新テクノロジーに興味がある方は、是非ともブログの読者に!

ABEJAという会社に興味が湧いてきた方はWantedlyで会社、事業、人の情報を発信しているので、是非ともフォローを!! www.wantedly.com

ABEJAの中の人と話ししたい!オフィス見学してみたいも随時受け付けておりますので、気軽にポチッとどうぞ↓↓

参考文献

[1] M. Nickel and D. Kiela, “Poincaré Embeddings for Learning Hierarchical Representations”, arXiv:1705.08039

[2] https://code.google.com/archive/p/word2vec/

[3] T. Mikolov et al., “Linguistic Regularities in Continuous Space Word Representations”, NAACL 2013

[4] T. Mikolov et al., “Efficient Estimation of Word Representations in Vector Space”, ICLR 2013

[5] Y. Bengio et al., “A Neural Probabilistic Language Model”, NIPS'00

[6] T. Mikolov et al., “Distributed Representations of Words and Phrases and Their Compositionality”, NIPS 2013

[7] J. Tang et al., “LINE: Large-scale Information Network Embedding”, WWW2015

[8] https://en.wikipedia.org/wiki/Hyperbolic_space

[9] Krioukov, Dmitri et al., “Hyperbolic geometry of complex networks”, Physical Review 2010

[10] https://wordnet.princeton.edu/

[11] B. P. Chamberlain et al., “Neural Embeddings of Graphs in Hyperbolic Space”, arXiv:1705.10359

[12] http://tech-blog.abeja.asia/entry/2017/04/27/105613

USB型 Deep Learning アクセラレーター「Movidius Neural Compute Stick」を使ってみた

7月20日、Intel (Movidius) がUSB接続タイプのスティック型ディープニューラルネットワーク処理用アクセラレータ「Movidius Neural Compute Stick」を発表しました。

NCSは、Deep Learningに特化した専用チップ「Myriad 2」が搭載された、外付けの演算装置です。USBポートに挿すだけでDeep Learningの推論処理を実行させることができるため、ラズパイやノートPCのようなデバイスでも比較的高速にDeep Learningアプリケーションを実行することができるようになります。

CVPR2017で先行発売されたMovidius Neural Compute Stick(以下、NCS)を入手しましたので使ってみました。CVPRでも数百個しか販売されてないとの事なので貴重です。 f:id:toshitanian:20170801215853j:plain

検証環境

  • Ubuntu 16.04
  • Linux kernel: 4.4.0-87-generic
  • Intel Core i7 3632QM

Getting started

NCSのGetting Startedに従って動かしてみます。Getting Startedのページはよくわからないので、詳細なAPIドキュメントとGetting StartedのPDFをここから入手します。

環境構築

最新版(2017/07/26時点: 1.07.07)のMovidius Neural Compute (MvNC) SDKを以下のコマンドに従ってダウンロード・展開します。参考

$ sudo apt-get update 
$ sudo apt-get upgrade

$ mkdir ~/ncsdk
$ cd ~/ncsdk
$ wget https://ncs-forum-uploads.s3.amazonaws.com/ncsdk/MvNC_SDK_01_07_07/MvNC_SDK_1.07.07.tgz
$ tar -xvf MvNC_SDK_1.07.07.tgz
x MvNC_Toolkit-1.07.06.tgz
x MvNC_API-1.07.07.tgz

Movidius Neural Compute Toolkitをインストールします。

Movidius Neural Compute ToolkitはDeep Learningのチューニング・検証・プロファイリングをするためのツールを提供しています。コンパイルは現在時点ではcaffeのmodelをNCSでロードして実行できる形式にする機能を持っています。 インストールに少し時間がかかる(15分くらい)ので気長に待ちます。

$  tar -xvf MvNC_Toolkit-1.07.06.tgz
$ cd bin
$ ./setup.sh

サンプルで使うcaffeモデルをダウンロードします。少し時間がかかるので気長に待ちます。

$ cd data
$ ./dlnets.sh

Movidius NC APIをインストールします。

Movidius NC APIはNCS上での推論処理をプログラムから実行するためのAPIを提供します。少し時間が(ry

$ ~/ncsdk
$ tar -xvf MvNC_API-1.07.07.tgz
$ cd ncapi
$ ./setup.sh

ここまででMovidius Neural Compute ToolkitとMovidius NC APIのインストールが完了しました。

サンプルを動かしてみる

ここからは実際にNCSを使ってサンプルを動かして行きます。ホストマシンのUSBポートにNCSを挿してください。lsusbを打つと新しいデバイスが認識されている事がわかります。

Python APIを使ったサンプル

PythonからNCSをどう使うかをサンプルを使って見てみたいと思います。classification_example.pyというpythonのサンプルではAlexnetを使って指定された画像の推論処理をしています。実行すると以下のようなログを出しました。

$ cd ~/ncsdk/ncapi/py_examples
$ python3 classification_example.py 2
Device 0 Address: 3 - VID/PID 03e7:2150
Starting wait for connect with 2000ms timeout
Found Address: 3 - VID/PID 03e7:2150
Found EP 0x81 : max packet size is 512 bytes
Found EP 0x01 : max packet size is 512 bytes
Found and opened device
Performing bulk write of 825136 bytes...
Successfully sent 825136 bytes of data in 54.576918 ms (14.418385 MB/s)
Boot successful, device address 3
Found Address: 3 - VID/PID 040e:f63b
done
Booted 3 -> VSC

------- predictions --------
prediction 1 is n02123159 tiger cat
prediction 2 is n02123045 tabby, tabby cat
prediction 3 is n02119022 red fox, Vulpes vulpes
prediction 4 is n02085620 Chihuahua
prediction 5 is n02326432 hare

以下の流れで、pythonからNCS使う事ができます。

  • NCSのデバイスをプログラム上で取得する
  • NNのモデルをデバイスに転送する
  • 推論したい画像をデバイスに転送する
  • 推論結果をデバイスから取得する

classification_example.pyの中身は以下のようになっています。

from mvnc import mvncapi as mvnc
import numpy
import cv2
import time
import csv
import os
import sys

if len(sys.argv) != 2:
    print ("Usage: enter 1 for Googlenet, 2 for Alexnet, 3 for Squeezenet")
    sys.exit()
if sys.argv[1]=='1':
    network="googlenet"
elif sys.argv[1]=='2':
    network='alexnet'
elif sys.argv[1]=='3':
    network='squeezenet'
else:
    print ("Usage: enter 1 for Googlenet, 2 for Alexnet, 3 for Squeezenet")
    sys.exit()

# get labels
labels_file='../tools/synset_words.txt'
labels=numpy.loadtxt(labels_file,str,delimiter='\t')
# configuration NCS
mvnc.SetGlobalOption(mvnc.GlobalOption.LOGLEVEL, 2)
devices = mvnc.EnumerateDevices()
if len(devices) == 0:
    print('No devices found')
    quit()
device = mvnc.Device(devices[0])
device.OpenDevice()
opt = device.GetDeviceOption(mvnc.DeviceOption.OPTIMISATIONLIST)

if network == "squeezenet":
    network_blob='../networks/SqueezeNet/graph'
    dim=(227,227)
elif network=="googlenet":
    network_blob='../networks/GoogLeNet/graph'
    dim=(224,224)
elif network=='alexnet':
    network_blob='../networks/AlexNet/graph'
    dim=(227,227)
#Load blob
with open(network_blob, mode='rb') as f:
    blob = f.read()
graph = device.AllocateGraph(blob)
graph.SetGraphOption(mvnc.GraphOption.ITERATIONS, 1)
iterations = graph.GetGraphOption(mvnc.GraphOption.ITERATIONS)

ilsvrc_mean = numpy.load('../mean/ilsvrc12/ilsvrc_2012_mean.npy').mean(1).mean(1) #loading the mean file
img = cv2.imread('../images/cat.jpg')
img=cv2.resize(img,dim)
img = img.astype(numpy.float32)
img[:,:,0] = (img[:,:,0] - ilsvrc_mean[0])
img[:,:,1] = (img[:,:,1] - ilsvrc_mean[1])
img[:,:,2] = (img[:,:,2] - ilsvrc_mean[2])
graph.LoadTensor(img.astype(numpy.float16), 'user object')
output, userobj = graph.GetResult()
order = output.argsort()[::-1][:6]
print('\n------- predictions --------')
for i in range(1,6):
    print ('prediction ' + str(i) + ' is ' + labels[order[i]])
graph.DeallocateGraph()
device.CloseDevice()

ちなみに

NCSをNCSで推論させるとハーモニカになります。

f:id:toshitanian:20170810100019j:plain

$ ./ncs-fullcheck -c1 ../networks/AlexNet ~/Desktop/b03.jpg 
OpenDevice 1 succeeded
Graph allocated
harmonica, mouth organ, harp, mouth harp (12.98%) rubber eraser, rubber, pencil eraser (12.19%) lighter, light, igniter, ignitor (9.95%) whistle (8.45%) sunscreen, sunblock, sun blocker (5.20%) 
Inference time: 283.648071 ms, total time 288.491546 ms
Deallocate graph, rc=0
Device closed, rc=0

ラズベリーパイでNCSを使う

Raspberrypi3でMovidius Neural Compute Stickのサンプルまでを動かしてみた - Qiita

RaspberryPI3でMovidius NCSのサンプルを少し真面目に動かしてみた - Qiita

こちらにABEJAのリサーチャーがNCSをラズベリーパイで動かしてみたポストがあるのでご参照ください。

まとめ

今回はあまり綿密な検証はしませんでしたが、NCSを使うことでGPUを搭載していないマシンでも比較的簡単にDeep Learningを使えることがわかりました。処理性能の高くないデバイスでもNCSを挿すだけでエッジサイドDeep Learningを実行できるというのは夢がありますね。

We are hiring!

ABEJAが発信する最新テクノロジーに興味がある方は、是非ともブログの読者に!

ABEJAという会社に興味が湧いてきた方はWantedlyで会社、事業、人の情報を発信しているので、是非ともフォローを!! www.wantedly.com

ABEJAの中の人と話ししたい!オフィス見学してみたいも随時受け付けておりますので、気軽にポチッとどうぞ↓↓

Vue.jsのバックエンドとしてのFirebase

このポストは英語版を日本語に直したものです。

Vue.jsのバックエンドとしてのFirebase

こんにちは、株式会社ABEJAのカンとです。AIの会社として知られているABEJAですが、フロントエンドをメインとするチームもあります。私が所属しているSaaS Development チームです。私達は(最近のフロントエンドチームは皆そうですが…)最新技術が大好きで、以下のような技術を使っています。

f:id:abeja:20170726184558p:plain

最近、社内ツールを作っています。元々使っていた「AWS Lambda+Vue(S3)」の代わりに「Firebase + Cloud functions for firebase」を利用していますが、とても便利で楽しいです。ぜひ皆さんにも使って頂きたく、このブログを書くことになりました。

以下のような流れで書きたいと思います。

  • なぜFirebaseなのか
  • Firebase as the database
    • Real-time
    • Authentication
    • Deploy and hosting
    • Vue.js and vuefire
  • Firebase with Cloud Functions
    • So good.
    • Not so good.
  • 学んだこと
    • Databaseの構造
    • 集計
  • さいごに

より理解を高めるためにABEJA totalizerというサンプルサービスを用意しました。このサービスは簡単に投票用課題を作り、リアルタイムで集計するサービスです。このサービスはVuejs, firebase, cloud function for firebaseを使って作りました。

なぜFirebaseなのか

理由は簡単です。我々フロントエンドチームはサーバーを運用したくないからです。小規模から中規模までカバーできるBaaSをどれにするか検討した結果、FirebaseとGraphcoolに辿り着きました。最近、GCPの勢いがいいこともありFirebaseに決めました。

Firebase as the database

Real-time

FirebaseはWeb用のSDKを入れるだけでRealTime Web systemが実現できます。WebSocketを入れる必要もEventをListenする必要もありません。DBの値が変わったら自動にブラウザ上の値が変更されます。さらに、DBを直接LoadしているのでAPIを書く必要もそれ用のサーバーを持つ必要もありません。

Authentication

認証。どのシステムにも必須で大事な仕組みであり、当然開発するのに時間もかかります。Firebaseは Emailはもちろん、Facebook、GoogleなどSocial Authenticationをサービスとして提供していて簡単に追加できます。これにはPassword変更やEmail Verificationなど開発・管理が面倒な機能も全て含まれています。

Deploy and hosting

ホスティング。新しいサービスができたらどうやってホスティングしますか?SSLは?Firebaseはfirebase deployコマンド一発でデプロイでき、すぐにあなたのサービスを提供することができます。

// abeja-totalizer/package.json
"scripts": {
  "prepackage-functions": "rimraf functions",
  "package-functions": "babel 'functionsES6' --out-dir 'functions' --presets=es2015 --copy-files --ignore 'node_modules'",
  "postpackage-functions": "cd functions && yarn",
  "deploy": "yarn run package-functions && firebase deploy"
}

実際私達が使ってるDeployコマンドです。イケてるフロントエンド開発者ならprepackagepostpackageでいったい何をやってるんだと思うかも知れません。簡単に説明しますと、Google Cloud FunctionsがES6に対応していないため、トランスパイルしています。Linkこのリンクを参考にしました。

Vue.js and vuefire

このイケてるDBとVue.jsをどうやって繋げるのか。Vue.jsの詳細については最近の人気からたくさん情報がありますのでふれないようにしますが、Vue.jsを使えば簡単かつ恐ろしいスピートでコンポネント化されたWebサービスを作ることができます! FirebaseとVue.jsを繋げるところに話を戻すと、使用するのはvuefireです。そして私達はそれをより簡単にするため、以下のようなPluginを書きました。

// abeja-totalizer/src/libs/FirebaseDBPlugin.js
import firebase from '../helper/firebaseDB'

const db = firebase.firebaseApp.database()
const DATABASE_NAME = process.env.DB_NAME
const fbRef = ref => {
  return db.ref(`${DATABASE_NAME}/${ref}`)
}
const questions = db.ref(`${DATABASE_NAME}/questions`)
const answers = db.ref(`${DATABASE_NAME}/answers`)
const FB_MAPPING = {
  'questions': function () {
    this.$bindAsArray('questions', questions)
  },
  'question': function (param) {
    if (!param.questionKey) return
    this.$bindAsObject('question', questions.child(param.questionKey))
  },
  'answers': function (param) {
    if (!param.questionKey) return
    this.$bindAsArray('answers', answers.child(param.questionKey))
  },
  'myAnswer': function (param) {
    if (!param.questionKey) return
    if (!param.myId) return
    this.$bindAsObject('myAnswer', answers.child(param.questionKey).child(param.myId))
  }
}

const fbBinding = function () {
  let fbBind = this.$options['fbBind']
  if (typeof fbBind === 'function') fbBind = fbBind.call(this)
  if (!fbBind) return
  Object.keys(fbBind).forEach(k => {
    const b = FB_MAPPING[k]
    b.call(this, fbBind[k])
  })
}
const init = function () {
  fbBinding.call(this)
}

const install = (Vue) => {
  Vue.prototype.$fbRef = fbRef
  Vue.prototype.$fbBinding = fbBinding
  Vue.mixin({
    created: init // 1.x and 2.x
  })
}

export default {
  install
}
    // abeja-totalizer/src/main.js
    import FirebaseDBPlugin from './libs/FirebaseDBPlugin'
    Vue.use(FirebaseDBPlugin)

このようにして使うことができます。

// abeja-totalizer/src/components/Main.vue
export default {
  fbBind: function () {
    return {
      'questions': {}
    }
  }
}

Firebase with Cloud Functions

So good

Cloud Functions for Firebase. これはFirebaseで開発を始めて以降一番興奮した機能です。FirebaseはRealtime DBを持つBaaSとしてだけでなく、様々なFunctionを持っています。このFunctionを使ってHTTP Request(普通のAPI)の処理はもちろん、DB、Storage、ユーザー登録変更イベントをキャッチし、ロジックを入れる(Event Sourcing)ことができます!Serverless Architectureに感謝です!functionを書いてDeployすれば、もうメンテナンスもスケーリングも気にする必要がありません。no worry be happy 😬

Not so good

完璧なシステムはないです。今のところHTTP triggered function以外のCloud Functions for FirebaseをLocal環境でDebugする術はありません。Firebase CLIを使えばHTTP triggered functionのみLocal環境でDebugすることができます。そしてGoogleは「cloud Functions Local Emulator」というツールをCloud Functions用に提供していますが、Cloud Functions for firebaseでは使うことができません!(なんと!)Cloud FunctionsとCloud Functions for firebaseはほぼ同じですが少し違います。では他にDeployしないでDebugする術はないでしょうか。答えはUnitTestを書くことです。このドキュメントを参考してください。

学んだこと

デバッグ以外にもFirebaseを使いながら学んだことがいくつかあります。

データの構造はFlatに!

可能な限りデータはフラットに持ちましょう。データをNestedして持つと2つ大きな問題に直面します。1つ目はレコードを1つ取得するQueryを書くことが難しいこと、2つ目はSingle RequestのPayloadが大きくなることです。それに対する対策は以下の例みたいにデータをフラットにすることです。

// customers has many shops, each shop has many devices, devices owned by customer
customers
  -KnrCVAqhTQ33fGkz50s
    shops
      -KnrCVBHeSztSd86_CVI: true
      ...
shops
  -KnrCVBHeSztSd86_CVI
    customerKey: -KnrCVAqhTQ33fGkz50s
    devices
      -KnXFP1dGQoLsu4dzran: true
      ...
devices
  -KnXFP1dGQoLsu4dzran
    customerKey: -KnrCVAqhTQ33fGkz50s
    shopKey: -KnrCVBHeSztSd86_CVI
    name: '

集計

Firebaseでは従来のRDBが提供するような集計機能を使うことができません。でもRealtimeDatabaseならではの方法でこれを実現することができます。それはCloud Functions for Firebaseを利用することです。集計ロジックをFunctionとして書いて、DBの値が変更された時集計することです。以下のサンプルを参考にしてください。

import 'babel-polyfill'
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
admin.initializeApp(functions.config().firebase)

const setFirebaseRef = async (ref, index, plusOrMinus) => {
  let countRef = ref.child(`${index}/count`)
  let countValue = (await countRef.once('value')).val()
  countRef.set(countValue + plusOrMinus)
}

export const countSelect = functions.database.ref('/totalizer/answers/{questionKey}/{userId}')
  .onWrite(async event => {
    console.log('on write')
    let selectionRef = admin.database().ref(`/totalizer/questions/${event.params.questionKey}/selections`)
    if (event.data.previous.exists()) {
      console.log('update')
      let prevIndex = event.data.previous.val()
      await setFirebaseRef(selectionRef, prevIndex, -1)
      let newIndex = event.data.val()
      await setFirebaseRef(selectionRef, newIndex, 1)
    } else {
      console.log('create')
      let index = event.data.val()
      await setFirebaseRef(selectionRef, index, 1)
    }
  })

※このFunctionにはBugがあります。答えの変更があった時元の答えを-1にし変更された答えを+1にしていますが、同時にたくさんの処理が走る場合正しく動作しません。Eventがあるタイミングで計算をし直す必要があると思います。

さいごに

数週間Firebaseを使って、FirebaseとVue.jsは一緒に使うことが非常に便利であることが分かりました。これからのProjectにもこの組み合わせでいくと思います。このPostに使ったコードを参考に皆さんもぜひ試してみてください😃

サンプルコードはこちらから利用できます。今度会う時まで、Happy coding!

ABEJA SaaS Dev

We are hiring!

ABEJAが発信する最新テクノロジーに興味がある方は、是非ともブログの読者に!

ABEJAという会社に興味が湧いてきた方はWantedlyで会社、事業、人の情報を発信しているので、是非ともフォローを!! www.wantedly.com

ABEJAの中の人と話ししたい!オフィス見学してみたいも随時受け付けておりますので、気軽にポチッとどうぞ↓↓

Firebase on Vue

On the top of ABEJA’s smart AI brain, Abeja also comes with a ‘pretty face’, and we, the SaaS development division team, is the one who’s responsible for it. We love embracing new and exciting technologies, and our technology stack proves it:

f:id:abeja:20170726184558p:plain

In the last couple of weeks, we’ve been working on a new internal project, this time around, instead of AWS Lambda + Vue.js combo, something we’ve been using for a long time, we switched to Firebase and Cloud Functions for Firebase as our backend solution. We had so much fun working with it, and we’d like to spread the love, share with you our experiences in this post, here’s what we gonna go about it.

  • Why Firebase
  • Firebase as the database
    • Real-time
    • Authentication
    • Deploy and hosting
    • Vue.js and vuefire
  • Firebase with Cloud Functions
    • The good
    • The not so good
  • Lesson learned
    • Best practice of structuring your data
    • Aggregations
  • Conclusion

You could probably find tons of articles online talking about the good or bad of either Firebase or Vue.js, as our fellow programmers always say, talk is cheap, show me the code, so, we’d like to take a different approach, not only we’ll be sharing the code, but also, to present you an actual working application that you can play around with. Now, we present you, the Abeja totalizer, a real-time voting application built with Firebase, Cloud Function for Firebase, and Vue.js. With this simple web application, you will be able to create a questionnaire, share it with your friends, and track the results in real-time, it’s a simple web application, and it perfectly demonstrate some of the awesome features this technology stack offers, which we will be getting into next.

Why Firebase

Well, the reason is simple, we’re a front-end focus team, and we don’t want to maintain a dedicated backend stack, we were looking for a BaaS solution for our small to medium size projects, looking at the most advanced BaaS solution out there, it comes down to Firebase and Graphcool, and since we were thinking about more Google Cloud Platform and less Amazon AWS, then Firebase became the most obvious choice.

Firebase as the database

Real-time

Real-time. As a front-end focus team, building a real-time application sounds a bit intimidating, it’s not easy to pull it off without a sophisticated backend solution. Firebase comes with real-time by default, you will not even notice the difference while developing your app, it just works.

Authentication

Authentication. again, this is one of those things that every non-trivial app will need, but will take some time to do it right. Firebase provides a solid solution that you can easily integrate with your front-end stack, email and password based authentication, federated identity provider (Google, Facebook, Twitter, Github) integration, phone number authentication, or even anonymous authentication, you name it, it has it.

Deploy and hosting

Hosting, your shining new web app is ready to roll, where to host it? CDN? SSL? They’re as important as the features your app offer. And they’re all taken good care by Firebase’s production-grade hosting solution. As for deployment, here’s our deployment script, it’s pretty straight forward, after JavaScript gets transpiled, firebase deploy that’s all you need to do, then your app is ready to be shared with millions of users :)

// abeja-totalizer/package.json
"scripts": {
  "prepackage-functions": "rimraf functions",
  "package-functions": "babel 'functionsES6' --out-dir 'functions' --presets=es2015 --copy-files --ignore 'node_modules'",
  "postpackage-functions": "cd functions && yarn",
  "deploy": "yarn run package-functions && firebase deploy"
}

If you’re not a front-end savvy developer, you may wondering what the hell with prepackage, package and postpackage, well, long story short, we’re basically using the babel to transpile our ES6 JavaScript code down to ES5 cause currently Google Cloud Functions supports only Node.js version 6.11, we know the versioning is a bit confusing, for more information on this, as well as how to write Cloud Functions with ES6, here’s a good article to get started. Link

Vue.js and vuefire

So now we have a solid database, how about connecting it to our front-end stack? Which is Vue.js in our case, we don’t plan to get too much into Vue.js as its popularity speaks for itself. All we’ll say is that Vue.js allow us to build componentized web applications blazing fast! Back to connect Firebase to Vue.js, vuefire is the library we rely upon to make it happen, and on the top of vuefire, we also wrote a plugin, which makes getting data from Firebase to your Vue component even easier.

Here’s our plugin implementation:

// abeja-totalizer/src/libs/FirebaseDBPlugin.js
import firebase from '../helper/firebaseDB'

const db = firebase.firebaseApp.database()
const DATABASE_NAME = process.env.DB_NAME
const fbRef = ref => {
  return db.ref(`${DATABASE_NAME}/${ref}`)
}
const questions = db.ref(`${DATABASE_NAME}/questions`)
const answers = db.ref(`${DATABASE_NAME}/answers`)
const FB_MAPPING = {
  'questions': function () {
    this.$bindAsArray('questions', questions)
  },
  'question': function (param) {
    if (!param.questionKey) return
    this.$bindAsObject('question', questions.child(param.questionKey))
  },
  'answers': function (param) {
    if (!param.questionKey) return
    this.$bindAsArray('answers', answers.child(param.questionKey))
   },
   'myAnswer': function (param) {
    if (!param.questionKey) return
    if (!param.myId) return
    this.$bindAsObject('myAnswer', answers.child(param.questionKey).child(param.myId))
  }
}
    
const fbBinding = function () {
  let fbBind = this.$options['fbBind']
  if (typeof fbBind === 'function') fbBind = fbBind.call(this)
  if (!fbBind) return
  Object.keys(fbBind).forEach(k => {
    const b = FB_MAPPING[k]
    b.call(this, fbBind[k])
  })
}
const init = function () {
  fbBinding.call(this)
}
    
const install = (Vue) => {
  Vue.prototype.$fbRef = fbRef
  Vue.prototype.$fbBinding = fbBinding
  Vue.mixin({
    created: init // 1.x and 2.x
  })
}
    
export default {
  install
}

You can use this as any other Vue.js plugins:

// abeja-totalizer/src/main.js
import FirebaseDBPlugin from './libs/FirebaseDBPlugin'
Vue.use(FirebaseDBPlugin)

Then in your Vue.js component, you can simply drop whichever firebase data object you’d like:

// abeja-totalizer/src/components/Main.vue
export default {
  fbBind: function () {
   return {
      'questions': {}
    }
  }
}

Firebase with Cloud Functions

Cloud Functions for Firebase, the good

Cloud Functions for Firebase. This is THE most exciting feature Firebase added not long ago. Firebase was never considered a one-stop BaaS solution cause having a real-time database is not enough. Everything changed with the arrival of Cloud Functions for Firebase, now you’ll be able to write your business logic, which could be triggered by either HTTP request or real-time Firebase change event (and much more), how cool is that! And thanks for the serverless architecture, you write your function, deploy it, then you don’t ever need to worry about it ever again, no maintenance, no scaling concerns, no worry be happy :D

Cloud Functions for Firebase, the not so good

Well, nothing is perfect, at the time of writing this post, Cloud Functions for Firebase has one downside as far as we can tell, you can’t really debug your non-HTTP triggered function in local development environment, Firebase command line tool has this experimental feature which allows you to debug HTTP triggered functions, and also Google does provide a tool called Cloud Functions Local Emulator for debugging, however, it only works with Cloud Functions, not Cloud Functions for Firebase, Google Cloud Functions and Cloud Functions for Firebase are kind of the same thing but with some slight differences. So, is there any alternatives besides deploy, run, stare at the logs? (by the way, Google Cloud Platform logging feature is very nice). The answer is unit tests, take a look at this (https://firebase.google.com/docs/functions/unit-testing) article for more information.

Lesson learned

Besides the tips on debugging, there are some other lesson-learned best practices when working with Firebase we’d like to share.

Best practice of structuring your data

Keep your database structure as flat as possible. It’s very attempting to nest one property within another one, nested properties have two major problems, almost impossible to query for one, and another one is too large of payload for a single request. And the solution would be flattening your data as always, here’s an example of how we structured the data for one of our internal projects, everything is flattened, and we use firebase generated key to form the relationship between data objects.

// customers has many shops, each shop has many devices, devices owned by customer
customers
  -KnrCVAqhTQ33fGkz50s
    shops
      -KnrCVBHeSztSd86_CVI: true
      ...
shops
  -KnrCVBHeSztSd86_CVI
    customerKey: -KnrCVAqhTQ33fGkz50s
    devices
      -KnXFP1dGQoLsu4dzran: true
      ...
devices
  -KnXFP1dGQoLsu4dzran
    customerKey: -KnrCVAqhTQ33fGkz50s
    shopKey: -KnrCVBHeSztSd86_CVI
    name: '

Aggregations

We do miss the powerful aggregation functions provided by the traditional relational databases. Turns out you’re not exactly out of luck, one potential solution could be taking advantage of Cloud Functions for Firebase, create your own aggregation functions, which get triggered upon Firebase data changes, yet another use case for Cloud Function for Firebase 😃. Here’s a naive example of doing this just for reference:

import 'babel-polyfill'
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
admin.initializeApp(functions.config().firebase)
  
const setFirebaseRef = async (ref, index, plusOrMinus) => {
  let countRef = ref.child(`${index}/count`)
  let countValue = (await countRef.once('value')).val()
  countRef.set(countValue + plusOrMinus)
}
    
export const countSelect = functions.database.ref('/totalizer/answers/{questionKey}/{userId}')
  .onWrite(async event => {
    console.log('on write')
    let selectionRef = admin.database().ref(`/totalizer/questions/${event.params.questionKey}/selections`)
    if (event.data.previous.exists()) {
      console.log('update')
      let prevIndex = event.data.previous.val()
      await setFirebaseRef(selectionRef, prevIndex, -1)
      let newIndex = event.data.val()
      await setFirebaseRef(selectionRef, newIndex, 1)
    } else {
      console.log('create')
      let index = event.data.val()
      await setFirebaseRef(selectionRef, index, 1)
    }
  })

The conclusion

After working with it for a couple of weeks, we all agree that Firebase on Vue will be our de-facto technology stack for the front-end team to build future projects. And we highly recommend you to do so if you want to build real-time, robust web applications in less time. Hopefully this blog post, the sample application as well as our code examples have done their job to win you over. 😃

That’s it, you can find rest of the sample codes in this repository https://github.com/abeja-inc/abeja-totalizer , until next time, happy coding.

ABEJA SaaS Dev

We are Hiring!

We are also continuously looking for motivated engineers on various positions on frontend, backend and AI platform. If you want to be a part of fast paced startup based in Tokyo please contact us via email ( recruit@abeja.asia ) or Linkedin page. To see what we are working on, have a look at other posts by our Engineers and Researchers.

To know more about ABEJA and its business: www.wantedly.com

AWS Greengrassの気になる所を調べてみた!

エンジニアの河崎です。

昨日、AWS GreengrassがGAになりました

AWS GreengrassはLambda等のAWSで利用可能なサービスの一部をローカルデバイス上で実行できるようにする事で、エッジ側でのアプリケーション実行を簡単にできるサービスです。 ABEJAではエッジコンピューティングを行っていますので、さっそく調査をしてみました。

以降の検証は Raspberry Pi 3 Model B で行っています。

※2017年6月9日時点の情報です。また、できるだけ正確な情報を書くようにしましたが、間違っている可能性もありますのでご留意ください。

調べてみた

AWS Greengrassとは?

公式ドキュメント

What Is AWS Greengrass? - AWS Greengrass

Developers.IOのブログエントリ

【新サービス】AWS GreengrassがGA(一般利用開始)になりました! | Developers.IO

どうやって動かすの?

GreengrassCoreのインストールされたデバイス上で常時起動型のLambda functionを実行するサンプル

Deploying a simple Lambda function to AWS Greengrass - AWS Greengrass

GreengrassCoreのインストールされたデバイスをハブにして、IoTデバイス間の通信をするサンプル

AWS Greengrass Example Scenario - AWS Greengrass

Developers.IOのブログエントリ

AWS Greengrass CoreをEC2でセットアップする | Developers.IO

Greengrass Coreの動作要件は?

AWS Greengrass FAQs - Amazon Web Servicesに記載があります。

  • Operating Systems: Ubuntu 14.04 LTS, Jessie Kernel 4.¼.4, and other Linux distributions with Kernel 4.4 or greater
  • CPU Architectures: x86_64, Armv7, Aarch64 (ArmV8)

また、以下のカーネルコンフィグの項目が有効化されている必要があります。世の中にはcgroupsやoverlayfsが有効化されていないカーネルが乗ったデバイスが有るので気をつけたいですね。

自分のカーネルでカーネルコンフィグの項目が有効化されているかはdockerのチェックスクリプトを使うと良いかもしれません。

2.Kernel configuration

·Mqueue: CONFIG_POSIX_MQUEUE

·Overlay: CONFIG_OF_OVERLAY

·Overlay FS: CONFIG_OVERLAY_FS

·Seccomp Arch Filter: CONFIG_HAVE_ARCH_SECCOMP_FILTER

·Seccomp Filter: CONFIG_SECCOMP_FILTER

·Seccomp: CONFIG_SECCOMP

·Devpts: CONFIG_DEVPTS_MULTIPLE_INSTANCES

 

3.Kernel configuration for Namespace - Kernels must be built with these configurations enabled:

·IPC isolation: CONFIG_IPC_NS

·Network isolation: CONFIG_NET_NS

·UTS isolation: CONFIG_UTS_NS

·User isolation: CONFIG_USER_NS

·PID isolation: CONFIG_PID_NS

 

4.Kernel configuration for Cgroup - Kernels must be built with these configurations enabled:

·Enable cgroups: CONFIG_CGROUPS

·Enable Memory cgroup: CONFIG_MEMCG

·Enable freezer cgroup: CONFIG_CGROUP_FREEZER

·Enable devices cgroup: CONFIG_CGROUP_DEVICE

·Enable pids cgroup: CONFIG_CGROUP_PIDS

デバイス上でLambdaが動くってどういうこと?

GreengrassCoreがインストールされたデバイス上でpythonのスクリプトを実行する事ができます。Lambda functionの実行はMQTTでメッセージを受けた時に実行、定期的に実行、常時起動などができるようです。 ちなみに、GreengrassCoreで実行されるLambda functionには実行時間の制限はありません。

Lambda functionの実行ログは?GreengrassCoreのログは?

以下の2種類のログを取得可能。それぞれ、CloudWatchとLocalファイルシステムに保存する事ができます。Localファイルシステムの場合はどれくらいのサイズまで保存するかを指定できます。

  • User lambda logs
  • Greengrass system logs

検証してみた

GreengrassからAWSの他のサービスへアクセスする権限はどうやって指定するの?

IAM Roleを使う

GrenngrassからCloudWatch Logsへログを送る場合は、Greengrass GroupにIAM Roleを付与します。また、LambdaからCloudWatch Logsや他のサービスにアクセスする時も同様にLambda functionにIAM Roleを付与します。

Greengrassからどういう感じでAWSサービス扱うのかな?

基本的にはMQTTでクラウドに送った後にRules Engineを使うのがよさそうです。Lambda functionに適切なIAM Roleを付与すればSDKからKinesisやDynamoDBにアクセスする事は可能だと思うので、ユーザ側の要件によりそう。

CloudWatch Eventsからデバイス側のLambdaを呼び出せる?

今のところできなさそう?

Greengrassコアがオフラインの状態でもLambda functionの実行はできる?

多分できる。未検証

このサンプルを動かしてる状態で、ネットワークの状態をいじると検証できます。

Lambda functionからpythonライブラリや設定ファイル等にアクセスできる?

可能

クラウドでの実行時と同様にDeployment Packageを作る事ができます。この際、AWS Greengrass Core SDKをダウンロードしてパッケージに含める必要があります。また、設定ファイルもパッケージにいれれば読み込みできそうです。 しかし、デプロイパッケージの容量制限は50MBなので、特定用途では使えない事がありそうです。 また、パッケージする時にコンパイルが走るライブラリは、GreengrassCore上のランタイムでは動かないと思われるので一工夫必要です。多くの場合、パッケージしたいマシンはx86でGreengrassCoreが動くデバイスはARMですよね。また、コンテナ内には必要依存ライブラリも存在しません。

こういうプロジェクトを参考にがんばるとよさそうですね。 GitHub - vitolimandibhrata/aws-lambda-numpy: NumPy Python Library for AWS Lambda

Lambda functionからデバイス側のファイルシステムにアクセスできる?

読み込みのみ可能

Lambda function起動時に、デバイスのファイルシステムを起点として、コンテナのファイルシステムをoverlayfsで構築しているようです。Lambda functionで書き込んだファイルは、デバイス側のファイルシステムに反映されませんし、Lambda functionを再起動した時はLambdaから見えているファイルシステムはリセットされています。 /tmpはコンテナ起動時にリセットされるので、デバイス側の/tmpとコンテナ側の/tmpは完全に独立しています。

以上の特性を利用すれば、Lambdaからデバイス側のファイルを参照だけする事ができますが、推奨されない気がします。

Lambda functionからデバイス側のカメラやGPIO等にアクセスできる?

今のところできない

Lamda functionの中から/devを見てみましたが、カメラデバイスやgpioデバイスは見えていませんでした。

ローカルネットワークでのデバイス間通信ってどんな感じでやるの?

APIを使う事でGreengrass Coreのローカルネットワーク内でのIPをIoTデバイス側から取得できます。 AWS IoT device SDKを使う事で簡単に、IoTデバイスから同一ネットワーク内のGreengrass Coreに接続する事ができます。Greengrass CoreにはMQTT brokerと同一GreengrassCore内のDeviceShadowを保持するDBが有るので、IoTデバイスとGreengrass CoreがAWSに繋がらない状態でも、デバイス同士のメッセージの交換や、DeviceShadowを使った状態の同期が可能です。このサンプルを動かしてみると良いと思います。

Local Rules Engineって何?

Subscriptionsの事だと思います。メッセージのソース、ターゲット、メッセージを流すトピックフィルターを指定できます。ソースとターゲットは、IoT Cloud(クラウド側のMQTT broker)、Local Device Shadow、同一Grenngrass Group内のデバイス、Lambda functionを指定できます。このサンプルをいじれば色々検証できそうです。 f:id:toshitanian:20170609101719p:plain

おわりに

現時点ではGreengrassCore上でのLambda functionは、センサー等を使ったヘビーな処理を実行するというよりは、ゲートウェイとして必要になるメッセージの処理と、デバイスの操作を簡易的に行うために使う事が想定されているのかなという感想を持ちました。 まだドキュメントが少ないので、増えていくと検証もすすみそうですね。当面は、APIドキュメントを読んでいこうと思います。

AWSを使う事でクラウドだけでなく、エッジ側でもシステムを簡単に構築・運用できるようになっていってほしいですね。

宣伝

ABEJAではエッジ・クラウドを問わずディープラーニングを実行できるプラットフォームを提供しています。 テクノロジーの最先端に挑戦したいイケてるしヤバイエンジニアの方のご参画をお待ちしております!

ABEJAが発信する最新テクノロジーに興味がある方は、是非ともブログの読者に!

ABEJAという会社に興味が湧いてきた方はWantedlyで会社、事業、人の情報を発信しているので、是非ともフォローを!! www.wantedly.com

ABEJAの中の人と話ししたい!オフィス見学してみたいも随時受け付けておりますので、気軽にポチッとどうぞ↓↓

学生向けの勉強会もしています!ガンガン応募してください!

https://abeja-innovation-meetup.connpass.com/event/57686/

タカハシ春の GAN 祭り!〜 一日一GAN(๑•̀ㅂ•́)و✧ 〜

f:id:taka_t0m0:20170530141554j:plain

ABEJAでリサーチャーをしています高橋です。

昨今 deep learning 界隈では Generative Adversarial Net(GAN) が流行っていて、世はまさにガンガン行こうぜ時代ですね。

GAN を用いると綺麗な絵が作成できたり二つの絵の中間のような絵を生成できたりします。例えばこの論文のような感じです。このように GAN は有用なモデルである一方、最近の GAN では急によくわからない式が出てきたりするので、勉強も兼ねて「一日一GAN」をやってみました。今回読んだ論文のリストは以下です。

それぞれの論文の概要と自分なりの考察を報告できればと思います! 論文を読む前や読んでいる途中に参考になったなら幸いです。

一応実装も行い こちらで公開していますが、パラメータの調節などはしておらずそのままでうまくいかなさそうである旨ご了承ください。今後各 GAN の比較実験を行なった際には、その結果や使い勝手なども報告できればと考えています。一日一GANでパラメータ調節や比較実験までやるのは厳しかったです。

本筋に入る前に


GAN の 概要

本筋に入る前に GAN について説明します。

GAN のたとえ話

数式で説明する前に、GAN の学習過程についてよくあるたとえ話を述べたいと思います。

GAN で行なっていることは、贋作師と真贋鑑定士の対決、つまりガンの飛ばし合いです。贋作師は世に偽物を売るために有名な絵とそっくりな絵を描く能力が重要で、真贋鑑定士は偽物が世の中に出回らないように偽物と本物を区別する能力が重要となります。真贋鑑定士が偽物と本物を見分ける能力が向上したとします。すると贋作師は自分の偽物の絵が世に出回らなくなり困ったことになるので、更に本物に近い絵を描けるように能力の向上を目指します。能力が向上し偽物が出回ると、今度は真贋鑑定士が困ったことになるので真贋鑑定士は本物と偽物を見分ける能力の向上を目指します。そうすると、また贋作師が困ったことになって。といった具合に、二人が対決することで二人の能力がどんどん上がっていくわけです。特に贋作師の方に注目すれば、最終的には本物と区別がつかないような絵を作れるようになる、という話です。

GAN の objective

上記のような話を考えるのは簡単ですが、具体的にどういう学習をするの、とか、うまくいく理論保証はあるの、と言った部分が気になります。ということでこの部分についても触れたいと思います。以下、絵など模倣したい対象 data set があり、そのうちの一つを $x$ と書くことにします。また、$x$ は 確率分布$p_{data}(x)$に従っているとします。

真贋鑑定士 Discriminator $D$は、data 内にある $x$ を本物と判定できる様に、つまり、$x\sim p_{data}(x)$に対して$D(x) = 1$ とできる様に学習を行います。贋作師 Generator $G$ はどの様に偽物を作るのかと言えば、何らかの事前に決めた分布 $p_z$ に従う $z$ を用意し、$G$ は $z$ を入力として贋作を出力します。つまり、Generator は $D(G(z)) = 1$ とする様に頑張り、Discriminator は $D(G(z)) = 0$ となる様に学習を行います。

以上から、例えば以下の様な objective を設定して学習を行えば良さそう、と考えられます。 \begin{align} \min_{G}\max_D \left[\mathbb{E}_{x\sim p_{data}}\left[\log D(x)\right]+\mathbb{E}_{z\sim p_{z}}\left[\log(1 - D(G(z)))\right]\right]\ . \end{align} 以上で良さそうだと思えるのは、$G$ を固定すれば$D$ は $D(x)$ を大きくし、$D(G(x))$ を小さくする様に学習され、$D$ を固定すれば $G$ は $D(G(z))$ を大きくする様に学習されるためです。

理論解析

この学習で良い偽物を作れる理由を述べたいと思います。 以下 $p_z$ と $G(z)$から誘導される確率分布$p_g(x)$ と書くことにすると、 良い偽物が作れるようになる理由は、この最適化問題の global optimum が $p_g(x)$と $p_{data}(x)$が一致する時だからです。以下でそれを確認します。

まず、$G$ を固定した際、最良の $D_G$ は以下のように計算できます。 objective $\mathcal{L}$ が \begin{align} \mathcal{L}(G, D) &= \int p_{data}(x) \log(D(x)) dx+ \int p_z(z)\log(1 - D(G(z)))dz \\ &=\int p_{data}(x) \log(D(x)) + p_g(x) \log(1 - D(x)) dx \end{align}

と書き直せますが、$f(x) = a\log(x) + b\log(1 - x)$ の最大値は $x = a/(a + b)$で取るから \begin{align} D_G = \frac{p_{data}}{p_{data} + p_g} \end{align} となります。これを用いてちょっと計算すると \begin{align} \max_{D}\mathcal{L}(G, D) &= \mathbb{E}_{x\sim p_{data}}\left[\log\left(\frac{p_{data}}{p_{data} + p_g}\right)\right] +\mathbb{E}_{x\sim p_g} \left[\log\left(\frac{p_g}{p_{data} + p_g}\right)\right] \\ &= -\log(4) + 2 JS(p_{data}||p_g)\ . \end{align} ここで JS は Jensen-Shannon divergence。JS は $p_{data} = p_g$ となる際に最小値を取るので、上式はこの時に最小となります。

以上をまとめれば、objective $\mathcal{L}$ を min max 最適化をしていけば、 確率分布が一致するという意味で $G$ は対象の data を良く模倣できるようになる、というのが理論的な根拠となります。

長くなりましたが、ようやく準備終了。以下では、一日一GAN を報告していきます!

Day 1 EBGAN


論文は https://arxiv.org/abs/1609.03126 です。

EBGAN の概要

Original GAN とやることは同じで、 objective だけを変更した論文で、違う objective でも二つの確率分布が一致することを示した論文です。 論文同様ポンと objective を紹介すると、EBGAN では以下を objective としています。

\begin{align} \mathcal{L} = \sum_{x\in p_{data}}D(x) + \sum_{z\in p_z}\max(0, m - D(G(z)))\ . \end{align}

この論文では、この objective でも original GAN の様に min max 最適化をしていけば、 $p_{data}$ と$p_g$ が一致するようになることを示しています。

理論解析

二つの確率分布$p_{data}$と$p_g$が一致するようになる証明は通常の GAN と似ているが少しだけ違う感じです。 GAN と同じように、任意の $G$ に対して最良の $D$を探し、$\mathcal{L}$ に代入しちょっと計算すると

\begin{align} \max_{D}\mathcal{L}(G, D) = m + m\int \mathbb{1}_{p_{data}(x) < p_g(x)}(p_{data}(x) - p_g(x)) dx \end{align}

となります。最良の $G$ を考えるとこれの第二項が $0$ にならなければならず、それは二つの確率が一致する時、という証明です。第二項が$0$になることの詳細は論文を。GAN と似ていますが、JS divergence に帰着させないのが違いです。

ちょっとした考察

論文を読んでいただければ分かるかと思いますが、論文の一番最初の式が上の objective なんですよね。いやぁ急にこの objective をポンと与えられても、というのが率直な感想ではないかと思います。

EBGAN は energy base ということなので、なんらかの分布と繋がってればと考えたのですが、ここ ではそれは無理と主張されています。それにもめげずにちょっと考えてみたところ、完璧な対応関係とはほど遠いですが、ある極限では GAN の objective と EBGAN の objective は Fermi分布を通して繋がっていそうと思えてきたので述べてみます。予め断っておきますが、本当に完璧な対応関係からはほど遠いです。。。

GAN の discriminator は[0, 1]の確率を返すので、ここでは Fermi 分布だと思いましょう。

\begin{align} D_{GAN}(x) = \frac{1}{1 + \exp( (E(x) -\mu)/k_BT )}\ . \end{align}

ここで $E(x) \geq 0$は energy, $\mu$ は chemical potential に対応する positive な constant、$T$ は温度に対応する定数、$k_B$は Boltzman constant。特に今回は低温($k_BT << \mu$)な Fermi 分布だとします。 この場合、$E(x) \sim 0$において

\begin{align} D_{GAN}(x) \sim 1 - \exp( (E(x) -\mu)/k_BT )\ \end{align}

$E(x) >> \mu$ に対して

\begin{align} D_{GAN}(x) \sim \exp(-E(x)/k_BT) \end{align}

と近似できます。この近似を利用して、GAN の discriminator にとって非理想的な極限(本物を偽物と思い、偽物を本物と思う)を考えると、

\begin{align} L_{GAN}& = -\sum_{x\in p_{data}}\log\left(D_{GAN}(x)\right) - \sum_{z\in p_z}\log\left(1-D_{GAN}(G_{GAN}(z))\right)\\ &\sim \frac{1}{k_BT}\left[\sum_{x\in p_{data}}E(x) + \sum_{z\in p_z}(\mu - E(G_{GAN}(z)))\right] \end{align}

となりEBGANの objective に近しいものが得られます。$max(0, *)$ がない(理論解析ではこの max が本質)などズレも大きいですが、ただポンと objective が与えられるよりかは energy という気分が伝わるかと思います!

Day 2 WGAN


論文は https://arxiv.org/abs/1701.07875 です。

WGAN 概要

WGAN も original GAN から objective を変更した論文です。

基本的に、GAN は $p_{data}$ と $p_g$ を一致させることで Generator が実際のデータを良く模倣できるという話なので、確率分布間の距離を定義して、その距離を近くすれば良いというのも自然な話です。最初に見たように、original GAN では間接的には JS divergence という距離を最小化していることに対応しています。WGAN では、Earth Mover’s distance(EMD) で距離を測り、直接これの最小化をすることで二つの分布を一致させることを狙います。EMD は扱いが面倒なので、それと等価である Wasserstein distance

\begin{align} D(p_{data},p_g) = \sup_{f \in 1-Lipschitz}\left[\mathbb{E}_{x\sim p_{data}}\left(f(x)\right)-\mathbb{E}_{x\sim p_{g}}\left(f(x)\right)\right] \end{align}

を用いて二つの確率分布を近づけるように学習を行う、というのがこの論文の話です。

理論解析

上記したように、確率分布間の距離最小化なので、original GAN のような理論解析は不要かと思います。

ちょっとした考察

この論文では、EMD の定義だとか Wasserstein distance が EMD の双対だとかポンと出てくる印象です。ここでは定義とか双対性に付いて少し考えてみようと思います。「ちょっとした」と書きましたが、結構長くなるのでご注意ください。

discrete 版 EMD

EMD の定義の気分を知ったり双対と言った部分をはっきりさせるために、以下、discrete に切って考えてみます。discrete に切るとは、$x\in R^n$をそのまま考えるのではなく、格子状に切って${x_i}$という集合を作って考えることを意味します。

まずは EMD から。論文には訳のわからない定義が書いてありますが、discrete に切ると、以下のような線形の最適化問題に帰着します。

\begin{align} &\min_{x_{ij}} \sum_{ij} d_{ij}x_{ij}\\ &s.t. x_{ij} \geq 0, \sum_{i}x_{ij} = p_{data}(x_j), \sum_{j}x_{ij} = p_g(x_{i}) \end{align}

ここに $d_{ij}$ が二点間の距離で、$x_{ij}$ が求めたい変数。このように書き下してみると、この問題は「最も効率よく$p_g$という山を$p_{data}$という山に移すためにはどうすれば良いか」という問題に対応することが分かります。というのも、$x_{ij}$が$i\rightarrow j$と輸送する量(対角成分は $i$ 地点に残す量)だと思えば、各制約は

  • 一つ目の制約$i\rightarrow j$ に輸送する量は$0$以上と言う自明な制約
  • 二つ目は $j$ 地点に輸送して欲しい量の制約
  • 三つ目は $i$ 地点から運び出すことができる量の制約

をそれぞれを表しているためです。「効率よく」というのは総移動距離$\times$輸送量の最小化を意味しています。

この線形最適化問題について、例えば、$p_{data} = p_g$ であれば、全部残せば良いので自明に最良解は $0$ となり、確率分布に少しでも差が出れば、輸送が必要になるので最良の目的関数値は $0$ より大きくなります。そのため、上記線形計画問題が二つの確率分布の一致具合を測る指標となるわけです。

EMD と Wasserstein distanceの双対性

EMD の気分が分かったので、 Wasserstein distance との双対性を確認してみます。まず、上記最適化問題は線形の最適化問題なので、最良の目的関数値を知る上ではLagrange 双対問題を考えても何ら問題ありません。Lagrange 双対の詳細はここ をご参照ください。上記最適化問題の Lagrange双対問題は

\begin{align} &\max_{\lambda,\nu} \sum_{i}\lambda_i p_g(x_i) + \sum_{j}\nu_j p_{data}(x_j)\\ &s.t.\ \ \lambda_i + \nu_j \leq d_{ij} \end{align} となります。ここに$\lambda, \nu$は双対変数を表します。以下ではこの問題を Original Problem と呼び、この問題の最適解を $z^{*}$ と書くことにします。

このOriginal Problemですが、実は以下のような$\lambda_i + \nu_i = 0$という制約を追加したような問題(Modified Problem)を考えても最良の目的関数値は同じになります。以下ではそのことを示します。

\begin{align} &\max \sum_{i}\lambda_i p_g(x_i) - \sum_{j}\lambda_j p_{data}(x_j)\\ &s.t.\ \ \lambda_i - \lambda_j \leq d_{ij} \end{align}

この Modified Problem の最適な目的関数値を $z_{mod}^*$と書くことにすれば、$z_{mod}^*\leq z^*$ と $z^*\leq z_{mod}^*$の両方が示せれば良く以下ではそれを確認します。

まずは前者ですが、これは自明。上述したように、Modified Problem は Original Problemに制約を追加して作っているため、最良の目的関数値は悪くなるため。 ということで、$z^*\leq z_{mod}^*$を頑張って示せば良い事になります。

OriginalProblem の最適解を $\lambda_i^*, \nu_j^*$と書くことにします。これらを使って、新しく次のような量を定義します。

\begin{align} \mu_i := \min_j\left(d_{ij} - \nu_j^* \right) \end{align} この$\mu$には、以下のような二つの性質があります。

  1. $\lambda_i^*\leq \mu_i\leq -\nu_i^*$
  2. $|\mu_i - \mu_j|\leq d_{ij}$

以下ではこの二つの性質を示しますが、この二つが示せた場合のご利益を先に述べます。Original Problemにおいて$p_{data}$, $p_g$が positiveであることから、一つ目の性質から、単なる値比較をすると

\begin{align} z^*\leq \sum_{i}\mu_i p_g(x_i) - \sum_{j}\mu_j p_{data}(x_j) \end{align} という不等式が得られます。よく見ると、右辺は Modified Problemの目的関数と同じ形をしており、さらに $\mu$ の二つ目の性質から、$\mu_i$は Modified Problemの制約を満たしています。このこととModified Problemが最大化問題であることを考慮すれば、 \begin{align} z^*\leq \sum_{i}\mu_i p_g(x_i) - \sum_{j}\mu_j p_{data}(x_j)\leq z_{mod}^* \end{align} という評価が得られ証明終了とななります。ということで頑張って$\mu_i$の性質をcheck できればOK。

まずは一つ目。$\lambda_i^*$がOriginalProblem の制約を満たしているので、$\lambda_i^* \leq d_{ij} - \nu_j^*$が成立。これがすべての$j$について成り立ち、 $\mu_i$の定義を思い出せば、$\lambda_i^{*}\leq \mu_i$が成立します。また$\mu_i$の定義から$\mu_i\leq d_{ii} - \nu_{i} = -\nu_i$となるので一つ目の性質は成り立ちます。 続いて二つ目。各$\mu_i = \min_k\left(d_{ik} - \nu_k^* \right)$について、右辺を最小とする$k$を$k_i$と書くことにすれば、任意の$j$に対して、$\mu_i = \min_k\left(d_{ik} - \nu_k^* \right)= d_{ik_i} - \nu_{k_i}^*\leq d_{ik_j} - \nu_{k_j}^*$が成立します。$\mu_i > \mu_j$のとき、この性質と三角不等式を使えば、

\begin{align} |\mu_i - \mu_j| \leq |d_{ik_j} - \nu_{k_j}^* - (d_{jk_j} - \nu_{k_j}^*)|\leq |d_{ik_j} - d_{jk_j}| \leq d_{ij} \end{align} となります。$\mu_i < \mu_j$の際もほぼ同様の式変形で同じ評価が得られるので、二つ目の性質が成り立ちます。 よって、Modified Probelm と Original Problem は同じ最良値を持つことが示せました。

Modified Probelm の目的関数から、これの連続版は Wasserstein distance の目的関数となることが想像できると思いますし、Modified Problem の制約から 1-Lipschitz の制約が出てくるのも見えるかと思います!

ふぅ、疲れた。。。

Day 3 LSGAN


論文は https://arxiv.org/abs/1611.04076 です。

LSGAN 概要

Original GAN から objective を変えた論文です。

そもそも Discriminator の学習では、本物に対しては $1$ を返し偽物に対しては $0$ を返せるようになれば良く、 Generator の学習では Discriminator の出力を $1$ にできるようにすれば良いです。とすると、素直に以下のような objective を minimize して行けば良いのでは、と思えてきます。

\begin{align} \mathcal{L}_D &= \mathbb{E}_{x\sim p_{data}}\left(D(x) - 1\right)^2 + \mathbb{E}_{z\sim p_{z}}\left(D(G(z))\right)^2\\ \mathcal{L}_G &= \mathbb{E}_{z\sim p_{z}}\left(D(G(z)) - 1\right)^2 \label{lsgan_naiive} \end{align} このように変更すると、sigmoid cross entropy よりも勾配損失が起こりにくくなり安定して計算がしやすくなりそう、というのがご利益です。

LSGAN論文ではもっと一般的に、 \begin{align} \mathcal{L}_D = \mathbb{E}_{x\sim p_{data}}\left(D(x) - b\right)^2 + \mathbb{E}_{z\sim p_{z}}\left(D(G(z)) - a\right)^2\\ \mathcal{L}_G = \mathbb{E}_{x\sim p_{data}}\left(D(x) - c\right)^2 + \mathbb{E}_{z\sim p_{z}}\left(D(G(z)) - c\right)^2 \end{align} とした場合に、どのような$(a, b, c)$だとうまく二つの確率分布が一致するのか、を調べています。なお、$\mathcal{L}_G$ の第一項は、最適化計算では関係ありませんが、テクニカルな理由で付けてあります。

理論解析

以下では二つの分布が一致する条件を調べますが、証明は完全に original GAN で紹介したものと同じ道筋を辿ります。original GAN では、任意の $G$ に対して最良の $D$ を計算し、それを元の objective に代入してちょっと計算すると JS divergence になって、という証明でした。LSGAN も全く同様の計算をしていけば大丈夫です。 $\mathcal{L}_D$ から $G$ を固定した場合の最良の $D$ は \begin{align} D_G = \frac{bp_{data} + ap_g}{p_{data} + p_g} \end{align} となります。これを $\mathcal{L}_G$ に代入してちょっと計算すると \begin{align} \mathcal{L}_G = \int \frac{( (b - c)(p_{data} + p_g) - (b - a)p_g)^2}{p_{data} + p_g} \end{align} という評価が得られます。この評価にたどり着く際に、先に付け加えた項が効いてきます。この評価から、例えば \begin{align} b - c = 1, b - a = 2 \end{align} の場合には \begin{align} \mathcal{L} = \chi^2_{Pearson}(p_{data} + p_g||2p_g) \end{align} と Pearson $\chi$ squared divergence の形になり、やっぱり $p_{data} = p_g$ の際に最小となることが示せます。

ちょっとした考察

LSGAN の最初に述べた objective は $b = c = 1, a = 0 $なので、条件を満たしていないように感じます。しかし、少し置き換えをして計算してやればやっぱりPearson $\chi$ squared の形になることを示せます。 論文では $(a, b, c)$として条件に合うものに注目していますが、論文推奨のパラメータ以外でももっと良いものがあるかもです!

Day 4 f-GAN


論文は https://arxiv.org/abs/1606.00709 です。

f-GAN の概要

f-GAN も original GAN から objective を変更した論文です。

GAN は二つの確率分布を同じにするのが基本です。なので、二つの確率分布間の距離を定義し、$p_{data}$ と $p_g$の距離を $0$ にするように学習するのは素直な話です。Wasserstein GAN もそうでしたね。Wasserstein GAN は EMD あるいは Wasserstein distance で考えていましたが、f-GAN では f-divergence class \begin{align} D(p_{data}||p_g) = \int p_gf\left(\frac{p_{data}}{p_{g}}\right) dx\ . \end{align} をベースとして考えています。ここに $f$ は $f(1) = 0$を満たす関数で、例えば、$f(x) = x\log(x)$ とすれば KL divergence になり、$f(x) = x^2-1$とすれば Pearson $\chi$ sqruaredとなります。この f-divergene ですが、凸解析の定理や Jensen不等式などを使用すると下から評価でき、f-GAN ではその下からの bound を良い指標と思って学習を行います。

理論解析

他の項目の理論解析とは少々異なりますが、ここでは f-divergence の下からの評価を確認したいと思います。

定理と定義

論文では凸解析の定理を使っているので、ここでは、定理の適用範囲を確認ということで定義とか定理を述べるだけ述べておこうと思います。詳しくは凸解析の参考書などをご覧ください。

論文で使用している定理は「閉真凸関数であれば、$f^{**} = f$」です。ということで、以下では閉真凸と$f^{*}$ の定義だけ述べておきます。

以下では $f: R^N \rightarrow [-\infty, \infty]$ とします。

  • 凸で、全ての $x$ について$f(x) > -\infty$ かつ $f(x) < \infty$ という $x$ が存在する関数が真凸関数。
  • 真凸かつ下半連続(任意の$x_0$ について $\liminf_{x \rightarrow x_0} f(x)\geq f(x_0)$)である関数が閉真凸関数。

$f^{*}$ の定義は以下です。

  • 真凸関数に対して $f^{*}(\xi) = \sup_x\left(\xi^Tx - f(x)\right)$ が Fenchel 双対。

今回利用している定理は最初にも述べたように「閉真凸関数であれば、$f^{**} = f$」なので、使う際には$f$ が上記した閉真凸であることの確認が必要になるのでご注意を。

f-GAN の objective

先に紹介したように、$f$ が閉真凸関数であった場合、$f = f^{**}$が成立するので \begin{align} f(x) = f^{**}(x) = \sup_{\xi}\left(x^T\xi - f^{*}(\xi)\right) \end{align} が成り立ちます。これを f-divergence の定義 \begin{align} D(p_{data}||p_g) = \int p_gf\left(\frac{p_{data}}{p_{g}}\right) dx\ . \end{align} に代入してJensen 不等式などを利用すると、 \begin{align} D(p_{data}||p_g) \geq \sup_{D}\left[ \mathbb{E}_{x \in p_{data}}\left(D(x)\right) - \mathbb{E}_{x\in p_g}\left(f^{*}(D(x))\right)\right] \end{align} という評価を得られます。右辺を f-divergence の良い指標と思い GAN のような min max 学習を行っていくのが f-GAN です。

個人的な印象ですが、motivation があり導出が一直線な気持ち良い論文です!

ちょっとした考察

実は、真凸などでも $f\geq f^{**}$ は成立するので、上記不等式自体はもう少し広いクラスでも成立はします。まぁ、真凸と閉真凸の差は$dom_f$の boundery くらいなので真凸ではあまり面白いことはできないかと思いますが。。。

Day 5 DualGAN


論文は https://arxiv.org/abs/1704.02510 です。

DualGAN の概要

GAN を利用したアプリケーション系の論文です。

やりたいことは domain 変換です。例えば、イラスト $\leftrightarrow$ 写真といった変換で、イラストを与えた時にそれっぽい写真を作成したり、その逆をしたりできるように学習を行います。 この論文の手法の特徴としては、pix2pix とは異なり、データが対にはなっていないこと。つまり、学習データとして「イラストとそれに対応する写真」が与えられるのではなく、「イラストの集合と写真の集合」がデータとして与えられている状況で上記の学習を行います。

DualGAN の学習方法

他の項目では理論解析と称していましたが、 アプリケーションよりの論文ということで学習方法という項とさせて頂きます。

学習の基本戦略は以下の図の通りです。図は論文より引用。 f:id:taka_t0m0:20170522161949j:plain 図のように、Generator と Discriminator を二組み用意します。Generator で domain 変換、Discriminator は違う domain からの絵を偽物と判定、といった感じで学習を行いますが、詳細な手順は以下の通りです。

domain U の絵に対しては、

  1. domain U から絵を draw。この絵については Discriminator B で True と判定。
  2. その絵を Generator A で domain V に変換
  3. 変換された絵を Discriminator A で Flase と判定
  4. 変換された絵を Generator B で domain U に戻し、reconstruction error を加える。

とします。domain V に対しても $A \leftrightarrow B$, $U \leftrightarrow V$ とした手続きをすれば OKです。 このように学習を行い、二つの Generator で domain が行き来できるようになることを狙います。

ちょっとした考察

具体的にどうやれば良いのか見えていませんが、auto encode する際に、$p(x^{\prime}|x) = \sum_y p(x^{\prime}|y)p(y|x)$ と思って sum を考慮したらどうなるかが気になります。DualGAN の論文でも指摘されているように、今回の変換は機械翻訳の話から inspire されています。そして機械翻訳の場合にはこれを考慮していることがあります。例えば、https://arxiv.org/abs/1606.04596 です。これを考慮できるのか否かや入れて良くなるかどうかは全く見えていませんが、今後も考察をと考えています!

Day 6 infoGAN


論文は https://arxiv.org/abs/1606.03657 です。

infoGAN の概要

GAN の学習ができれば、latent の各次元にも意味が出てくることが期待されます。例えば、MNIST であれば、latent の変数をうまく動かせば違う数字を出力できるようになる、といった具合です。ただ、どの変数をどうすればどう変わるのか、というのは予めは分からないので、そこをなんとかできないか、と考えたのが infoGAN です。つまり、latent の中である次元に指定の意味を持つように学習を行います。この論文では、それを目指すために相互情報量を正則化項として加えています。

理論解析

理論解析ではありませんが、正則化に用いる項や objective を紹介できればと思います。

上記した通り、latent の一部に明確な意味を持たせるために、相互情報量を正則化項として加えます。明確な意味を持たせるには、generator に食わせる latent を $z_{full} = (c, z)$ と分けた時、なんらかの事後確率 $P(c|G(c, z))$ が高くなりなさい、という制約のもとで学習を行えば良いことが想像されます。例えば、MNIST であれば、c を数字の種類だと思うと、$c$ と $z$ を与えて絵を generate し、その絵は $c$ である確率が高くありなさいという条件を課せば、latent の中でも$c$は数字の種類の情報を持つことが期待できます。論文では $P(c|G(c, z))$ ではなく相互情報量を正則化項として加えています。ここに相互情報量の定義は以下の通りです。 \begin{align} I(c; G(c, z)) = H( c) - H(c|G(c, z)) = -\log(p( c)) + \log(p(c|G(c, z)))\ . \end{align} 本当の $P(c|G(c, z))$ を求めるのは難しいので、DNN で近似した $Q(c|G(c, z))$ を用いると、上記相互情報量を以下のように下から抑えることができます。(詳細は論文を。) \begin{align} I(c; G(c, z)) \geq H( c) + \mathbb{E}_{c\in P( c), x \in G(c, z)}\left[Q(c| G(c, z))\right] := \mathcal{L_{m}}\ . \end{align}

infoGAN ではこの下限を用いて、 \begin{align} \mathcal{L} = \mathcal{L}_{GAN} - \lambda\mathcal{L_{m}} \end{align} という objective を利用して学習を行います。$\mathcal{L}_m$を見ればわかるように、$P( c)$から $c$ を選び、その $c$ について、$Q(c|G(c, z))$ が高くなるという制約のもと $G$ の学習が進めるので、これで目的が達成できる訳です。

ちょっとした考察

完全に的外れ感もあり、また、具体的な話ではありませんが、やはり相互情報量というとnegative sampling を考えてみたくなります。もちろんラベルが少ないうちはこんなものを考える必要はありませんが、多くなった場合などのためにうまく組み合わせられないかな、と考えています!

Day 7 CVAEGAN


論文は https://arxiv.org/abs/1703.10155 です。

CVAEGAN の概要

CVAEGAN は、GAN を利用したアプリケーションよりの論文です。

やりたいこととしては、人の写真から「表情は同じで違う人」の絵を作ることです。もう少し言えば、写真と人の名前を input として一つのベクトルを作り、そのベクトルと別な名前から対象の人で同じ表情の写真を作ることを目指します。同じ表情で違う人、だけであればCVAEでもできそうですが、VAE 系の場合、loss として pixel 毎の二乗誤差 を使うためか絵がボヤけることが多い印象があります。そこで、mean squared error で測るのではなく、Discriminator で測りましょうというのがこの論文です。

CVAEGAN の学習方法

学習方法ですが、以下の絵を見ればそのまんまです。絵は論文から引用。 f:id:taka_t0m0:20170522162007j:plain

VAE 部分については以下のような手続き。

  1. 絵とそのラベルを draw
  2. 絵とラベルから Encoder で hidden vector zを作成。
  3. z とラベルから絵を Generator が作成。作成した絵と元の絵で pixel wise mean squared error。

GAN 部分については以下のような手続き。

  1. 絵とそのラベルを draw。z も draw。
  2. z とラベルから絵を Generator が作成。その絵を Discriminator が False と判定。
  3. draw してきた絵を Discriminator が True と判定。

残りのロスは feature map loss です。

このように学習すれば、ある $(x, c)$から hidden $z$ を作り、その $z$ と $c^{\prime}$ を使って Generator にかけてやれば目的が達成できるかと思います。

ちょっとした考察

論文では supervised、つまり全ての絵にクラスが付いている状況でやっているようですが、おそらくこのモデルは semi-supervised に拡張可能かと思います。上記CVAE論文のごとくやれば行けると思います!

終わりに


以上、一日一GAN を報告しました。一応論文で急に出てきた式を考察したり、論文を読んでこういうこともできるかもを意識して書いてみましたが、いかがだったでしょうか? 考察中と述べた部分もございましたし、この辺り一緒に議論して頂ける人がいらっしゃいますと幸いです。最初にも述べましたが、それぞれ implementation はしたけれどもパラメータ調節などはしていない、という状況です。機会があればこれらの比較実験もして報告できれば、と思います! またその時まで!長々とありがとうございました。

宣伝


ABEJA では、イケててヤバいエンジニアを募集しています。GAN はしばらく流行りそうですし、今後 GAN でできることもガンガン広がっていくと思います。新しいGAN を含め最先端技術を使ってガンガン行きたい方是非お待ちしております!

ABEJAが発信する最新テクノロジーに興味がある方は、是非ともブログの読者に!

ABEJAという会社に興味が湧いてきた方はWantedlyで会社、事業、人の情報を発信しているので、是非ともフォローを!! www.wantedly.com

ABEJAの中の人と話ししたい!オフィス見学してみたいも随時受け付けておりますので、気軽にポチッとどうぞ↓↓

学生向けの勉強会もしています!ガンガン応募してください!

https://abeja-innovation-meetup.connpass.com/event/57686/

参考文献

[1] Goodfellow, Ian, et al. “Generative adversarial nets.” Advances in neural information processing systems. 2014.

[2]Radford, Alec, Luke Metz, and Soumith Chintala. “Unsupervised representation learning with deep convolutional generative adversarial networks.” arXiv preprint arXiv:1511.06434 (2015).

[3] Zhao, Junbo, Michael Mathieu, and Yann LeCun. “Energy-based generative adversarial network.” arXiv preprint arXiv:1609.03126 (2016).

[4] Arjovsky, Martin, Soumith Chintala, and Léon Bottou. “Wasserstein gan.” arXiv preprint arXiv:1701.07875 (2017).

[5] Mao, Xudong, et al. “Least squares generative adversarial networks.” arXiv preprint arXiv:1611.04076 (2016).

[6] Nowozin, Sebastian, Botond Cseke, and Ryota Tomioka. “f-GAN: Training generative neural samplers using variational divergence minimization.” Advances in Neural Information Processing Systems. 2016.

[7] Yi, Zili, Hao Zhang, and Ping Tan Gong. “DualGAN: Unsupervised Dual Learning for Image-to-Image Translation.” arXiv preprint arXiv:1704.02510 (2017).

[8] Xi Chen, Yan Duan, Rein Houthooft, John Schulman, Ilya Sutskever, Pieter Abbeel. “InfoGAN: Interpretable Representation Learning by Information Maximizing Generative Adversarial Nets.” arXiv preprint arXiv:1606.03657(2016).

[9] Bao, Jianmin, et al. “CVAE-GAN: Fine-Grained Image Generation through Asymmetric Training.” arXiv preprint arXiv:1703.10155 (2017).

[10] http://www.inference.vc/are-energy-based-gans-actually-energy-based

[11] http://www.msi.co.jp/nuopt/glossary/term_c93514f7967b5ea6f4c77d569addef8d655072df.html

[12] Cheng, Yong, et al. “Semi-supervised learning for neural machine translation.” arXiv preprint arXiv:1606.04596 (2016).

[13]Kingma, Diederik P., et al. “Semi-supervised learning with deep generative models.” Advances in Neural Information Processing Systems. 2014.