聊天視窗

數據驅動的投資策略:從數據清洗到模型部署 - 第 4 章

第4章 模型訓練與驗證:從演算法選擇到風險控制

發布於 2026-03-05 05:41

# 第4章 模型訓練與驗證:從演算法選擇到風險控制 在前一章中,我們已經建立了一個結構化且可解釋的特徵集。接下來的挑戰是將這些特徵轉化為能夠在實際交易中獲利的預測模型。這一章將帶領讀者從演算法選擇、時序交叉驗證、參數調整,到最終的模型評估與風險控制。全章以實務案例為主軸,搭配程式碼範例,確保理論與實務的橋樑無縫接軌。 --- ## 4.1 演算法選擇與風險偏好 金融市場的時序性、非線性與噪聲特徵,使得演算法的選擇成為關鍵。以下為常見的幾類演算法與其適用情境: | 類型 | 優點 | 局限 | 典型範例 | |------|------|------|----------| | 線性模型 (LR, Ridge, Lasso) | 可解釋、訓練速度快 | 無法捕捉複雜關係 | 基礎回歸、風險因子回歸 | | 決策樹族 (Decision Tree, Random Forest, XGBoost) | 非線性、對缺失值不敏感 | 易過擬合、可解釋性下降 | 量化因子組合、風險敞口預測 | | 神經網路 (MLP, LSTM) | 強大非線性擬合能力 | 需要大量資料、調參繁瑣 | 高頻交易、序列預測 | | 時序專用模型 (ARIMA, Prophet, SARIMAX) | 捕捉季節性、長期趨勢 | 不適用於高頻 | 長期趨勢分析、宏觀因子預測 | **風險偏好對演算法的影響**: - 風險承受度低者:傾向於可解釋性高、調參簡單的模型,並且在模型結構上加入**風險控制**的機制(如罰款、限制單筆敞口)。 - 風險承受度高者:可嘗試更複雜的模型,但必須同時構建**風險評估指標**(如VaR、CVaR)作為後端檢核。 > **實務提示**:先以線性模型做基線,確認預測能力;再逐步加入決策樹族或XGBoost,以評估是否有顯著提升。 --- ## 4.2 時序交叉驗證 金融資料本質上是**時間序列**,隨機打亂資料順序會產生**資訊洩漏**。最合適的驗證方法是 **TimeSeriesSplit** 或 **Rolling Window**。以下是兩種常用策略: ### 4.2.1 時間分割 (TimeSeriesSplit) python from sklearn.model_selection import TimeSeriesSplit tscv = TimeSeriesSplit(n_splits=5) for fold, (train_idx, val_idx) in enumerate(tscv.split(X)): X_train, X_val = X[train_idx], X[val_idx] y_train, y_val = y[train_idx], y[val_idx] # 訓練模型 model.fit(X_train, y_train) # 驗證 preds = model.predict(X_val) # 計算指標... ### 4.2.2 滾動窗口 (Rolling Forecast Origin) python import pandas as pd train_window = 252 # 1 年 step_size = 21 # 1 個月 n_samples = len(df) for i in range(train_window, n_samples, step_size): train_idx = slice(i - train_window, i) val_idx = slice(i, i + step_size) X_train, X_val = X[train_idx], X[val_idx] y_train, y_val = y[train_idx], y[val_idx] model.fit(X_train, y_train) # ... 評估 ... > **注意**:在每個fold中,**所有統計量(均值、標準差、滑動窗口參數)必須僅基於訓練集**,以避免洩漏。 --- ## 4.3 參數調整與正則化 參數調整是提升模型表現的關鍵,但同時容易導致過擬合。以下是幾種常見技巧: | 技巧 | 說明 | |------|------| | 網格搜尋 (GridSearchCV) | 逐一嘗試所有參數組合,計算交叉驗證分數 | | 隨機搜尋 (RandomizedSearchCV) | 隨機抽樣參數空間,節省時間 | | Bayesian Optimization (Optuna, Hyperopt) | 以貝葉斯優化方式搜尋,收斂更快 | | 正則化 (L1/L2, Tree Depth, Early Stopping) | 控制模型複雜度,減少過擬合 | ### XGBoost 參數範例 python import xgboost as xgb params = { 'n_estimators': 500, 'max_depth': 6, 'learning_rate': 0.05, 'subsample': 0.8, 'colsample_bytree': 0.8, 'reg_alpha': 0.1, # L1 'reg_lambda': 1.0, # L2 'early_stopping_rounds': 50, 'eval_metric': 'rmse' } model = xgb.XGBRegressor(**params) model.fit(X_train, y_train, eval_set=[(X_val, y_val)], verbose=False) > **實務建議**:對於高頻交易模型,**early stopping** 是防止過擬合最有效的方式;對於長期投資模型,**L1/L2 正則化** 兼顧可解釋性與泛化。 --- ## 4.4 評估指標與模型比較 選擇合適的評估指標能確保模型在實際交易中表現穩定。常用指標如下: | 指標 | 適用情境 | |------|----------| | RMSE / MAE | 短期預測、波動率模型 | | R² | 整體解釋力、基線比較 | | Sharpe Ratio / Sortino Ratio | 風險調整後報酬 | | Max Drawdown | 最大回撤控制 | | Calmar Ratio | 長期持倉風險 | > **提示**:在實際投資策略中,**風險調整後報酬(Sharpe、Sortino)**往往比純粹的預測準確率更具價值。可以在模型評估階段直接計算這些指標,並將其作為選模型的準則。 --- ## 4.5 過擬合診斷與防禦 過擬合是金融模型最常見的陷阱,以下是診斷與防禦方法: 1. **學習曲線**:繪製訓練集與驗證集的損失曲線,觀察兩者差距。 2. **交叉驗證**:使用多重時序交叉驗證,確保模型在不同時間窗口下穩定。 3. **特徵重要性審查**:若某些特徵重要性過高且沒有物理意義,可能是模型在抓取噪聲。 4. **模型簡化**:減少樹深、限制L1/L2,或使用更簡單的基線模型做對照。 5. **正則化與 Dropout**:對於深度模型,可加入 dropout 或 L2 正則化。 > **案例**:一個 XGBoost 模型在歷史資料上達到 RMSE 0.3,但在 2023 年 3 月的驗證集上跌至 0.6。學習曲線顯示訓練損失持續下降,而驗證損失已開始上升,說明已發生過擬合。調整 `max_depth` 從 6 降至 4,並加入 `early_stopping_rounds`,最終將驗證 RMSE 降至 0.42。 --- ## 4.6 版本管理與再現性 在金融領域,模型的再現性與可追蹤性至關重要。建議採用以下工具: - **MLflow**:追蹤實驗、參數、指標與模型。示例: python import mlflow mlflow.set_experiment("stock_prediction") with mlflow.start_run(): mlflow.log_params(params) mlflow.log_metrics({"rmse": rmse, "sharpe": sharpe}) mlflow.sklearn.log_model(model, "model") - **DVC** 或 **Git LFS**:管理大型資料集、特徵工程腳本與模型檔。 - **Docker**:將模型封裝成容器,確保部署環境一致。 > **提醒**:在實戰中,請將**所有數據預處理、特徵工程、模型訓練**流程都納入 MLflow 的追蹤範圍,避免「黑盒」操作。 --- ## 4.7 案例:使用 XGBoost 預測日內波動 以下為一完整範例,從資料讀取、特徵工程、時間交叉驗證、模型訓練到評估。程式碼以 Pandas + XGBoost 為主。 python import pandas as pd import numpy as np from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import mean_squared_error import xgboost as xgb import mlflow import mlflow.sklearn # 1. 讀取資料 price_df = pd.read_csv("AAPL_daily.csv", parse_dates=["Date"]).sort_values("Date") # 2. 基礎特徵 price_df['Return'] = price_df['AdjClose'].pct_change() price_df['SMA_20'] = price_df['AdjClose'].rolling(window=20).mean() price_df['RSI_14'] = compute_rsi(price_df['AdjClose'], 14) # 自訂函式 price_df['Vol_20'] = price_df['Volume'].rolling(window=20).std() # 3. 資料清洗 price_df.dropna(inplace=True) X = price_df[['SMA_20', 'RSI_14', 'Vol_20']].values y = price_df['Return'].shift(-1).dropna().values # 以次日回報作為目標 # 4. 時間交叉驗證 tscv = TimeSeriesSplit(n_splits=4) rmse_list = [] sharpe_list = [] with mlflow.start_run(): mlflow.log_param("model", "XGBoost") for fold, (train_idx, val_idx) in enumerate(tscv.split(X)): X_train, X_val = X[train_idx], X[val_idx] y_train, y_val = y[train_idx], y[val_idx] model = xgb.XGBRegressor( n_estimators=300, max_depth=5, learning_rate=0.05, subsample=0.8, colsample_bytree=0.8, reg_alpha=0.1, reg_lambda=1.0, objective='reg:squarederror', eval_metric='rmse' ) model.fit(X_train, y_train, eval_set=[(X_val, y_val)], early_stopping_rounds=20, verbose=False) preds = model.predict(X_val) rmse = np.sqrt(mean_squared_error(y_val, preds)) sharpe = np.mean(preds) / np.std(preds) rmse_list.append(rmse) sharpe_list.append(sharpe) mlflow.log_metric(f"rmse_fold_{fold}", rmse) mlflow.log_metric(f"sharpe_fold_{fold}", sharpe) mlflow.sklearn.log_model(model, "xgb_model") mlflow.log_metric("avg_rmse", np.mean(rmse_list)) mlflow.log_metric("avg_sharpe", np.mean(sharpe_list)) > **說明**: > - `compute_rsi` 為自訂計算 RSI 的函式。 > - `shift(-1)` 代表以 **次日** 回報作為目標,符合預測行為。 > - `eval_metric='rmse'` 讓 XGBoost 在訓練時即以 RMSE 為目標,便於早停。 > - `mlflow` 的使用確保每一次實驗都被記錄,可隨時回溯參數與指標。 --- ## 4.8 小結 本章闡述了從演算法選擇到時序交叉驗證、參數調整、評估指標、過擬合防禦以及實務部署的完整流程。關鍵點在於: 1. **避免資訊洩漏**:所有統計量必須基於訓練集。 2. **時間序列驗證**:使用 TimeSeriesSplit 或 Rolling Window,確保模型在不同時間窗口下泛化。 3. **正則化與早停**:兼顧預測準確與風險控制。 4. **風險調整後報酬**:評估指標要與投資策略風險相匹配。 5. **再現性與可追蹤性**:MLflow/DVC 等工具是量化交易項目必備。 接下來的第5章,我們將把訓練好的模型落地,進入「模型部署與風險管理」的實戰環節,並探討如何在真實交易中實現自動化執行與風險控制。