새로운 종류의 서비스 가능 항목 만들기

이 문서에서는 새로운 종류의 서빙 가능 항목으로 TensorFlow Serving을 확장하는 방법을 설명합니다. 가장 눈에 띄는 servable 유형은 SavedModelBundle 이지만, 모델과 함께 제공되는 데이터를 제공하기 위해 다른 종류의 servable을 정의하는 것이 유용할 수 있습니다. 예에는 어휘 조회 테이블, 기능 변환 논리가 포함됩니다. 모든 C++ 클래스는 servable이 될 수 있습니다(예: int , std::map<string, int> 또는 바이너리에 정의된 모든 클래스). 이를 YourServable 이라고 부르겠습니다.

YourServable 에 대한 LoaderSourceAdapter 정의

TensorFlow Serving이 YourServable 관리하고 제공할 수 있도록 하려면 다음 두 가지를 정의해야 합니다.

  1. YourServable 인스턴스를 로드, 액세스 제공 및 언로드하는 Loader 클래스입니다.

  2. 파일 시스템 경로와 같은 일부 기본 데이터 형식에서 로더를 인스턴스화하는 SourceAdapter . SourceAdapter 대신 완전한 Source 작성할 수 있습니다. 그러나 SourceAdapter 접근 방식은 더 일반적이고 모듈화되어 있으므로 여기서는 이에 중점을 둡니다.

Loader 추상화는 core/loader.h 에 정의되어 있습니다. 이를 위해서는 제공 가능 유형을 로드, 액세스 및 언로드하기 위한 메소드를 정의해야 합니다. 서버블이 로드되는 데이터는 어디에서나 가져올 수 있지만 일반적으로 스토리지 시스템 경로에서 가져옵니다. YourServable 의 경우가 그렇다고 가정해 보겠습니다. 만족스러운 Source<StoragePath> 가 이미 있다고 가정해 보겠습니다(그렇지 않은 경우 사용자 정의 소스 문서 참조).

Loader 외에도 지정된 스토리지 경로에서 Loader 를 인스턴스화하는 SourceAdapter 정의해야 합니다. 대부분의 간단한 사용 사례에서는 SimpleLoaderSourceAdapter 클래스( core/simple_loader.h 에 있음)를 사용하여 두 객체를 간결하게 지정할 수 있습니다. 고급 사용 사례에서는 하위 수준 API를 사용하여 LoaderSourceAdapter 클래스를 별도로 지정하도록 선택할 수 있습니다(예: SourceAdapter 가 일부 상태를 유지해야 하는 경우 및/또는 Loader 인스턴스 간에 상태를 공유해야 하는 경우).

servables/hashmap/hashmap_source_adapter.ccSimpleLoaderSourceAdapter 사용하는 간단한 해시맵 서버블의 참조 구현이 있습니다. HashmapSourceAdapter 의 복사본을 만든 다음 필요에 맞게 수정하는 것이 편리할 수 있습니다.

HashmapSourceAdapter 의 구현은 두 부분으로 구성됩니다.

  1. LoadHashmapFromFile() 의 파일에서 해시맵을 로드하는 논리입니다.

  2. 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() 호출은 이를 분해해야 합니다.