終末 A.I.

データいじりや機械学習するエンジニアのブログ

ベイズ統計を理解する(2) 〜 判別モデルと生成モデル 〜

先週に引き続き、Computer Vision Modelsのテキストとベイズ推定とグラフィカルモデル:コンピュータビジョン基礎1 | Udemyを使用して、ベイズ統計について勉強中です。今週は、判別モデルと生成モデル、それぞれのモデリング方法での基礎的な学習方法を、回帰と識別それぞれのケースでどのように行うかを書いていきたいと思います。

ベイズ推論とモデリング

まずベイズを用いた学習では、観測変数xが与えられた時に、その時の状態wを求めるためのモデルPr(w|x)を学習により獲得することを目標とします。この事後分布Pr(w|x)を利用するプロセスは、

  1. xとwを関連付けるモデルをパラメーターθを用いて表す

  2. 学習データからパラメーターθを学習する

  3. 与えられた観測データxからPr(w|x,\theta)を計算する。その計算が難しい場合は、サンプル結果やMAP推定値を返す

といったようになります。

1.で使用するモデリングの方法には、Pr(w|x)を直接モデル化しパラメーターを学習する方法(判別モデル:Discriminative Model)と、Pr(x|w)をモデル化しパラメーターを学習する方法(生成モデル:Generative Model)の2つがあります。

判別モデルでは直接推論を行うことができるので簡易でありまた計算コストも抑えることができます。一方、生成モデルはデータセットをサンプリングでき、また推論時にwの事前分布を使用できるため、ノイズの多いデータでは判別モデルより頑健性の高い結果を得ることができます。

それぞれのモデルで、回帰と分類で行われるモデリングの例を見ていきましょう。

まず、回帰の例です。判別モデルでは

{ Pr(w|x,\theta) = Norm_w(\phi_0 + \phi_1 x, {\sigma}^2) }

と表現することができます。この場合、\sigma^2が0であれば、w = \phi_0 + \phi_1 xとなることからわかるように、線形回帰モデルとしてxとwの関係が表現されています。もちろん、Pr(w|x)正規分布であると仮定する必要もありませんし、wとxの関係が線形なものである必要も、分散に影響を与えないようなモデルにする必要もありません。モデル化後は、お好きな推定手法を用いてパラメーターθを推定すればOKです。

一方、生成モデルでは

{ Pr(x|w,\theta) = Norm_x(\phi_0 + \phi_1 w, {\sigma}^2) }

と表現することができます。見ていただいたように、xとwが反転しているだけで、これも線形回帰モデルとなります。この場合も基本的にモデリング自体はもっと自由に行うことができます。生成モデルが判別モデルと異なるところは、事前分布を仮定してやる必要がある点です。この場合、事前分布Pr(w)正規分布として仮定しており、必要に応じてパラメーターを学習する必要があります。

次に、分類の例です。判別モデルでは、

{ Pr(w|x,\theta) = Bern_w(sig(\phi_0 + \phi_1 x)) }

と表現することができます。この場合、Pr(w)をベルヌーイ分布で仮定し、wとxの関係をシグモイド関数で表現していることから、ロジスティック回帰となります。もちろん、分布や関係式は自由に設定することができます。

一方、生成モデルでは大きく異なり、

{ Pr(x|w,\theta) = Norm_x(\mu_w, {\sigma}^2_w) }

と表現されます。これは、wの値ごとにxの分布が変化することを確率密度関数として表しています。この時、Pr(w)はベルヌーイ分布で表現し、このパラメーターも必要に応じて学習する必要があります。

生成モデルによる分類

生成モデルによる分類は、確率密度関数の推定という形で行われます。

例えば、Pr(x|w) = Norm_w(\mu_w, \Sigma_w)とwの値ごとの確率変数xの確率密度関数を定義します。この時最尤推定を行うことにより、推定値を

\hat{\mu}_w, \hat{\Sigma}_w = argmax(\prod_{i \in S_w} Pr(x_i | \mu_w, \Sigma_w))

と求めることができます。S_wはカテゴリがwである観測データの集合であるとします。つまり二値分類でいうと、w=0の場合のパラメーターとw=1の場合のパラメーターはそれぞれのカテゴリに分類される観測データxから別々に推定されることになります。

最後に評価を行う場合は、Pr(w) = Bern_w(\lambda)を利用して、Pr(w=0|x)Pr(w=1|x)ベイズルールで求め利用します。ちなみに\lambdaの値も学習データを利用して、何かしらの方法(例えば頻度とか)で決めておく必要がありますが、以降でもあまり話題には上がりませんので「何か決まってるんだなー」くらいに思っておいてください。

以上で生成モデルによる分類を行う基本的な方法を説明しましたが、尤度が正規分布で表現されるため、この場合認識精度はそこまでよくはなりません。精度を上げるには一般的に尤度に複雑な分布を利用する必要がありますが、その場合、通常の方法では推定値を求めることができません。

そこで利用するのが、隠れ変数とEMアルゴリズムです。

隠れ変数の考え方は非常にシンプルです。尤度関数を

Pr(x|\theta) = \int Pr(x,h|\theta) dh

と隠れ変数hをおいて定義します。この時最尤推定を行うことにより、以下のように推定値を求めます。

\hat{\theta} = argmax(\sum_{i=1}^I log(\int Pr(x_i,h_i|\theta) dh_i ))

このように隠れ変数を設定すると、単純なモデルを利用してモデルの表現力を上げることができます。例えば、以下で説明する正規混合分布では、h=1の時の分布、h=2の時の分布といったように複数正規分布を利用し、それを足し合わせることにより複雑な分布を表現することができています。

しかし、上記のような積分の入った式では解析的に簡単にパラメーターの推定を行うことができません。そこで利用されるのがEMアルゴリズムと呼ばれる手法です。EMアルゴリズムでは、下界(Lower Bound)の考え方を利用して、パラメーターを段階的に更新していきます。この手法で一番重要な式は、下記の下界の定義式です。

 {LB(q_i(h_i), \theta) = \sum_{i=1}^I \int q_i(h_i) log(\frac{Pr(x_i,h_i|\theta)} {q_i(h_i)}) dh_i ) \le \sum_{i=1}^I log(\int Pr(x_i,h_i|\theta) dh_i )}

ちなみに、\int q_i(h_i) log(\frac{Pr(x_i,h_i|\theta)} {q_i(h_i)}) dh_iはKLダイバージェンスと呼ばれるもので、確率分布の非類似度を表す情報量の一つです。EMアルゴリズムはこの下界を最大化する分布q_i(h_i)とパラメーターθを交互に求めます。Eステップでは、

 \hat{q}_i(h_i) = Pr(h_i|x_i,\theta^t) = \frac{\Pr(x_i|h_i,\theta^t)Pr(h_i|\theta^t)} {Pr(x_i)}

\theta^tを固定して \hat{q}_i(h_i)を求めます。Mステップでは、

\hat{\theta}^{t+1} = argmax(\sum_{i=1}^I \int \hat{q}_i(h_i) log(Pr(x_i,h_i|\theta)) dh_i ))

のようにパラメーターの推定値を求めます。この計算を繰り返し、パラメーターをより元の尤度関数を最大化するパラメーターに近づけていきます。この手順は、Eステップで下界の関数を更新し、Mステップでその関数を最大にするパラメーターを求めていることに相当します。

では、混合正規分布を例に、EMアルゴリズムの手順を見ていきましょう。混合正規分布は、複数正規分布の和で確率分布を表現するものとなります。求める確率分布は

Pr(x|\theta) = \sum_{k=1}^K \lambda_k Norm_x(\mu_k,\Sigma_k)

と定義できます。\lambdaは合計すると1になる値です。この時、

Pr(x|h,\theta) = Norm_x(mu_h,\Sigma_h)

Pr(h|\theta) = Cat_h(\lambda)

と定義することができる。Eステップは、

\hat{q}_i(h_i) = \frac{\lambda_k Norm_{x_i}(\mu_k,\Sigma_k)} {\sum_{j=1}^K \lambda_j Norm_{x_i}(\mu_j,\Sigma_j)} = r_{ik}

となる。r_{ik}は負担率とも呼ばれる値で、観測データx_iをどの分布で負担するかを表すような値になっています。Mステップでは、この負担率を用いて以下のように更新する。

\lambda_k^{t+1} = \frac{\sum_{i=1}^I r_{ik}} {\sum_{j=1}^K \sum_{i=1}^I r_{ij}}

\mu_k^{t+1} = \frac{\sum_{i=1}^I r_{ik} x_i} {\sum_{i=1}^I r_{ik}}

\Sigma_k^{t+1} = \frac{\sum_{i=1}^I r_{ik}(x_i - \mu_k^{t+1}){(x_i - \mu_k^{t+1})}^T} {\sum_{i=1}^I r_{ik}}

このように、それぞれの正規分布の分布全体への影響度を示す\lambda_kは負担率の合計値が、平均や分散は負担率を重みとして利用した値が使われており、直感的にも妥当そうな更新式になっています。

以上で混合正規分布を用いたEMアルゴリズムの推定の方法となります。EMアルゴリズムは他にも様々な分布に適用することができます。外れ値に強くするために用いられるt-分布、部分空間を用いて正規分布の表現力を上げる因子分析、そしてt-分布に部分空間を適用したものなど、それこそ適用方法は様々で、詳しくは上記テキストの7.5 - 7.7章をご確認ください。

判別モデルによる回帰

次に、判別モデルによる回帰の方法を説明していきます。判別モデルでの回帰は、上で示したように{ Pr(w|x,\theta) = Norm_w(\phi_0 + \phi_1 x, {\sigma}^2) }と書くことができます。この式に対して下記のような最尤推定を行うことを考えます。

\hat{\theta} = argmax(Pr(w|x,\theta))

この式を解くと、推定値は解析的に求めることができ下記のようになります。

\hat{\phi} = (XX^T )^{-1}Xw

\hat{\sigma}^2 = \frac{(w - X^T \phi)^T (w - X^T \phi)} {I}

以上が判別モデルでの線形回帰の例となります。しかし、この単純なモデルで単純な推定を行うと問題があります。一つは、学習データ点のない部分についても過剰に推定の信頼性がたかくなってしまうover-confident、さらに線形関数にしか適用できない点、さらに過学習や高次元な観測値の場合パラメーターの次元が大きくなりすぎる点が挙げられます。

over-confident に対しては、ベイズ推定を用いることで、非線形化は関数の非線形化およびカーネルトリック過学習やパラメーターの次元の削減にはスパース回帰を用いることで改善できます。これらの手法は組み合わせることにより、Gaussian Process Regression やRelative Vector Regression などより高度な手法でモデリングすることができます。

それぞれを簡単に見ていきましょう。まず、ベイズ推定による線形回帰です。これは何も難しいことはありません。ベイズ推定により観測データが与えられた時のパラメーターの確率分布を推定すればよいのです。

Pr(\phi|X,w) = \frac{Pr(w|X,\phi)Pr(\phi)} {Pr(w|X)}

ですので、{ Pr(w|x,\theta) = Norm_w(\phi_0 + \phi_1 x, {\sigma}^2) }で、Pr(\phi) = Norm_\phi(0, \sigma^2 I)とおくと、Pr(\phi|X,w)を解析的に求めることができます。また新しい観測値x^*が与えられた際に、

Pr(w^*|x^*,X,w) = \int Pr(w^*|x^*,\phi) Pr(\phi|X,w) d \phi

も解析的に求めることができます。このように推定を行うと、学習データに近い部分のxとwの組み合わせの信頼性は高く、そうでない点については信頼性があまり高くない分布を得ることができます。ちなみに上記の推定はパラメーター\phiについてのものだけであり、\sigma^2については別途推定をする必要があります。このテキストでは、\phiについての周辺尤度Pr(w|X,\sigma^2)を最大化する\sigma^2を用いて、説明を行っています。

次に非線形関数の例です。非線形関数ではz_i=f(x_i)となる非線形関数fを定義し、Pr(w|X,\theta) = Norm_w(Z^T\phi,\sigma^2I)とおいてパラメーターを推定します。関数fにはRBFやarctanなどが用いられます。いずれにおいても、最尤推定ベイズ推定も線形回帰と同様に行うことができます。

ただし、新たな観測値xに対してwを計算すると逆行列等の行列計算が非常に大変になります。そこでカーネルトリックを用いてzの内積計算を簡単になるような関数fを用います。RBFカーネルなどが有名なカーネル化関数となります。

スパースな回帰では、Pr(\phi)正規分布をステューデントのt-分布を用いることにより、パラメーターが各軸が独立になるように学習させます。具体的には下記のように定義します。

Pr(\phi) = \prod_{d=1}^D Stud_{\phi_d}(0,1,\nu)

しかしこの分布を用いると単純に解析的に推定を行うことができません。そこで、\phiについて事後確率分布を周辺化することによりその値を求めることにより、直接事後確率分布を計算します。

また、次元を削減するには双対モデルを解く方法もあります。双対モデルは、パラメーター\phi\phi = X \psiとおくことでパラメーターを推定する方法です。このモデルの面白いところは、元の式で推定した場合と同じ結果を得ることができるところです。ここで\phiの次元は変数の次元数に依存し、\psiの次元は学習データの数に依存します。つまり、双対モデルを用いることにより、求めるパラメーターの次元数を削減することができます。

このように、様々なテクニックを用いることにより判別モデルの回帰を行い、分布を求めることができます。

判別モデルによる分類

判別モデルによる分類は、モデルとして{ Pr(w|x,\theta) = Bern_w(sig(\phi_0 + \phi_1 x)) }を使う以外は、回帰モデルと似たような工夫を行い、様々な分類モデルを表現することができます。

一番の違いは、このモデルでは解析的に解を求めることができず、ニュートン法などを用いて最適なパラメーターを推定する必要がある点です。最尤推定でもベイズ推定でもそれは変わりません。

また、多クラス問題への拡張を考える必要もあります。多クラス問題への拡張は、モデルをカテゴリカル分布と仮定し、そのパラメーターの値をソフトマックス関数により定義することにより表現します。

ベイズ統計を理解する(1) 〜 最尤推定、MAP推定、ベイズ推定 〜

先日、下記のような記事が上がっていまして、小説生成を目論む私としては気になって論文にも目を通してみたのですが、どうやら生成モデルを利用しているらしいということしか分からず、理解が足踏みしてしまっています。

wired.jp

最近、Deep Learning 周りでは生成モデルが流行ってるようで、DCGANを使ってイラストを自動で生成するアルゴリズムが年末にバズっていたように、この周辺はじわじわホットトピックになってきている感があります。

qiita.com

しかし、生成モデルを理解するには Deep Learning だけでなくベイズ統計周りの知識が必須になります。利用するだけならどなたかが実装するのを待てばよいのですが、新しくでた論文の内容を実装してみようと思うと、ベイズの基礎くらいは必要な印象を受けました。

そこで、今回からしばらくはComputer Vision Modelsのテキストを使用して、ベイズ統計について学んでいきたいと思います。また、こちらのテキストは日本語での解説動画が上がっていますので、こちらもご参考ください。

www.udemy.com

前置きが長くなりましたが、今回はモデルのフィッテングに使用される、最尤推定、MAP推定推定、ベイズ推定について書いていきたいと思います。

以下では、{\theta}はモデルのパラメーター、{\hat{\theta}}はパラメーターの推定値、{x_{1...I}}は観測値(省略して単に{x}と記述することもあります)、{x^*}はモデルにフィットするか評価する値とします。

最尤推定

最尤推定は、下記のように観測値の尤度が最大化するようなパラメーターを推定します。

{\hat{\theta} = argmax(Pr(x_{1...I}| \theta))}

新しいデータがモデルにどれだけ適合するかは、{Pr(x^*|\hat{\theta})} を計算すれば良いので簡単に求めることができます。

例として観測変数が正規分布から生成されるようなモデルを考えます。このとき、推定するパラメーター{\hat{\theta}} {\hat{\mu}} {\hat{\sigma}^2}になります。式展開の詳細は、上記で紹介してある本や資料を見ていただくとして、{Pr(x|\theta)}正規分布の場合、最尤推定を行うと{\hat{\theta}}は解析的に簡単に求める事ができ、下記のようになります。

 {\hat{\mu} = \frac{\sum_{i=0}^I x_i} {I}}

 {\hat{\sigma}^2 = \sum_{i=0}^I \frac{{(x_i - \hat{\mu})}^2} {I}}

見ていただいてわかるように、それぞれ観測点の平均と分散になります。以上が最尤推定による学習の概要となります。

MAP推定

MAP推定は、事後確率を最大にするパラメーターを推定する方法です。式は下記のようになります。

 { \displaystyle
\hat{\theta} = argmax(Pr(\theta|x)) \
= argmax(Pr(x | \theta) Pr(\theta))
}

ベイズの定理に従い素直に展開すると{Pr(x)}が分母につきますが、この値は{\theta}によらず一定であり、式を最大にする{\theta}を求める際には不要なので除いています。最尤推定法と比較してみると、事前確率が推定に影響を与えるかどうかの違いがあります。

新しいデータがモデルにどれだけ適合するかは、{Pr(x^*|\hat{\theta})} を計算することで求めることができます。これは最尤推定の時と同じですね。

同じく、例として観測変数が正規分布から生成されるようなモデルを考えます。この時、事前分布である{Pr(\theta)}正規分布の共役事前分布である逆ガンマ分布であると仮定します。式展開の詳細は省略しますが、この場合も解析的に簡単に{Pr(x|\theta)}を求めることができ、下記のようになります。

 {\hat{\mu} = \frac{\sum_{i=0}^I x_i + \gamma \delta} {I + \gamma}}

 {\hat{\sigma}^2 = \sum_{i=0}^I \frac{{(x_i - \hat{\mu})}^2 + 2 \beta + \gamma{(\delta - \hat{\mu})}^2} {I + 3 + 2 \alpha}}

 {\alpha , \beta , \gamma , \delta}は逆ガンマ分布のハイパーパラメーターです。 {\hat{\mu}}に注目していただくと分かりやすいように、事前分布の情報の分、最尤推定の時と比べてハイパーパラメーターで推定値が補正されています。以上がMAP推定による学習の概要となります。

ベイズ推定

ベイズ推定は、観測値にが当てられた時にパラメーターの事後確率分布を推定する方法です。先程までの最尤推定やMAP推定はベイズ推定がパラメーターの分布を推定するのに対して、最大になるパラメーター一点のみを推定するので点推定と呼ばれます。

ベイズ推定の式は下記のようになります。

 { \displaystyle
Pr(\theta|x) = \frac{\prod Pr(x_i | \theta) Pr(\theta)} {Pr(x)}
}

ここで求めたパラメーターの事後分布を用いて新しい値の評価を行いますが、この評価の仕方も点推定の場合とはかなり異なります。ベイズ推定では、ある値が観測されるようなモデルに新しく観測された値がフィットするかを評価します。式は下記のようになります。

[tex: { \displaystyle Pr(x^ | x) = {\int Pr(x^ | \theta)Pr(\theta | x) d \theta} }]

例として観測変数が正規分布から生成されるようなモデルを考えます。{Pr(\theta)}はMAP推定の時と同じく逆ガンマ分布であると仮定します。 この時、推定されるパラメーターの事後分布は解析的に求めることができ下記のようになります。

 { \displaystyle
Pr(\theta|x) = NormInvGam(\tilde{\alpha}, \tilde{\beta}, \tilde{\gamma}, \tilde{\delta})
}

また、{Pr(x^*|x)} も解析的に求める事ができ、下記のようになります。

{ \displaystyle
Pr(x^*|x) = {\frac{1} {\sqrt{2 \pi}} {\frac{\sqrt{\tilde{\gamma}} \tilde{\beta}^{\tilde{\alpha}}} {\sqrt{\breve{\gamma}} \breve{\beta}^{\breve{\alpha}}}}} {\frac{\Gamma(\breve{\alpha})} {\Gamma(\tilde{\alpha})}}
}

{ \tilde{\alpha}, \tilde{\beta}, \tilde{\gamma}, \tilde{\delta}, \breve{\alpha}, \breve{\beta}, \breve{\gamma}} {\alpha , \beta , \gamma , \delta}と観測値{x}から計算されるパラメーターです。このようにだいぶ複雑になりますが、解析的に求める事ができるため素早く簡単に計算できます。以上がベイズ推定による学習の概要となります。

学習結果の比較

平均が0、標準偏差が2の正規分布からサンプリングしたデータを使用して、上記の各モデリング手法による学習結果を比較してみました。ちなみにベイズ推定は式を入力するのが面倒くさかったのと、ベイズ推定ライブラリの使用方法がよくわからなかったので結果は載せていません。 赤が元の分布、青が最尤推定で学習した分布、緑がMAP推定で学習した分布です。MAP推定では、中心が0、標準偏差が2~4くらいのパラメーターが最も表れやすい事前分布を与えています。

I=10

f:id:KSKSKSKS2:20160522210026p:plain

I=100

f:id:KSKSKSKS2:20160522210256p:plain

観測数が10の時は、学習が不完全で中心付近の大きく分布がよってしまっています。MAP推定は最尤推定とくらべ、事前分布の影響を見て取ることができます。一方、単純な分布なこともあり観測数が100もあれば、いずれも十分に元の分布を再現できるに至っています。

学習に使用したコードは下記のとおりです。といっても、上記の式に合わせてコード書いてあるだけなんですけどね。

word2vec の結果を利用して RNN で文章を生成してみる(2)

word2vec の出力結果を元に文章を作ってみるコーナーの第二弾です。 今回はエンコーダーデコーダーモデルを使用して word2vec の出力から文章を生成できないかを試してみました。 使用したモデルは以前の記事で紹介した Skip-Thought Vectors です。

ksksksks2.hatenadiary.jp

Skip-Thought Vectors を簡単に説明すると、入力文をエンコーダーエンコードしその文の情報をコンテキスト情報として指定サイズのベクトルに圧縮、そしてこのコンテキスト情報を元に、デコーダーを使用して入力文の前後の文を出力しようというモデルとなります。 この Skip-Thought Vectors の入力および出力は、単語のIDを各ユニットに割り当てた形となります。今回は、この入力と出力を word2vec にして日本語で動かしてみました。

chainer を用いて、前回も利用した mecab分かち書きにした wikipedia の記事文章とその文章から生成した word2vec を利用して処理を行いました。ロス関数は本家のソフトマックスのままでは利用できないので、正解単語とデコーダーによって出力された単語との二乗誤差を用いました。対象となった単語は40万語ほどで、それ以外は未知語としてすべて同じベクトルを割り当てるようにしています。

利用したコードは記事の最下部に記載しています。一部 util.py に逃がしたコードは記載していませんが、だいたい何をやっているかは分かっていただけるかと思います。chainer にはコンディショナルな GRU を扱う方法がないので、StatefulGRU のコードを利用して ConditionalStatefulGRU を自作、それをさらに自作の Skip-Thoughts モデルで使用しています。Skip-Thoghts モデルでは、まず入力単語をエンコーダー部でエンコードし、その出力をデコーダーで使用して前後の文が出力されるように学習しています。

だいたい6万文ほど学習させた段階で誤差が小さくならなくなってきたので、今回はそこで処理を停止させました。以下はその時のモデルを利用して、文章を入力した際に、次の文章として生成されたものです。文章の生成は、「。」が現れるか、30単語生成した時点で停止するようにしています。ちなみに入力文章は、学習したデータの中にも含まれている、wikipedia のアンパサンドに関する説明の一節です。

  • 入力文:アンパサンド(&)とは「…と…」を意味する記号である。
  • 出力単語列:'000', '受け取る', '回数', '推定', '平均', '000', '賃金', '月末', '政令', '条', '詐欺', '弁護士', '監査', '地裁', '年金', '協定', '加盟', '審議', '令', '奉仕', '自治体', '在日', '捕鯨', '定める', '通知', '土木', '請求', '職員', '実務', '事務'

  • 入力文:その使用は1世紀に遡ることができ(1)、5世紀中葉(2,3)から現代(4-6)に至るまでの変遷がわかる。

  • 出力単語列:'フラッシュ', 'メニュー', 'フォーマット', '送信', 'ケーブル', '媒体', 'ホスト', '動画', 'MHz', '閲覧', 'エージェント', '互換', '通信', '準拠', 'タイミング', 'サポート', 'OS', 'メニュー', 'SD', 'ボード', 'ファイル', '周波数', 'ゲート', '本体', 'マイクロソフト', 'シフト', 'コンテンツ', 'オブジェクト', 'Microsoft', 'ロード', 'フォーマット', 'ドメイン', '選択肢', 'オブジェクト', 'ソニー', 'ウェア', 'マイクロソフト', 'データ', '素子', 'オート', 'アプリ', 'コア', '動画', 'スケジュール', 'グループ', '夏季', 'メイン', '祭り', 'ライフ', 'ロケ', 'ショップ'

ご覧いただいた通り、全然うまく学習できていません。入力文の話の流れを完全に無視しているだけでなく、出力がただの名詞の羅列でありそもそも文章として成り立たせるための学習が行われたように全く見えません。一方、出力する単語列はその前の単語に大きく影響されるところがあり、連想ゲームのように関連する単語が生成されるようになってしまっています。

「てにおは」が全くなく、その前の単語に近い単語が生成されるというのは、学習動作から考えて一応想定していた結果ではあります。ある文章には、それ以前に入力されてきた単語に関連する単語(word2vec 的に距離が近い単語)が現れる確率が、それ以外の単語が現れる確率より格段に高いので、入力された単語と近い単語を出力するような学習となったのでしょう。

入力文のコンテキストを完全に無視するという挙動はなんとも言えません。同じ文の組み合わせを一回ずつしか学習させていないので十分に学習しきれていない可能性もありますし、自前で実装した ConditionalStatefulGRU がうまく機能していない可能性もあります。一応 ConditionalStatefulGRU のパラメーター更新は行われているようですので、そんなことはないと思いたいものですが。

とりま、前者の挙動をなんとかしないと、文章っぽいものを生成することすらできません。この辺りをどう解決するかが今後の鍵になりそうです。

Deep Learning で使われてる attention ってやつを調べてみた

先週 Skip-Thought Vectors について調べてみたわけですが、その中でよく何を言っているのかよく分かっていなかった、 attention mechanism について調べてみました。 調べるにあたって、最近のDeep Learning (NLP) 界隈におけるAttention事情 が大変参考になりました。ありがとうございます。

まず attention 、特にエンコーダーデコーダーモデルにおける attention について簡単に説明すると、入力情報全体ではなく、その一部のみを特にフォーカスしたベクトルをデコーダーで使用する仕組みのことです。そのことにより、デコードの特定のタイミングにだけ必要になる入力情報を精度よく出力に反映させることができるようになります。

これだけでは何のことかちょっと分かりにくいので、Neural machine translation by jointly learning to align and translate を例に説明していきたいと思います。この論文は、Neural Machine Translation という Neural Network を利用して翻訳タスクを行う分野の論文ですが、最近その分野でも著しい成果を出しているエンコーダ・デコーダーモデルでの翻訳処理に、attention を導入することによって、翻訳前の文章が長文であっても精度よく翻訳処理が行えるようにしたという成果を紹介しています。

通常のエンコーダ・デコーダモデルでは、エンコーダの出力は一つしかデコーダでは使用されません。それをどのように用いるかは手法によりけりですが、この方法では、入力文の情報を特定のサイズのベクトルにまとめる必要があり、長文になればなるほど元の情報の圧縮精度が悪くなってしまいます。

一方、attention を用いたモデルでは、エンコーダーの隠れ層のうち、特定の入力単語やその周辺の単語にフォーカスしたベクトルをデコーダで用います。これにより、デコーダのある時点で必要な情報にフォーカスして使用することができ、入力文の長さに関係なくデコードを効率よく行うことができます。attention の利用方法も手法によりけりですが、すべてのベクトルを重み付けして利用する global attention や特定のベクトルのみを用いる local attention と呼ばれる方法に分けている提案もあります。

上記の論文では、長文での翻訳精度が上昇したでけでなく、alignment と呼ばれる機械翻訳分野で、翻訳前の文と翻訳語の文の対照関係を分析する処理でも有用な結果を生成することができているようです。

個人的には、エンコーダで生成するベクトルに十分な長さがあれば特に問題なく似たような状況を学習できそうな気もするのですが、なかなかそれではうまくいったりしないものなんでしょうか。

参考文献

Skip-Thought Vectors を解説してみる

本日は、インスピレーションと予算の枯渇のため、実験ができていなかったので、論文の解説をいたします。まあ、解説とか偉そうなことを言っていますが、主に自分用のメモみたいなものなのですが。

紹介する論文は、「Skip-Thought Vectors」です。この手法は、文(センテンス)をベクトル化する手法の一つで、様々なNLPタスクで好成績を挙げたことで知られている去年の6月にarxivに公開された論文です。ちなみに著者の方が Theano 上で動くソースコードを公開しているので、実際に動かしてみることも可能です。(ただし、学習に時間がかかる) github.com

さて、この Skip-Thought Vectorsのですが、最大の特徴は教師なし学習でかなり質の高い文ベクトルを生成できる点にあります。実際に使用する入力データは、文章のコーパス(論文中ではブックコーパス)だけでできてしまいます。計算資源さえあればOKという、日曜大工な人間には最高に相性の良い手法となっています。

その構造ですが、下記の図が一番わかりやすいでしょう。入力しているのは、文書中のi番目の文。出力は、同じくi-1番目とi+1番目の文である入力文書の前後の文です。正確に言うと、入力文を元に文ベクトルを生成するエンコーダと、文ベクトルを元に前の文と次の文を生成する2つのデコーダから成っているモデルです。

論文中では、エンコーダに用いたモデルはGRU、デコーダに用いたモデルは conditional GRUを使用していますが、エンコーダ・デコーダとして使用できればモデル上なんでもいいことになります。

f:id:KSKSKSKS2:20160424184856p:plain

では実際にどのように学習していくのか。手順は下記になります。

  1. エンコーダに入力となる文を最後まで入力する(入力に用いる単語ベクトルはあらかじめ用意しておく。ID形式とかなんでもOK)。
  2. エンコーダの学習により獲得した隠れ層の値を入力文のベクトルとして使用する。
  3. デコーダに入力文の引数と、生成する文の一つ前の単語を入力する(最初の単語の場合は、eos 記号を入力とする)。
  4. デコーダのこの時の隠れ層の値と、出力される単語に対応する語彙ベクトルの内積を求め、この値をその時点での該当単語の出力される確率として扱う。
  5. 出力文の最後まで 3−4 の操作を繰り返し、単語生成確率の和を求め、この値が最大化されるようにエンコーダとデコーダのパラメータを学習する

デコーダは、前の文を生成するものと次の文を生成するものの2つがあり、基本的にそれぞれで使用するパラメータは違うものを使用しますが、語彙ベクトルだけは共通のものを使用します。というわけで、以上で学習は完了です。簡単ですね!

注意点としては、学習時に出現していない単語がテストセットに含まれている時の扱いです。論文中では、テストセットと学習セットに含まれる単語をword2vec等でベクトル化した後、学習セットに含まれる単語のベクトルを word2vec ベクトルから学習時に使用したベクトルに射影する線形変換を学習しすることにより、未知語の表現を獲得しエンコーダでも使用できるようにしています。 デコード時に出力される語彙に関する言及は特にないので、増やすことについての考慮は特にされていないと思われます。難しそうですものね。

また、学習時に使用する文ですが、 attention mechanism (参照:Neural machine translation by jointly learning to align and translate.) を使用して、適切な単語のみを考慮して学習できるように一部改変しながらもちているようです。

というわけで、後はエンコードしたりデコードしたりして遊んでみてねーとのことですね。エンコーダ・デコーダに使用するモデルを変更してみても面白いかもしてません。

2016.04.30 追記

attention mechanism については、エンコーダーデコーダーモデルにそういう+αもあるよという一般的な説明であって、実際に論文で説明している手法には使用されていないようです。

word2vec の結果を利用して RNN で文章を生成してみる(1)

chainer のサンプルの中には RNN 利用して文章を学習し、コンテキストに沿った単語を選択できるようになる ptb のサンプルが付属しています。

今回はこいつをちょっと改造して、単語の識別IDではなく、word2vec で生成したベクトルを用いて ptb サンプルと同じことをやってみようと思いま......したが、残念がら chainer の仕様理解ができていなかったようで、一切パラメーター更新ができておらず、4000円ほどドブに捨てる結果となってしまいました。辛すぎる!

そういうわけで今日のところは、こういう風にやったらうまく学習できなかったという記録のみ記載しておきたいと思います。原因分かり次第、追記か別記事を書きます。

今回学習がうまくいかなかったモデルは以下のように実装しました。元の ptb サンプルから embedID のレイヤの関数を取り除き、Classification で loss を算出する代わりに、huber 損失関数という二乗誤差の親戚みたいな関数で loss を求めると言った構成になっています。入力は単語ベクトルで出力も単語ベクトルになります。LSTMかましていることからも分かるように、model をリセットするまでは以前の入力値も反映した出力をしてくれるモデルです。

class RNNLM(chainer.Chain):
    def __init__(self, n_vocab, n_units, train=True):
        super(RNNLM, self).__init__(
            l1=L.LSTM(n_vocab, n_units),
            l2=L.LSTM(n_units, n_units),
            l3=L.Linear(n_units, n_vocab),
        )
        self.train = train

    def reset_state(self):
        self.l1.reset_state()
        self.l2.reset_state()

    def __call__(self, x, t):
        h1 = self.l1(F.dropout(x, train=self.train))
        h2 = self.l2(F.dropout(h1, train=self.train))
        y = self.l3(F.dropout(h2, train=self.train))
        return F.huber_loss(y, t)

そして、このモデルの loss に対して特に何も考えずにもとの ptb サンプルと同様に、backword して、unchain して、optimizer を update したのですが、全くろくな結果を得ることができませんでした。調べてみると、重み値の更新が一切行われておらず、初期値のまま。 ......なんでや。Function でなくちゃんとレイヤをかまさなければならないのか、それとも loss の扱い方が悪かったのか。

<<追記>> 2016.4.17 夜

huber_loss で学習ができない件について、自己解決しました。huber_loss はミニバッチの学習には対応していないようで、そのために全く学習ができていなかったようです。実装上の都合なのか、関数の制限なのかは勉強不足ため理解できていませんが、ひとまず huber_loss の部分も二乗誤差に差し替えることにより無事学習が行えました。やったね!

しかし学習はできているようであるものの、全くいい感じに文章を覚えてくれません。やたらめったらΣを押してくるこになってしまいました。バッチサイズがでかすぎたのか、エポック数が足りないのか、単純に二乗誤差では学習がうまくいきにくいのか。ちょっと文献をあさってみる必要がありそうです。

Wikipedia を word2vec する

前回、青空文庫で word2vec を試してみましたが、結果を見ての通り、作家によって類似する単語が違ったり、そもそも語彙が少ないため、あまり汎用性のある結果を得ることはできませんでした。

ksksksks2.hatenadiary.jp

そこで今回は、日本語 Wikipedia のダンプデータを使用して、word2vec で学習させてみました。 Wikipedia ではこちらに記載されているように、Wikipedia 上で作成された様々なデータのダンプデータを配布しています。主なものだと、全ページの要約や全文、変わったものだと、ページ間のリンク関係のデータなどが含まれています。 今回は、日本語の最新情報から全文情報を取得して、使用しました。ちなみに、このデータは圧縮時でも2GB、展開すると10GB近くあるデータになります。このデータは XMLwiki 記法で記述されておりそのままでは使用できないため、wikiextractorを使用して平文になおした後、mecab分かち書きに変換して学習に用いました。ちなみに学習に用いたデータは3GBほどになります。

結果の比較のため、イテレーション回数と学習アルゴリズムを変えて試してみました。イテレーション回数は、1回と10回。アルゴリズムは、cbow と skip−gram を使用しました。word2vec と cbow の詳細は、前回の記事ををご覧ください。skip−gram は、cbow とは逆で単語を入力し文脈語を出力するニューラルネットワークを学習する点が特徴です。サンプリングなどの学習に用いるテクニックは cbow と共通ですが、バックプロバケーションの必要がある出力が多いため、cbow より3〜4倍ほど学習に時間がかかります。 CPU4コア、メモリ16GHzの環境だと、skip−gramでイテレーション回数を10回にした場合、だいたい丸二日ほどかかりました。

できれば評価データを用いて定量的に見たかったのですが、日本語での評価データを探しきれなかったため、前回と同様、何個かピックアップして結果を見ていきたいと思います。結果を見るのは、単語同士の類似度比較と、単語の組み合わせでの類似度(足し算引き算の結果)比較を行いました。

  • 単語類似度

問題

ランク cbow 1 skip−gram 1 cbow 10 skip−gram 10
1 弊害 解決 課題 解決
2 課題 課題 矛盾 課題
3 不都合 コンフリクト 不都合 難問

明日

ランク cbow 1 skip−gram 1 cbow 10 skip−gram 10
1 きっと あした いつか あした
2 あした 明後日 きっと あす
3 君たち きっと あした きっと
  • 単語の組み合わせ類似度

日本 + 東京 - アメリカ

ランク cbow 1 skip−gram 1 cbow 10 skip−gram 10
1 ロサンゼルス ニューヨーク ニューヨーク ニューヨーク
2 ニューヨーク ヨンカーズ ロサンゼルス ロサンゼルス
3 サンフランシスコ ファロン ロンドン カリフォルニア

昼 + 太陽 - 夜

ランク cbow 1 skip−gram 1 cbow 10 skip−gram 10
1 満月 満月 満月 満月
2 夜空 夜空 夜空 トラウィスカルパンテクートリ
3 地平線 まばゆい 地平線 ぃだ

基本的にイテレーション回数が多い方が良いように見えますが、4番目の「昼 + 太陽 - 夜」を見ると、skip−gram 10の2,3番目の結果は全く意味が分かりません。「トラウィスカルパンテクートリ」はアステカ神話における明けの明星を擬人化した神様であるとのことです。「ぃだ」については全くわかりません。「てぃだ」であれば沖縄で太陽を意味する方言になりますが、mecab分かち書きする時点でうまく処理できていないものと思われます。

また、「明日」の類似単語として「きっと」が現れたり、「日本 + 東京 - アメリカ 」の結果に「ワシントン」が出てこないなど、必ずしも意図していない結果が出てきています。このあたりのアルゴリズムコーパスと欲しい結果とのギャップを埋めるようなワンクッションが、学習過程か学習結果の利用時にまだまだ必要そうです。