מסמך זה מסביר כיצד להרחיב את TensorFlow Serving עם סוג חדש של הגשה. הסוג הבולט ביותר שניתן להגשה הוא SavedModelBundle
, אבל זה יכול להיות שימושי להגדיר סוגים אחרים של שרתים, כדי להגיש נתונים שמשתלבים עם המודל שלך. דוגמאות כוללות: טבלת חיפוש אוצר מילים, לוגיקה של שינוי תכונות. כל מחלקה C++ יכולה להיות שרת, למשל int
, std::map<string, int>
או כל מחלקה המוגדרת בבינארי שלך -- בואו נקרא לזה YourServable
.
הגדרת Loader
SourceAdapter
עבור YourServable
כדי לאפשר ל-TensorFlow Serving לנהל ולשרת YourServable
, עליך להגדיר שני דברים:
מחלקה
Loader
שטוענת, מספקת גישה ופורקת מופע שלYourServable
.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
כולל שני חלקים:
ההיגיון לטעון hashmap מקובץ, ב-
LoadHashmapFromFile()
.השימוש ב-
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() לשרת הסופי באמצעות המצב המשותף יקר/גדול אמורה להרוס אותו.