本文說明如何使用新型可伺服物件擴充 TensorFlow Serving。最常見的可伺服物件類型是 SavedModelBundle
,但定義其他類型的可伺服物件也相當實用,以便提供與模型搭配使用的資料。範例包括:詞彙查閱表、特徵轉換邏輯。任何 C++ 類別都可以是可伺服物件,例如 int
、std::map<string, int>
或二進位檔中定義的任何類別 (我們將其稱為 YourServable
)。
為 YourServable
定義 Loader
和 SourceAdapter
如要讓 TensorFlow Serving 管理及提供 YourServable
服務,您需要定義兩件事:
Loader
類別,用於載入、提供存取權及卸載YourServable
的執行個體。SourceAdapter
,可從某些基礎資料格式 (例如檔案系統路徑) 具現化載入器。除了SourceAdapter
之外,您也可以編寫完整的Source
。不過,由於SourceAdapter
方法更常見且更模組化,因此我們在此著重說明這個方法。
Loader
抽象化定義於 core/loader.h
中。您必須定義方法,才能載入、存取及卸載您的可伺服物件類型。載入可伺服物件的資料可能來自任何位置,但通常來自儲存系統路徑。假設 YourServable
的情況就是如此。進一步假設您已經有符合需求的 Source<StoragePath>
(如果沒有,請參閱自訂 Source 文件)。
除了 Loader
之外,您還需要定義 SourceAdapter
,以便從指定的儲存路徑具現化 Loader
。SimpleLoaderSourceAdapter
類別 (位於 core/simple_loader.h
中) 可簡潔地指定大多數簡單的使用情境。進階使用情境可能會選擇使用較低階的 API 分別指定 Loader
和 SourceAdapter
類別,例如,如果 SourceAdapter
需要保留某些狀態,及/或狀態需要在 Loader
執行個體之間共用。
在 servables/hashmap/hashmap_source_adapter.cc
中,有使用 SimpleLoaderSourceAdapter
的簡易雜湊對應表可伺服物件的參考實作範例。您可能會覺得複製 HashmapSourceAdapter
,然後修改成符合自身需求的形式會很方便。
HashmapSourceAdapter
的實作包含兩個部分:
從檔案載入雜湊對應表的邏輯,位於
LoadHashmapFromFile()
中。使用
SimpleLoaderSourceAdapter
定義SourceAdapter
,以便根據LoadHashmapFromFile()
產生雜湊對應表載入器。新的SourceAdapter
可以從HashmapSourceAdapterConfig
類型的設定通訊協定訊息中具現化。目前,設定訊息僅包含檔案格式,且為了參考實作範例,僅支援單一簡易格式。請注意解構函式中對
Detach()
的呼叫。這個呼叫是避免在拆解狀態與其他執行緒中 Creator lambda 的任何進行中調用之間發生競爭狀況所必要的。(即使這個簡易來源配接器沒有任何狀態,基底類別仍然會強制呼叫 Detach()。)
安排將 YourServable
物件載入管理工具
以下說明如何將新的 YourServable
載入器 SourceAdapter
連結到儲存路徑的基本來源和管理工具 (錯誤處理不佳;實際程式碼應更加謹慎)
首先,建立管理工具
std::unique_ptr<AspiredVersionsManager> manager = ...;
接著,建立 YourServable
來源配接器,並將其插入管理工具
auto your_adapter = new YourServableSourceAdapter(...);
ConnectSourceToTarget(your_adapter, manager.get());
最後,建立簡易路徑來源,並將其插入配接器
std::unique_ptr<FileSystemStoragePathSource> path_source;
// Here are some FileSystemStoragePathSource config settings that ought to get
// it working, but for details please see its documentation.
FileSystemStoragePathSourceConfig config;
// We just have a single servable stream. Call it "default".
config.set_servable_name("default");
config.set_base_path(FLAGS::base_path /* base path for our servable files */);
config.set_file_system_poll_wait_seconds(1);
TF_CHECK_OK(FileSystemStoragePathSource::Create(config, &path_source));
ConnectSourceToTarget(path_source.get(), your_adapter.get());
存取已載入的 YourServable
物件
以下說明如何取得已載入 YourServable
的控制代碼並加以使用
auto handle_request = serving::ServableRequest::Latest("default");
ServableHandle<YourServable*> servable;
Status status = manager->GetServableHandle(handle_request, &servable);
if (!status.ok()) {
LOG(INFO) << "Zero versions of 'default' servable have been loaded so far";
return;
}
// Use the servable.
(*servable)->SomeYourServableMethod();
進階:安排讓多個可伺服物件執行個體共用狀態
SourceAdapter 可以存放多個發出的可伺服物件之間共用的狀態。例如:
多個可伺服物件使用的共用執行緒集區或其他資源。
多個可伺服物件使用的共用唯讀資料結構,以避免在每個可伺服物件執行個體中複製資料結構的時間和空間成本。
初始化時間和大小可忽略不計的共用狀態 (例如執行緒集區) 可由 SourceAdapter 搶先建立,然後 SourceAdapter 會在每個發出的可伺服物件載入器中嵌入指向該狀態的指標。昂貴或大型共用狀態的建立作業應延後到第一個適用的 Loader::Load() 呼叫,也就是由管理工具控管。對稱地,對使用昂貴/大型共用狀態的最終可伺服物件發出的 Loader::Unload() 呼叫應將其拆解。