ガウス過程の事前分布 ==================== ガウス過程(GP)を理解することは、モデル構築と汎化について推論するうえで重要であり、能動学習や深層学習におけるハイパーパラメータ調整を含む、さまざまな応用で最先端の性能を達成するためにも重要である。GP は至るところにあり、それが何であり、どのように使えるのかを知っておくことは私たちにとって有益である。 この節では、関数に対するガウス過程の *事前分布* を導入する。次のノートブックでは、これらの事前分布を使って *事後推論* を行い、予測を作る方法を示す。次の節は「GP の要点」と見なすことができ、実際にガウス過程を適用するために必要なことを手早く示す。 .. raw:: latex \diilbookstyleinputcell .. code:: python from d2l import torch as d2l import numpy as np from scipy.spatial import distance_matrix d2l.set_figsize() 定義 ---- ガウス過程は、\ *任意の有限個を取ると同時ガウス分布に従う確率変数の集合* として定義される。関数 :math:`f(x)` が、\ *平均関数* :math:`m(x)` と *共分散関数* または *カーネル* :math:`k(x,x')` をもつガウス過程であるとき、\ :math:`f(x) \\sim \\mathcal{GP}(m, k)` と書く。このとき、任意の入力点 :math:`x`\ (時刻、空間位置、画像の画素など)の集合で問い合わせた関数値の集合は、平均ベクトル :math:`\\mu` と共分散行列 :math:`K` をもつ同時多変量ガウス分布に従う。すなわち、\ :math:`f(x_1),\\dots,f(x_n) \\sim \\mathcal{N}(\\mu, K)` であり、ここで :math:`\\mu_i = E[f(x_i)] = m(x_i)`\ 、\ :math:`K_{ij} = \\textrm{Cov}(f(x_i),f(x_j)) = k(x_i,x_j)` である。 この定義は抽象的でとっつきにくく見えるかもしれないが、ガウス過程は実際には非常に単純な対象である。任意の関数 .. math:: f(x) = w^{\top} \phi(x) = \langle w, \phi(x) \rangle, :label: eq_gp-function は、\ :math:`w` がガウス(正規)分布から引かれ、\ :math:`\\phi` が任意の基底関数ベクトル、たとえば :math:`\\phi(x) = (1, x, x^2, ..., x^d)^{\\top}` であるとき、ガウス過程である。さらに、任意のガウス過程 :math:`f(x)` は式 :eq:`eq_gp-function` の形で表せる。まずはガウス過程に少しずつ親しむために、いくつか具体例を見ていこう。そのあとで、それらが実際にはどれほど単純で有用かを理解できるはずである。 単純なガウス過程 ---------------- :math:`f(x) = w_0 + w_1 x` とし、\ :math:`w_0, w_1 \sim \mathcal{N}(0,1)`\ 、さらに :math:`w_0, w_1, x` はすべて 1 次元であるとする。この関数は、内積 :math:`f(x) = (w_0, w_1)(1, x)^{\top}` と等価に書ける。上の :eq:`eq_gp-function` では、\ :math:`w = (w_0, w_1)^{\top}`\ 、\ :math:`\phi(x) = (1,x)^{\top}` である。 任意の :math:`x` に対して、\ :math:`f(x)` は 2 つのガウス確率変数の和である。ガウス分布は加法に関して閉じているので、\ :math:`f(x)` も任意の :math:`x` に対してガウス確率変数である。実際、任意の特定の :math:`x` について :math:`f(x)` は :math:`\mathcal{N}(0,1+x^2)` に従うことが計算できる。同様に、任意の入力 :math:`x_1,\dots,x_n` に対する関数値の集合 :math:`(f(x_1),\dots,f(x_n))` の同時分布は、多変量ガウス分布である。したがって :math:`f(x)` はガウス過程である。 要するに、\ :math:`f(x)` は *ランダム関数*\ 、あるいは *関数の分布* である。\ :math:`w_0, w_1` の値を繰り返しサンプルし、それに対応する関数 :math:`f(x)` を可視化することで、この分布についていくつかの洞察を得られる。これらは傾きと切片が異なる直線である。以下のようになる。 .. raw:: latex \diilbookstyleinputcell .. code:: python def lin_func(x, n_sample): preds = np.zeros((n_sample, x.shape[0])) for ii in range(n_sample): w = np.random.normal(0, 1, 2) y = w[0] + w[1] * x preds[ii, :] = y return preds x_points = np.linspace(-5, 5, 50) outs = lin_func(x_points, 10) lw_bd = -2 * np.sqrt((1 + x_points ** 2)) up_bd = 2 * np.sqrt((1 + x_points ** 2)) d2l.plt.fill_between(x_points, lw_bd, up_bd, alpha=0.25) d2l.plt.plot(x_points, np.zeros(len(x_points)), linewidth=4, color='black') d2l.plt.plot(x_points, outs.T) d2l.plt.xlabel("x", fontsize=20) d2l.plt.ylabel("f(x)", fontsize=20) d2l.plt.show() .. figure:: output_gp-priors_9892af_3_0.svg もし :math:`w_0` と :math:`w_1` を :math:`\mathcal{N}(0,\alpha^2)` から引くとしたら、\ :math:`\alpha` を変えることが関数分布にどのような影響を与えると考えますか? 重み空間から関数空間へ ---------------------- 上の図では、モデルのパラメータ分布がどのように関数分布を誘導するかを見た。私たちはしばしば、モデル化したい関数が滑らかか、周期的か、急激に変化するかなどについては考えを持っているが、主として解釈しにくいパラメータについて推論するのは比較的面倒である。幸い、ガウス過程は関数について *直接* 推論するための簡単な仕組みを提供する。ガウス分布は第 1・第 2 モーメント、すなわち平均と共分散行列だけで完全に定まるので、ガウス過程も同様に平均関数と共分散関数で定まる。 上の例では、平均関数は .. math:: m(x) = E[f(x)] = E[w_0 + w_1x] = E[w_0] + E[w_1]x = 0+0 = 0. 同様に、共分散関数は .. math:: k(x,x') = \textrm{Cov}(f(x),f(x')) = E[f(x)f(x')]-E[f(x)]E[f(x')] = E[w_0^2 + w_0w_1x' + w_1w_0x + w_1^2xx'] = 1 + xx'. これで、パラメータ分布からサンプルする必要なく、関数分布を直接指定してサンプルできるようになった。たとえば :math:`f(x)` から引くには、問い合わせたい任意の :math:`x` の集合に対応する多変量ガウス分布を作り、そこから直接サンプルすればよいのである。この定式化がどれほど有利か、これから見えてくるだろう。 まず、上の単純な直線モデルに対するほぼ同じ導出が、\ :math:`w \sim \mathcal{N}(u,S)` をもつ任意の形 :math:`f(x) = w^{\top} \phi(x)` のモデルに対して、平均関数と共分散関数を求めるために適用できることに注意する。この場合、平均関数は :math:`m(x) = u^{\top}\phi(x)`\ 、共分散関数は :math:`k(x,x') = \phi(x)^{\top}S\phi(x')` である。\ :math:`\phi(x)` は任意の非線形基底関数ベクトルを表せるので、私たちは非常に一般的なモデルクラスを扱っており、\ *無限個* のパラメータをもつモデルさえ含まれる。 放射基底関数(RBF)カーネル --------------------------- *放射基底関数*\ (RBF)カーネルは、ガウス過程および一般のカーネル法において最もよく使われる共分散関数である。 このカーネルは :math:`k_{\textrm{RBF}}(x,x') = a^2\exp\left(-\frac{1}{2\ell^2}||x-x'||^2\right)` の形をしており、\ :math:`a` は振幅パラメータ、\ :math:`\ell` は *長さ尺度* ハイパーパラメータである。 重み空間から出発してこのカーネルを導出してみよう。次の関数を考える。 .. math:: f(x) = \sum_{i=1}^J w_i \phi_i(x), w_i \sim \mathcal{N}\left(0,\frac{\sigma^2}{J}\right), \phi_i(x) = \exp\left(-\frac{(x-c_i)^2}{2\ell^2 }\right). :math:`f(x)` は、幅 :math:`\ell` をもち、点 :math:`c_i` を中心とする放射基底関数の和であり、次の図に示すようになる。 :math:`f(x)` は :math:`w = (w_1,\dots,w_J)^{\top}`\ 、\ :math:`\phi(x)` は各放射基底関数を含むベクトルとして、\ :math:`w^{\top} \phi(x)` の形をしているとみなせる。このガウス過程の共分散関数は .. math:: k(x,x') = \frac{\sigma^2}{J} \sum_{i=1}^{J} \phi_i(x)\phi_i(x'). では、パラメータ(および基底関数)の数を無限大にすると何が起こるかを考えよう。\ :math:`c_J = \log J`\ 、\ :math:`c_1 = -\log J`\ 、\ :math:`c_{i+1}-c_{i} = \Delta c = 2\frac{\log J}{J}` とし、\ :math:`J \to \infty` とする。すると共分散関数はリーマン和になり、 .. math:: k(x,x') = \lim_{J \to \infty} \frac{\sigma^2}{J} \sum_{i=1}^{J} \phi_i(x)\phi_i(x') = \int_{c_0}^{c_\infty} \phi_c(x)\phi_c(x') dc. :math:`c_0 = -\infty`\ 、\ :math:`c_\infty = \infty` とすると、無限個の基底関数を実数直線全体に広げ、それぞれを :math:`\Delta c \to 0` の間隔で配置することになる。 .. math:: k(x,x') = \int_{-\infty}^{\infty} \exp(-\frac{(x-c)^2}{2\ell^2}) \exp(-\frac{(x'-c)^2}{2\ell^2 }) dc = \sqrt{\pi}\ell \sigma^2 \exp(-\frac{(x-x')^2}{2(\sqrt{2} \ell)^2}) \propto k_{\textrm{RBF}}(x,x'). ここで少し立ち止まって、何をしたのかを噛みしめる価値がある。関数空間表現へ移ることで、\ *無限個* のパラメータをもつモデルを、有限の計算量で表現する方法を導出したのである。RBF カーネルをもつガウス過程は *万能近似器* であり、任意の連続関数を任意の精度で表現できる。上の導出から、その理由は直感的にも分かる。\ :math:`\\ell \\to 0` として各放射基底関数を点質量に潰し、各点質量に好きな高さを与えればよいのである。 したがって、RBF カーネルをもつガウス過程は無限個のパラメータをもつモデルであり、どんな有限のニューラルネットワークよりもはるかに柔軟である。もしかすると、\ *過剰パラメータ化* されたニューラルネットワークをめぐる大騒ぎは見当違いなのかもしれない。これから見るように、RBF カーネルをもつ GP は過学習せず、実際、小規模データセットでは特に優れた汎化性能を示す。さらに、 :cite:`zhang2021understanding` にある、ランダムラベル付き画像を完全に当てはめられるのに、構造化された問題では依然としてよく汎化する、といった例は、ガウス過程を用いて完全に再現できる :cite:`wilson2020bayesian`\ 。ニューラルネットワークは、私たちが思っているほど別物ではないのである。 RBF カーネルをもつガウス過程や、\ *長さ尺度* のようなハイパーパラメータについてさらに直感を深めるために、関数分布から直接サンプルしてみよう。これまでと同様、手順は単純である。 1. GP に問い合わせたい入力 :math:`x` の点 :math:`x_1,\dots,x_n` を選ぶ。 2. :math:`m(x_i)` を :math:`i = 1,\dots,n` について、また :math:`k(x_i,x_j)` を :math:`i,j = 1,\dots,n` について評価し、それぞれ平均ベクトルと共分散行列 :math:`\mu` と :math:`K` を作る。ここで :math:`(f(x_1),\dots,f(x_n)) \sim \mathcal{N}(\mu, K)` である。 3. この多変量ガウス分布からサンプルして、サンプル関数値を得る。 4. さらに何度もサンプルして、それらの点で問い合わせた複数のサンプル関数を可視化する。 この過程を下の図に示す。 .. raw:: latex \diilbookstyleinputcell .. code:: python def rbfkernel(x1, x2, ls=4.): #@save dist = distance_matrix(np.expand_dims(x1, 1), np.expand_dims(x2, 1)) return np.exp(-(1. / ls / 2) * (dist ** 2)) x_points = np.linspace(0, 5, 50) meanvec = np.zeros(len(x_points)) covmat = rbfkernel(x_points,x_points, 1) prior_samples= np.random.multivariate_normal(meanvec, covmat, size=5); d2l.plt.plot(x_points, prior_samples.T, alpha=0.5) d2l.plt.show() .. figure:: output_gp-priors_9892af_5_0.svg ニューラルネットワークカーネル ------------------------------ 機械学習におけるガウス過程の研究は、ニューラルネットワークの研究によって引き起こされた。ラドフォード・ニールは、ますます大きなベイズニューラルネットワークを追求し、最終的に 1994 年に(後に 1996 年に出版された。これは最も悪名高い NeurIPS の却下例の 1 つだったためである)、無限個の隠れユニットをもつそのようなネットワークが、特定のカーネル関数をもつガウス過程になることを示した :cite:`neal1996bayesian`\ 。この導出への関心は再燃しており、ニューラル接線カーネルのような考え方が、ニューラルネットワークの汎化特性を調べるために使われている :cite:`matthews2018gaussian` :cite:`novak2018bayesian`\ 。以下のようにしてニューラルネットワークカーネルを導出できる。 1 つの隠れ層をもつニューラルネットワーク関数 :math:`f(x)` を考える。 .. math:: f(x) = b + \sum_{i=1}^{J} v_i h(x; u_i). :math:`b` はバイアス、\ :math:`v_i` は隠れ層から出力層への重み、\ :math:`h` は任意の有界な隠れユニットの活性化関数、\ :math:`u_i` は入力から隠れ層への重み、\ :math:`J` は隠れユニット数である。\ :math:`b` と :math:`v_i` は独立で、それぞれ平均 0、分散 :math:`\\sigma_b^2` と :math:`\\sigma_v^2/J` をもち、\ :math:`u_i` は独立同分布に従うとする。すると中心極限定理を用いて、任意の関数値の集合 :math:`f(x_1),\dots,f(x_n)` が同時多変量ガウス分布に従うことを示せる。 対応するガウス過程の平均関数と共分散関数は次のとおりである。 .. math:: m(x) = E[f(x)] = 0 .. math:: k(x,x') = \textrm{cov}[f(x),f(x')] = E[f(x)f(x')] = \\sigma_b^2 + \frac{1}{J} \sum_{i=1}^{J} \\sigma_v^2 E[h_i(x; u_i)h_i(x'; u_i)] 場合によっては、この共分散関数をほぼ閉じた形で評価できる。\ :math:`h(x; u) = \textrm{erf}(u_0 + \sum_{j=1}^{P} u_j x_j)`\ 、ここで :math:`\textrm{erf}(z) = \frac{2}{\sqrt{\pi}} \int_{0}^{z} e^{-t^2} dt`\ 、かつ :math:`u \sim \mathcal{N}(0,\Sigma)` とする。このとき :math:`k(x,x') = \frac{2}{\pi} \textrm{sin}(\frac{2 \tilde{x}^{\top} \Sigma \tilde{x}'}{\sqrt{(1 + 2 \tilde{x}^{\top} \Sigma \tilde{x})(1 + 2 \tilde{x}'^{\top} \Sigma \tilde{x}')}})` となる。 RBF カーネルは *定常*\ 、つまり *平行移動不変* であり、したがって :math:`\tau = x-x'` の関数として書ける。直感的には、定常性とは、入力空間を移動しても、変化率のような関数の高レベルな性質が変わらないことを意味する。一方、ニューラルネットワークカーネルは *非定常* である。以下では、このカーネルをもつガウス過程からのサンプル関数を示す。原点付近で関数の見た目が質的に異なることが分かるだろう。 まとめ ------ ベイズ推論を行う最初のステップは、事前分布を指定することである。ガウス過程は、関数全体にわたる事前分布を指定するために使える。従来の「重み空間」からのモデリングの見方から出発すると、モデルの関数形を先に定め、そのパラメータに分布を導入することで、関数に対する事前分布を誘導できる。あるいは、カーネルによって性質が制御される関数空間で、事前分布を直接指定することもできる。関数空間アプローチには多くの利点がある。実際には無限個のパラメータに対応するモデルを、有限の計算量で構築できるのである! さらに、これらのモデルは非常に柔軟である一方、どのような関数が事前に起こりやすいかについて強い仮定を置くため、小規模データセットでは比較的良い汎化を示す。 関数空間におけるモデルの仮定は、直感的にはカーネルによって制御される。カーネルはしばしば、滑らかさや周期性のような関数の高次の性質を符号化する。多くのカーネルは定常的で、つまり平行移動不変である。定常カーネルをもつガウス過程から引かれた関数は、入力空間のどこを見るかに関係なく、おおむね同じ高レベルの性質(たとえば変化率)をもつ。 ガウス過程は比較的一般的なモデルクラスであり、パラメータにガウス事前分布がある限り、多項式やフーリエ級数など、私たちがすでに知っている多くのモデル例を含む。また、パラメータにガウス分布がなくても、無限個のパラメータをもつニューラルネットワークも含まれる。ラドフォード・ニールによって発見されたこのつながりは、機械学習研究者をニューラルネットワークからガウス過程へと向かわせるきっかけとなった。 演習 ---- 1. オーンシュタイン=ウーレンベック(OU)カーネル :math:`k_{\textrm{OU}}(x,x') = \exp\left(-\frac{1}{2\ell}||x - x'|\right)` をもつ GP から、事前関数のサンプルを描いてみなさい。長さ尺度 :math:`\ell` を同じに固定したとき、これらの関数は RBF カーネルをもつ GP からのサンプル関数と比べてどのように異なって見えるか? 2. RBF カーネルの *振幅* :math:`a^2` を変えると、関数分布にどのような影響があるか? 3. :math:`u(x) = f(x) + 2g(x)` を作るとする。ここで :math:`f(x) \sim \mathcal{GP}(m_1,k_1)`\ 、\ :math:`g(x) \sim \mathcal{GP}(m_2,k_2)` である。\ :math:`u(x)` はガウス過程か? もしそうなら、その平均関数と共分散関数は何か? 4. :math:`g(x) = a(x)f(x)` を作るとする。ここで :math:`f(x) \sim \mathcal{GP}(0,k)`\ 、\ :math:`a(x) = x^2` である。\ :math:`g(x)` はガウス過程か? もしそうなら、その平均関数と共分散関数は何か? :math:`a(x)` の効果は何か? :math:`g(x)` から引いたサンプル関数はどのように見えるか? 5. :math:`u(x) = f(x)g(x)` を作るとする。ここで :math:`f(x) \sim \mathcal{GP}(m_1,k_1)`\ 、\ :math:`g(x) \sim \mathcal{GP}(m_2,k_2)` である。\ :math:`u(x)` はガウス過程か? もしそうなら、その平均関数と共分散関数は何か?