本篇文章Master Hyperband — An Efficient Hyperparameter Tuning Method in Machine Learning深入探討了Hyperband這一高效的超參數調優方法。文章的技術亮點在于其結合了多臂老虎機策略和逐次減半算法,能夠在大搜索空間中快速剔除表現不佳的配置,從而節省計算資源。
文章目錄
- 1 介紹
- 2 什么是 Hyperband
- 2.1 Hyperband 的工作原理
- 2.1.1 第1步 定義預算和淘汰因子
- 2.1.2 第2步 遍歷各個 Bracket
- 2.1.3 第3步 運行 Successive Halving Algorithm (SHA)
- 2.1.4 第4步 選出最佳配置
- 2.1.5 例子演示 — 支持向量分類器(SVC)
- 3 模擬實驗
- 3.1 創建訓練和測試數據集
- 3.2 模型定義 — LSTM網絡
- 3.3 搜索空間
- 3.4 定義驗證函數
- 3.5 運行 Hyperband
- 3.6 結果
- 3.7 與其他調優方法的對比
- 3.8 Bayesian Optimization
- 3.9 Random Search
- 3.10 Genetic Algorithms (GA)
- 4 考慮與總結
- 4.1 提升 Hyperband 性能的建議
1 介紹
Hyperband 是一種強大的機器學習超參數調優方法,利用**successive halving(逐步淘汰)**策略來高效分配資源。
然而,執行 Hyperband 需要對其核心機制和參數進行細致考量,以最大化其優勢。
本文將通過調優用于股票價格預測的 LSTM 網絡,深入探討 Hyperband 的核心機制,并與其他主要調優方法進行性能對比:
- Bayesian Optimization(貝葉斯優化),
- Random Search(隨機搜索),以及
- Genetic Algorithms(遺傳算法)。
2 什么是 Hyperband
Hyperband 是一種高效的超參數調優算法,結合了multi-armed bandit(多臂老虎機)策略和successive halving algorithm(SHA,逐步淘汰算法)。
多臂老虎機問題是概率論中的一個問題,展示了以下基本權衡:
- 探索(Exploration): 探索廣泛的超參數配置,
- 利用(Exploitation): 利用最有前景的配置。
SHA 是一種資源分配策略,它為隨機采樣的配置分配固定的預算(如訓練的epoch數)。
在每個階段,SHA 評估超參數配置的表現,剔除表現最差的配置,同時將剩余預算重新分配給幸存的配置,稱為survivors(幸存者)。
Hyperband 更進一步,通過使用不同的初始預算運行 SHA,以平衡探索和利用。
下面的圖表分類展示了主要的超參數調優方法:
在眾多調優方法中,Hyperband 在速度和效率上具有明顯優勢,尤其適合處理大規模搜索空間。
2.1 Hyperband 的工作原理
下圖展示了 Hyperband 如何將更多預算分配給最終勝出者(配置#4),盡管在 Bracket 1 中的初始預算分配是隨機的:
Hyperband 以在Bracket 1中創建大量超參數配置并分配較小預算開始。
隨后,隨著進入后續的各個 Bracket,配置數量逐步減少,而幸存配置獲得更多預算。
在圖中的Bracket 2,Hyperband 將更多預算分配給來自 Bracket 1 的幸存者(配置#1和#4)。
最終,在Bracket 3中,將全部預算分配給最終勝出者配置#4。
這種方法有效地探索了廣泛的配置,同時快速剔除表現差的配置,實現了探索與利用的平衡。
這一過程可分為四步:
2.1.1 第1步 定義預算和淘汰因子
首先定義:
- 最大資源預算(R): 單個模型可訓練的最大epoch數,
- 淘汰因子(η): 預設的因子,用于決定淘汰的激進程度。
常見的淘汰因子值有2、3或4。
每個階段,超參數配置數量除以η,幸存配置的預算乘以η。
2.1.2 第2步 遍歷各個 Bracket
算法運行一系列 Bracket,每個 Bracket 是使用不同起始預算的完整 Successive Halving 算法(SHA)運行。
Bracket 數量由最大Bracket索引 smaxs_{max}smax? 決定:
其中:
- ηηη:淘汰因子,
- RRR:最大資源預算。
算法從 smaxs_{max}smax? 迭代到0。
2.1.3 第3步 運行 Successive Halving Algorithm (SHA)
對于每個 Bracket sss,Hyperband 確定開始時的超參數配置數量 nsn_sns?。
Hyperband 有意在預算小的 Bracket 中設置較多配置,預算大的 Bracket 中配置較少。
數學表達式如下:
其中:
- nsn_sns?:當前Bracket的配置數量,
- RRR:最大資源預算,
- ηηη:淘汰因子,
- smaxs_{max}smax?:最大Bracket數,
- sss:當前Bracket索引,范圍從 smaxs_{max}smax? 到 0。
Hyperband 還確定每個Bracket的初始預算 rsr_srs?:
其中:
- rsr_srs?:當前Bracket的初始預算,
- RRR,ηηη,smaxs_{max}smax? 同上。
Hyperband 隨機采樣 nsn_sns? 個超參數配置,每個訓練 rsr_srs? 個 epoch。
然后根據表現選出前 ns/ηn_s / ηns?/η 個幸存者。
這些幸存者再接受更大預算訓練,總訓練epoch達到 rs?ηr_s \cdot ηrs??η。
此過程不斷進行,配置數量逐步減半,預算逐步增加,直到只剩一個配置或達到最大預算。
2.1.4 第4步 選出最佳配置
所有 Bracket 運行完畢后,選出表現最優的超參數配置作為最終結果。
Hyperband 的效率來自于它能快速剔除表現不佳的配置,釋放資源訓練更有潛力的配置更長時間。
2.1.5 例子演示 — 支持向量分類器(SVC)
下面通過調優支持向量分類器(SVC)的正則化參數 C
和核函數系數 gamma
,演示 Hyperband 的工作過程。
模型: 支持向量分類器(SVC)
搜索空間:
C
: [0.1, 1, 10, 100]gamma
: [‘scale’, ‘auto’, 0.1, 1, 10]
第1步 定義預算和淘汰因子
設最大預算 R=81R=81R=81,淘汰因子 η=3η=3η=3。
第2步 遍歷 Bracket
計算最大Bracket索引:
即 Hyperband 將運行 s=4,3,2,1,0s=4,3,2,1,0s=4,3,2,1,0 五個 Bracket。
每個 Bracket 的配置數量 nsn_sns? 和初始預算 rsr_srs? 如下:
- Bracket 1 (s=4s=4s=4): ns=1n_s=1ns?=1, rs=9r_s=9rs?=9
- Bracket 2 (s=3s=3s=3): ns=3n_s=3ns?=3, rs=3r_s=3rs?=3
- Bracket 3 (s=2s=2s=2): ns=9n_s=9ns?=9, rs=1r_s=1rs?=1
- Bracket 4 (s=1s=1s=1): ns=27n_s=27ns?=27, rs=1/3r_s=1/3rs?=1/3
- Bracket 5 (s=0s=0s=0): ns=81n_s=81ns?=81, rs=1/9r_s=1/9rs?=1/9
預算 R=81R=81R=81 在這些 Bracket 中分配,以高效尋找最佳配置。
第3步 運行 SHA
以 Bracket 3 (s=2s=2s=2) 為例:
- 初始運行:
- 隨機采樣9個配置,
- 每個訓練1個 epoch,
- 記錄表現,
- 保留表現最好的3個(9/3=39/3=39/3=3),其余剔除。
- 第二輪運行:
- 這3個幸存者訓練3個 epoch(1×3=31 \times 3=31×3=3),
- 記錄表現,
- 保留表現最好的1個(3/3=13/3=13/3=1)。
- 最終運行:
- 剩下的單個幸存者訓練9個 epoch(3×3=93 \times 3=93×3=9),
- 記錄表現。
第4步 選出最佳
Hyperband 對所有 Bracket 執行上述步驟,最終選出表現最優的配置。
3 模擬實驗
接下來,演示 Hyperband 在更復雜模型——LSTM網絡上的應用。
模型用于預測選定股票代碼 NVDA
的收盤價。
3.1 創建訓練和測試數據集
通過 Alpha Vantage API 獲取歷史日線股價數據。
將數據加載到 Pandas DataFrame 并預處理,劃分為訓練集和測試集。
訓練集用于模型訓練和驗證,測試集保持獨立以防止數據泄露。
import torch
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformertarget_col = 'close'
y = df.copy()[target_col].shift(-1)
y = y.iloc[:-1] input_cols = [col for col in df.columns if col not in [target_col, 'dt']]
X = df.copy()[input_cols]
X = X.iloc[:-1] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=800, shuffle=False, random_state=42
)cat_cols = ['year', 'month', 'date']
num_cols = list(set(input_cols) - set(cat_cols))
preprocessor = ColumnTransformer( transformers=[ ('num', StandardScaler(), num_cols), ('cat', OneHotEncoder(handle_unknown='ignore'), cat_cols) ]
)
X_train = preprocessor.fit_transform(X_train)
X_test = preprocessor.transform(X_test)X_train = torch.from_numpy(X_train.toarray()).float()
y_train = torch.from_numpy(y_train.values).float().unsqueeze(1)
X_test = torch.from_numpy(X_test.toarray()).float()
y_test = torch.from_numpy(y_test.values).float().unsqueeze(1)
原始數據包含 NVDA
的 6,501 條歷史股價記錄:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6501 entries, 0 to 6500
Data columns (total 15 columns): 0 dt 6501 non-null datetime64[ns] 1 open 6501 non-null float32 2 high 6501 non-null float32 3 low 6501 non-null float32 4 close 6501 non-null float32 5 volume 6501 non-null int32 6 ave_open 6501 non-null float32 7 ave_high 6501 non-null float32 8 ave_low 6501 non-null float32 9 ave_close 6501 non-null float32 10 total_volume 6501 non-null int32 11 30_day_ma_close 6501 non-null float32 12 year 6501 non-null object 13 month 6501 non-null object 14 date 6501 non-null object
dtypes: datetime64[ns](1), float32(9), int32(2), object(3)
memory usage: 482.6+ KB
3.2 模型定義 — LSTM網絡
定義基于 PyTorch 的多對一架構的 LSTMModel
類。
import torch
import torch.nn as nnclass LSTMModel(nn.Module): def __init__(self, input_dim, hidden_dim, layer_dim, output_dim, dropout): super(LSTMModel, self).__init__() self.hidden_dim = hidden_dim self.layer_dim = layer_dim self.dropout = dropout self.lstm = nn.LSTM( input_dim, hidden_dim, layer_dim, batch_first=True, dropout=dropout ) self.fc = nn.Linear(hidden_dim, output_dim)def forward(self, x): h0 = torch.zeros(self.layer_dim, x.size(0), self.hidden_dim).to(x.device) c0 = torch.zeros(self.layer_dim, x.size(0), self.hidden_dim).to(x.device) o_t, _ = self.lstm(x, (h0.detach(), c0.detach())) o_final = self.fc(o_t[:, -1, :]) return o_final
3.3 搜索空間
Hyperband 在較大的搜索空間表現更佳。定義如下搜索空間:
import randomdef search_space(): return { 'lr': 10**random.uniform(-6, -1), 'hidden_dim': random.choice([16, 32, 64, 128, 256]), 'layer_dim': random.choice([1, 2, 3, 4, 5]), 'dropout': random.uniform(0.1, 0.6), 'batch_size': random.choice([16, 32, 64, 128, 256]) }
3.4 定義驗證函數
定義用于時間序列數據的 walk-forward validation(滾動前移驗證) 的 train_and_val_wfv
函數:
def train_and_val_wfv(hyperparams, budget, X, y, train_window, val_window): total_val_loss = 0 all_loss_histories = []num_folds = (X.size(0) - train_window - val_window) // val_window + 1for i in range(num_folds): train_start = i * val_window train_end = train_start + train_window val_start = train_end val_end = val_start + val_windowif val_end > X.size(0): breakX_train_fold = X[train_start:train_end] y_train_fold = y[train_start:train_end] X_val_fold = X[val_start:val_end] y_val_fold = y[val_start:val_end]fold_val_loss, fold_loss_history = train_and_val( hyperparams=hyperparams, budget=budget, X_train=X_train_fold, y_train=y_train_fold, X_val=X_val_fold, y_val=y_val_fold ) total_val_loss += fold_val_loss all_loss_histories.append(fold_loss_history)avg_val_loss = total_val_loss / num_folds return avg_val_loss, all_loss_histories
3.5 運行 Hyperband
定義 run_hyperband
函數,接受以下參數:
- 搜索空間函數
search_space_fn
, - 驗證函數
val_fn
, - 總預算
R
, - 淘汰因子
η
。
示例中,R=100R=100R=100, η=3η=3η=3,訓練和驗證窗口分別為3000和500。
from math import log, floordef run_hyperband(search_space_fn, val_fn, R, eta): s_max = int(log(R, eta))overall_best_config = None overall_best_loss = float('inf') all_loss_histories = []for s in range(s_max, -1, -1): n = int(R / eta**s) r = int(R / n) main_logger.info(f'... running bracket s={s}: {n} configurations, initial budget={r} ...')configs = [search_space_fn() for _ in range(n)]for i in range(s + 1): budget = r * (eta**i) main_logger.info(f'... training {len(configs)} configurations for budget {budget} epochs ...')evaluated_results = [] for config in configs: loss, loss_history = val_fn(config, budget) evaluated_results.append((config, loss, loss_history))all_loss_histories.append((evaluated_results, budget))evaluated_results.sort(key=lambda x: x[1])if evaluated_results and evaluated_results[0][1] < overall_best_loss: overall_best_loss = evaluated_results[0][1] overall_best_config = evaluated_results[0][0]num_to_keep = floor(len(configs) / eta) configs = [result[0] for result in evaluated_results[:num_to_keep]]if not configs: breakreturn overall_best_config, overall_best_loss, all_loss_histories, s_maxR = 100
eta = 3train_window = 3000
val_window = 500best_config, best_loss, all_loss_histories, s_max = run_hyperband( search_space_fn=search_space, val_fn=lambda h, b: train_and_val_wfv(h, b, X_train, y_train, train_window=train_window, val_window=val_window), R=R, eta=eta
)
3.6 結果
最佳超參數配置:
- ‘lr’: 0.0001614172022855225
- ‘hidden_dim’: 128
- ‘layer_dim’: 3
- ‘dropout’: 0.5825758700895215
- ‘batch_size’: 16
最佳驗證損失(均方誤差 MSE):
0.0519
損失歷史:
下圖中,實線表示訓練周期中平均驗證損失(MSE)的變化,垂直虛線表示 Hyperband 剪枝不佳模型的時刻:
提前停止(大多為紫色)的曲線代表表現不佳被剪枝的模型。
持續訓練到100個epoch(大多為青色和藍色)的曲線代表表現優異的配置,損失迅速下降并穩定在較低值,顯示出良好性能。
這種方式能高效快速地剔除表現差的配置,避免長時間訓練。
3.7 與其他調優方法的對比
為比較不同方法,進行了20次試驗:
- Bayesian Optimization(貝葉斯優化),
- Random Search(隨機搜索),
- Genetic Algorithms(遺傳算法),
使用相同的搜索空間、模型和訓練/驗證窗口。
3.8 Bayesian Optimization
貝葉斯優化使用概率模型(如高斯過程)擬合驗證誤差,選擇下一個最優配置進行評估。
最佳超參數配置:
- ‘lr’: 0.00016768631941614767
- ‘hidden_dim’: 256
- ‘layer_dim’: 3
- ‘dropout’: 0.3932769195043036
- ‘batch_size’: 64
最佳驗證損失(MSE):
0.0428
損失歷史:
3.9 Random Search
隨機搜索從搜索空間隨機采樣固定數量配置,不利用之前試驗結果。
最佳超參數配置:
- ‘lr’: 0.0004941205117774383
- ‘hidden_dim’: 128
- ‘layer_dim’: 2
- ‘dropout’: 0.3398469430820351
- ‘batch_size’: 64
最佳驗證損失(MSE):
0.03620
損失歷史:
3.10 Genetic Algorithms (GA)
遺傳算法受生物進化啟發,維護一組配置,通過變異和交叉生成新的潛在更優配置。
最佳超參數配置:
- ‘lr’: 0.006441170552290832
- ‘hidden_dim’: 128
- ‘layer_dim’: 3
- ‘dropout’: 0.2052570911345997
- ‘batch_size’: 128
最佳驗證損失(MSE):
0.1321
損失歷史:
完整源碼請見 我的 Github 倉庫。
4 考慮與總結
Random Search(0.0362)和 Bayesian Optimization(0.0428)在最終驗證損失上略優于 Hyperband(0.0519)。
這體現了效率與全局最優發現能力之間的權衡。
Hyperband 的效率來源于其在訓練早期快速剔除表現差的配置。
雖然節省了大量時間,但也存在誤刪“后期表現優異”配置的風險。
本案例中,Random Search 和 Bayesian Optimization 更成功:
- Random Search 允許高性能配置獲得完整訓練預算,
- Bayesian Optimization 通過智能采樣更有效地尋找最佳超參數。
4.1 提升 Hyperband 性能的建議
推薦調整 Hyperband 參數并結合其他方法:
- 調整關鍵參數
- 設定較大 RRR(總預算)允許“后期表現優異”模型有機會充分訓練,減少誤刪,
- 設定較小 ηηη(淘汰因子)使淘汰過程更溫和,更多配置進入下一輪。
- 結合貝葉斯優化
BOHB(Bayesian Optimization and HyperBand) 是一種混合方法,使用 Hyperband 的逐步淘汰框架,但用貝葉斯優化替代隨機采樣。
BOHB 利用貝葉斯優化選擇最有潛力的候選配置進入 Hyperband Bracket。
該方法兼具 Hyperband 的快速性和貝葉斯優化的高性能。