大規模部署的功能¶
本說明討論了幾個擴充點和技巧,這些點和技巧在較大的系統中執行 PyTorch 或在較大的組織中使用 PyTorch 操作多個系統時可能會有所幫助。
它不涵蓋將模型部署到生產環境的主題。請查看 torch.jit 或其中一個對應的教學課程。
本說明假設您是在組織中從原始碼建置 PyTorch,或者能夠靜態連結其他程式碼,以便在使用 PyTorch 時載入。因此,許多掛鉤會公開為 C++ API,這些 API 可以在集中位置觸發一次,例如在靜態初始化程式碼中。
全裝置群組運算子效能分析¶
PyTorch 附帶了 torch.autograd.profiler,能夠根據需要測量個別運算子所花費的時間。您可以使用相同的機制對任何執行 PyTorch 的程序進行「始終開啟」測量。這對於收集有關在給定程序或整個機器集中執行的 PyTorch 工作負載的資訊可能會有所幫助。
可以使用 torch::addGlobalCallback 新增任何運算子呼叫的新回呼。掛鉤將使用 torch::RecordFunction 結構呼叫,該結構描述呼叫上下文(例如 名稱)。如果已啟用,則 RecordFunction::inputs() 包含以 torch::IValue 變數類型表示的函數參數。請注意,輸入記錄的成本相對較高,因此必須明確啟用。
運算子回呼也可以存取 c10::ThreadLocalDebugInfo::get() 介面,該介面傳回指向保存偵錯資訊的結構的指標。可以使用 at::DebugInfoGuard 物件事先設定此偵錯資訊。偵錯資訊會透過正向(包括非同步 fork 任務)和反向傳遞傳播,並且可用於將有關執行環境的一些額外資訊(例如模型 ID)從應用程式的較高層級傳遞到運算子回呼。
呼叫回呼會增加一些負擔,因此通常只需隨機抽樣運算子呼叫即可。可以使用傳遞給 torch::addGlobalCallback 的可選抽樣率,在每個回呼的基礎上啟用此功能。
請注意,addGlobalCallback 不是執行緒安全的,並且只能在沒有 PyTorch 運算子執行時呼叫。通常,最好在初始化期間呼叫它們一次。
以下是一個範例
// Called somewhere in the program beginning
void init() {
    // Sample one in a hundred operator runs randomly
    addGlobalCallback(
      RecordFunctionCallback(
        &onFunctionEnter,
        &onFunctionExit)
      .needsInputs(true)
      .samplingProb(0.01)
    );
    // Note, to enable observers in the model calling thread,
    // call enableRecordFunction() in the thread before running a model
}
void onFunctionEnter(const RecordFunction& fn) {
    std::cerr << "Before function " << fn.name()
              << " with " << fn.inputs().size() << " inputs" << std::endl;
}
void onFunctionExit(const RecordFunction& fn) {
    std::cerr << "After function " << fn.name();
}
API 使用情況記錄¶
在更廣泛的生態系統中執行時(例如,在託管作業排程器中),追蹤哪些二進制檔案呼叫了特定的 PyTorch API 通常很有用。在幾個重要的 API 點插入了簡單的檢測,這些檢測會觸發給定的回呼。因為 PyTorch 通常是在一次性 Python 腳本中呼叫的,所以回呼對於每個 API 在給定程序中只會觸發一次。
c10::SetAPIUsageHandler 可用於註冊 API 使用情況檢測處理常式。傳遞的參數將是一個「API 金鑰」,用於識別使用的點,例如用於 PyTorch 擴充匯入的 python.import 或在觸發 TorchScript 編譯時使用的 torch.script.compile。
SetAPIUsageLogger([](const std::string& event_name) {
    std::cerr << "API was used: " << event_name << std::endl;
});
開發者注意事項:可以使用 C++ 中的 C10_LOG_API_USAGE_ONCE("my_api") 或 Python 中的 torch._C._log_api_usage_once("my.api") 在程式碼中新增新的 API 觸發點。
將中繼資料附加到已儲存的 TorchScript 模型¶
TorchScript 模組可以儲存為一個封存檔案,其中包含序列化參數和 TorchScript 的模組程式碼(請參閱 torch.jit.save())。將其他資訊與模型捆綁在一起通常很方便,例如模型產生器的描述或輔助成品。
這可以透過將 _extra_files 參數傳遞給 torch.jit.save() 和 torch::jit::load 來實現,以便在儲存過程中儲存和擷取任意二進制 Blob。由於 TorchScript 檔案是常規的 ZIP 封存檔案,因此額外資訊會作為常規檔案儲存在封存檔案的 extra/ 目錄中。
還有一個全域掛鉤,允許將額外檔案附加到目前程序中產生的任何 TorchScript 封存檔案。這對於使用產生器中繼資料標記模型可能很有用,類似於數位相機產生的 JPEG 中繼資料。範例用法可能如下所示
SetExportModuleExtraFilesHook([](const Module&) {
    ExtraFilesMap files;
    files["producer_info.json"] = "{\"user\": \"" + getenv("USER") + "\"}";
    return files;
});
建置環境注意事項¶
TorchScript 的編譯需要存取原始 Python 檔案,因為它使用 Python 的 inspect.getsource 呼叫。在某些生產環境中,可能需要明確地將 .py 檔案與預先編譯的 .pyc 檔案一起部署。
常見的擴展點¶
PyTorch API 通常是鬆散耦合的,很容易用專門的版本替換組件。常見的擴展點包括:
- 使用 C++ 實作的自定義運算子 - 請參閱教學了解更多詳細信息。 
- 自定義數據讀取通常可以直接通過調用相應的 Python 庫來集成。通過擴展 - torch.utils.data的- Dataset或- IterableDataset,可以利用其現有功能。