Hyperopt中文文档:FMin
FMin
Font Tian translated this article on 22 December 2017
這一頁是關于 hyperopt.fmin() 的基礎教程. 主要寫了如何寫一個可以利用fmin進行優化的函數,以及如何描述fmin的搜索空間。
Hyperopt的工作是通過一組可能的參數找到標量值,possibly-stochastic function的最佳值(注意在數學中stochastic與random并不完全相同)。雖然許多優化包假設這些輸入是從矢量空間中抽取的,但Hyperopt的不同之處在于它鼓勵您更詳細地描述搜索空間。通過提供有關定義函數的位置以及您所認為最佳值位置的更多信息,可以讓hyperopt中的算法更有效地進行搜索。
使用hyperopt的方式的過程總結:
- 用于最小化的目標函數
- 搜索空間
- 存儲搜索的所有點評估的數據庫
- 要使用的搜索算法
這個(最基本的)教程將介紹如何編寫函數和搜索空間,使用默認 Trials 數據庫和偽 random 搜索算法。第(1)部分是關于目標函數與hyperopt之間通信的不同調用約定。第(2)部分是關于搜索空間的描述。
用 MongoTrials 代替 Trials 以進行并行搜索;除此之外,還有一個有關如何使用MongDB進行并行所有的wiki頁面
我們可以通過選擇 algo=hyperopt.tpe.suggest 代替 algo=hyperopt.random.suggest 來進行搜索算法的選擇.實際上,搜索算法是一個可調用的對象,同時其構造函數也傳入配置參數.不過,這幾乎已經是有關選擇搜索算法的所有有關技術了.
1.定義一個用來最小化的函數
當涉及到指定一個最小化的目標函數時,Hyperopt提供了一些增加靈活性/復雜性的選項。作為一個目標函數設計者可能會考慮的問題:
- 除了函數返回值之外,還需要保存其他信息,例如計算目標時收集的其他統計信息和診斷信息?
- 你是否想要使用比函數值更多的優化算法?
- 你想在并行進程之間進行通信嗎?(例如任務,或最小化算法)
接下來的幾節將介紹實現一個目標函數的各種方法,這個目標函數可以使單個變量的二次目標函數最小化。在每一節中,我們將搜索從-10到+10的有界范圍,我們可以用一個搜索空間來描述:
space = hp.uniform('x', -10, 10)而之后的第二節則介紹如何指定更加復雜的搜索空間
1.1 最簡單的情況
hyperopt的優化算法與您的目標函數之間進行通信的最簡單協議是您的目標函數從搜索空間接收到有效點,并返回與該點關聯的浮點 損失(也稱為負效用)。
from hyperopt import fmin, tpe, hpbest = fmin(fn=lambda x: x ** 2,space=hp.uniform('x', -10, 10),algo=tpe.suggest,max_evals=100)print best這個協議的優點是可讀性高并且易于上手。正如你所看到的,這只有一行代碼。這種協議的缺點是:(1)這種功能不能將關于每個評估的額外信息返回到試驗數據庫中;(2)這種功能不能與搜索算法或其他并發功能評估相互作用。你會在下面的例子中看到為什么你可能想要做這些事情。
1.2通過試用對象附加額外的信息
如果你的目標函數很復雜,需要很長時間才能運行,那么你幾乎肯定會想要保存更多的統計數據和診斷信息,而不僅僅是最后出現的浮點數損失。對于這種情況,fmin函數被編寫來處理字典返回值。這個想法是,你的損失函數可以返回你想要的所有統計和診斷的嵌套字典。但實際情況要比這個靈活一點:當使用mongodb時,字典必須是有效的JSON文檔。不過,存儲特定領域的備用結果還是有很大的靈活性的。
當目標函數返回一個字典時,fmin函數會在返回值中查找一些特殊的鍵值對,并將其傳遞給優化算法。有兩個必須寫的鍵值對:
- status- 其中的一個鍵 hyperopt.STATUS_STRINGS ,例如成功完成時的“OK”,以及在功能變得未定義的情況時的“失敗”。
- loss - 如果你嘗試最小化浮點值函數值,如果狀態是“ok”,那么這個值必須存在。
fmin函數也有一些可選的鍵
- attachments - 鍵值對的字典,其鍵是短字符串(如文件名),其值可能是長字符串(如文件內容),每次訪問記錄時都不應從數據庫加載。(另外,MongoDB限制正常的鍵值對的長度,所以一旦你的值是megabytes,你可能必須把它作為附件。)
- loss_variance - 浮點型數值 - 隨機目標函數的不確定性(鍵的直譯是 損失-方差 )。
- `true_loss ‘- 浮點型數值 - 當進行超參數優化時,如果使用這個名稱存儲模型的泛化錯誤,那么有時可以從內置的繪圖例程中獲得更加精確的輸出。
- true_loss_variance - 浮點型數值 - 泛化誤差的不確定性(鍵的直譯時 真實的-損失-方差 )。
下面時原文(譯者補充):
- attachments - a dictionary of key-value pairs whose keys are short strings (like filenames) and whose values are potentially long strings (like file contents) that should not be loaded from a database every time we access the record. (Also, MongoDB limits the length of normal key-value pairs so once your value is in the megabytes, you may have to make it an attachment.)
- loss_variance - float - the uncertainty in a stochastic objective function
- true_loss - float - When doing hyper-parameter optimization, if you store the generalization error of your model with this name, then you can sometimes get spiffier output from the built-in plotting routines.
- true_loss_variance - float - the uncertainty in the generalization error
由于字典是為了適應各種后端存儲機制,你應該確保它是JSON兼容的。只要它是字典,列表,元組,數字,字符串和日期時間的樹形結構圖,就可以。
提示:要存儲numpy數組,請將它們序列化為一個字符串,并考慮將它們存儲為附件(attachments)。
在字典返回樣式中編寫上面的函數,它看起來像這樣:
import pickleimport timefrom hyperopt import fmin, tpe, hp, STATUS_OKdef objective(x):return {'loss': x ** 2, 'status': STATUS_OK }best = fmin(objective,space=hp.uniform('x', -10, 10),algo=tpe.suggest,max_evals=100)print best1.3 Trials對象
為了能夠真正看到我們需要返回的字典,讓我們修改目標函數返回更多的東西,并傳遞一個明確的trials參數fmin。
import pickleimport timefrom hyperopt import fmin, tpe, hp, STATUS_OK, Trialsdef objective(x):return {'loss': x ** 2,'status': STATUS_OK,# -- store other results like this'eval_time': time.time(),'other_stuff': {'type': None, 'value': [0, 1, 2]},# -- attachments are handled differently'attachments':{'time_module': pickle.dumps(time.time)}}trials = Trials()best = fmin(objective,space=hp.uniform('x', -10, 10),algo=tpe.suggest,max_evals=100,trials=trials)print best在這種情況下,對fmin的調用與以前一樣進行,但是通過直接傳遞一個Trials對象,我們可以檢查實驗期間計算的所有返回值。
舉個例子:
- trials.trials - 一個代表所有搜索內容的字典列表(a list of dictionaries)
- trials.results - 一個在搜索過程中由“目標”返回的字典列表(a list of dictionaries)
- trials.losses() - 一個關于損失的List列表(一個“OK”Trial的浮動值)
- trials.statuses() - 一個狀態字符串的List列表
這個試驗對象可以保存,傳遞到內置的繪圖程序,或用自己的自定義代碼進行分析。
該附件(attachments)是通過一種特殊的機制,使得它可以使用相同的代碼都處理Trials和MongoTrials。
您可以像這樣檢索trial附件,它可以檢索第5次trial的“time_module”附件:
msg = trials.trial_attachments(trials.trials[5])['time_module']time_module = pickle.loads(msg)語法有點復雜難懂,主要是因為設計時想法是將附件當做大字符串。所以當我們使用 MongoTrials 時,我們并不想下載額外的我們不需要的數據。而字符串則可以通過設置為全局(globally)來連接 trials 對象 trials.attachments ,這就像是一個字符串對字符串的字典(a string-to-string dictionary)。
注意目前,Trials對象的 trial-specific attachments 被放入同樣的 global trials attachment dictionary 中,但是將來可能會改變,從而導致這對于 MongoTrials 對象并不正確。
1.4用于實時與 MongoDB 通信的 Ctrl 對象
可以 fmin() 給你的目標函數一個并行實驗使用的 mongodb 的句柄。這種機制使得用部分結果更新數據庫成為可能,并且與其他正在評估不同點的并發進程進行通信。你的目標函數甚至可以添加新的搜索點,就像 random.suggest 。
基本的技術包括:
- 使用 fmin_pass_expr_memo_ctrl 裝飾器
- 在你自己的方法中使用 pyll.rec_eval ,從 expr 和 memo 中構建搜索空間中的點。
- 使用 ctrl, 一個與實況對象( trials )進行交流的 hyperopt.Ctrl 實例。
如果這沒有太多的意義你這個簡短的教程后,這是正常的,但我想給一些提與當前代碼庫什么是可行的,并提供一些術語 從而使你能夠在 HyperOpt 源文件,單元測試,和示例項目中進行有效檢索,如 術語 HyperOpt ConvNet 。如果你想要一些幫助快速掌握這部分代碼可以給我發電子郵件或者 file a github issue。
2.定義一個搜索空間
搜索空間由嵌套函數表達式組成,包括隨機表達式。隨機表達式是超參數。從這個嵌套的隨機程序中抽樣定義了隨機搜索算法。超參數優化算法通過將正常的“采樣”邏輯替換為自適應探索策略來工作,其并不試圖從搜索空間中指定的分布進行采樣。
最好把搜索空間看作是隨機的抽樣程序。例如
from hyperopt import hpspace = hp.choice('a',[('case 1', 1 + hp.lognormal('c1', 0, 1)),('case 2', hp.uniform('c2', -10, 10))])運行這段代碼片段的結果是一個變量 space ,它引用了表達式標識符及其參數的圖表。實際上沒有采樣,只是描述如何采樣點的圖表。處理這種表達式圖的代碼在 hyperopt.pyll中 ,我將這些圖作為 pyll 圖或 pyll 程序( pyll graphs or pyll programs)。
如果你喜歡,你可以通過抽樣來評估樣本空間。
import hyperopt.pyll.stochasticprint hyperopt.pyll.stochastic.sample(space)通過 space 描述的搜索空間一共有三個超參數:
- ‘a’ - 選擇案例
- ‘c1’ - 在’case’中使用的正值參數
- ‘c2’ - 在’case 2’中使用的有界實值參數
有一件事要注意,每個可優化的隨機表達式都有一個標簽作為第一個參數。這些標簽用于選擇參數并將其返回給調用者,并以各種方式在內部進行。
第二件要注意的是,我們在圖的中間使用了元組(在’case 1’和’case 2’的每一個的周圍)。列表,字典和元組都被升級為“確定性函數表達式”,以便它們可以成為搜索空間隨機程序 search space stochastic program 的一部分。
第三件要注意的是數字表達式 1 + hp.lognormal('c1', 0, 1) ,它嵌入到搜索空間的描述中。就優化算法而言,直接在搜索空間中加1和在目標函數本身的邏輯內加1是沒有區別的。作為程序設計者,您可以選擇將這種處理放在哪里,以達到您想要的種類模塊化。請注意,搜索空間中的中間表達式結果可以是任意Python對象,即使在使用 mongodb 并行優化時也是如此。向搜索空間描述中添加新的非隨機表達式是很容易的,參見下面(2.3節)了解如何去做。
第四點要注意的是,’c1’和’c2’是我們稱之為條件參數的例子。每一個返回的樣本中的“c1”和“c2”都有一個關于“a”的特定值。如果“a”是0,則使用“c1”而不是“c2”。如果’a’是1,那么使用’c2’而不是’c1’。只有這樣做有意義,應該用這種方式將參數編碼為條件參數,而不是簡單地忽略目標函數中的參數。如果您向程序展示了“c1”有時對目標函數沒有影響的事實(因為它對目標函數的參數沒有影響),那么Hyperot在搜索可以進行更有效地進行分配。
2.1參數表達式
目前hyperopt的優化算法所識別的隨機表達式是:
hp.choice(label, options)
- 返回其中一個選項,它應該是一個列表或元組。options元素本身可以是[嵌套]隨機表達式。在這種情況下,僅出現在某些選項中的隨機選擇(stochastic choices)將成為條件參數。
hp.randint(label, upper)
- 返回范圍:[0,upper]中的隨機整數。這種分布的語義是意味著鄰整數值之間的損失函數沒有更多的相關性,與更遠的整數值相比較。例如,這是描述隨機種子的適當分布。如果損失函數可能更多的與相鄰整數值相關聯,那么你或許應該用“量化”連續分布的一個,比如 quniform , qloguniform , qnormal 或 qlognormal 。
hp.uniform(label, low, high)
- 返回位于[low,hight]之間的均勻分布的值。
- 在優化時,這個變量被限制為一個雙邊區間。
hp.quniform(label, low, high, q)
- 返回一個值,如 round(uniform(low,high)/ q)* q
- 適用于目標仍然有點“光滑”的離散值,但是在它上下存在邊界(雙邊區間)。
hp.loguniform(label, low, high)
- 返回根據 exp(uniform(low,high)) 繪制的值,以便返回值的對數是均勻分布的。
優化時,該變量被限制在[exp(low),exp(high)]區間內。
- 返回根據 exp(uniform(low,high)) 繪制的值,以便返回值的對數是均勻分布的。
hp.qloguniform(label, low, high, q)
- 返回類似 round(exp(uniform(low,high))/ q)* q 的值
- 適用于一個離散變量,其目標是“平滑”,并隨著值的大小變得更平滑,但是在它上下存在邊界(雙邊區間)。
hp.normal(label, mu, sigma)
- 返回正態分布的實數值,其平均值為 mu ,標準偏差為 σ。優化時,這是一個無約束(unconstrained)的變量。
hp.qnormal(label, mu, sigma, q)
- 返回像 round(正常(mu,sigma)/ q)* q 的值
- 適用于離散值,可能需要在 mu 附近的取值,但從基本上上是無約束(unbounded)的。
hp.lognormal(label, mu, sigma)(對數正態分布)
- 返回根據 exp(normal(mu,sigma)) 繪制的值,以便返回值的對數正態分布。優化時,這個變量被限制為正值。
hp.qlognormal(label, mu, sigma, q)
- 返回類似 round(exp(normal(mu,sigma))/ q)* q 的值
- 適用于一個離散變量,其目標是“平滑”,并隨著值的大小變得更平滑,變量的大小是從一個邊界開始的(單邊區間)。
- 返回類似 round(exp(normal(mu,sigma))/ q)* q 的值
2.2搜索空間示例:scikit-learn
為了看到所有可行方案,我們來看看如何在scikit-learn中描述分類算法超參數的空間。(這個想法正在hyperopt-sklearn中開發)
from hyperopt import hpspace = hp.choice('classifier_type', [{'type': 'naive_bayes',},{'type': 'svm','C': hp.lognormal('svm_C', 0, 1),'kernel': hp.choice('svm_kernel', [{'ktype': 'linear'},{'ktype': 'RBF', 'width': hp.lognormal('svm_rbf_width', 0, 1)},]),},{'type': 'dtree','criterion': hp.choice('dtree_criterion', ['gini', 'entropy']),'max_depth': hp.choice('dtree_max_depth',[None, hp.qlognormal('dtree_max_depth_int', 3, 1, 1)]),'min_samples_split': hp.qlognormal('dtree_min_samples_split', 2, 1, 1),},])2.3用 ‘pyll’ 添加非隨機表達式
你可以使用這樣的節點作為 pyll 函數的參數(參見pyll)。如果你想知道更多關于這一點,File a github issue 。
import hyperopt.pyllfrom hyperopt.pyll import scope@scope.definedef foo(a, b=0):print 'runing foo', a, breturn a + b / 2# -- this will print 0, foo is called as usual.print foo(0)# In describing search spaces you can use `foo` as you# would in normal Python. These two calls will not actually call foo,# they just record that foo should be called to evaluate the graph.space1 = scope.foo(hp.uniform('a', 0, 10))space2 = scope.foo(hp.uniform('a', 0, 10), hp.normal('b', 0, 1)# -- this will print an pyll.Apply nodeprint space1# -- this will draw a sample by running foo()print hyperopt.pyll.stochastic.sample(space1)2.4增加新的超參數
如果可能的話,應該避免添加新的用于描述參數搜索空間的隨機表達式。為了使所有的搜索算法能夠在所有的空間上工作,搜索算法必須就描述空間的超參數類型達成一致。作為library的維護者,我可能會不時地加入一些表達方式,但是就像我說的那樣,我想盡可能地避免這種表達方式。添加并不是hyperopt的新類型的隨機表達式意味著其是可擴展(Adding new kinds of stochastic expressions is not one of the ways hyperopt is meant to be extensible.)。
可擴展。
總結
以上是生活随笔為你收集整理的Hyperopt中文文档:FMin的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hyperopt中文文档:Cite引用
- 下一篇: hyperopt中文文档:Install