יצירת סוג חדש של הגשה

מסמך זה מסביר כיצד להרחיב את TensorFlow Serving עם סוג חדש של הגשה. הסוג הבולט ביותר שניתן להגשה הוא SavedModelBundle , אבל זה יכול להיות שימושי להגדיר סוגים אחרים של שרתים, כדי להגיש נתונים שמשתלבים עם המודל שלך. דוגמאות כוללות: טבלת חיפוש אוצר מילים, לוגיקה של שינוי תכונות. כל מחלקה C++ יכולה להיות שרת, למשל int , std::map<string, int> או כל מחלקה המוגדרת בבינארי שלך -- בואו נקרא לזה YourServable .

הגדרת Loader ומתאם SourceAdapter עבור YourServable

כדי לאפשר ל-TensorFlow Serving לנהל ולשרת YourServable , עליך להגדיר שני דברים:

  1. מחלקה Loader שטוענת, מספקת גישה ופורקת מופע של YourServable .

  2. SourceAdapter שמציג מעמיסים מפורמט נתונים בסיסי כלשהו, ​​למשל נתיבים של מערכת קבצים. כחלופה למתאם SourceAdapter , אתה יכול לכתוב Source שלם. עם זאת, מכיוון שגישת SourceAdapter נפוצה יותר ומודולרית יותר, אנו מתמקדים בה כאן.

ההפשטה Loader מוגדרת ב- core/loader.h . זה דורש ממך להגדיר שיטות לטעינה, גישה ופריקה של סוג ההגשה שלך. הנתונים שמהם נטען ה-servable יכולים להגיע מכל מקום, אבל מקובל שהם מגיעים מנתיב מערכת אחסון. הבה נניח שזה המקרה עבור YourServable . תן לנו עוד נניח שכבר יש לך Source<StoragePath> שאתה מרוצה ממנו (אם לא, עיין במסמך המקור המותאם אישית ).

בנוסף ל- Loader שלך, תצטרך להגדיר SourceAdapter שמציג גורם ל- Loader מנתיב אחסון נתון. רוב מקרי השימוש הפשוטים יכולים לציין את שני האובייקטים בצורה תמציתית עם המחלקה SimpleLoaderSourceAdapter (ב- core/simple_loader.h ). מקרי שימוש מתקדמים עשויים לבחור לציין מחלקות Loader ו- SourceAdapter בנפרד באמצעות ממשקי API ברמה נמוכה יותר, למשל אם ה- SourceAdapter צריך לשמור על מצב מסוים, ו/או אם המצב צריך להיות משותף בין מופעי Loader .

יש יישום ייחוס של קובץ hashmap פשוט להגשה המשתמש SimpleLoaderSourceAdapter ב- servables/hashmap/hashmap_source_adapter.cc . ייתכן שיהיה לך נוח ליצור עותק של HashmapSourceAdapter ולאחר מכן לשנות אותו כך שיתאים לצרכים שלך.

היישום של HashmapSourceAdapter כולל שני חלקים:

  1. ההיגיון לטעון hashmap מקובץ, ב- LoadHashmapFromFile() .

  2. השימוש ב- SimpleLoaderSourceAdapter כדי להגדיר SourceAdapter שפולט מטעני hashmap המבוססים על LoadHashmapFromFile() . ניתן ליצור את SourceAdapter החדש מהודעת פרוטוקול תצורה מסוג HashmapSourceAdapterConfig . נכון לעכשיו, הודעת התצורה מכילה רק את פורמט הקובץ, ולצורך יישום ההפניה נתמך רק פורמט פשוט יחיד.

    שימו לב לקריאה ל- Detach() ב-Destructor. קריאה זו נדרשת כדי להימנע ממרוצים בין מצב הרס לבין כל קריאות מתמשכות של הבורא למבדה בשרשורים אחרים. (למרות שלמתאם המקור הפשוט הזה אין שום מצב, מחלקת הבסיס בכל זאת אוכפת שה-Detach() נקרא.)

הסדרת טעינת אובייקטי YourServable במנהל

הנה איך לחבר את SourceAdapter החדש שלך למעמיסי YourServable למקור בסיסי של נתיבי אחסון ולמנהל (עם טיפול שגיאות גרוע; קוד אמיתי צריך להיות זהיר יותר):

ראשית, צור מנהל:

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, אשר לאחר מכן מטמיע אליו מצביע בכל מטעין הניתן להגשה שנפלט. יש לדחות יצירת מצב משותף יקר או גדול לקריאה הראשונה הרלוונטית ל-Loader::Load(), כלומר מנוהלת על ידי המנהל. באופן סימטרי, הקריאה Loader::Unload() לשרת הסופי באמצעות המצב המשותף יקר/גדול אמורה להרוס אותו.