1. לפני שמתחילים
במעבדה זו תלמדו איך להסיק מסקנות של סיווג תמונות מאתר באמצעות TensorFlow serving עם REST ו-gRPC.
דרישות מוקדמות
- ידע בסיסי בפיתוח אתרים, כמו HTML ו-JavaScript
- ידע בסיסי על למידה חישובית עם TensorFlow, כמו הדרכה ופריסה
- ידע בסיסי על מסופים ותחנות עגינה
מה תלמדו
- איך למצוא מודלים ייעודיים לסיווג תמונות ב-TensorFlow Hub.
- איך לבנות אתר פשוט וליצור תחזיות עם מודל סיווג התמונות שהורד באמצעות TensorFlow serving (REST ו-gRPC).
- איך לעבד את תוצאת הזיהוי בממשק המשתמש.
מה תצטרך להכין
- אביזר עגינה
- Google Chrome
- שרת האינטרנט ל-Chrome
- Node.js ו-NPM
- שיט
- מהדר protocol buffer (נדרש רק אם רוצים ליצור מחדש את סקר ה-gRPC בעצמך)
- פלאגין מחולל קוד אינטרנט gRPC (נדרש רק אם רוצים ליצור מחדש את srPC stub לבד)
2. להגדרה
כדי להוריד את הקוד של Lab Lab זה:
- עוברים אל מאגר GitHub.
- לוחצים על Code > הורדת zip כדי להוריד את כל הקוד של מעבדת הקוד הזו.
- יש לבטל את הדחיסה של קובץ ה-ZIP שהורדת כדי לפתוח את תיקיית הבסיס של
codelabs
עם כל המשאבים.
ב-codelab זה נדרשים רק הקבצים בספריית המשנה TFServing/ImageClassificationWeb
במאגר, המכיל שתי תיקיות:
- התיקייה
starter
מכילה את הקוד למתחילים שעליו כדאי לבנות את מעבדת הקוד הזו. - התיקייה
finished
מכילה את הקוד המלא לאפליקציה לדוגמה שהושלמה.
3. התקנת תלויות
כדי להתקין את התלות:
- במסוף, עוברים לתיקייה
starter
ומתקינים את החבילות הנדרשות של NPM:
npm install
4. להפעלת האתר למתחילים
משתמשים בשרת האינטרנט של Chrome כדי לטעון את הקובץ TFServing/ImageClassificationWeb/starter/dist/index.html
:
- מזינים את הכתובת
Chrome://apps/
בסרגל הכתובות של Chrome, ולאחר מכן מוצאים את שרת האינטרנט של Chrome ברשימת האפליקציות. - מפעילים את שרת האינטרנט של Chrome ובוחרים את התיקייה
TFServing/ImageClassificationWeb/starter/dist/
. - לוחצים על לחצן החלפת המצב שרת אינטרנט כדי להפעיל אותו ואז מנווטים אל http://localhost:8887/ בדפדפן.
הפעלה ועיון באתר
אתם אמורים לראות את האתר עכשיו. ממשק המשתמש די פשוט: יש תמונת חתול שבה רוצים לסווג את המשתמש, והיא יכולה לשלוח את הנתונים לקצה העורפי באמצעות REST או gRPC. הקצה העורפי מבצע סיווג תמונות בתמונה ומחזיר לאתר את תוצאת הסיווג, שבה מוצגת התוצאה.
אם לוחצים על סיווג, לא יקרה דבר כי עדיין לא ניתן לתקשר עם הקצה העורפי.
5. פריסת מודל של סיווג תמונה באמצעות TensorFlow
סיווג תמונה הוא משימה נפוצה מאוד מסוג סיווג ML שמסווגת תמונה לקטגוריות לפי הגדרה מראש, על סמך התוכן העיקרי של התמונה. הנה דוגמה לסיווג פרחים:
ב-TensorFlow Hub יש כמה מודלים לסיווג תמונות. אתם משתמשים במודל Inccetion v3 פופולרי ל-Codelab הזה.
כדי לפרוס את מודל סיווג התמונות באמצעות TensorFlow serving:
- מורידים את קובץ המודל התחלה של גרסה 3.
- יש לבטל את הדחיסה של הקובץ
.tar.gz
שהורדת באמצעות כלי לדחיסת נתונים, כמו 7-Zip. - יצירת תיקייה של
inception_v3
ולאחר מכן יצירת תיקיית משנה123
. - מעבירים את התיקייה
variables
ואת הקובץsaved_model.pb
לתיקיית המשנה של123
.
אפשר להתייחס לתיקייה inception_v3
כתיקייה של SavedModel
. 123
הוא מספר גרסה לדוגמה. אפשר לבחור מספר אחר.
מבנה התיקייה אמור להיראות כך:
התחלת הצגה של TensorFlow
- במסוף, פותחים את TensorFlow serving עם Docker, אבל מחליפים את
PATH/TO/SAVEDMODEL
בנתיב המוחלט של התיקייהinception_v3
במחשב.
docker pull tensorflow/serving docker run -it --rm -p 8500:8500 -p 8501:8501 -v "PATH/TO/SAVEDMODEL:/models/inception" -e MODEL_NAME=inception tensorflow/serving
אביזר העגינה מוריד תחילה באופן אוטומטי את התמונה של TensorFlow serving. לאחר מכן, ההצגה של TensorFlow אמורה להתחיל. היומן אמור להיראות כמו קטע קוד זה:
2022-02-25 06:01:12.513231: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:206] Restoring SavedModel bundle. 2022-02-25 06:01:12.585012: I external/org_tensorflow/tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 3000000000 Hz 2022-02-25 06:01:13.395083: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:190] Running initialization op on SavedModel bundle at path: /models/inception/123 2022-02-25 06:01:13.837562: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:277] SavedModel load for tags { serve }; Status: success: OK. Took 1928700 microseconds. 2022-02-25 06:01:13.877848: I tensorflow_serving/servables/tensorflow/saved_model_warmup_util.cc:59] No warmup data file found at /models/inception/123/assets.extra/tf_serving_warmup_requests 2022-02-25 06:01:13.929844: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: inception version: 123} 2022-02-25 06:01:13.985848: I tensorflow_serving/model_servers/server_core.cc:486] Finished adding/updating models 2022-02-25 06:01:13.985987: I tensorflow_serving/model_servers/server.cc:367] Profiler service is enabled 2022-02-25 06:01:13.988994: I tensorflow_serving/model_servers/server.cc:393] Running gRPC ModelServer at 0.0.0.0:8500 ... [warn] getaddrinfo: address family for nodename not supported 2022-02-25 06:01:14.033872: I tensorflow_serving/model_servers/server.cc:414] Exporting HTTP/REST API at:localhost:8501 ... [evhttp_server.cc : 245] NET_LOG: Entering the event loop ...
6. הגדרת proxy של Envoy
נכון לעכשיו, שירות TensorFlow לא מגדיר את הכותרת Access-Control-Allow-Origin
, כך שהדפדפן חוסם את הבקשה מ-JavaScript בממשק הקצה ל-TensorFlow הגשה מסיבות אבטחה. כדי לפתור את הבעיה, צריך להשתמש בשרת proxy, כמו Envoy, כדי להעביר את הבקשה מ-JavaScript לקצה העורפי של TensorFlow.
להתחיל לעיתים קרובות
- במסוף, מורידים את התמונה של Envoy ומתחילים ב-Anvoy עם Docker, אבל מחליפים את ה-placeholder
PATH/TO/ENVOY-CUSTOM.YAML
בנתיב המוחלט של הקובץenvoy-custom.yaml
בתיקייהstarter
.
docker pull envoyproxy/envoy-dev:fd3e8370ddb7a96634c192d1461516e6de1d1797 docker run --add-host host.docker.internal:host-gateway --rm -it -p 9901:9901 -p 8000:8000 -p 8080:8080 -v PATH/TO/ENVOY-CUSTOM.YAML:/envoy-custom.yaml envoyproxy/envoy-dev:fd3e8370ddb7a96634c192d1461516e6de1d1797 -c /envoy-custom.yaml
אביזר העגינה מוריד תחילה את תמונת Envoy באופן אוטומטי. לאחר מכן, Envoy צריך להתחיל. היומן אמור להיראות כמו קטע קוד זה:
[2022-03-02 07:51:48.563][1][info][main] [source/server/server.cc:436] response trailer map: 152 bytes: grpc-message,grpc-status [2022-03-02 07:51:48.681][1][info][main] [source/server/server.cc:772] runtime: {} [2022-03-02 07:51:48.682][1][info][admin] [source/server/admin/admin.cc:134] admin address: 0.0.0.0:9901 [2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:127] loading tracing configuration [2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:87] loading 0 static secret(s) [2022-03-02 07:51:48.683][1][info][config] [source/server/configuration_impl.cc:93] loading 2 cluster(s) [2022-03-02 07:51:48.687][1][info][config] [source/server/configuration_impl.cc:97] loading 2 listener(s) [2022-03-02 07:51:48.694][1][info][config] [source/server/configuration_impl.cc:109] loading stats configuration [2022-03-02 07:51:48.696][1][info][main] [source/server/server.cc:868] starting main dispatch loop [2022-03-02 07:51:48.881][1][info][runtime] [source/common/runtime/runtime_impl.cc:446] RTDS has finished initialization [2022-03-02 07:51:48.881][1][info][upstream] [source/common/upstream/cluster_manager_impl.cc:207] cm init: all clusters initialized [2022-03-02 07:51:48.881][1][info][main] [source/server/server.cc:849] all clusters initialized. initializing init manager [2022-03-02 07:51:48.881][1][info][config] [source/server/listener_manager_impl.cc:784] all dependencies initialized. starting workers [2022-03-02 07:51:48.902][1][warning][main] [source/server/server.cc:747] there is no configured limit to the number of allowed active connections. Set a limit via the runtime key overload.global_downstream_max_connections
7. חיבור האתר עם TensorFlow דרך REST
הקצה העורפי מוכן עכשיו כדי שאפשר יהיה לשלוח בקשות לקוחות ל-TensorFlow serving כדי לסווג תמונות. יש שתי דרכים לשלוח בקשות ל-TensorFlow serving:
- REST
- gRPC
שליחת בקשות וקבלת תשובות דרך REST
יש שלושה שלבים פשוטים לשליחה וקבלה של בקשות דרך REST:
- יוצרים את הבקשה ל-REST.
- שליחת בקשת REST להגשה של TensorFlow.
- יש לחלץ את התוצאה הצפויה מתגובת ה-REST ולהציג את התוצאה.
עליך להשיג את השלבים האלה בקובץ src/index.js
.
יצירה של בקשת REST
נכון לעכשיו, הפונקציה classify_img()
לא שולחת את הבקשה ל-REST להצגת TensorFlow. כדי ליצור בקשת REST, תחילה יש להטמיע את סניף REST:
if (radioButtons[0].checked) {
console.log('Using REST');
// TODO: Add code to send a REST request to TensorFlow Serving.
}
להצגה של TensorFlow יש בקשת POST שמכילה את טנור התמונה של מודל Inciption v3 שבו אתם משתמשים. לכן, עליכם לחלץ את ערכי ה-RGB מכל פיקסל של התמונה במערך, ולאחר מכן לכווץ את המערך ב-JSON, שהוא המטען הייעודי של הבקשה.
- מוסיפים את הקוד הזה לסניף של REST:
//Create the REST request.
let imgTensor = new Array();
let pixelArray = new Array();
context.drawImage(img, 0, 0);
for(let i=0; i<inputImgHeight; i++) {
pixelArray[i] = new Array();
for (let j=0; j<inputImgWidth; j++) {
pixelArray[i][j] = new Array();
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[0]/255);
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[1]/255);
pixelArray[i][j].push(context.getImageData(i, j, 1, 1).data[2]/255);
}
}
imgTensor.push(pixelArray);
const RESTURL = 'http://localhost:8000/v1/models/inception:predict';
let xhr = new XMLHttpRequest();
xhr.open('POST', RESTURL);
xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8;');
let data = JSON.stringify({
instances: imgTensor
});
xhr.onload = () => {
}
xhr.onerror = () => {
console.log('REST request error');
}
שליחת בקשת REST להצגת TensorFlow
עכשיו אפשר לשלוח את הבקשה.
- צריך להוסיף את הקוד מיד אחרי הקוד שלמעלה בסניף של REST:
// Send the REST request.
xhr.send(data);
עיבוד התגובה ל-REST מ-TensorFlow serving
מודל מודל v3 מחזיר מערך של הסתברות שהתמונה שייכת לקטגוריות שהוגדרו מראש. כאשר החיזוי מצליח, עליך ליצור פלט של הקטגוריה הסבירה ביותר בממשק המשתמש.
אתם מטמיעים את ההאזנה של onload()
כדי לטפל בתגובה.
xhr.onload = () => {
}
- יש להוסיף את הקוד הזה לפונקציות מסוג listener של
onload()
:
// Process the REST response.
const response = JSON.parse(xhr.responseText);
const maxIndex = argmax(response['predictions'][0])
document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;
עכשיו המאזינים מחלצים את הההסתברות החזויה מהתגובה, מזהה את הקטגוריה הסבירה ביותר של האובייקט ומציגת את התוצאה בממשק המשתמש.
הפעלה
- בטרמינל, עוברים אל התיקייה
starter
ומשתמשים בחבילה באינטרנט כדי לקבץ את כל קובצי ה-JavaScript לקובץ אחד שאפשר להטמיע בקובץdist/index.html
:
npm install -g npx npm install --save-dev webpack npx webpack
- מרעננים את הכתובת http://localhost:8887/ בדפדפן ולוחצים על REST > Classify.
האתר מציג את 286
כקטגוריה החזויה, שממפה לתווית Egyptian Cat
במערך הנתונים של תמונהNet.
8. חיבור האתר ל-TenororFlow serving דרך gRPC
בנוסף ל-REST, שירות TensorFlow תומך גם ב-gRPC.
gRPC היא מסגרת מודרנית ומודרנית של ביצועים טובים (RTT) שניתן להפעיל בכל סביבה. הוא יכול לחבר שירותים ביעילות למרכזי נתונים, ובהם בתמיכה, לאיזון עומסים, למעקב אחר נתונים, לבדיקת תקינות ולאימות. התגלה ש-gRPC מניב ביצועים טובים יותר בהשוואה ל-REST בפועל.
שליחה של בקשות וקבלת תשובות באמצעות gRPC
יש ארבעה שלבים פשוטים:
- אופציונלי: יצירת קוד stub של לקוח gRPC.
- יוצרים את בקשת ה-gRPC.
- שליחת בקשת gRPC לשירות TensorFlow.
- מחלצים את התוצאה החזויה מהתגובה של gRPC ומציגים אותה בממשק המשתמש.
יש להשלים את השלבים האלה בקובץ src/index.js
.
אופציונלי: יצירת קוד stub של לקוח gRPC
כדי להשתמש ב-gRPC עם TensorFlow serving, יש לפעול לפי תהליך העבודה של gRPC. למידע נוסף, ניתן לעיין בתיעוד של gRPC.
TensorFlow הגשה ו-TensorFlow מגדירים את קובצי .proto
עבורך. נכון ל-TensorFlow ול-TensorFlow 2.8, אלו קובצי ה-.proto
הנדרשים:
tensorflow/core/example/example.proto
tensorflow/core/example/feature.proto
tensorflow/core/protobuf/struct.proto
tensorflow/core/protobuf/saved_object_graph.proto
tensorflow/core/protobuf/saver.proto
tensorflow/core/protobuf/trackable_object_graph.proto
tensorflow/core/protobuf/meta_graph.proto
tensorflow/core/framework/node_def.proto
tensorflow/core/framework/attr_value.proto
tensorflow/core/framework/function.proto
tensorflow/core/framework/types.proto
tensorflow/core/framework/tensor_shape.proto
tensorflow/core/framework/full_type.proto
tensorflow/core/framework/versions.proto
tensorflow/core/framework/op_def.proto
tensorflow/core/framework/graph.proto
tensorflow/core/framework/tensor.proto
tensorflow/core/framework/resource_handle.proto
tensorflow/core/framework/variable.proto
tensorflow_serving/apis/inference.proto
tensorflow_serving/apis/classification.proto
tensorflow_serving/apis/predict.proto
tensorflow_serving/apis/regression.proto
tensorflow_serving/apis/get_model_metadata.proto
tensorflow_serving/apis/input.proto
tensorflow_serving/apis/prediction_service.proto
tensorflow_serving/apis/model.proto
- במסוף שלך, עליך להיכנס לתיקייה
starter/src/proto/
וליצור את הסימן הבא:
bash generate_grpc_stub_js.sh
יצירת בקשת gRPC
בדומה לבקשת ה-REST, יוצרים את הבקשה ל-gRPC בסניף gRPC.
if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {
}
else {
print("Using gRPC")
// TODO: Add code to send a gRPC request to TensorFlow Serving.
}
- צריך להוסיף את הקוד לסניף של gRPC:
// Create the gRPC request.
const PredictModule = require('./proto/generated/tensorflow_serving/apis/predict_pb.js');
const PredictionServiceClientModule = require('./proto/generated/tensorflow_serving/apis/prediction_service_grpc_web_pb.js');
const ModelModule = require('./proto/generated/tensorflow_serving/apis/model_pb.js');
const TensorModule = require('./proto/generated/tensorflow/core/framework/tensor_pb.js');
const GPRCURL = 'http://localhost:8080';
const stub = new PredictionServiceClientModule.PredictionServiceClient(GPRCURL);
const modelSpec = new ModelModule.ModelSpec();
modelSpec.setName('inception');
const tensorProto = new TensorModule.TensorProto();
const tensorShapeProto = new TensorModule.TensorShapeProto();
const batchDim = (new TensorModule.TensorShapeProto.Dim()).setSize(1);
const heightDim = (new TensorModule.TensorShapeProto.Dim()).setSize(inputImgHeight);
const widthDim = (new TensorModule.TensorShapeProto.Dim()).setSize(inputImgWidth);
const channelDim = (new TensorModule.TensorShapeProto.Dim()).setSize(3);
tensorShapeProto.setDimList([batchDim, heightDim, widthDim, channelDim]);
tensorProto.setDtype(proto.tensorflow.DataType.DT_FLOAT);
tensorProto.setTensorShape(tensorShapeProto);
context.drawImage(img, 0, 0);
for(let i=0; i<inputImgHeight; i++) {
for (let j=0; j<inputImgWidth; j++) {
tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[0]/255);
tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[1]/255);
tensorProto.addFloatVal(context.getImageData(i, j, 1, 1).data[2]/255);
}
}
const predictionServiceRequest = new PredictModule.PredictRequest();
predictionServiceRequest.setModelSpec(modelSpec);
predictionServiceRequest.getInputsMap().set('inputs', tensorProto);
שליחת בקשת gRPC לשירות TensorFlow
עכשיו אפשר לשלוח את הבקשה.
- צריך להוסיף את הקוד מיד אחרי הקוד בסניף של gRPC בקטע הקוד הקודם:
// Send the gRPC request.
stub.predict(predictionServiceRequest, {}, function(err, response) {
// TODO: Add code to process the response.
});
מעבד את תגובת ה-gRPC מ-TensorFlow serving
לבסוף, אתם מטמיעים את פונקציית הקריאה החוזרת שלמעלה כדי לטפל בתגובה.
- צריך להוסיף את הקוד לגוף הפונקציה בקטע הקוד הקודם:
// Process the gRPC response.
if (err) {
console.log(err.code);
console.log(err.message);
}
else {
const maxIndex = argmax(response.getOutputsMap().get('logits').getFloatValList());
document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;
}
עכשיו המאזינים מחלצים את הההסתברות החזויה מהתגובה, מזהה את הקטגוריה הסבירה ביותר של האובייקט ומציגת את התוצאה בממשק המשתמש.
הפעלה
- בטרמינל, צריך להשתמש ב-webpack כדי לקבץ את כל קובצי ה-JavaScript לקובץ יחיד שניתן להטמיע בקובץ
index.html
:
npx webpack
- מרעננים את הכתובת http://localhost:8887/ בדפדפן.
- לוחצים על gRPC > Classification.
האתר מציג את הקטגוריה החזויה של 286
, שממופה לתווית Egyptian Cat
במערך הנתונים של תמונהNet.
9. מזל טוב
השתמשת ב-TenororFlow serving כדי להוסיף יכולות של סיווג תמונות לאתר!