irace package -- 引數調優神器

chegxy發表於2021-04-01


作者:chegxy
說明:轉載請註明出處,原文在部落格園中,如有勘誤會及時更新,喜歡還請點個贊再走吧


1. irace 是什麼

相信許多做過演算法的同學都知道引數調優這個過程,例如在做PID演算法的時候,需要調節P、I以及D這三個引數以使得系統可以快速的達到期望值,又或者在做進化演算法時,你需要調整種群規模、交叉概率、遺傳概率以及其他一些引數。當然啦,這些引數都可以憑自己的經驗去調整,但這樣依然略顯麻煩。這時,irace出現了,其目的就是通過迭代計算從若干組引數中得到一組最優引數,其演算法也是使用的進化計算,感興趣的小夥伴可以看看這篇文章。irace package 是實現該調優演算法的軟體包,使用的語言是R語言(學統計學的朋友應該非常熟悉了),即使你沒學過R語言也沒有關係,你只需要學會怎麼配置調優環境即可,接下來我們會詳細討論這個問題。

2. 安裝 irace

提前申明一下,以下內容均以Ubuntu系統為例,更多詳細內容請閱讀The irace Package: User Guide

  1. 首先你需要先安裝R語言環境,執行命令 sudo apt install r-base ,中間可能會有兩次提示,第一次回車,第二次輸入 y 回車,安裝完成後命令列執行命令 R ,出現版本說明以及提示行表示安裝成功;
  2. 輸入 install.packages("irace") 回車,出現以下內容:
irace package -- 引數調優神器
  1. 接下來,會彈處一個對話方塊,讓你選擇映象站點,你選擇一個國內的,點選 ok 開始安裝;
  2. 安裝完成後,輸入 library("irace") 看是否會報錯,沒有資訊說明安裝成功,輸入 q() 退出 R 命令列。
  3. 為了可以在任意位置執行 irace ,我們還需要將其執行目錄新增到環境變數中,執行命令 sudo vim ~/.bashrc,在檔案最後新增如下內容(記得將 IRACE_HOME 修改為你自己的 irace 安裝目錄):
irace package -- 引數調優神器
  1. 儲存退出編輯,執行命令 irace --help ,正常顯示幫助資訊說明配置成功。

3. irace 的執行機制

下面的一張圖是 irace 使用者手冊中的一張圖,展示了 irace 的執行基本流程:

irace package -- 引數調優神器

在 irace 執行的開始,使用者需要為其配置測試環境,包括測試集、引數空間(也就是引數的取值範圍)以及初始引數等,然後 irace 會使用這些配置進行迭代計算使用者的程式,同時使用者程式必須返回一個用以評估使用該引數獲得效果的好壞,這些都是 targetRunner 的任務。通過幾次迭代,irace 最終會得到一個相對較好的引數取值反饋給使用者。

4. irace 的配置環境

之前談到,irace 的配置環境是很重要的一環,它為使用者提供了足夠的自由度來設計他自己的調優過程,其主要由7個檔案組成:

  • configurations.txt
  • instances-list.txt
  • scenario.txt
  • forbidden.txt
  • parameters.txt
  • target-runner
  • target-evaluator

這些檔案可以在 $IRACE_HOME/templates/ 下找到,接下來我們分別闡述這些配置的作用。

4.1. parameters

引數配置可以通過修改 parameters.txt 實現,在parameters.txt中,所有引數以 <name> <label> <type> <range> [ | <condition> ] 的方式組織起來,每個引數佔一行,不使用的引數可以在行首新增 #

name 表示的是引數名,是一個字串,不需要加引號。

label 用於作為在命令列中傳遞這個引數的識別符號,例如label是 "--ants ", 那麼當取該引數值為5時,會在命令列中使用 --ants 5,需要注意的是這一項引號和字串後的空格是需要加上的。

type 表示該引數的型別,使用單字元表示,無需加引號。range 為引數取值提供了範圍,無需加引號。irace 中為引數提供了4種型別:

  • 實數(r),意味著引數可以是浮點數,其range由兩個實數確定(,),引數可以是上界或下界,且最後保留的小數位應該是與你在range中小數位相同的。另外支援使用log取值(以e為底,需要將 r 改為 r,log)
  • 整數(i),引數應該是指定範圍內的一個整數,是實數的一個特殊型別
  • 分類的引數(c),range 由若干個值組成,例如 (<value 1>, ..., ),這些值無需加引號,但如果值中包含空格或者逗號時需要加引號
  • 順序引數(o),與 c 相似,但其 range 是順序的,在 irace 內部會將其看作整數處理,對應於值的索引

condition 可以看作是引數的開關,這個項是可選的,當condition成立時,引數會被傳遞到命令列中,否則不會傳遞該引數。值得注意的是,該引數是一個遵循R語言規則的條件表示式,且使用該條件後不能導致所有引數的依賴關係出現環。

以parameters.txt中的例子為例:

# 1:            2:                   3: 4:      5:
param1          "--param1 "          i  (1, 10) | mode %in% c("x1", "x2")
param2          "--param2 "          i  (1, 10) | mode %in% c("x1", "x3") && real > 2.5 && real <= 3.5
mode            "--"                 c  ("x1" ,"x2", "x3")
real            "--paramreal="       r  (1.5, 4.5)
mutation        "--mutation="        o  ("none", "very low", "low", "medium", "high", "very high", "all")
#unused         "-u "                c  (1, 2, 10, 20)

param1 會在引數 mode 是 x1 或 x2 時使用,其取值範圍為(1,10),param2 會在引數 mode 是 x1 或 x3,且2.5 < real <= 3.5 時使用,其取值範圍為(1,10),其他引數都是無條件的,描述比較簡單,就不一一列舉了。

4.2. target algorithm runner

這個對應於targetRunner,其作為一個指令碼呼叫使用者程式,並需要列印出一個使用者程式的評估值,預設應該是列印最小值,如果要列印最大值可以將其乘上-1。target-runner呼叫使用者的程式格式為 <id.configuration> <id.instance> <seed> <instance> [bound] <configuration>,其含義如下:

  • id.configuration,一個數值串,唯一標識了一種配置
  • id.instance,一個數值串,唯一標識了一個測試項
  • seed,隨機數,忽略確定性演算法的種子
  • instance,用於計算的測試項檔案位置
  • bound,可選項,執行時間界限,與maxBound相關
  • configuration,引數項

例如:

target-runner 1 113 734618556 /home/cxy/instances/tsp/2000-533.tsp \
-- eas --localsearch 0 -alpha 2.92 --beta 3.06 --rho 0.6 --ants 80

還需要注意的一點是,通過設定 execDir 或 --exec-dir 引數可以指定 target-runner 的執行目錄。除此之外,最重要的是修改這個檔案中的 EXE 對應的值,將其修改為被測程式的全路徑。

4.3. target evaluator

在一般情況下,target-runner在每個測試項上會為每一組引數配置輸出一個cost值。但在一些時候,我們需要在每個測試項上,計算完所有的引數配置後再進行cost評估,這時就要用到target evaluator。這個用到的地方有限,因此不做過多闡述,詳細說明請參考The irace Package: User Guide

4.4. training instance

測試集可以由 trainInstancesDir 或者 trainInstancesFile 指定,一般情況下,trainInstancesFile 是一個空檔案,trainInstancesDir 預設為 ./Instances,因此 irace 會將該目錄下的所有檔案作為測試項。你也可以編寫 trainInstancesFile 檔案指定要使用的測試項,每一行表示一個測試項,例如:

# Example training instances file 
100/100-1_100-2.tsp --time 1 
100/100-1_100-3.tsp --time 2 
100/100-1_100-4.tsp --time 3

有趣的是,每一行最後都會完整的作為引數傳遞給target-runner,且其字首都是trainInstancesDir表示的字串,例如:

target-runner 1 113 734718 ./Instances/100/100-1_100-2.tsp --time 1 ...

也正是由於這個特性,training instance 可以不是檔案,而是一些其他的符號。

4.5. initial configurations

初始配置可以由 configurationsFile 檔案描述,其中每行表示一種配置(每列對應一個引數值),第一行是所有引數的名稱,且與之後每列的引數相對應。同時,引數值需要滿足條件(在 range 範圍內),NA 表示不使用該引數。例如:

## Initial candidate configuration for irace 
algorithm localsearch alpha beta rho  ants nnls dlb q0 rasrank elitistants 
as        0           1.0   1.0  0.95 10   NA   NA  0  NA      NA

4.6. forbidden configurations

這一部分主要用於丟棄掉一些無用的配置項,其由若干個符合R語言的表示式組成,每行一個。例如:

## Examples of valid logical operators are: 
## == != >= <= > < & | ! %in% 
(alpha == 0.0) & (beta == 0.0)

對於這個例子,只要配置項中 alpha 等於 0 且 beta 等於 0,那麼該配置項將會被丟棄不做計算,這可以避免計算一些特殊的配置項導致系統崩潰。如果需要非常複雜的表示式,可以參考一下repairing configurations。

4.7. scenario

$IRACE_HOME/templates/ 資料夾下儲存了一個 scenario.txt.tmpl 的檔案,其內容包含了所有 irace 執行時使用的變數值,通過修改其中的任意項可以配置 irace 執行的預設值,例如,通過修改 execDir 切換 irace 的執行目錄,通過修改 parameterFile 指定要使用的引數檔案,等等。

5. 一個小例子帶你感受一下 irace

假設我們有一組座標對,現在給出一個函式式,要求計算該式的引數,以至於其可以很好的擬合這些座標對。我們的這些座標對提取的是sin函式[-1, 1]區間的點,如下所示:

-1 -0.841471
-0.8 -0.717356
-0.6 -0.564642
-0.4 -0.389418
-0.2 -0.198669
0 0
0.2 0.198669
0.4 0.389418
0.6 0.564642
0.8 0.717356
1 0.841471

用於擬合的函式式為 \(y = ax+bx^3+cx^5\) ,因此代求引數為a,b,c。接下來我們一步步配置 irace 環境去執行它獲得最佳引數。

  1. 首先,我們新建一個 irace 的執行目錄,執行 mkdir -p ~/Documents/tuning-fit

  2. 切換到新建的目錄然後將配置模板檔案copy過來,執行命令 cp $IRACE_HOME/templates/*.tmpl ./

  3. 刪除沒有用到的檔案:forbidden.txt.tmpl,target-evaluator.tmpl,將其他檔案的tmpl字尾刪掉

  4. 修改 scenario.txt,刪除下面變數的註釋,並修改其值:

parameterFile = "./parameters.txt"
execDir = "./"
trainInstancesDir = "./Instances"
trainInstancesFile = "instances-list.txt"
configurationsFile = "configurations.txt"
targetRunner = "./target-runner"
maxExperiments = 5000
digits = 10
  1. 設定初始值,修改configurations.txt:
a b     c
1 0.000 0.000
  1. 設定測試例項,修改instances-list.txt,並新建Instances資料夾,執行 mkdir Instances,切換到該目錄下新建檔案instance1,併為其新增測試內容:

instances-list.txt:

## This is an example of specifying instances with a file.

# Each line is an instance relative to trainInstancesDir
# (see scenario.txt.tmpl) and an optional sequence of instance-specific
# parameters that will be passed to target-runnerx when invoked on that
# instance.

# Empty lines and comments are ignored.

instance1

./Instances/instance1:

-1 -0.841471
-0.8 -0.717356
-0.6 -0.564642
-0.4 -0.389418
-0.2 -0.198669
0 0
0.2 0.198669
0.4 0.389418
0.6 0.564642
0.8 0.717356
1 0.841471
  1. 設定引數,修改parameters.txt:
# 1:            2:                   3: 4:      5:
a               "-a "                 r  (0.5, 1.5)
b               "-b "                 r  (-0.500, 0.500)
c               "-c "                 r  (-0.500, 0.500)
  1. 設定target-runner,修改target-runner:
EXE=~/Documents/tuning-fit/bin/fit.py
  1. 編寫我們的測試程式,新建目錄 mkdir -p ~/Documents/tuning-fit/bin,切換到該目錄下,編寫python檔案fit.py:
#! /usr/bin/python3
import sys, getopt

instance = ""
a = 0.0
b = 0.0
c = 0.0

def main(argv):
    global instance, a, b, c
    try:
        opts, args = getopt.getopt(argv, "i:a:b:c:s:",["instance=","a=","b=","c=","seed="])
    except getopt.GetoptError:
        print("fit.py -i <instance> -a <param a> -b <param b> -c <param c>")
        sys.exit(2)
    for opt, arg in opts:
        if opt in ("-i", "--instance"):
            instance = arg
        elif opt in ("-a", "--a"):
            a = float(arg)
        elif opt in ("-b", "--b"):
            b = float(arg)
        elif opt in ("-c", "--c"):
            c = float(arg)

def get_test(filepath):
    instanceFile = open(filepath, mode = 'r')
    if(instanceFile):
        res = []
        line = instanceFile.readline()
        while line:
            pack = line.split()
            pack = list(map(float, pack))
            res.append(pack)
            line = instanceFile.readline()
        instanceFile.close()
        return res
    else:
        print("Filepath is not existing.")
        return None

def fun(x):
    global a,b,c
    return (a * x + b * x ** 3 + c * x ** 5)

def evaluate(test):
    var = 0.0
    vals = []
    for x , y in test:
        vals.append(fun(x))
    average = sum(vals) / len(test)
    i = 0
    while(i < len(vals)):
        var += (vals[i] - test[i][1]) ** 2
        i += 1
    return (var / (len(vals) - 1))


if __name__ == "__main__":
    main(sys.argv[1:])
    test = get_test(instance)
    if(len(test) == 0):
        print("empty test")
        sys.exit(0)
    else:
        res = evaluate(test)
        print(res)
  1. 儲存後退出,為其增加執行許可權,執行 chmod u+x fit.py,切換到tuning-fit目錄下,執行irace,執行 irace,最後可以看到如下所示內容:
# Best configurations as commandlines (first number is the configuration ID; same order as above):
971  -a 0.9887557725 -b -0.1196516576 -c -0.0287763796
947  -a 0.9887960867 -b -0.1196100851 -c -0.0290062498
918  -a 0.9891314065 -b -0.1196766357 -c -0.0302310215

我們用第一組引數繪圖並與sin函式做了一個對比,如下圖所示,可以看出,其在[-1,1]區間內基本擬合。

irace package -- 引數調優神器

如果你還想嘗試更多,更復雜的例項,你可以在irace的安轉目錄的examples資料夾下尋找,或者使用者手冊也提供了一個關於蟻群演算法計算旅行商問題的調優例項。

6. 其他的一些說明

The irace Package: User Guide中有更多的關於irace的詳細說明,以及一些更為高階的用法,本文作為一個入門文章內容已經足夠了,因此若有興趣或需要還請閱讀更為詳細的官方文件。

另外,若以後我發現了新的有趣用法也會繼續更新這篇文章,還請點個關注。

相關文章