本記事ではガウス過程回帰の実装を行っていきたいと思います。
理論などのインプットばかりではわかった気にはなっても使いこなすことはできないので、本記事を通して実装をアウトプットしました。
ガウス過程回帰とは
ガウス過程回帰は入力Xから出力yへの関数を確率変数として推定します。ガウス過程はデータの平均値を予測すると同時に,その分散を出力します。
いま、学習データx1,⋯,xNに対応する出力f(x1),⋯,⋯,f(xN)がガウス分布に従っているとき、 新しい入力データ集合Xに対応する出力yを予測したいときを考えると、その出力は次の多変量ガウス分布に従っています。
p(y∗∣x∗,X,Y)=N(k∗TK−1Y,k∗,∗−k∗TK−1k∗)k∗は学習データXと新規のデータx∗のカーネル、Kは学習データXと学習Xのカーネル、K∗,∗は新規のデータx∗と新規のデータx∗のカーネルです.
使用データ
import numpy np.random.seed(0) resolution = 20 x_train = np.sort(np.random.rand(resolution) * 15) y = np.sin(x_train) + np.random.rand(len(x_train)) x_test = np.linspace(0, 20, resolution)
xを0から15の範囲でランダムに生成してy=sin(x)+ϵの関数へ入力して学習データを作成します。ϵはガウスノイズです。 テストデータに0から20の範囲を等間隔で学習データ分を入力として作成します。
学習データをプロットすると次のようになります。
平均 k∗TK−1Y を求める
ガウス過程回帰では任意のカーネル関数を用います。予測される確率分布の平均k∗TK−1Yにもカーネル関数があります。
ここではカーネル関数はガウス関数を用います。
k(x,x′)=exp(−2σ∣∣x−x′∣∣2)X_X_dist = np.sqrt((x_train[:, None] - x_train[None, :]) ** 2) K = np.exp((-0.5 * self.σ * X_X_dist)) K_inv = np.linalg.inv(K) X_test_dist = np.sqrt((x_train[:, None] - x_test[None, :]) ** 2) k_star = np.exp((-0.5 * self.σ * X_test_dist)) y_pred_mean = k_star.T @ K_inv @ y
分散k∗,∗−k∗TK−1k∗を求める
予測される確率分布の分散k∗,∗−k∗TK−1k∗にもカーネル関数があります。
ここでのカーネル関数もガウス関数を用います。
k(x,x′)=exp(−2σ∣∣x−x′∣∣2)X_X_dist = np.sqrt((x_train[:, None] - x_train[None, :]) ** 2) K = np.exp((-0.5 * self.σ * X_X_dist)) K_inv = np.linalg.inv(K) X_test_dist = np.sqrt((x_train[:, None] - x_test[None, :]) ** 2) k_star = np.exp((-0.5 * self.σ * X_test_dist)) test_test_dist = np.sqrt((x_test - x_test) ** 2) k_star_star = np.exp((-0.5 * self.σ * test_test_dist)) y_pred_cov = k_star_star - ((k_star.T @ K_inv) @ k_star) # 分散共分散行列 y_pred_std = np.sqrt(np.diag(y_pred_cov))
ここで標準偏差=分散から標準偏差を求めています。
この標準偏差を求めることから、予測分布の不確かさがわかります。
実行結果
ガウス過程回帰の実装コードを描画のコードを足して Class にしてまとめました。
全体コードは次のようになります。
import numpy as np import matplotlib.pyplot as plt class gpr(object): def __init__(self, σ): self.σ = σ def fit(self, x_train, x_test, y): X_X_dist = np.sqrt((x_train[:, None] - x_train[None, :]) ** 2) K = np.exp((-0.5 * self.σ * X_X_dist)) K_inv = np.linalg.inv(K) X_test_dist = np.sqrt((x_train[:, None] - x_test[None, :]) ** 2) k_star = np.exp((-0.5 * self.σ * X_test_dist)) test_test_dist = np.sqrt((x_test - x_test) ** 2) k_star_star = np.exp((-0.5 * self.σ * test_test_dist)) y_pred_mean = k_star.T @ K_inv @ y y_pred_cov = k_star_star - ((k_star.T @ K_inv) @ k_star) # 分散共分散行列 y_pred_std = np.sqrt(np.diag(y_pred_cov)) # 標準偏差 return y_pred_mean, y_pred_cov, y_pred_std if __name__ == "__main__": np.random.seed(0) resolution = 20 x_train = np.sort(np.random.rand(resolution) * 15) y = np.sin(x_train) + np.random.rand(len(x_train)) x_test = np.linspace(0, 20, resolution) model = gpr(σ=1.0) mu, var, std = model.fit(x_train, x_test, y) # 描画 fig = plt.figure(figsize=(12, 4)) plt.cla() plt.scatter(x_train, y, c="black", marker="o", label="train data") plt.plot(x_test, mu, color="red", linewidth=1, label="mean") plt.fill_between(x_test, mu + std, mu - std, facecolor='green', alpha=0.2, label="confidence") plt.legend() plt.show()
黒点は学習データです。赤い曲線が予測結果(推定したガウス分布の平均)で、緑色の領域は推定したガウス分布の共分散行列を使って計算した2σ区間を表していて、予測の信頼度が表現されています。
学習データの少ないところは分散(σの値)が大きくなっているなど、関数の不確定性まで扱えています。
このように、予測値の不確実性を出力できることはガウス過程をはじめとするベイズモデルの長所です.
終わりに
ここまで読んでくださってありがとうございます。
編集リクエストもお待ちしてます。