Membuat jenis baru yang dapat diservis

Dokumen ini menjelaskan cara memperluas Penyajian TensorFlow dengan jenis servis baru. Jenis servable yang paling menonjol adalah SavedModelBundle , namun dapat berguna untuk menentukan jenis servable lainnya, untuk menyajikan data yang sesuai dengan model Anda. Contohnya meliputi: tabel pencarian kosakata, logika transformasi fitur. Kelas C++ apa pun bisa menjadi servable, misalnya int , std::map<string, int> atau kelas apa pun yang ditentukan dalam biner Anda -- sebut saja YourServable .

Mendefinisikan Loader dan SourceAdapter untuk YourServable

Untuk mengaktifkan TensorFlow Serving untuk mengelola dan menyajikan YourServable , Anda perlu menentukan dua hal:

  1. Kelas Loader yang memuat, menyediakan akses ke, dan membongkar instance YourServable .

  2. SourceAdapter yang membuat instance loader dari beberapa format data dasar, misalnya jalur sistem file. Sebagai alternatif dari SourceAdapter , Anda dapat menulis Source lengkap. Namun, karena pendekatan SourceAdapter lebih umum dan lebih modular, kami fokus pada pendekatan ini di sini.

Abstraksi Loader didefinisikan dalam core/loader.h . Hal ini mengharuskan Anda untuk menentukan metode untuk memuat, mengakses, dan membongkar jenis server Anda. Data yang memuat serverable dapat berasal dari mana saja, namun biasanya data tersebut berasal dari jalur sistem penyimpanan. Mari kita asumsikan hal itu terjadi pada YourServable . Mari kita asumsikan lebih lanjut bahwa Anda sudah memiliki Source<StoragePath> yang Anda sukai (jika belum, lihat dokumen Sumber Kustom ).

Selain Loader Anda, Anda perlu menentukan SourceAdapter yang membuat instance Loader dari jalur penyimpanan tertentu. Kebanyakan kasus penggunaan sederhana dapat menentukan dua objek secara ringkas dengan kelas SimpleLoaderSourceAdapter (dalam core/simple_loader.h ). Kasus penggunaan tingkat lanjut dapat memilih untuk menentukan kelas Loader dan SourceAdapter secara terpisah menggunakan API tingkat yang lebih rendah, misalnya jika SourceAdapter perlu mempertahankan beberapa status, dan/atau jika status perlu dibagikan di antara instance Loader .

Ada implementasi referensi dari hashmap servable sederhana yang menggunakan SimpleLoaderSourceAdapter di servables/hashmap/hashmap_source_adapter.cc . Anda mungkin merasa nyaman untuk membuat salinan HashmapSourceAdapter dan kemudian memodifikasinya sesuai kebutuhan Anda.

Implementasi HashmapSourceAdapter memiliki dua bagian:

  1. Logika untuk memuat peta hash dari file, di LoadHashmapFromFile() .

  2. Penggunaan SimpleLoaderSourceAdapter untuk mendefinisikan SourceAdapter yang memancarkan pemuat peta hash berdasarkan LoadHashmapFromFile() . SourceAdapter baru dapat dibuat instance-nya dari pesan protokol konfigurasi bertipe HashmapSourceAdapterConfig . Saat ini, pesan konfigurasi hanya berisi format file, dan untuk tujuan implementasi referensi hanya didukung satu format sederhana.

    Perhatikan panggilan ke Detach() di destruktor. Panggilan ini diperlukan untuk menghindari perlombaan antara penghancuran status dan pemanggilan lambda Pencipta yang sedang berlangsung di thread lain. (Meskipun adaptor sumber sederhana ini tidak memiliki status apa pun, kelas dasar tetap memaksa Detach() dipanggil.)

Mengatur objek YourServable untuk dimuat di manajer

Berikut adalah cara menghubungkan loader SourceAdapter for YourServable baru Anda ke sumber dasar jalur penyimpanan, dan manajer (dengan penanganan kesalahan yang buruk; kode sebenarnya harus lebih berhati-hati):

Pertama, buat manajer:

std::unique_ptr<AspiredVersionsManager> manager = ...;

Kemudian, buat adaptor sumber YourServable dan sambungkan ke manajer:

auto your_adapter = new YourServableSourceAdapter(...);
ConnectSourceToTarget(your_adapter, manager.get());

Terakhir, buat sumber jalur sederhana dan sambungkan ke adaptor Anda:

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());

Mengakses objek YourServable yang dimuat

Berikut ini cara menangani YourServable yang dimuat, dan menggunakannya:

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();

Lanjutan: Mengatur beberapa instance yang dapat diservis untuk berbagi status

SourceAdapters dapat menampung status yang dibagikan di antara beberapa server yang dipancarkan. Misalnya:

  • Kumpulan thread bersama atau sumber daya lain yang digunakan oleh beberapa server.

  • Struktur data read-only bersama yang digunakan oleh beberapa servable, untuk menghindari overhead waktu dan ruang dalam mereplikasi struktur data di setiap instance servable.

Status bersama yang waktu dan ukurannya inisialisasi dapat diabaikan (misalnya kumpulan thread) dapat dibuat dengan cepat oleh SourceAdapter, yang kemudian menyematkan penunjuk ke status tersebut di setiap pemuat yang dapat diservis yang dipancarkan. Pembuatan status bersama yang mahal atau besar harus ditunda ke panggilan Loader::Load() pertama yang berlaku, yaitu diatur oleh pengelola. Secara simetris, panggilan Loader::Unload() ke server akhir yang menggunakan status bersama mahal/besar akan menghancurkannya.