Swiftでガウス過程
ガウス過程回帰をSwiftで実装します。
ガウス過程は線形回帰の重みパラメータを積分消去し、カーネル関数(基底関数の内積)のみで関数を表現するモデルです。
確率モデルなので、予測に対する分散も表現できます。
Pythonで実装したものがあるので、これを参考にしてSwiftでも実行できるようにしたいと思います。
Calc()は前回の記事で作成した行列計算をまとめたクラスです。
今回は負の対数尤度、対数尤度をガウスカーネルの各ハイパーパラメータで微分したものまで書きました。
import Foundation class GaussianProcess { let calc = Calc() let X: [[Double]] var theta: [Double] var y: [Double] init(X: [[Double]]){ self.X = X self.theta = [Double.random(in: 0 ..< 1), Double.random(in: 0 ..< 1), Double.random(in: 0 ..< 1)] self.y = [Double](repeating: 0, count: X.count) } func kernel(X_i: [Double], X_j: [Double]) -> Double{ let r: Double = calc.vsum(vector: calc.vpow(vector: calc.vmv(vector: X_i, vector_: X_j), power: 2)) let k: Double = exp(self.theta[0]) * exp(-r / exp(self.theta[1])) return k } func Kernel_matrix() -> [[Double]] { let N: Int = self.X.count var K = [[Double]](repeating: [Double](repeating: 0, count: N), count: N) for i in 0..<N { for j in 0..<N { K[i][j] = self.kernel(X_i: self.X[i], X_j: self.X[j]) } } K = calc.mpm(matrix: K, matrix_: calc.sxm(scalar: exp(self.theta[2]), matrix: calc.eye(n: N))) return K } func partial_kernel(X_i: [Double], X_j: [Double]) -> [Double] { let r: Double = calc.vsum(vector: calc.vpow(vector: calc.vmv(vector: X_i, vector_: X_j), power: 2)) var partial_k = [Double](repeating: 0, count: self.theta.count) partial_k[0] = exp(theta[0]) * exp(-r / exp(self.theta[1])) partial_k[1] = exp(theta[0]) * exp(-r / exp(self.theta[1])) * exp(self.theta[1]) * r partial_k[2] = exp(theta[2]) return partial_k } func partial_Kernel_matrix() -> [[[Double]]] { let N: Int = self.X.count var partial_K = [[[Double]]](repeating: [[Double]](repeating: [Double](repeating: 0, count: N), count: N), count: N) for i in 0..<N { for j in 0..<N { let partial_k = self.partial_kernel(X_i: self.X[i], X_j: self.X[j]) for k in 0..<self.theta.count { partial_K[k][i][j] = partial_k[k] } } } return partial_K } func log_likelihood(K: [[Double]], K_inv: [[Double]], det_K: Double) -> Double { let N: Double = Double(K.count) let L: Double = -(N/2)*log(2*Double.pi) - (1/2)*log(det_K) - (1/2)*calc.mdot(matrix: calc.mdot(matrix: calc.transpose(matrix: [self.y]), _matrix: K_inv), _matrix: [self.y])[0][0] return -L //負の対数尤度 } func partial_L(K_inv: [[Double]]) -> [Double] { let dK: [[[Double]]] = self.partial_Kernel_matrix() var partial_L = [Double](repeating: 0, count: self.theta.count) let K_inv_dot_y: [Double] = calc.mdot(matrix: K_inv, _matrix: [self.y])[0] for i in 0..<self.theta.count { partial_L[i] = calc.trace(matrix: calc.mdot(matrix: K_inv , _matrix: dK[i])) - calc.mdot(matrix: calc.mdot(matrix: calc.transpose(matrix: [K_inv_dot_y]), _matrix: dK[i]), _matrix: [K_inv_dot_y])[0][0] } return partial_L } }