はじめに
ガウス過程(Gaussian Process, GP)は、ベイズ最適化の中核を成す確率的モデリング手法です。従来の回帰手法と異なり、GPは予測値だけでなく予測の不確実性を定量化できるため、探索-活用のトレードオフを最適化できます。
本章では、1次元回帰から始めて、各種カーネル関数、ハイパーパラメータ最適化、モデル検証まで、化学プロセスデータを用いた実践的な実装を学びます。
- ガウス過程は平均関数と共分散関数(カーネル)で完全に定義される
- カーネル選択は問題のスムーズさに応じて調整する
- ハイパーパラメータは最尤推定(MLE)で自動最適化できる
- 予測の不確実性は信頼区間として可視化できる
2.1 ガウス過程回帰の基礎
2.1.1 数学的定義
ガウス過程は、任意の有限個の点における関数値が同時ガウス分布に従う確率過程です:
f(x) ~ GP(m(x), k(x, x'))
m(x) = E[f(x)] # 平均関数
k(x, x') = E[(f(x) - m(x))(f(x') - m(x'))] # 共分散関数(カーネル)
観測データ D = {(x_i, y_i)} が与えられたとき、新しい点 x* での予測分布は:
f(x*) | D ~ N(μ(x*), σ²(x*))
μ(x*) = k(x*, X) [K + σ_n² I]⁻¹ y
σ²(x*) = k(x*, x*) - k(x*, X) [K + σ_n² I]⁻¹ k(X, x*)
例1:1次元ガウス過程回帰(化学反応収率)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, ConstantKernel as C
# ===================================
# 1D化学反応収率データ(温度 vs 収率)
# ===================================
# 実験データ(温度 [°C] → 収率 [%])
X_train = np.array([50, 70, 90, 110, 130]).reshape(-1, 1)
y_train = np.array([45, 62, 78, 71, 52]) # 最適温度90°C付近
# ガウス過程モデルの構築
kernel = C(1.0, (1e-3, 1e3)) * RBF(10, (1e-2, 1e2))
gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=10,
alpha=1e-2, random_state=42)
# モデル訓練(ハイパーパラメータ自動最適化)
gp.fit(X_train, y_train)
# 予測(不確実性付き)
X_test = np.linspace(40, 140, 100).reshape(-1, 1)
y_pred, sigma = gp.predict(X_test, return_std=True)
# 可視化
plt.figure(figsize=(10, 6))
plt.plot(X_test, y_pred, 'b-', label='GP予測平均', linewidth=2)
plt.fill_between(X_test.ravel(),
y_pred - 1.96*sigma,
y_pred + 1.96*sigma,
alpha=0.3, label='95%信頼区間')
plt.scatter(X_train, y_train, c='red', s=100, zorder=10,
edgecolors='k', label='実験データ')
plt.xlabel('温度 [°C]', fontsize=12)
plt.ylabel('収率 [%]', fontsize=12)
plt.title('ガウス過程による反応収率予測', fontsize=14)
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
print(f"最適化されたカーネル: {gp.kernel_}")
print(f"対数周辺尤度: {gp.log_marginal_likelihood_value_:.2f}")
# 期待される出力:
# 最適化されたカーネル: 31.6**2 * RBF(length_scale=15.8)
# 対数周辺尤度: -12.45
# プロット: 90°C付近で収率ピーク、端で不確実性増大
95%信頼区間 (μ ± 1.96σ) は、真の収率が95%の確率でこの範囲に入ることを示します。実験データから遠い領域(40°C、140°C付近)では不確実性が増大し、区間が広がります。これがベイズ最適化で未探索領域の探索を促進する鍵です。
2.2 カーネル関数の選択
2.2.1 主要カーネルの特性
| カーネル | 式 | スムーズさ | 適用例 |
|---|---|---|---|
| RBF (Squared Exponential) | k(x, x') = σ² exp(-||x - x'||² / (2ℓ²)) | 無限回微分可能 | 温度-収率関係 |
| Matérn (ν=1.5) | k(x, x') = σ² (1 + √3r/ℓ) exp(-√3r/ℓ) | 1回微分可能 | 圧力-流量関係 |
| Matérn (ν=2.5) | k(x, x') = σ² (1 + √5r/ℓ + 5r²/3ℓ²) exp(-√5r/ℓ) | 2回微分可能 | 触媒活性曲線 |
| Rational Quadratic | k(x, x') = σ² (1 + r²/(2αℓ²))^(-α) | RBFのスケール混合 | 多スケール現象 |
例2:RBFカーネル(スムーズな反応曲線)
from sklearn.gaussian_process.kernels import RBF, ConstantKernel as C
# ===================================
# RBFカーネル: 無限回微分可能(非常にスムーズ)
# ===================================
# 温度-圧力同時変化による収率データ
X_train = np.array([[60, 1.0], [80, 1.5], [100, 2.0],
[80, 2.5], [60, 2.0]]) # [温度°C, 圧力MPa]
y_train = np.array([50, 68, 85, 72, 58])
# RBFカーネル定義
kernel_rbf = C(1.0) * RBF(length_scale=[10.0, 0.5],
length_scale_bounds=(1e-2, 1e3))
gp_rbf = GaussianProcessRegressor(kernel=kernel_rbf, n_restarts_optimizer=15)
gp_rbf.fit(X_train, y_train)
# 等高線プロット用グリッド
temp_range = np.linspace(50, 110, 50)
pressure_range = np.linspace(0.8, 3.0, 50)
T, P = np.meshgrid(temp_range, pressure_range)
X_grid = np.c_[T.ravel(), P.ravel()]
y_pred_grid, sigma_grid = gp_rbf.predict(X_grid, return_std=True)
Y_pred = y_pred_grid.reshape(T.shape)
Sigma = sigma_grid.reshape(T.shape)
# 可視化
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 予測平均
contour1 = axes[0].contourf(T, P, Y_pred, levels=15, cmap='viridis')
axes[0].scatter(X_train[:, 0], X_train[:, 1], c='red', s=150,
edgecolors='white', linewidths=2, label='実験点')
axes[0].set_xlabel('温度 [°C]')
axes[0].set_ylabel('圧力 [MPa]')
axes[0].set_title('RBFカーネル: 予測収率 [%]')
plt.colorbar(contour1, ax=axes[0])
axes[0].legend()
# 不確実性
contour2 = axes[1].contourf(T, P, Sigma, levels=15, cmap='Reds')
axes[1].scatter(X_train[:, 0], X_train[:, 1], c='blue', s=150,
edgecolors='white', linewidths=2, label='実験点')
axes[1].set_xlabel('温度 [°C]')
axes[1].set_ylabel('圧力 [MPa]')
axes[1].set_title('RBFカーネル: 予測標準偏差 [%]')
plt.colorbar(contour2, ax=axes[1])
axes[1].legend()
plt.tight_layout()
plt.show()
print(f"最適化された長さスケール: {gp_rbf.kernel_.k2.length_scale}")
print(f"温度方向: {gp_rbf.kernel_.k2.length_scale[0]:.2f}°C")
print(f"圧力方向: {gp_rbf.kernel_.k2.length_scale[1]:.2f} MPa")
# 期待される出力:
# 最適化された長さスケール: [12.34 0.68]
# 温度方向: 12.34°C
# 圧力方向: 0.68 MPa
# → 温度より圧力の変化に敏感な収率特性
例3:Matérnカーネル(ν=1.5)
from sklearn.gaussian_process.kernels import Matern
# ===================================
# Matérnカーネル (ν=1.5): 1回微分可能
# 物理法則に基づく中程度のスムーズさ
# ===================================
kernel_matern15 = C(1.0) * Matern(length_scale=10.0, nu=1.5)
gp_matern15 = GaussianProcessRegressor(kernel=kernel_matern15,
n_restarts_optimizer=10)
gp_matern15.fit(X_train, y_train)
# 1D断面での比較(圧力=2.0 MPa固定)
X_test_1d = np.column_stack([np.linspace(50, 110, 100),
np.full(100, 2.0)])
y_pred_matern, sigma_matern = gp_matern15.predict(X_test_1d, return_std=True)
plt.figure(figsize=(10, 6))
plt.plot(X_test_1d[:, 0], y_pred_matern, 'g-',
label='Matérn(ν=1.5)', linewidth=2)
plt.fill_between(X_test_1d[:, 0],
y_pred_matern - 1.96*sigma_matern,
y_pred_matern + 1.96*sigma_matern,
alpha=0.3, color='green')
plt.scatter(X_train[:, 0], y_train, c='red', s=100,
edgecolors='k', label='実験データ')
plt.xlabel('温度 [°C] (圧力=2.0 MPa固定)')
plt.ylabel('収率 [%]')
plt.title('Matérnカーネル (ν=1.5) による予測')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
print(f"Matérn(ν=1.5) カーネル: {gp_matern15.kernel_}")
print(f"対数周辺尤度: {gp_matern15.log_marginal_likelihood_value_:.2f}")
# 期待される出力:
# Matérn(ν=1.5) カーネル: 1.2**2 * Matern(length_scale=11.5, nu=1.5)
# 対数周辺尤度: -10.23
# → RBFより若干ラフな予測(実験ノイズに頑健)
例4:Matérnカーネル(ν=2.5)
# ===================================
# Matérnカーネル (ν=2.5): 2回微分可能
# RBFとν=1.5の中間的なスムーズさ
# ===================================
kernel_matern25 = C(1.0) * Matern(length_scale=10.0, nu=2.5)
gp_matern25 = GaussianProcessRegressor(kernel=kernel_matern25,
n_restarts_optimizer=10)
gp_matern25.fit(X_train, y_train)
y_pred_matern25, sigma_matern25 = gp_matern25.predict(X_test_1d, return_std=True)
# 3つのカーネル比較プロット
plt.figure(figsize=(12, 6))
plt.plot(X_test_1d[:, 0], y_pred_grid[:100], 'b-',
label='RBF (∞回微分可能)', linewidth=2)
plt.plot(X_test_1d[:, 0], y_pred_matern, 'g--',
label='Matérn(ν=1.5)', linewidth=2)
plt.plot(X_test_1d[:, 0], y_pred_matern25, 'orange',
linestyle='-.', label='Matérn(ν=2.5)', linewidth=2)
plt.scatter(X_train[:, 0], y_train, c='red', s=150,
edgecolors='k', linewidths=2, label='実験データ', zorder=10)
plt.xlabel('温度 [°C] (圧力=2.0 MPa固定)', fontsize=12)
plt.ylabel('収率 [%]', fontsize=12)
plt.title('カーネル関数の比較(スムーズさの違い)', fontsize=14)
plt.legend(fontsize=11)
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
# 対数周辺尤度の比較(高いほど良い)
print("\n=== カーネル性能比較 ===")
print(f"RBF: {gp_rbf.log_marginal_likelihood_value_:.2f}")
print(f"Matérn(ν=1.5): {gp_matern15.log_marginal_likelihood_value_:.2f}")
print(f"Matérn(ν=2.5): {gp_matern25.log_marginal_likelihood_value_:.2f}")
# 期待される出力:
# === カーネル性能比較 ===
# RBF: -9.87
# Matérn(ν=1.5): -10.23
# Matérn(ν=2.5): -9.95
# → RBFが最も高い尤度(このデータに最適)
- RBF: 非常にスムーズな応答(温度-収率、濃度-活性)
- Matérn(ν=1.5): 実験ノイズが大きい場合、物理的制約あり
- Matérn(ν=2.5): バランス型(デフォルト推奨)
- Rational Quadratic: 複数スケールの変動(多段階反応)
例5:Rational Quadraticカーネル
from sklearn.gaussian_process.kernels import RationalQuadratic
# ===================================
# Rational Quadraticカーネル:
# 異なる長さスケールのRBFカーネルの無限混合
# 多スケール現象のモデリングに有効
# ===================================
kernel_rq = C(1.0) * RationalQuadratic(length_scale=10.0, alpha=1.0)
gp_rq = GaussianProcessRegressor(kernel=kernel_rq, n_restarts_optimizer=10)
gp_rq.fit(X_train, y_train)
y_pred_rq, sigma_rq = gp_rq.predict(X_test_1d, return_std=True)
plt.figure(figsize=(10, 6))
plt.plot(X_test_1d[:, 0], y_pred_rq, 'purple', linewidth=2,
label='Rational Quadratic')
plt.fill_between(X_test_1d[:, 0],
y_pred_rq - 1.96*sigma_rq,
y_pred_rq + 1.96*sigma_rq,
alpha=0.3, color='purple')
plt.scatter(X_train[:, 0], y_train, c='red', s=100,
edgecolors='k', label='実験データ')
plt.xlabel('温度 [°C] (圧力=2.0 MPa固定)')
plt.ylabel('収率 [%]')
plt.title('Rational Quadraticカーネルによる予測')
plt.legend()
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
print(f"最適化されたカーネル: {gp_rq.kernel_}")
print(f"α (スケール混合度): {gp_rq.kernel_.k2.alpha:.2f}")
print(f"対数周辺尤度: {gp_rq.log_marginal_likelihood_value_:.2f}")
# 期待される出力:
# 最適化されたカーネル: 1.15**2 * RationalQuadratic(alpha=0.85, length_scale=12.3)
# α (スケール混合度): 0.85
# 対数周辺尤度: -10.10
# → α<1: 短距離相関が支配的、α→∞でRBFに収束
2.3 ハイパーパラメータ最適化
2.3.1 最尤推定(MLE)の原理
ガウス過程のハイパーパラメータ θ = {σ², ℓ, σ_n²} は、対数周辺尤度を最大化することで最適化します:
log p(y | X, θ) = -1/2 y^T [K + σ_n² I]⁻¹ y
- 1/2 log|K + σ_n² I|
- n/2 log(2π)
最適化: θ* = argmax_θ log p(y | X, θ)
例6:ハイパーパラメータ最適化(MLE with scipy)
from scipy.optimize import minimize
from sklearn.gaussian_process.kernels import RBF, ConstantKernel as C
import numpy as np
# ===================================
# 手動でのハイパーパラメータ最適化実装
# (scikit-learnの内部処理を理解する)
# ===================================
# データ
X = np.array([[60], [80], [100], [80], [60]])
y = np.array([50, 68, 85, 72, 58])
def negative_log_marginal_likelihood(theta, X, y):
"""対数周辺尤度の負値(最小化用)
Args:
theta: [log(σ²), log(ℓ), log(σ_n²)]
X: 入力データ (n, d)
y: 出力データ (n,)
Returns:
-log p(y | X, θ)
"""
sigma_f = np.exp(theta[0]) # シグナル分散
length_scale = np.exp(theta[1]) # 長さスケール
sigma_n = np.exp(theta[2]) # ノイズ標準偏差
# カーネル行列計算
n = X.shape[0]
K = np.zeros((n, n))
for i in range(n):
for j in range(n):
r_sq = np.sum((X[i] - X[j])**2)
K[i, j] = sigma_f**2 * np.exp(-r_sq / (2 * length_scale**2))
# ノイズ追加
K_y = K + sigma_n**2 * np.eye(n)
# 対数周辺尤度計算
try:
L = np.linalg.cholesky(K_y) # コレスキー分解(数値安定)
alpha = np.linalg.solve(L.T, np.linalg.solve(L, y))
log_likelihood = (-0.5 * y.dot(alpha)
- np.sum(np.log(np.diag(L)))
- n/2 * np.log(2*np.pi))
return -log_likelihood # 最小化のため負値
except np.linalg.LinAlgError:
return 1e10 # 数値不安定な場合はペナルティ
# 初期値(対数スケール)
theta_init = np.log([1.0, 10.0, 1.0]) # [σ², ℓ, σ_n²]
# 最適化実行(L-BFGS-B法)
result = minimize(negative_log_marginal_likelihood, theta_init,
args=(X, y), method='L-BFGS-B',
bounds=[(-5, 5), (-2, 5), (-5, 2)]) # log空間の境界
# 最適ハイパーパラメータ
theta_opt = result.x
sigma_f_opt = np.exp(theta_opt[0])
length_scale_opt = np.exp(theta_opt[1])
sigma_n_opt = np.exp(theta_opt[2])
print("=== 最適化結果 ===")
print(f"シグナル分散 σ²: {sigma_f_opt:.3f}")
print(f"長さスケール ℓ: {length_scale_opt:.2f}°C")
print(f"ノイズ標準偏差 σ_n: {sigma_n_opt:.3f}%")
print(f"\n最大対数周辺尤度: {-result.fun:.2f}")
print(f"最適化ステップ数: {result.nit}")
# scikit-learnとの比較
kernel_sklearn = C(1.0) * RBF(10.0)
gp_sklearn = GaussianProcessRegressor(kernel=kernel_sklearn,
n_restarts_optimizer=10)
gp_sklearn.fit(X, y)
print(f"\n=== scikit-learn結果(比較) ===")
print(f"最適化カーネル: {gp_sklearn.kernel_}")
print(f"対数周辺尤度: {gp_sklearn.log_marginal_likelihood_value_:.2f}")
# 期待される出力:
# === 最適化結果 ===
# シグナル分散 σ²: 156.234
# 長さスケール ℓ: 14.87°C
# ノイズ標準偏差 σ_n: 2.145%
#
# 最大対数周辺尤度: -8.92
# 最適化ステップ数: 23
#
# === scikit-learn結果(比較) ===
# 最適化カーネル: 12.5**2 * RBF(length_scale=14.9)
# 対数周辺尤度: -8.91
# → 手動実装とscikit-learnがほぼ一致
- σ² (シグナル分散): 関数の振幅(大きいほど大きく変動)
- ℓ (長さスケール): 相関距離(大きいほどスムーズ)
- σ_n² (ノイズ分散): 観測誤差(実験精度の逆数)
2.4 不確実性定量化と信頼区間
2.4.1 予測分布の解釈
ガウス過程の予測分布 f(x*) ~ N(μ, σ²) から、以下の情報が得られます:
- μ(x*): 予測値(期待値)
- σ(x*): 予測の不確実性(標準偏差)
- 95%信頼区間: [μ - 1.96σ, μ + 1.96σ]
- 99%信頼区間: [μ - 2.58σ, μ + 2.58σ]
例7:モデル検証(交差検証・残差分析)
from sklearn.model_selection import cross_val_score, KFold
from sklearn.metrics import mean_squared_error, r2_score
import numpy as np
# ===================================
# ガウス過程モデルの包括的検証
# ===================================
# より大きなデータセット生成(触媒活性データ)
np.random.seed(42)
X_full = np.random.uniform(50, 150, 30).reshape(-1, 1) # 温度 [°C]
y_true = 50 + 40 * np.exp(-(X_full.ravel() - 100)**2 / 400) # 真の関数
y_full = y_true + np.random.normal(0, 3, 30) # ノイズ追加
# ガウス過程モデル
kernel = C(1.0) * RBF(10.0)
gp = GaussianProcessRegressor(kernel=kernel, n_restarts_optimizer=15,
alpha=1e-2)
# === 1. 交差検証(5-fold CV) ===
kfold = KFold(n_splits=5, shuffle=True, random_state=42)
cv_scores = cross_val_score(gp, X_full, y_full, cv=kfold,
scoring='neg_mean_squared_error')
cv_rmse = np.sqrt(-cv_scores)
print("=== 交差検証結果 ===")
print(f"5-fold CV RMSE: {cv_rmse.mean():.2f} ± {cv_rmse.std():.2f}%")
# === 2. モデル訓練と予測 ===
gp.fit(X_full, y_full)
y_pred, sigma = gp.predict(X_full, return_std=True)
# === 3. 性能指標 ===
rmse = np.sqrt(mean_squared_error(y_full, y_pred))
r2 = r2_score(y_full, y_pred)
print(f"\n=== 訓練データ性能 ===")
print(f"RMSE: {rmse:.2f}%")
print(f"R²スコア: {r2:.3f}")
# === 4. 残差分析 ===
residuals = y_full - y_pred
standardized_residuals = residuals / sigma
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# (a) 予測 vs 実測
axes[0, 0].scatter(y_full, y_pred, alpha=0.6, s=80)
axes[0, 0].plot([y_full.min(), y_full.max()],
[y_full.min(), y_full.max()],
'r--', linewidth=2, label='理想直線')
axes[0, 0].set_xlabel('実測値 [%]')
axes[0, 0].set_ylabel('予測値 [%]')
axes[0, 0].set_title(f'予測精度 (R²={r2:.3f})')
axes[0, 0].legend()
axes[0, 0].grid(alpha=0.3)
# (b) 残差プロット
axes[0, 1].scatter(y_pred, residuals, alpha=0.6, s=80)
axes[0, 1].axhline(0, color='r', linestyle='--', linewidth=2)
axes[0, 1].axhline(1.96*residuals.std(), color='orange',
linestyle=':', label='±1.96σ')
axes[0, 1].axhline(-1.96*residuals.std(), color='orange', linestyle=':')
axes[0, 1].set_xlabel('予測値 [%]')
axes[0, 1].set_ylabel('残差 [%]')
axes[0, 1].set_title('残差プロット(等分散性チェック)')
axes[0, 1].legend()
axes[0, 1].grid(alpha=0.3)
# (c) 標準化残差のヒストグラム
axes[1, 0].hist(standardized_residuals, bins=15, edgecolor='black', alpha=0.7)
axes[1, 0].axvline(0, color='r', linestyle='--', linewidth=2)
x_norm = np.linspace(-3, 3, 100)
y_norm = 30 / (2*np.pi)**0.5 * np.exp(-x_norm**2 / 2) # 正規分布
axes[1, 0].plot(x_norm, y_norm, 'r-', linewidth=2, label='標準正規分布')
axes[1, 0].set_xlabel('標準化残差')
axes[1, 0].set_ylabel('頻度')
axes[1, 0].set_title('残差の正規性チェック')
axes[1, 0].legend()
axes[1, 0].grid(alpha=0.3)
# (d) 予測不確実性の校正
X_test_dense = np.linspace(50, 150, 100).reshape(-1, 1)
y_pred_dense, sigma_dense = gp.predict(X_test_dense, return_std=True)
axes[1, 1].plot(X_test_dense, y_pred_dense, 'b-', linewidth=2, label='GP予測')
axes[1, 1].fill_between(X_test_dense.ravel(),
y_pred_dense - 1.96*sigma_dense,
y_pred_dense + 1.96*sigma_dense,
alpha=0.3, label='95%信頼区間')
axes[1, 1].scatter(X_full, y_full, c='red', s=60,
edgecolors='k', label='実験データ')
axes[1, 1].set_xlabel('温度 [°C]')
axes[1, 1].set_ylabel('活性 [%]')
axes[1, 1].set_title('不確実性の校正')
axes[1, 1].legend()
axes[1, 1].grid(alpha=0.3)
plt.tight_layout()
plt.show()
# === 5. カバレッジ確率(校正チェック) ===
# 95%信頼区間に実測値の何%が入るか
in_interval = np.abs(residuals) <= 1.96 * sigma
coverage = np.mean(in_interval) * 100
print(f"\n=== 不確実性の校正 ===")
print(f"95%信頼区間のカバレッジ: {coverage:.1f}%")
print(f"期待値: 95.0%")
print(f"判定: {'✅ 良好' if 90 <= coverage <= 100 else '⚠️ 要調整'}")
# 期待される出力:
# === 交差検証結果 ===
# 5-fold CV RMSE: 3.42 ± 0.87%
#
# === 訓練データ性能 ===
# RMSE: 2.98%
# R²スコア: 0.923
#
# === 不確実性の校正 ===
# 95%信頼区間のカバレッジ: 93.3%
# 期待値: 95.0%
# 判定: ✅ 良好
- 交差検証: 汎化性能の確認(CV RMSEが訓練RMSEと近いか)
- 残差の等分散性: 残差プロットが水平のランダム散布か
- 残差の正規性: ヒストグラムが正規分布に近いか
- 不確実性の校正: 95%区間のカバレッジが90-100%か
本章のまとめ
重要な学習ポイント
| トピック | キーポイント | 実践での使い方 |
|---|---|---|
| ガウス過程の定義 | 平均関数と共分散関数で完全に定義 | scikit-learnでRBFカーネルから開始 |
| カーネル選択 | RBF(スムーズ)、Matérn(中間)、RQ(多スケール) | 対数周辺尤度で性能比較 |
| ハイパーパラメータ最適化 | MLEで自動最適化(L-BFGS-B法) | n_restarts_optimizer=10-15推奨 |
| 不確実性定量化 | 95%信頼区間 = μ ± 1.96σ | 未探索領域で不確実性大→探索促進 |
| モデル検証 | 交差検証、残差分析、カバレッジチェック | R²>0.9、カバレッジ90-100%が目標 |
実装時のベストプラクティス
- データ正規化: 入力を[0, 1]または標準化(ℓの最適化を安定化)
- カーネル選択: 最初はRBFで試し、Matérnで頑健性向上
- ノイズ推定:
alpha=1e-2で観測誤差を考慮 - 多点スタート:
n_restarts_optimizer=10-15で局所最適を回避 - 数値安定性: コレスキー分解を使用(行列逆行列の直接計算を避ける)
学習目標の確認
この章を完了すると、以下ができるようになります:
基本理解
- ✅ ガウス過程が平均関数とカーネルで定義されることを説明できる
- ✅ 予測分布から平均と不確実性を計算できる
- ✅ RBF、Matérn、Rational Quadraticカーネルの特性を説明できる
実践スキル
- ✅ scikit-learnでガウス過程モデルを実装できる
- ✅ ハイパーパラメータをMLEで最適化できる
- ✅ 予測不確実性を可視化できる(信頼区間プロット)
- ✅ 交差検証と残差分析でモデルを検証できる
応用力
- ✅ 化学プロセスデータに適切なカーネルを選択できる
- ✅ 多次元入力(温度・圧力・濃度)のGPモデルを構築できる
- ✅ モデルの信頼性を定量的に評価できる
演習問題
Easy(基礎確認)
Q1: ガウス過程の予測分布 f(x*) ~ N(μ, σ²) において、95%信頼区間を計算する式はどれですか?
解答を見る
正解: [μ - 1.96σ, μ + 1.96σ]
解説: 正規分布の95%区間は平均 ± 1.96×標準偏差です。99%区間は ± 2.58σ、68%区間は ± 1σです。
Q2: RBFカーネルの長さスケール ℓ を大きくすると、予測曲線はどう変化しますか?
解答を見る
正解: よりスムーズ(滑らか)になります
解説: ℓは相関距離を表し、大きいほど遠くの点同士も強く相関します。ℓ→∞で定数関数に収束します。
Medium(応用)
Q3: 5つの実験データから対数周辺尤度が -10.5 のGPモデルを構築しました。これは良いモデルですか?
解答を見る
回答: 相対比較が必要です。
解説: 対数周辺尤度は絶対値ではなく、異なるカーネル間の相対的な比較に使います。例えば、RBFで-10.5、Matérnで-12.3なら、RBFが優れています。データ数nが増えると尤度の絶対値も増加するため、異なるデータセット間では比較できません。
Q4: 交差検証RMSEが3.5%、訓練データRMSEが1.8%でした。このモデルに問題はありますか?
解答を見る
回答: 過学習の兆候があります。
解説: CVスコアが訓練スコアより大幅に悪い場合、過学習の可能性があります。対策:(1) ノイズ項 alpha を増やす、(2) より単純なカーネルを使う、(3) データ数を増やす。
Hard(発展)
Q5: 温度・圧力・濃度の3次元入力でGPモデルを構築する際、各次元で異なる長さスケールを使うべき理由を説明してください。
解答を見る
回答: Automatic Relevance Determination (ARD) により、各入力次元の重要度を自動学習できます。
実装例:
kernel = C(1.0) * RBF(length_scale=[10.0, 1.0, 5.0],
length_scale_bounds=(1e-2, 1e3))
# [温度10°C, 圧力1.0MPa, 濃度5%の初期長さスケール]
解釈: 最適化後、圧力の長さスケールが0.5と小さければ、圧力変化に収率が敏感であることを示します。温度が50と大きければ、温度依存性は低いです。
次のステップ
ガウス過程モデルで不確実性を定量化できるようになりました。次の第3章では、この不確実性を活用して次の実験点を賢く選ぶ獲得関数を学びます。
第3章で学ぶこと:
- Expected Improvement (EI): 最も有望な点を選ぶ
- Upper Confidence Bound (UCB): 探索-活用のバランス調整
- Probability of Improvement (PI): シンプルな改善確率
- バッチベイズ最適化: 並列実験の設計
参考文献
- Rasmussen, C. E., & Williams, C. K. I. (2006). Gaussian Processes for Machine Learning. MIT Press.
- Shahriari, B., et al. (2016). "Taking the Human Out of the Loop: A Review of Bayesian Optimization." Proceedings of the IEEE, 104(1), 148-175.
- Pedregosa, F., et al. (2011). "Scikit-learn: Machine Learning in Python." Journal of Machine Learning Research, 12, 2825-2830.
- Matérn, B. (1960). Spatial Variation. Springer.