Ray Tune 建立在 Ray Core 的分散式運算架構之上,提供一個彈性且功能豐富的超引數最佳化平台。它不僅支援多種搜尋演算法,例如隨機搜尋、貝葉斯最佳化等,還整合瞭如 Hyperband 等高效率的排程器,能有效地管理計算資源,並透過提前終止表現不佳的試驗來加速搜尋過程。開發者可以透過 Tune 的 API 定義搜尋空間、設計可訓練物件,並監控試驗的執行與結果分析。此外,Tune 還支援自定義 Callback,讓開發者能在訓練過程中加入客製化邏輯,例如記錄特定指標、儲存模型檢查點等,更精細地控制最佳化流程。對於需要處理大量資料和複雜模型的機器學習任務,Ray Tune 提供了一個強大的解決方案,能有效提升模型訓練效率和效能。
使用 Ray Tune 進行超引數最佳化
在第四章中,您學習瞭如何建立和執行各種強化學習實驗。執行這些實驗可能非常昂貴,無論是在計算資源還是執行時間方面。當您處理更具挑戰性的任務時,這種成本只會增加,因為您不太可能直接選擇一個演算法並執行它來獲得良好的結果。換句話說,在某些時候,您需要調整演算法的超引數以獲得最佳結果。正如我們將在本章中看到的那樣,調整機器學習模型很困難,但 Ray Tune 是幫助您解決此任務的絕佳選擇。
為什麼超引數最佳化很困難
讓我們簡要回顧一下超引數最佳化的基礎知識。如果您熟悉 HPO,可以跳過本文,但由於我們還討論了分散式 HPO 的某些方面,您仍然可以從中受益。和往常一樣,您可以在本文的 GitHub 儲存函式庫中找到本章的筆記本。
在我們的第一個 RL 實驗中,我們定義了一個非常基本的 Q-Learning 演算法,其內部狀態-動作值根據明確的更新規則進行更新。初始化後,我們再也沒有直接觸控這些模型引數;它們是由演算法學習的。相比之下,在設定演算法時,我們在訓練之前明確選擇了 weight 和 discount_factor 引數。我們沒有告訴您如何選擇這些引數;我們只是接受它們足以解決當前的問題。
同樣,在第四章中,我們透過設定 num_rollout_workers=2 初始化了一個 RLlib 演算法,該演算法使用兩個 rollout workers。像這樣的引數稱為超引數,找到它們的良好選擇對於成功的實驗至關重要。超引數最佳化領域致力於有效地找到這樣的良好選擇。
使用 Ray 進行隨機搜尋
像 Q-Learning 演算法的 weight 或 discount_factor 這樣的超引數是連續引數,因此我們無法測試它們的所有組合。更重要的是,這些引數的選擇可能不是相互獨立的。如果我們希望它們被自動選擇,我們還需要為每個引數指定一個值範圍(在本例中,兩個超引數都需要介於 0 和 1 之間)。那麼,我們如何確定良好甚至最佳的超引數?
讓我們看一個例子,該例子實作了一種天真但有效的方法來調整超引數。這個例子還將允許我們介紹稍後將使用的一些術語。核心思想是,我們可以嘗試隨機抽樣超引數,為每個樣本執行演算法,然後根據結果選擇最佳執行。但為了符合本文的主題,我們不想在順序迴圈中執行它;我們希望使用 Ray 平行計算我們的執行。
為了保持簡單,我們將重新審視第三章中的簡單 Q-Learning 演算法。我們將主訓練函式的簽名定義為 train_policy(env, num_episodes=10000, weight=0.1, discount_factor=0.9)。這意味著我們可以透過將不同的值傳遞給 train_policy 函式來調整 weight 和 discount_factor 引數,並檢視演算法的表現。
定義搜尋空間
為了做到這一點,讓我們為我們的超引數定義一個所謂的搜尋空間。對於這兩個超引數,我們可以定義一個範圍,例如介於 0 和 1 之間。
import ray
from ray import tune
# 定義搜尋空間
search_space = {
"weight": tune.uniform(0, 1),
"discount_factor": tune.uniform(0, 1)
}
# 初始化 Ray
ray.init()
# 使用 Ray Tune 進行超引數最佳化
tune.run(
tune.with_parameters(train_policy, env="your_env", num_episodes=10000),
config=search_space,
num_samples=10
)
# 關閉 Ray
ray.shutdown()
超引數最佳化的關鍵概念
- 搜尋空間:定義超引數的可能值範圍。
- 隨機搜尋:從搜尋空間中隨機抽樣超引陣列合。
- 評估:對每個抽樣的超引陣列合執行演算法並評估其效能。
使用 Ray Tune 的優勢
Ray Tune 是一個強大的超引數最佳化工具,它不僅預設以分散式方式工作(並且與本文中討論的任何其他 Ray 函式庫一起工作),而且還是最具特色的 HPO 函式庫之一。此外,Tune 與一些最著名的 HPO 函式庫(如 Hyperopt、Optuna 等)整合,使其成為分散式 HPO 實驗的理想選擇。
圖表翻譯:隨機搜尋流程圖
此圖示展示了隨機搜尋的基本流程,包括定義搜尋空間、隨機抽樣超引數、評估每個樣本的效能以及選擇最佳結果。
圖表翻譯: 此圖表呈現了隨機搜尋的基本流程。首先,定義超引數的搜尋空間;接著,從該空間中隨機抽樣不同的超引陣列合;然後,對每個抽樣的組合進行評估,以確定其效能;最後,根據評估結果選擇最佳的超引陣列合。整個過程有效地利用了 Ray Tune 的分散式計算能力,以提高最佳化的效率和效果。
超引數調優的挑戰與 Ray Tune 的應用
超引數調優(Hyperparameter Optimization, HPO)是機器學習和深度學習中一個至關重要的步驟。適當的超引數可以顯著提高模型的效能和訓練效率。在本文中,我們將探討超引數調優的困難之處,並介紹如何使用 Ray Tune 來簡化這一過程。
為什麼超引數調優如此困難?
超引數調優的過程涉及多個複雜因素,包括:
- 搜尋空間可能包含大量超引數,且這些引數可能具有不同的資料型別和範圍。
- 部分引數之間可能存在相關性或依賴關係,使得從複雜的高維空間中取樣良好的候選引數變得困難。
- 隨機取樣引數有時能取得令人驚訝的好效果,但並非總是最優選擇。通常需要測試更複雜的搜尋演算法來找到最佳引數。
- 即使平行化超引數搜尋,單次目標函式的評估也可能需要很長時間。這意味著無法進行過多的搜尋,尤其是在訓練神經網路等耗時任務時。
- 在分散式搜尋中,需要足夠的計算資源來有效地執行目標函式。例如,可能需要 GPU 來加速計算,因此所有搜尋執行都需要能夠存取 GPU。
- 需要方便的工具來管理 HPO 實驗,例如提前終止不良執行、儲存中間結果、從之前的試驗中重新啟動或暫停和還原執行。
使用 Ray Tune 進行超引數調優
Ray Tune 是一個成熟的分散式 HPO 框架,能夠解決上述問題並提供簡單的介面來執行超引數調優實驗。讓我們透過一個例子來瞭解如何使用 Tune。
定義搜尋空間
首先,我們使用 tune.uniform 定義搜尋空間,而不是使用 random 函式庫:
from ray import tune
search_space = {
"weight": tune.uniform(0, 1),
"discount_factor": tune.uniform(0, 1),
}
定義目標函式
接下來,我們定義一個目標函式。與之前的例子類別似,但這次我們傳回一個字典,並且不需要 ray.remote 裝飾器,因為 Tune 會內部處理目標函式的分佈:
def tune_objective(config):
environment = Environment()
policy = train_policy(
environment,
weight=config["weight"],
discount_factor=config["discount_factor"]
)
score = evaluate_policy(environment, policy)
return {"score": score}
執行超引數調優
最後,我們將目標函式和搜尋空間傳遞給 tune.run 呼叫。預設情況下,Tune 會執行隨機搜尋:
tune.run(tune_objective, config=search_space)
詳細解析
1. 平行化目標函式
在原始範例中,我們使用 Ray Core 來平行化目標函式的執行。透過使用 ray.remote 裝飾器,我們將目標函式轉換為 Ray 任務:
@ray.remote
def objective(config):
# 省略實作細節
return [score, config]
2. 蒐集結果
我們迭代搜尋空間,並使用 ray.get 蒐集結果:
result_objects = [objective.remote(choice) for choice in search_space]
results = ray.get(result_objects)
results.sort(key=lambda x: x[0])
print(results[-1])
3. 切換到 Ray Tune
Ray Tune 提供了一個更高層次的抽象,使得超引數調優變得更加容易。透過使用 tune.run,我們可以輕鬆地執行隨機搜尋或其他更複雜的搜尋演算法。
使用 Ray Tune 進行超引數最佳化
Ray Tune 是用於超引數最佳化的強大工具,其運作原理根據 Ray Core 的資源模型,能夠有效地分配計算資源進行最佳化實驗。在本章中,我們將探討 Ray Tune 的核心元件及其使用方法。
Ray Tune 的基本概念
要有效地使用 Ray Tune,必須瞭解其六個關鍵概念,其中四個已經在前面的範例中使用過。這些概念包括:
- 搜尋空間(Search Spaces):定義超引數的取值範圍和取樣方法。
- 可訓練物件(Trainables):代表需要最佳化的目標函式,通常是一個包含單一引數的函式,該引數代表搜尋空間。
- 試驗(Trials):每次使用特定超引陣列合執行目標函式的過程。
- 分析(Analyses):對試驗結果的匯總和分析,用於找出最佳超引數。
- 搜尋演算法(Search Algorithms):用於選擇超引數的演算法,支援多種貝葉斯最佳化方法。
- 排程器(Schedulers):負責規劃和執行搜尋演算法所選的試驗,可用於加速實驗過程。
搜尋演算法與排程器
搜尋演算法:Tune 支援多種搜尋演算法,包括貝葉斯最佳化、隨機搜尋和網格搜尋等。貝葉斯最佳化是一種根據機率的搜尋方法,能夠根據先前的試驗結果更新對超引數範圍的信念,從而做出更明智的選擇。
from ray.tune.suggest.bayesopt import BayesOptSearch algo = BayesOptSearch(random_search_steps=4)排程器:Tune 的排程器預設採用先進先出(FIFO)的策略,但也支援其他策略,如提前終止不成功的試驗,以加速實驗過程。
使用 Ray Tune 的範例
以下是一個簡單的範例,展示如何使用 Ray Tune 進行隨機搜尋並取得最佳超引數:
analysis = tune.run(tune_objective, config=search_space)
print(analysis.get_best_config(metric="score", mode="min"))
內容解密:
tune.run函式用於啟動超引數最佳化實驗,並傳回一個包含所有試驗結果的ExperimentAnalysis物件。get_best_config方法用於從ExperimentAnalysis物件中取得最佳超引陣列態,根據指定的指標(metric)和模式(mode)進行選擇。
圖表說明
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Ray Tune 超引數最佳化技巧
package "機器學習流程" {
package "資料處理" {
component [資料收集] as collect
component [資料清洗] as clean
component [特徵工程] as feature
}
package "模型訓練" {
component [模型選擇] as select
component [超參數調優] as tune
component [交叉驗證] as cv
}
package "評估部署" {
component [模型評估] as eval
component [模型部署] as deploy
component [監控維護] as monitor
}
}
collect --> clean : 原始資料
clean --> feature : 乾淨資料
feature --> select : 特徵向量
select --> tune : 基礎模型
tune --> cv : 最佳參數
cv --> eval : 訓練模型
eval --> deploy : 驗證模型
deploy --> monitor : 生產模型
note right of feature
特徵工程包含:
- 特徵選擇
- 特徵轉換
- 降維處理
end note
note right of eval
評估指標:
- 準確率/召回率
- F1 Score
- AUC-ROC
end note
@enduml
圖表翻譯: 此圖示呈現了 Ray Tune 的核心元件及其相互關係。搜尋空間定義了超引數的取值範圍,可訓練物件代表了需要最佳化的目標函式。試驗是使用特定超引陣列合執行目標函式的過程,其結果被匯總和分析,以選擇最佳超引數。搜尋演算法負責選擇超引數,而排程器則規劃和執行試驗。
使用Ray Tune進行超引數最佳化
簡介
超引數最佳化是機器學習工作流程中的關鍵步驟。Ray Tune是一個強大的工具,能夠簡化並加速這一過程。本篇文章將介紹如何使用Ray Tune進行超引數最佳化,包括基本用法、排程器和資源管理。
基本用法
Ray Tune的核心功能是執行超引數搜尋。要使用Tune,首先需要定義一個目標函式,該函式接受一個組態物件並報告其效能指標。
def objective(config):
for step in range(30):
score = config["weight"] * (step ** 0.5) + config["bias"]
tune.report(score=score)
search_space = {"weight": tune.uniform(0, 1), "bias": tune.uniform(0, 1)}
內容解密:
objective函式定義了一個簡單的目標函式,用於計算分數。config引數包含超引數,這裡是weight和bias。tune.report用於向Tune報告中間分數。
接下來,使用Tune.run執行超引數搜尋:
analysis = tune.run(
objective,
config=search_space,
num_samples=10,
)
內容解密:
tune.run啟動超引數搜尋。num_samples=10表示Tune將從搜尋空間中抽樣10組超引數進行測試。
使用Bayesian Optimization進行超引數搜尋
Tune支援多種搜尋演算法,包括貝葉斯最佳化(Bayesian Optimization)。貝葉斯最佳化是一種根據模型的最佳化方法,能夠有效地探索超引數空間。
from ray.tune.search.bayesopt import BayesOptSearch
algo = BayesOptSearch(metric="score", mode="min")
tune.run(
tune_objective,
config=search_space,
metric="score",
mode="min",
search_alg=algo,
stop={"training_iteration": 10},
)
內容解密:
BayesOptSearch建立了一個貝葉斯最佳化搜尋演算法例項。metric="score"和mode="min"指定了最佳化目標是最小化score指標。stop={"training_iteration": 10}設定了停止條件,即當訓練迭代次數達到10時停止。
使用排程器最佳化試驗效率
Tune的排程器(Schedulers)可以提前終止表現不佳的試驗,從而提高搜尋效率。Hyperband是一種流行的排程器,能夠動態分配資源給有前途的試驗。
from ray.tune.schedulers import HyperBandScheduler
scheduler = HyperBandScheduler(metric="score", mode="min")
analysis = tune.run(
objective,
config=search_space,
scheduler=scheduler,
num_samples=10,
)
內容解密:
HyperBandScheduler建立了一個Hyperband排程器例項。- 排程器根據試驗的中間結果決定是否提前終止試驗。
資源管理
Tune允許自定義每個試驗的資源需求,包括CPU、GPU和記憶體。
tune.run(
objective,
config=search_space,
num_samples=10,
resources_per_trial={"cpu": 2, "gpu": 0.5}
)
內容解密:
resources_per_trial指定了每個試驗所需的資源。- 在這個例子中,每個試驗需要2個CPU和0.5個GPU。
使用自定義 Callback 最佳化 Ray Tune 超引數調優
在 Ray Tune 中,Callback 是一種強大的工具,能夠幫助開發者在超引數調優過程中實作自定義邏輯。本章節將介紹如何在 Tune 中使用 Callback,以及如何建立自己的 Callback 來滿足特定需求。
實作自定義 Callback
要建立自定義 Callback,首先需要繼承 ray.tune.Callback 類別並實作所需的方法。例如,若要在每次試驗傳回結果時記錄特定訊息,可以實作 on_trial_result 方法。
from ray import tune
from ray.tune import Callback
from ray.tune.logger import pretty_print
class PrintResultCallback(Callback):
def on_trial_result(self, iteration, trials, trial, result, **info):
print(f"Trial {trial} in iteration {iteration}, "
f"got result: {result['score']}")
def objective(config):
for step in range(30):
score = config["weight"] * (step ** 0.5) + config["bias"]
tune.report(score=score, step=step, more_metrics={})
search_space = {"weight": tune.uniform(0, 1), "bias": tune.uniform(0, 1)}
analysis = tune.run(
objective,
config=search_space,
mode="min",
metric="score",
callbacks=[PrintResultCallback()])
best = analysis.best_trial
print(pretty_print(best.last_result))
內容解密:
- 繼承 Callback 類別:透過繼承
ray.tune.Callback,我們可以建立自定義的 Callback。 - 實作
on_trial_result方法:此方法在每次試驗傳回結果時被呼叫,能夠存取試驗的當前結果。 objective函式:定義了一個簡單的目標函式,用於計算score並報告給 Tune。tune.run呼叫:執行超引數調優,並傳入自定義的 Callback。
使用檢查點(Checkpoints)與停止條件
Tune 提供了檢查點機制,能夠在試驗失敗或需要停止時保護進度。同時,也可以透過定義停止條件來控制試驗的執行時間。
# 繼續上一個實驗
analysis = tune.run(
objective,
name="<your-logdir>",
resume=True,
config=search_space)
# 定義停止條件
def stopper(trial_id, result):
return result["score"] < 2
tune.run(
objective,
config=search_space,
stop=stopper)
內容解密:
- 還原實驗:透過指定
resume=True和name引數,可以從之前的檢查點繼續實驗。 - 定義停止條件:使用
stopper函式定義了當score小於 2 時停止試驗的條件。
自定義搜尋空間
Tune 允許開發者定義複雜的搜尋空間,以滿足不同的超引數調優需求。
import numpy as np
search_space = {
"weight": tune.sample_from(
lambda context: np.random.uniform(low=0.0, high=1.0)
),
# 其他引數定義...
}
tune.run(objective, config=search_space)
內容解密:
tune.sample_from方法:允許從自定義的分佈中取樣超引數。- 使用 NumPy 的
random.uniform:在這個例子中,使用 NumPy 從均勻分佈中取樣weight引數。