在我們深入研究該方法之前,我們將首先討論一個(gè)基本的代碼結(jié)構(gòu),它使我們能夠有效地實(shí)現(xiàn)各種 HPO 算法。一般來(lái)說(shuō),這里考慮的所有 HPO 算法都需要實(shí)現(xiàn)兩個(gè)決策原語(yǔ),即搜索和調(diào)度。首先,他們需要對(duì)新的超參數(shù)配置進(jìn)行采樣,這通常涉及對(duì)配置空間的某種搜索。其次,對(duì)于每個(gè)配置,HPO 算法需要安排其評(píng)估并決定為其分配多少資源。一旦我們開始評(píng)估配置,我們就會(huì)將其稱為試用。我們將這些決定映射到兩個(gè)類,HPOSearcher和 HPOScheduler。除此之外,我們還提供HPOTuner執(zhí)行優(yōu)化過(guò)程的類。
這種調(diào)度器和搜索器的概念也在流行的 HPO 庫(kù)中實(shí)現(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。搜尋器
下面我們定義一個(gè)搜索器的基類,通過(guò)函數(shù)提供一個(gè)新的候選配置sample_configuration。實(shí)現(xiàn)此功能的一種簡(jiǎn)單方法是隨機(jī)對(duì)配置進(jìn)行統(tǒng)一采樣,就像我們?cè)?第 19.1 節(jié)中對(duì)隨機(jī)搜索所做的那樣。更復(fù)雜的算法,例如貝葉斯優(yōu)化,將根據(jù)先前試驗(yàn)的表現(xiàn)做出這些決定。因此,隨著時(shí)間的推移,這些算法能夠?qū)Ω邢M暮蜻x人進(jìn)行抽樣。我們添加該update 功能是為了更新以前試驗(yàn)的歷史,然后可以利用它來(lái)改進(jìn)我們的抽樣分布。
class HPOSearcher(d2l.HyperParameters): #@save def sample_configuration() -> dict: raise NotImplementedError def update(self, config: dict, error: float, additional_info=None): pass
以下代碼顯示了如何在此 API 中實(shí)現(xiàn)我們上一節(jié)中的隨機(jī)搜索優(yōu)化器。作為一個(gè)輕微的擴(kuò)展,我們?cè)试S用戶通過(guò) 指定要評(píng)估的第一個(gè)配置 initial_config,而隨后的配置是隨機(jī)抽取的。
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。調(diào)度程序
除了新試驗(yàn)的采樣配置外,我們還需要決定何時(shí)進(jìn)行試驗(yàn)以及進(jìn)行多長(zhǎng)時(shí)間。實(shí)際上,所有這些決定都是由 完成的HPOScheduler,它將新配置的選擇委托給HPOSearcher. suggest只要某些訓(xùn)練資源可用,就會(huì)調(diào)用該方法。除了調(diào)用sample_configuration搜索器之外,它還可以決定諸如max_epochs(即訓(xùn)練模型的時(shí)間)之類的參數(shù)。update每當(dāng)試驗(yàn)返回新觀察時(shí)調(diào)用該方法。
class HPOScheduler(d2l.HyperParameters): #@save def suggest(self) -> dict: raise NotImplementedError def update(self, config: dict, error: float, info=None): raise NotImplementedError
要實(shí)現(xiàn)隨機(jī)搜索以及其他 HPO 算法,我們只需要一個(gè)基本的調(diào)度程序,它可以在每次新資源可用時(shí)調(diào)度新的配置。
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。調(diào)諧器
最后,我們需要一個(gè)組件來(lái)運(yùn)行調(diào)度器/搜索器并對(duì)結(jié)果進(jìn)行一些簿記。下面的代碼實(shí)現(xiàn)了 HPO 試驗(yàn)的順序執(zhí)行,在下一個(gè)訓(xùn)練作業(yè)之后評(píng)估一個(gè)訓(xùn)練作業(yè),并將作為一個(gè)基本示例。我們稍后將使用 Syne Tune來(lái)處理更具可擴(kuò)展性的分布式 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 算法的性能
對(duì)于任何 HPO 算法,我們最感興趣的是性能最佳的配置(稱為incumbent)及其在給定掛鐘時(shí)間后的驗(yàn)證錯(cuò)誤。這就是我們跟蹤runtime每次迭代的原因,其中包括運(yùn)行評(píng)估的時(shí)間(調(diào)用 objective)和做出決策的時(shí)間(調(diào)用 scheduler.suggest)。在續(xù)集中,我們將繪制 cumulative_runtimeagainstincumbent_trajectory以可視化根據(jù)( 和) 定義的 HPO 算法的任何時(shí)間性能。這使我們不僅可以量化優(yōu)化器找到的配置的工作情況,還可以量化優(yōu)化器找到它的速度。schedulersearcher
@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)化卷積神經(jīng)網(wǎng)絡(luò)的超參數(shù)
我們現(xiàn)在使用隨機(jī)搜索的新實(shí)現(xiàn)來(lái)優(yōu)化 第 7.6 節(jié)中卷積神經(jīng)網(wǎng)絡(luò)的批量大小和學(xué)習(xí)率。我們通過(guò)定義目標(biāo)函數(shù),這將再次成為驗(yàn)證錯(cuò)誤。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
我們還需要定義配置空間。此外,要評(píng)估的第一個(gè)配置是 第 7.6 節(jié)中使用的默認(rèn)設(shè)置。
config_space = { "learning_rate": stats.loguniform(1e-2, 1), "batch_size": stats.randint(32, 256), } initial_config = { "learning_rate": 0.1, "batch_size": 128, }
現(xiàn)在我們可以開始隨機(jī)搜索了:
searcher = RandomSearcher(config_space, initial_config=initial_config) scheduler = BasicScheduler(searcher=searcher) tuner = HPOTuner(scheduler=scheduler, objective=hpo_objective_lenet) tuner.run(number_of_trials=5)
error = 0.17130666971206665, runtime = 125.33143877983093
下面我們繪制了現(xiàn)任者的優(yōu)化軌跡,以獲得隨機(jī)搜索的任何時(shí)間性能:
board = d2l.ProgressBoard(xlabel="time", ylabel="error") for time_stamp, error in zip( tuner.cumulative_runtime, tuner.incumbent_trajectory ): board.draw(time_stamp, error, "random search", every_n=1)
19.2.6. 比較 HPO 算法
正如訓(xùn)練算法或模型架構(gòu)一樣,了解如何最好地比較不同的 HPO 算法非常重要。每次 HPO 運(yùn)行取決于隨機(jī)性的兩個(gè)主要來(lái)源:訓(xùn)練過(guò)程的隨機(jī)效應(yīng),例如隨機(jī)權(quán)重初始化或小批量排序,以及 HPO 算法本身的內(nèi)在隨機(jī)性,例如隨機(jī)搜索的隨機(jī)抽樣。因此,在比較不同的算法時(shí),至關(guān)重要的是多次運(yùn)行每個(gè)實(shí)驗(yàn)并報(bào)告基于隨機(jī)數(shù)生成器的不同種子的算法多次重復(fù)的總體統(tǒng)計(jì)數(shù)據(jù),例如平均值或中值。
為了說(shuō)明這一點(diǎn),我們比較隨機(jī)搜索(參見第 19.1.2 節(jié))和貝葉斯優(yōu)化(Snoek等人,2012 年)在調(diào)整前饋神經(jīng)網(wǎng)絡(luò)的超參數(shù)方面的作用。每個(gè)算法都經(jīng)過(guò)評(píng)估50次使用不同的隨機(jī)種子。實(shí)線表示現(xiàn)任者在這些方面的平均表現(xiàn) 50重復(fù)和虛線標(biāo)準(zhǔn)偏差。我們可以看到隨機(jī)搜索和貝葉斯優(yōu)化在大約 1000 秒內(nèi)的表現(xiàn)大致相同,但貝葉斯優(yōu)化可以利用過(guò)去的觀察來(lái)識(shí)別更好的配置,從而在之后迅速超越隨機(jī)搜索。
圖 19.2.1示例任意時(shí)間性能圖來(lái)比較兩種算法 A 和 B。
19.2.7。概括
本節(jié)列出了一個(gè)簡(jiǎn)單而靈活的接口來(lái)實(shí)現(xiàn)我們將在本章中看到的各種 HPO 算法。在流行的開源 HPO 框架中可以找到類似的接口。我們還研究了如何比較 HPO 算法,以及需要注意的潛在陷阱。
19.2.8。練習(xí)
本練習(xí)的目標(biāo)是為一個(gè)更具挑戰(zhàn)性的 HPO 問(wèn)題實(shí)現(xiàn)目標(biāo)函數(shù),并運(yùn)行更真實(shí)的實(shí)驗(yàn)。我們將使用第 5.6 節(jié)DropoutMLP 中實(shí)現(xiàn)的兩個(gè)隱藏層 MLP 。
編寫目標(biāo)函數(shù),它應(yīng)該取決于模型的所有超參數(shù)和batch_size。使用 max_epochs=50。GPU 在這里無(wú)濟(jì)于事,所以num_gpus=0. 提示:修改hpo_objective_lenet.
選擇一個(gè)合理的搜索空間,其中num_hiddens_1, num_hiddens_2是整數(shù)[8,1024], dropout 值位于[0,0.95], 而batch_size在于 [16,384]. 為 提供代碼config_space,使用來(lái)自 的合理分布scipy.stats。
對(duì)此示例運(yùn)行隨機(jī)搜索number_of_trials=20并繪制結(jié)果。確保首先評(píng)估第 5.6 節(jié)的默認(rèn)配置,即 .initial_config = {'num_hiddens_1': 256, 'num_hiddens_2': 256, 'dropout_1': 0.5, 'dropout_2': 0.5, 'lr': 0.1, 'batch_size': 256}
在本練習(xí)中,您將實(shí)現(xiàn)一個(gè)新的搜索器( 的子類 HPOSearcher),它根據(jù)過(guò)去的數(shù)據(jù)做出決策。這取決于參數(shù)probab_local, num_init_random。它的 sample_configuration工作原理如下。對(duì)于第一次 num_init_random調(diào)用,執(zhí)行與 相同的操作 RandomSearcher.sample_configuration。否則,以概率 ,執(zhí)行與 相同的操作 。否則,選擇迄今為止達(dá)到最小驗(yàn)證錯(cuò)誤的配置,隨機(jī)選擇其超參數(shù)之一,并像中一樣隨機(jī)采樣其值,但保持所有其他值相同。返回此配置,除了這個(gè)超參數(shù)外,它與迄今為止的最佳配置相同。1 - probab_localRandomSearcher.sample_configurationRandomSearcher.sample_configuration
編寫這個(gè)新的LocalSearcher. 提示:您的搜索者需要 config_space作為構(gòu)造參數(shù)。隨意使用 type 的成員RandomSearcher。您還必須實(shí)施該update方法。
重新運(yùn)行上一個(gè)練習(xí)中的實(shí)驗(yàn),但使用新的搜索器而不是RandomSearcher. 對(duì),嘗試不同的值probab_local。num_init_random但是,請(qǐng)注意,不同 HPO 方法之間的適當(dāng)比較需要多次重復(fù)實(shí)驗(yàn),并且理想情況下要考慮許多基準(zhǔn)任務(wù)。
Discussions
-
算法
+關(guān)注
關(guān)注
23文章
4623瀏覽量
93110 -
參數(shù)
+關(guān)注
關(guān)注
11文章
1846瀏覽量
32329 -
pytorch
+關(guān)注
關(guān)注
2文章
808瀏覽量
13283
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論