NVIDIA GPU卷积网络的自动调谐
NVIDIA GPU卷積網絡的自動調諧
針對特定設備和工作負載的自動調整對于獲得最佳性能至關重要。這是關于如何為NVIDIA GPU調整整個卷積網絡。
NVIDIA GPU在TVM中的操作實現是以模板形式編寫的。模板有許多可調旋鈕(平鋪系數、展開等)。將調整神經網絡中的所有卷積和深度卷積算子。在調優之后,生成一個日志文件,其中存儲了所有所需操作符的最佳旋鈕值。當TVM編譯器編譯這些運算符時,它將查詢此日志文件以獲得最佳的旋鈕值。
還發布了一些NVIDIA GPU的預調參數。可以去NVIDIA GPU基準測試看看結果。
本文不會在Windows或最新版本的macOS上運行。要讓它運行,需要將主體包裝在if name == “main”: 塊中。
安裝依賴項
要在tvm中使用autotvm包,需要安裝一些額外的依賴項。(如果使用python2,請將“3”更改為“2”):
pip3 install --user psutil xgboost tornado
為了使TVM在調諧過程中運行更快,建議使用cython作為TVM的FFI。在tvm的根目錄下,執行:
pip3 install --user cython
sudo make cython3
現在回到python代碼。導入包。
import os
import numpy as np
import tvm
from tvm import relay, autotvm
import tvm.relay.testing
from tvm.autotvm.tuner import XGBTuner, GATuner, RandomTuner, GridSearchTuner
import tvm.contrib.graph_runtime as runtime
Define Network
首先需要在中繼前端API中定義網絡。可以從tvm中轉測試. 也可以從MXNet、ONNX和TensorFlow加載模型。
def get_network(name, batch_size):
“”“Get the symbol definition and random weight of a network”""
input_shape = (batch_size, 3, 224, 224)
output_shape = (batch_size, 1000)
if "resnet" in name:n_layer = int(name.split("-")[1])mod, params = relay.testing.resnet.get_workload(num_layers=n_layer, batch_size=batch_size, dtype=dtype)
elif "vgg" in name:n_layer = int(name.split("-")[1])mod, params = relay.testing.vgg.get_workload(num_layers=n_layer, batch_size=batch_size, dtype=dtype)
elif name == "mobilenet":mod, params = relay.testing.mobilenet.get_workload(batch_size=batch_size, dtype=dtype)
elif name == "squeezenet_v1.1":mod, params = relay.testing.squeezenet.get_workload(batch_size=batch_size, version="1.1", dtype=dtype)
elif name == "inception_v3":input_shape = (batch_size, 3, 299, 299)mod, params = relay.testing.inception_v3.get_workload(batch_size=batch_size, dtype=dtype)
elif name == "mxnet":# an example for mxnet modelfrom mxnet.gluon.model_zoo.vision import get_modelblock = get_model("resnet18_v1", pretrained=True)mod, params = relay.frontend.from_mxnet(block, shape={"data": input_shape}, dtype=dtype)net = mod["main"]net = relay.Function(net.params, relay.nn.softmax(net.body), None, net.type_params, net.attrs)mod = tvm.IRModule.from_expr(net)
else:raise ValueError("Unsupported network: " + name)return mod, params, input_shape, output_shape
Set Tuning Options
在調整之前,應用一些配置。
DEVICE CONFIG
target = tvm.target.cuda()
TUNING OPTION
network = “resnet-18”
log_file = “%s.log” % network
dtype = “float32”
tuning_option = {
“log_filename”: log_file,
“tuner”: “xgb”,
“n_trial”: 2000,
“early_stopping”: 600,
“measure_option”: autotvm.measure_option(
builder=autotvm.LocalBuilder(timeout=10),
runner=autotvm.LocalRunner(number=20, repeat=3, timeout=4, min_repeat_ms=150),
),
}
注意
如何設置調整選項
一般來說,這里提供的默認值工作正常。
如果有大量的時間預算,可以設置n_trial, early_stopping,這使調整運行更長時間。
如果有多個設備,則可以使用所有設備進行測量,以加快調整過程。(請參閱下面的“放大測量”部分)。
開始調諧
現在可以從網絡中提取調優任務并開始調優。這里,提供了一個簡單的實用函數來優化任務列表。這個函數只是一個按順序調整它們的初始實現。將在將來引入更復雜的調優調度程序。
You can skip the implementation of this function for this tutorial.
def tune_tasks(
tasks,
measure_option,
tuner=“xgb”,
n_trial=1000,
early_stopping=None,
log_filename=“tuning.log”,
use_transfer_learning=True,
):
# create tmp log file
tmp_log_file = log_filename + “.tmp”
if os.path.exists(tmp_log_file):
os.remove(tmp_log_file)
for i, tsk in enumerate(reversed(tasks)):prefix = "[Task %2d/%2d] " % (i + 1, len(tasks))# create tunerif tuner == "xgb" or tuner == "xgb-rank":tuner_obj = XGBTuner(tsk, loss_type="rank")elif tuner == "ga":tuner_obj = GATuner(tsk, pop_size=100)elif tuner == "random":tuner_obj = RandomTuner(tsk)elif tuner == "gridsearch":tuner_obj = GridSearchTuner(tsk)else:raise ValueError("Invalid tuner: " + tuner)if use_transfer_learning:if os.path.isfile(tmp_log_file):tuner_obj.load_history(autotvm.record.load_from_file(tmp_log_file))# do tuningtsk_trial = min(n_trial, len(tsk.config_space))tuner_obj.tune(n_trial=tsk_trial,early_stopping=early_stopping,measure_option=measure_option,callbacks=[autotvm.callback.progress_bar(tsk_trial, prefix=prefix),autotvm.callback.log_to_file(tmp_log_file),],)# pick best records to a cache file
autotvm.record.pick_best(tmp_log_file, log_filename)
os.remove(tmp_log_file)
最后,啟動優化作業并評估端到端性能。
def tune_and_evaluate(tuning_opt):
# extract workloads from relay program
print(“Extract tasks…”)
mod, params, input_shape, out_shape = get_network(network, batch_size=1)
tasks = autotvm.task.extract_from_program(
mod[“main”], target=target, params=params, ops=(relay.op.get(“nn.conv2d”),)
)
# run tuning tasks
print("Tuning...")
tune_tasks(tasks, **tuning_opt)# compile kernels with history best records
with autotvm.apply_history_best(log_file):print("Compile...")with tvm.transform.PassContext(opt_level=3):lib = relay.build_module.build(mod, target=target, params=params)# load parametersctx = tvm.context(str(target), 0)module = runtime.GraphModule(lib["default"](ctx))data_tvm = tvm.nd.array((np.random.uniform(size=input_shape)).astype(dtype))module.set_input("data", data_tvm)# evaluateprint("Evaluate inference time cost...")ftimer = module.module.time_evaluator("run", ctx, number=1, repeat=600)prof_res = np.array(ftimer().results) * 1000 # convert to millisecondprint("Mean inference time (std dev): %.2f ms (%.2f ms)"% (np.mean(prof_res), np.std(prof_res)))
We do not run the tuning in our webpage server since it takes too long.
Uncomment the following line to run it by yourself.
tune_and_evaluate(tuning_option)
Sample Output
調整需要編譯許多程序并從中提取特性。因此建議使用高性能CPU。下面列出了一個示例輸出。它需要大約4個小時來獲得以下輸出在一個32T的AMD Ryzen Threadripper。調諧目標是NVIDIA 1080TI。(在編譯過程中可以看到一些錯誤。如果調諧沒有卡住,就可以了。)
Extract tasks…
Tuning…
[Task 1/12] Current/Best: 541.83/3570.66 GFLOPS | Progress: (960/2000) | 1001.31 s Done.
[Task 2/12] Current/Best: 0.56/ 803.33 GFLOPS | Progress: (704/2000) | 608.08 s Done.
[Task 3/12] Current/Best: 103.69/1141.25 GFLOPS | Progress: (768/2000) | 702.13 s Done.
[Task 4/12] Current/Best: 2905.03/3925.15 GFLOPS | Progress: (864/2000) | 745.94 sterminate called without an active exception
[Task 4/12] Current/Best: 2789.36/3925.15 GFLOPS | Progress: (1056/2000) | 929.40 s Done.
[Task 5/12] Current/Best: 89.06/1076.24 GFLOPS | Progress: (704/2000) | 601.73 s Done.
[Task 6/12] Current/Best: 40.39/2129.02 GFLOPS | Progress: (1088/2000) | 1125.76 s Done.
[Task 7/12] Current/Best: 4090.53/5007.02 GFLOPS | Progress: (800/2000) | 903.90 s Done.
[Task 8/12] Current/Best: 4.78/1272.28 GFLOPS | Progress: (768/2000) | 749.14 s Done.
[Task 9/12] Current/Best: 1391.45/2325.08 GFLOPS | Progress: (992/2000) | 1084.87 s Done.
[Task 10/12] Current/Best: 1995.44/2383.59 GFLOPS | Progress: (864/2000) | 862.60 s Done.
[Task 11/12] Current/Best: 4093.94/4899.80 GFLOPS | Progress: (224/2000) | 240.92 sterminate called without an active exception
[Task 11/12] Current/Best: 3487.98/4909.91 GFLOPS | Progress: (480/2000) | 534.96 sterminate called without an active exception
[Task 11/12] Current/Best: 4636.84/4912.17 GFLOPS | Progress: (1184/2000) | 1381.16 sterminate called without an active exception
[Task 11/12] Current/Best: 50.12/4912.17 GFLOPS | Progress: (1344/2000) | 1602.81 s Done.
[Task 12/12] Current/Best: 3581.31/4286.30 GFLOPS | Progress: (736/2000) | 943.52 s Done.
Compile…
Evaluate inference time cost…
Mean inference time (std dev): 1.07 ms (0.05 ms)
作為參考基線,MXNet+TensorRT在resnet-18上的時間開銷是1.30ms,所以要快一點。
注意
遇到困難?
自動調諧模塊容易出錯。如果總是看到“0.00/0.00 GFLOPS”,那么一定是出了什么問題。
首先,確保設置了正確的設備配置。然后,可以通過在腳本開頭添加這些行來打印調試信息。它將打印每個測量結果,可以在其中找到有用的錯誤消息。
導入日志記錄
logging.getLogger(‘autotvm’).setLevel(logging.DEBUG)
最后,請隨時向尋求幫助https://discus.tvm.apache.org
使用多個設備放大測量
如果有多個設備,可以使用所有設備進行測量。TVM使用RPC跟蹤器來管理分布式設備。RPC跟蹤器是一個集中的控制器節點。可以把所有設備注冊到跟蹤器上。例如,如果有10個GPU卡,可以將它們全部注冊到跟蹤器中,并并行運行10個測量,從而加快調諧過程。
要啟動RPC跟蹤器,在主機上運行此命令。在整個整定過程中需要跟蹤器,所以需要為這個命令打開一個新的終端:
python -m tvm.exec.rpc_tracker --host=0.0.0.0 --port=9190
The expected output is
INFO:RPCTracker:bind to 0.0.0.0:9190
然后為RPC服務器打開另一個新終端。需要為每個專用設備啟動一個服務器。使用字符串鍵來區分設備的類型。可以選一個你喜歡的名字。(注意:對于rocm后端,編譯器有一些內部錯誤,需要在參數列表中add –no-fork。)
python -m tvm.exec.rpc_server --tracker=0.0.0.0:9190 --key=1080ti
After registering devices, we can confirm it by querying rpc_tracker
python -m tvm.exec.query_rpc_tracker --host=0.0.0.0 --port=9190
For example, if we have four 1080ti, two titanx and one gfx900, the output can be
Queue Status
key total free pending
1080ti 4 4 0
titanx 2 2 0
gfx900 1 1 0
最后,需要將調優選項更改為使用RPCRunner。使用下面的代碼替換上面相應的部件。
tuning_option = {
“log_filename”: log_file,
“tuner”: “xgb”,
“n_trial”: 2000,
“early_stopping”: 600,
“measure_option”: autotvm.measure_option(
builder=autotvm.LocalBuilder(timeout=10),
runner=autotvm.RPCRunner(
“1080ti”, # change the device key to your key
“0.0.0.0”,
9190,
number=20,
repeat=3,
timeout=4,
min_repeat_ms=150,
),
),
}
https://tvm.apache.org/docs/tutorials/autotvm/tune_relay_cuda.html
下載Python源代碼:tune_relay_cuda.py
下載Jupyter筆記本:tune_relay_cuda.ipynbDownload Python source code: tune_relay_cuda.py
Download Jupyter notebook: tune_relay_cuda.ipynb
總結
以上是生活随笔為你收集整理的NVIDIA GPU卷积网络的自动调谐的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 编写可调模板并使用Auto-tuner自
- 下一篇: x86 cpu卷积网络的自动调谐