在我們深入研究該方法之前,我們將首先討論一個基本的代碼結構,它使我們能夠有效地實現(xiàn)各種 HPO 算法。一般來說,這里考慮的所有 HPO 算法都需要實現(xiàn)兩個決策原語,即搜索和調度。首先,他們需要對新的超參數(shù)配置進行采樣,這通常涉及對配置空間的某種搜索。其次,對于每個配置,HPO 算法需要安排其評估并決定為其分配多少資源。一旦我們開始評估配置,我們就會將其稱為試用。我們將這些決定映射到兩個類,HPOSearcher
和 HPOScheduler
。除此之外,我們還提供HPOTuner
執(zhí)行優(yōu)化過程的類。
這種調度器和搜索器的概念也在流行的 HPO 庫中實現(xiàn),例如 Syne Tune (Salinas等人,2022 年)、Ray Tune (Liaw等人,2018 年)或 Optuna (Akiba等人,2019 年)。
import time
from scipy import stats
from d2l import torch as d2l
19.2.1。搜尋器
下面我們定義一個搜索器的基類,通過函數(shù)提供一個新的候選配置sample_configuration
。實現(xiàn)此功能的一種簡單方法是隨機對配置進行統(tǒng)一采樣,就像我們在 第 19.1 節(jié)中對隨機搜索所做的那樣。更復雜的算法,例如貝葉斯優(yōu)化,將根據先前試驗的表現(xiàn)做出這些決定。因此,隨著時間的推移,這些算法能夠對更有希望的候選人進行抽樣。我們添加該update
功能是為了更新以前試驗的歷史,然后可以利用它來改進我們的抽樣分布。
以下代碼顯示了如何在此 API 中實現(xiàn)我們上一節(jié)中的隨機搜索優(yōu)化器。作為一個輕微的擴展,我們允許用戶通過 指定要評估的第一個配置 initial_config
,而隨后的配置是隨機抽取的。
class RandomSearcher(HPOSearcher): #@save
def __init__(self, config_space: dict, initial_config=None):
self.save_hyperparameters()
def sample_configuration(self) -> dict:
if self.initial_config is not None:
result = self.initial_config
self.initial_config = None
else:
result = {
name: domain.rvs()
for name, domain in self.config_space.items()
}
return result
19.2.2。調度程序
除了新試驗的采樣配置外,我們還需要決定何時進行試驗以及進行多長時間。實際上,所有這些決定都是由 完成的HPOScheduler
,它將新配置的選擇委托給HPOSearcher
. suggest
只要某些訓練資源可用,就會調用該方法。除了調用sample_configuration
搜索器之外,它還可以決定諸如max_epochs
(即訓練模型的時間)之類的參數(shù)。update
每當試驗返回新觀察時調用該方法。
要實現(xiàn)隨機搜索以及其他 HPO 算法,我們只需要一個基本的調度程序,它可以在每次新資源可用時調度新的配置。
class BasicScheduler(HPOScheduler): #@save
def __init__(self, searcher: HPOSearcher):
self.save_hyperparameters()
def suggest(self) -> dict:
return self.searcher.sample_configuration()
def update(self, config: dict, error: float, info=None):
self.searcher.update(config, error, additional_info=info)
19.2.3。調諧器
最后,我們需要一個組件來運行調度器/搜索器并對結果進行一些簿記。下面的代碼實現(xiàn)了 HPO 試驗的順序執(zhí)行,在下一個訓練作業(yè)之后評估一個訓練作業(yè),并將作為一個基本示例。我們稍后將使用 Syne Tune來處理更具可擴展性的分布式 HPO 案例。
class HPOTuner(d2l.HyperParameters): #@save
def __init__(self, scheduler: HPOScheduler, objective: callable):
self.save_hyperparameters()
# Bookeeping results for plotting
self.incumbent = None
self.incumbent_error = None
self.incumbent_trajectory = []
self.cumulative_runtime = []
self.current_runtime = 0
self.records = []
def run(self, number_of_trials):
for i in range(number_of_trials):
start_time = time.time()
config = self.scheduler.suggest()
print(f"Trial {i}: config = {config}")
error = self.objective(**config)
error = float(error.cpu().detach().numpy())
self.scheduler.update(config, error)
runtime = time.time() - start_time
self.bookkeeping(config, error, runtime)
print(f" error = {error}, runtime = {runtime}")
19.2.4。簿記 HPO 算法的性能
對于任何 HPO 算法,我們最感興趣的是性能最佳的配置(稱為incumbent)及其在給定掛鐘時間后的驗證錯誤。這就是我們跟蹤runtime
每次迭代的原因,其中包括運行評估的時間(調用 objective
)和做出決策的時間(調用 scheduler.suggest
)。在續(xù)集中,我們將繪制 cumulative_runtime
againstincumbent_trajectory
以可視化根據( 和) 定義的 HPO 算法的任何時間性能。這使我們不僅可以量化優(yōu)化器找到的配置的工作情況,還可以量化優(yōu)化器找到它的速度。scheduler
searcher
@d2l.add_to_class(HPOTuner) #@save
def bookkeeping(self, config: dict, error: float, runtime: float):
self.records.append({"config": config, "error": error, "runtime": runtime})
# Check if the last hyperparameter configuration performs better
# than the incumbent
if self.incumbent is None or self.incumbent_error > error:
self.incumbent = config
self.incumbent_error = error
# Add current best observed performance to the optimization trajectory
self.incumbent_trajectory.append(self.incumbent_error)
# Update runtime
self.current_runtime += runtime
self.cumulative_runtime.append(self.current_runtime)
19.2.5。示例:優(yōu)化卷積神經網絡的超參數(shù)
我們現(xiàn)在使用隨機搜索的新實現(xiàn)來優(yōu)化 第 7.6 節(jié)中卷積神經網絡的批量大小和學習率。我們通過定義目標函數(shù),這將再次成為驗證錯誤。LeNet
def hpo_objective_lenet(learning_rate, batch_size, max_epochs=10): #@save
model = d2l.LeNet(lr=learning_rate, num_classes=10)
trainer = d2l.HPOTrainer(max_epochs=max_epochs, num_gpus=1)
data = d2l.FashionMNIST(batch_size=batch_size)
model.apply_init([next(iter(data.get_dataloader(True)))[0]], d2l.init_cnn)
trainer.fit(model=model, data=data)
validation_error = trainer.validation_error()
return validation_error
我們還需要定義配置空間。此外,要評估的第一個配置是 第 7.6 節(jié)中使用的默認設置。
現(xiàn)在我們可以開始隨機搜索了:
error = 0.17130666971206665, runtime = 125.33143877983093
下面我們繪制了現(xiàn)任者的優(yōu)化軌跡,以獲得隨機搜索的任何時間性能:
19.2.6. 比較 HPO 算法
正如訓練算法或模型架構一樣,了解如何最好地比較不同的 HPO 算法非常重要。每次 HPO 運行取決于隨機性的兩個主要來源:訓練過程的隨機效應,例如隨機權重初始化或小批量排序,以及 HPO 算法本身的內在隨機性,例如隨機搜索的隨機抽樣。因此,在比較不同的算法時,至關重要的是多次運行每個實驗并報告基于隨機數(shù)生成器的不同種子的算法多次重復的總體統(tǒng)計數(shù)據,例如平均值或中值。
為了說明這一點,我們比較隨機搜索(參見第 19.1.2 節(jié))和貝葉斯優(yōu)化(Snoek等人,2012 年)在調整前饋神經網絡的超參數(shù)方面的作用。每個算法都經過評估
評論