PRML 読書会 #8 が来週に迫る中。
カーネル法わからん……
ガウス過程わからん……
そもそも今回の会場無事たどり着けるかな……
3つめの不安はとりあえず置いといて、わからんときは手を動かすしかない。
というわけで PRML 6.4.2 「ガウス過程による回帰」を R で試す。
訓練データは PRML のサンプルデータを使う。
# PRML's synthetic data set curve_fitting <- data.frame( x=c(0.000000,0.111111,0.222222,0.333333,0.444444,0.555556,0.666667,0.777778,0.888889,1.000000), t=c(0.349486,0.830839,1.007332,0.971507,0.133066,0.166823,-0.848307,-0.445686,-0.563567,0.261502))
与えられた x_1,...,x_N について、対応する予測値を y_1,...,y_N としたとき、ガウス過程とは、y_1, ... , y_N がガウス分布に従うとしたものである。
って、いきなりちょっと待って!
y_n はどうやって求めるの!?
と焦ってしまうが、それは野暮らしい。
y_1, ... , y_N は求めるものではなくて、ガウス分布に従う確率変数であり、その分布はカーネル関数という1つの関数によって制御されるから、カーネル関数さえ決めればモデルが決まるよ、ということ(だと思う)。
PRML 3章〜5章 では y_n を求める方法をなにがしか用意してそれを評価する、という手法だったわけだが、ガウス過程による回帰はまるっきり異なっていて、ちょっと戸惑う。
ん? ガウス過程とカーネル法は独立した概念なのか? PRML ではそこは若干曖昧だなー。
y_1, ... , y_N の事前分布は、グラム行列 K を共分散とする p(y)=N(y|0,K) とする(PRML 6.4.1)。
このとき、周辺分布 p(t) の共分散 C_N は K + β^-1 I となり、条件付き分布 p(t_N+1|t) の平均は となる。ただし、, (n=1,...,N)。
この平均が訓練データにフィットする関数を与える。まさに回帰。
カーネル関数が決まれば、上記計算を行うことができる。
機械学習ではもうさんざん言われることだが、モデル選択(この場合はカーネル選び)が最も重要。
6.4.2 では広く使われているという4つのパラメータを持つカーネル関数を紹介されているが、そんな難しいの、いきなりなんて使いこなせない!(苦笑)
今回は安直にガウスカーネルを使おう(ガウス過程だし!←関係ないし)。
ガウスカーネルのσはとりあえず 0.1。3章のお試しでは σ=0.1 でおおむねうまくいったので(あっちはガウス基底だけどね)。
精度もいるな……わかんないから、これも3章で使ったことのある β=25 とかにしておく。てきとー。
データが1次元であることを利用して、ちょこちょこ手抜きしつつも、R で regression function を求める実装を書いてみた。
# Gaussian kernel sigma <- 0.1; kernel <- function(x1, x2) exp(-(x1-x2)^2/(2*0.1^2)); # Gram matrix K <- outer(curve_fitting$x, curve_fitting$x, kernel); # covariance of marginal beta <- 25 C_N <- K + diag(10)/beta # mean of posterior = regression function m <- function(x) (outer(x, curve_fitting$x, kernel) %*% solve(C_N) %*% curve_fitting$t)
求められた回帰関数 m のグラフを描く。
plot(m, xlim=c(0,1), ylim=c(-1,1)) par(new=T) plot(curve_fitting, xlim=c(0,1), ylim=c(-1,1), xlab="", ylab="")
over fitting 気味ではあるけど、ちゃんと関数を推定できている。
ちんぷんかんぷんだったガウス過程の数式が、こうやって実際に値を放り込んでみれば、なるほど、少し意味を持って見えてきた。
気がする。
【追記】
σとβを変えて回帰させてみた。
f <- function(beta, sigma) { kernel <- function(x1, x2) exp(-(x1-x2)^2/(2*sigma^2)); K <- outer(curve_fitting$x, curve_fitting$x, kernel); C_N <- K + diag(10)/beta m <- function(x) (outer(x, curve_fitting$x, kernel) %*% solve(C_N) %*% curve_fitting$t) plot(m, xlim=c(0,1), ylim=c(-1,1), xlab="", ylab="") par(new=T) plot(curve_fitting, xlim=c(0,1), ylim=c(-1,1), xlab=paste("beta=",beta,", sigma=",sigma), ylab="") } par(mfrow=c(2,2)) f(100, 0.1); f(4, 0.1); f(25, 0.3); f(25, 0.03);
βは精度。「誤差の許容度」と言ってもいいかもしれない。
βが大きいと誤差が少なくなるように(強引に)寄せていくので、over fitting しやすくなる。
βが小さいと訓練データに従おうとする力が弱くなる分「精度」が低くなるが、汎化性能はあがる(かもしれない)。
σはガウスカーネルの尖り具合。訓練点が周りに及ぼす影響の広さ、という見方も出来る。
σが大きいと、隣接する訓練点が互いに強く影響を及ぼし合うため、どうしても精度は下がるが汎化性能は上がる(かもしれない)。
σが小さい、特に訓練点の間隔より小さいと、個々の訓練点にのみフィットする不自然な結果になる。
これらのハイパーパラメータを決めるには、交差検定するかエビデンスを見るか……というあたりは他の手法と一緒。