이 문서에서는 새로운 종류의 서빙 가능 항목으로 TensorFlow Serving을 확장하는 방법을 설명합니다. 가장 눈에 띄는 servable 유형은 SavedModelBundle
이지만, 모델과 함께 제공되는 데이터를 제공하기 위해 다른 종류의 servable을 정의하는 것이 유용할 수 있습니다. 예에는 어휘 조회 테이블, 기능 변환 논리가 포함됩니다. 모든 C++ 클래스는 servable이 될 수 있습니다(예: int
, std::map<string, int>
또는 바이너리에 정의된 모든 클래스). 이를 YourServable
이라고 부르겠습니다.
YourServable
에 대한 Loader
및 SourceAdapter
정의
TensorFlow Serving이 YourServable
관리하고 제공할 수 있도록 하려면 다음 두 가지를 정의해야 합니다.
YourServable
인스턴스를 로드, 액세스 제공 및 언로드하는Loader
클래스입니다.파일 시스템 경로와 같은 일부 기본 데이터 형식에서 로더를 인스턴스화하는
SourceAdapter
.SourceAdapter
대신 완전한Source
작성할 수 있습니다. 그러나SourceAdapter
접근 방식이 더 일반적이고 모듈화되어 있으므로 여기서는 이에 중점을 둡니다.
Loader
추상화는 core/loader.h
에 정의되어 있습니다. 이를 위해서는 제공 가능 유형을 로드, 액세스 및 언로드하기 위한 메소드를 정의해야 합니다. 서버블이 로드되는 데이터는 어디에서나 가져올 수 있지만 일반적으로 스토리지 시스템 경로에서 가져옵니다. YourServable
의 경우가 그렇다고 가정해 보겠습니다. 만족스러운 Source<StoragePath>
가 이미 있다고 가정해 보겠습니다(그렇지 않은 경우 사용자 정의 소스 문서 참조).
Loader
외에도 지정된 스토리지 경로에서 Loader
를 인스턴스화하는 SourceAdapter
정의해야 합니다. 대부분의 간단한 사용 사례에서는 SimpleLoaderSourceAdapter
클래스( core/simple_loader.h
에 있음)를 사용하여 두 객체를 간결하게 지정할 수 있습니다. 고급 사용 사례에서는 하위 수준 API를 사용하여 Loader
및 SourceAdapter
클래스를 별도로 지정하도록 선택할 수 있습니다(예: SourceAdapter
가 일부 상태를 유지해야 하는 경우 및/또는 Loader
인스턴스 간에 상태를 공유해야 하는 경우).
servables/hashmap/hashmap_source_adapter.cc
에 SimpleLoaderSourceAdapter
사용하는 간단한 해시맵 서버블의 참조 구현이 있습니다. HashmapSourceAdapter
의 복사본을 만든 다음 필요에 맞게 수정하는 것이 편리할 수 있습니다.
HashmapSourceAdapter
의 구현은 두 부분으로 구성됩니다.
LoadHashmapFromFile()
의 파일에서 해시맵을 로드하는 논리입니다.LoadHashmapFromFile()
기반으로 해시맵 로더를 내보내는SourceAdapter
정의하기 위해SimpleLoaderSourceAdapter
를 사용합니다. 새로운SourceAdapter
HashmapSourceAdapterConfig
유형의 구성 프로토콜 메시지에서 인스턴스화될 수 있습니다. 현재 구성 메시지에는 파일 형식만 포함되어 있으며 참조 구현을 위해 단일 단순 형식만 지원됩니다.소멸자에서
Detach()
호출을 확인하세요. 이 호출은 상태 해제와 다른 스레드에서 진행 중인 Creator 람다 호출 간의 경합을 방지하는 데 필요합니다. (이 간단한 소스 어댑터에는 상태가 없더라도 기본 클래스는 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();
고급: 여러 제공 가능 인스턴스가 상태를 공유하도록 준비
SourceAdapters는 여러 방출된 servable 간에 공유되는 상태를 수용할 수 있습니다. 예를 들어:
여러 servable이 사용하는 공유 스레드 풀 또는 기타 리소스입니다.
각 제공 가능 인스턴스에서 데이터 구조를 복제하는 데 따른 시간 및 공간 오버헤드를 피하기 위해 여러 제공 가능 항목이 사용하는 공유 읽기 전용 데이터 구조입니다.
초기화 시간과 크기가 무시할 수 있는 공유 상태(예: 스레드 풀)는 SourceAdapter에 의해 열심히 생성될 수 있으며, 그런 다음 방출된 각 제공 가능 로더에 이에 대한 포인터를 포함합니다. 비용이 많이 들거나 큰 공유 상태의 생성은 적용 가능한 첫 번째 Loader::Load() 호출로 연기되어야 합니다. 즉, 관리자가 관리합니다. 대칭적으로 비용이 많이 드는/큰 공유 상태를 사용하여 최종 servable에 대한 Loader::Unload() 호출은 이를 분해해야 합니다.