packages feed

hanalyze-0.1.0.0: demo/regression/RobustGPDemo.hs

{-# LANGUAGE OverloadedStrings #-}
-- | ロバスト GP のデモ。
--
-- 真の関数 sin(x) + 0.3 cos(3x) からデータを生成し、3 点を **大きな外れ値**
-- (+5σ レベル) に置き換える。次の 3 モデルで RMSE を比較:
--
-- 1. 通常 GP (Gaussian 観測)
-- 2. ロバスト GP w/ Cauchy(γ=0.5) — 重い裾、外れ値に強い
-- 3. ロバスト GP w/ StudentT(ν=4, σ=0.5) — Cauchy より軽い裾
module Main where

import Hanalyze.Model.GP        (Kernel (..), GPParams (..), GPModel (..), fitGP, gpMean)
import Hanalyze.Model.GPRobust  (RobustLikelihood (..), RobustGPFit (..),
                        fitGPRobust, predictGPRobust)
import Text.Printf     (printf)

trueF :: Double -> Double
trueF x = sin x + 0.3 * cos (3 * x)

pseudoNoise :: Int -> Double -> Double
pseudoNoise seed x = 0.1 * sin (fromIntegral seed * 2.3998 + x * 17.3)

main :: IO ()
main = do
  putStrLn "=================================="
  putStrLn " Robust GP Demo (StudentT / Cauchy)"
  putStrLn "=================================="
  putStrLn ""

  -- 訓練データ: 50 点
  let n      = 50
      trainX = [ fromIntegral i * (2 * pi) / fromIntegral (n - 1)
               | i <- [0 .. n - 1 :: Int] ]
      cleanY = zipWith (\i x -> trueF x + pseudoNoise i x)
                 [0 :: Int ..] trainX
      -- 3 点を外れ値に置換 (index 10, 25, 40)
      trainY = [ if i `elem` [10, 25, 40]
                   then y + 4.0          -- +4σ レベルの外れ値
                   else y
               | (i, y) <- zip [0 :: Int ..] cleanY ]

      -- テスト点
      m     = 100
      testX = [ fromIntegral i * (2 * pi) / fromIntegral (m - 1)
              | i <- [0 .. m - 1 :: Int] ]
      testY = map trueF testX

      -- ハイパラ (ノイズ含めて固定)
      hp = GPParams { gpLengthScale  = 0.6
                    , gpSignalVar    = 1.0
                    , gpNoiseVar     = 0.05
                    , gpPeriod       = 1.0
                    , gpLengthScales = Nothing
                    }

  printf "Training: %d points (3 outliers at index 10, 25, 40 with +4 offset)\n" n
  printf "Test:     %d points (clean true f)\n" m
  printf "Hyperparams (fixed): l=%.2f sigma_f^2=%.2f noise=%.4f\n"
         (gpLengthScale hp) (gpSignalVar hp) (gpNoiseVar hp)
  putStrLn ""

  -- 1. 通常 GP (Gaussian)
  putStrLn "--- 1. Gaussian GP (Hanalyze.Model.GP) ---"
  let gpRes = fitGP (GPModel RBF hp) trainX trainY testX
      gaussRMSE = rmse testY (gpMean gpRes)
  printf "  RMSE (vs true f): %.4f\n" gaussRMSE
  putStrLn ""

  -- 2. Robust GP w/ Cauchy
  putStrLn "--- 2. Robust GP w/ Cauchy(gamma=0.5) ---"
  let cauchyFit  = fitGPRobust RBF hp (RCauchy 0.5) trainX trainY
      cauchyPred = predictGPRobust cauchyFit testX
      cauchyRMSE = rmse testY (map fst cauchyPred)
  printf "  IRLS converged in %d iterations\n" (rgpIters cauchyFit)
  printf "  RMSE (vs true f): %.4f\n" cauchyRMSE
  printf "  Improvement over Gaussian: %.1f%%\n"
         (100 * (gaussRMSE - cauchyRMSE) / gaussRMSE)
  putStrLn ""

  -- 3. Robust GP w/ StudentT
  putStrLn "--- 3. Robust GP w/ StudentT(nu=4, sigma=0.5) ---"
  let stFit  = fitGPRobust RBF hp (RStudentT 4 0.5) trainX trainY
      stPred = predictGPRobust stFit testX
      stRMSE = rmse testY (map fst stPred)
  printf "  IRLS converged in %d iterations\n" (rgpIters stFit)
  printf "  RMSE (vs true f): %.4f\n" stRMSE
  printf "  Improvement over Gaussian: %.1f%%\n"
         (100 * (gaussRMSE - stRMSE) / gaussRMSE)
  putStrLn ""

  putStrLn "Done."
  putStrLn ""
  putStrLn "Cauchy is most robust (heaviest tails, lowest RMSE)."
  putStrLn "StudentT(nu=4) is intermediate."
  putStrLn "Gaussian is most distorted by the 3 outliers."

rmse :: [Double] -> [Double] -> Double
rmse a b =
  let n = length a
      sse = sum [ (x - y) ^ (2 :: Int) | (x, y) <- zip a b ]
  in sqrt (sse / fromIntegral n)