איך ליצור אתר פשוט שמסווג תמונות

1. לפני שמתחילים

במעבדה זו תלמדו איך להסיק מסקנות של סיווג תמונות מאתר באמצעות TensorFlow serving עם REST ו-gRPC.

דרישות מוקדמות

  • ידע בסיסי בפיתוח אתרים, כמו HTML ו-JavaScript
  • ידע בסיסי על למידה חישובית עם TensorFlow, כמו הדרכה ופריסה
  • ידע בסיסי על מסופים ותחנות עגינה

מה תלמדו

  • איך למצוא מודלים ייעודיים לסיווג תמונות ב-TensorFlow Hub.
  • איך לבנות אתר פשוט וליצור תחזיות עם מודל סיווג התמונות שהורד באמצעות TensorFlow serving (REST ו-gRPC).
  • איך לעבד את תוצאת הזיהוי בממשק המשתמש.

מה תצטרך להכין

2. להגדרה

כדי להוריד את הקוד של Lab Lab זה:

  1. עוברים אל מאגר GitHub.
  2. לוחצים על Code > הורדת zip כדי להוריד את כל הקוד של מעבדת הקוד הזו.

a72f2bb4caa9a96.png

  1. יש לבטל את הדחיסה של קובץ ה-ZIP שהורדת כדי לפתוח את תיקיית הבסיס של codelabs עם כל המשאבים.

ב-codelab זה נדרשים רק הקבצים בספריית המשנה TFServing/ImageClassificationWeb במאגר, המכיל שתי תיקיות:

  • התיקייה starter מכילה את הקוד למתחילים שעליו כדאי לבנות את מעבדת הקוד הזו.
  • התיקייה finished מכילה את הקוד המלא לאפליקציה לדוגמה שהושלמה.

3. התקנת תלויות

כדי להתקין את התלות:

  • במסוף, עוברים לתיקייה starter ומתקינים את החבילות הנדרשות של NPM:
npm install

4. להפעלת האתר למתחילים

משתמשים בשרת האינטרנט של Chrome כדי לטעון את הקובץ TFServing/ImageClassificationWeb/starter/dist/index.html:

  1. מזינים את הכתובת Chrome://apps/ בסרגל הכתובות של Chrome, ולאחר מכן מוצאים את שרת האינטרנט של Chrome ברשימת האפליקציות.
  2. מפעילים את שרת האינטרנט של Chrome ובוחרים את התיקייה TFServing/ImageClassificationWeb/starter/dist/.
  3. לוחצים על לחצן החלפת המצב שרת אינטרנט כדי להפעיל אותו ואז מנווטים אל http://localhost:8887/ בדפדפן.

f7b43cd44ebf1f1b.png

הפעלה ועיון באתר

אתם אמורים לראות את האתר עכשיו. ממשק המשתמש די פשוט: יש תמונת חתול שבה רוצים לסווג את המשתמש, והיא יכולה לשלוח את הנתונים לקצה העורפי באמצעות REST או gRPC. הקצה העורפי מבצע סיווג תמונות בתמונה ומחזיר לאתר את תוצאת הסיווג, שבה מוצגת התוצאה.

837d97a27c59a0b3.png

אם לוחצים על סיווג, לא יקרה דבר כי עדיין לא ניתן לתקשר עם הקצה העורפי.

5. פריסת מודל של סיווג תמונה באמצעות TensorFlow

סיווג תמונה הוא משימה נפוצה מאוד מסוג סיווג ML שמסווגת תמונה לקטגוריות לפי הגדרה מראש, על סמך התוכן העיקרי של התמונה. הנה דוגמה לסיווג פרחים:

a6da16b4a7665db0.png

ב-TensorFlow Hub יש כמה מודלים לסיווג תמונות. אתם משתמשים במודל Inccetion v3 פופולרי ל-Codelab הזה.

כדי לפרוס את מודל סיווג התמונות באמצעות TensorFlow serving:

  1. מורידים את קובץ המודל התחלה של גרסה 3.
  2. יש לבטל את הדחיסה של הקובץ .tar.gz שהורדת באמצעות כלי לדחיסת נתונים, כמו 7-Zip.
  3. יצירת תיקייה של inception_v3 ולאחר מכן יצירת תיקיית משנה 123.
  4. מעבירים את התיקייה variables ואת הקובץ saved_model.pb לתיקיית המשנה של 123.

אפשר להתייחס לתיקייה inception_v3 כתיקייה של SavedModel. 123 הוא מספר גרסה לדוגמה. אפשר לבחור מספר אחר.

מבנה התיקייה אמור להיראות כך:

21a8675ac8d31907.png

התחלת הצגה של 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:

  1. יוצרים את הבקשה ל-REST.
  2. שליחת בקשת REST להגשה של TensorFlow.
  3. יש לחלץ את התוצאה הצפויה מתגובת ה-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;

עכשיו המאזינים מחלצים את הההסתברות החזויה מהתגובה, מזהה את הקטגוריה הסבירה ביותר של האובייקט ומציגת את התוצאה בממשק המשתמש.

הפעלה

  1. בטרמינל, עוברים אל התיקייה starter ומשתמשים בחבילה באינטרנט כדי לקבץ את כל קובצי ה-JavaScript לקובץ אחד שאפשר להטמיע בקובץ dist/index.html:
npm install -g npx
npm install --save-dev webpack
npx webpack
  1. מרעננים את הכתובת http://localhost:8887/ בדפדפן ולוחצים על REST > Classify.

האתר מציג את 286 כקטגוריה החזויה, שממפה לתווית Egyptian Cat במערך הנתונים של תמונהNet.

c865a93b9b58335d.png

8. חיבור האתר ל-TenororFlow serving דרך gRPC

בנוסף ל-REST, שירות TensorFlow תומך גם ב-gRPC.

b6f4449c2c850b0e.png

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

שליחה של בקשות וקבלת תשובות באמצעות gRPC

יש ארבעה שלבים פשוטים:

  1. אופציונלי: יצירת קוד stub של לקוח gRPC.
  2. יוצרים את בקשת ה-gRPC.
  3. שליחת בקשת gRPC לשירות TensorFlow.
  4. מחלצים את התוצאה החזויה מהתגובה של gRPC ומציגים אותה בממשק המשתמש.

יש להשלים את השלבים האלה בקובץ src/index.js.

אופציונלי: יצירת קוד stub של לקוח gRPC

כדי להשתמש ב-gRPC עם TensorFlow serving, יש לפעול לפי תהליך העבודה של gRPC. למידע נוסף, ניתן לעיין בתיעוד של gRPC.

a9d0e5cb543467b4.png

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;
}

עכשיו המאזינים מחלצים את הההסתברות החזויה מהתגובה, מזהה את הקטגוריה הסבירה ביותר של האובייקט ומציגת את התוצאה בממשק המשתמש.

הפעלה

  1. בטרמינל, צריך להשתמש ב-webpack כדי לקבץ את כל קובצי ה-JavaScript לקובץ יחיד שניתן להטמיע בקובץ index.html:
npx webpack
  1. מרעננים את הכתובת http://localhost:8887/ בדפדפן.
  2. לוחצים על gRPC > Classification.

האתר מציג את הקטגוריה החזויה של 286, שממופה לתווית Egyptian Cat במערך הנתונים של תמונהNet.

9. מזל טוב

השתמשת ב-TenororFlow serving כדי להוסיף יכולות של סיווג תמונות לאתר!

מידע נוסף