1. Sebelum memulai
Dalam codelab ini, Anda akan mempelajari cara menjalankan inferensi klasifikasi gambar dari situs menggunakan TensorFlow Serving dengan REST dan gRPC.
Prasyarat
- Pengetahuan dasar tentang pengembangan web, seperti HTML dan JavaScript
- Pengetahuan dasar tentang machine learning dengan TensorFlow, seperti pelatihan dan deployment
- Pengetahuan dasar tentang terminal dan Docker
Yang akan Anda pelajari
- Cara menemukan model klasifikasi gambar terlatih di TensorFlow Hub.
- Cara membuat situs sederhana dan membuat prediksi dengan model klasifikasi gambar yang didownload melalui TensorFlow Serving (REST dan gRPC).
- Cara merender hasil deteksi di UI.
Yang Anda butuhkan
- Docker
- Google Chrome
- Server Web untuk Chrome
- Node.js dan NPM
- Bash
- Compiler buffering protokol (hanya diperlukan jika Anda ingin membuat ulang stub gRPC sendiri)
- Plugin generator kode gRPC-web (hanya diperlukan jika Anda ingin membuat ulang stub gRPC sendiri)
2. Memulai persiapan
Untuk mendownload kode codelab ini:
- Buka repositori GitHub ini.
- Klik Code > Download zip guna mendownload semua kode untuk codelab ini.
- Ekstrak file zip yang didownload untuk mengekstrak folder root
codelabs
dengan semua resource yang Anda butuhkan.
Untuk codelab ini, Anda hanya memerlukan file dalam subdirektori TFServing/ImageClassificationWeb
di repositori, yang berisi dua folder:
- Folder
starter
berisi kode awal yang Anda buat untuk codelab ini. - Folder
finished
berisi kode yang sudah selesai untuk aplikasi contoh yang telah selesai.
3. Instal dependensi
Untuk menginstal dependensi:
- Di terminal Anda, buka folder
starter
, lalu instal paket NPM yang diperlukan:
npm install
4. Menjalankan situs awal
Gunakan Web Server for Chrome untuk memuat file TFServing/ImageClassificationWeb/starter/dist/index.html
:
- Masukkan
Chrome://apps/
di kolom URL Chrome, lalu temukan Web Server for Chrome di daftar aplikasi. - Luncurkan Server Web untuk Chrome, lalu pilih folder
TFServing/ImageClassificationWeb/starter/dist/
. - Klik tombol Web Server untuk mengaktifkannya, lalu buka http://localhost:8887/ di browser Anda.
Menjalankan dan menjelajahi situs
Anda akan melihat situs sekarang. UI-nya cukup mudah: ada gambar kucing yang ingin Anda klasifikasikan dan pengguna dapat mengirim data ke backend dengan REST atau gRPC. Backend melakukan klasifikasi gambar pada gambar dan menampilkan hasil klasifikasi ke situs, yang kemudian akan menampilkan hasilnya.
Jika Anda mengklik Classify, tidak akan terjadi apa-apa karena komunikasi dengan backend masih belum dapat dilakukan.
5. Men-deploy model klasifikasi gambar dengan TensorFlow Serving
Klasifikasi gambar adalah tugas ML yang sangat umum yang mengklasifikasikan gambar ke dalam kategori yang telah ditentukan berdasarkan konten utama gambar. Berikut contoh mengklasifikasikan bunga:
Ada sejumlah model klasifikasi gambar terlatih di TensorFlow Hub. Anda menggunakan model Inception v3 yang populer untuk codelab ini.
Untuk men-deploy model klasifikasi gambar dengan TensorFlow Serving:
- Download file model Inception v3.
- Buka kompresi file
.tar.gz
yang didownload dengan alat dekompresi, seperti 7-Zip. - Buat folder
inception_v3
, lalu buat subfolder123
di dalamnya. - Masukkan folder
variables
yang diekstrak dan filesaved_model.pb
ke dalam subfolder123
.
Anda dapat merujuk ke folder inception_v3
sebagai folder SavedModel
. 123
adalah contoh nomor versi. Jika mau, Anda dapat memilih nomor lain.
Struktur folder akan terlihat seperti gambar ini:
Memulai TensorFlow Serving
- Di terminal Anda, mulai TensorFlow Serving dengan Docker, tetapi ganti
PATH/TO/SAVEDMODEL
dengan jalur absolut folderinception_v3
di komputer Anda.
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
Docker akan otomatis mendownload image TensorFlow Serving terlebih dahulu, yang memerlukan waktu sebentar. Setelah itu, TensorFlow Serving akan dimulai. Log akan terlihat seperti cuplikan kode berikut:
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. Menyiapkan proxy Envoy
Saat ini, TensorFlow Serving tidak menetapkan header Access-Control-Allow-Origin
, sehingga browser memblokir permintaan dari JavaScript frontend ke TensorFlow Serving karena alasan keamanan. Untuk mengatasinya, Anda perlu menggunakan proxy, seperti Envoy, untuk mem-proxy permintaan dari JavaScript ke backend TensorFlow Serving.
Mulai Envoy
- Di terminal Anda, download image Envoy dan mulai Envoy dengan Docker, tetapi ganti placeholder
PATH/TO/ENVOY-CUSTOM.YAML
dengan jalur absolut fileenvoy-custom.yaml
di folderstarter
.
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
Docker akan otomatis mendownload image Envoy terlebih dahulu. Setelah itu, Envoy akan dimulai. Log akan terlihat seperti cuplikan kode berikut:
[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. Menghubungkan situs dengan TensorFlow melalui REST
Backend sudah siap, sehingga Anda dapat mengirim permintaan klien ke TensorFlow Serving untuk mengklasifikasikan gambar. Ada dua cara untuk mengirim permintaan ke TensorFlow Serving:
- REST
- gRPC
Mengirim permintaan dan menerima respons melalui REST
Ada tiga langkah sederhana untuk mengirim dan menerima permintaan melalui REST:
- Buat permintaan REST.
- Kirim permintaan REST ke TensorFlow Serving.
- Ekstrak hasil prediksi dari respons REST dan tampilkan hasilnya.
Anda dapat menyelesaikan langkah-langkah ini di file src/index.js
.
Buat permintaan REST
Saat ini, fungsi classify_img()
tidak mengirimkan permintaan REST ke TensorFlow Serving. Anda harus mengimplementasikan cabang REST ini untuk membuat permintaan REST terlebih dahulu:
if (radioButtons[0].checked) {
console.log('Using REST');
// TODO: Add code to send a REST request to TensorFlow Serving.
}
TensorFlow Serving mengharapkan permintaan POST yang berisi tensor gambar untuk model Inception v3 yang Anda gunakan, jadi Anda perlu mengekstrak nilai RGB dari setiap piksel gambar ke dalam array, lalu membungkus array dalam JSON, yang merupakan payload permintaan.
- Tambahkan kode ini ke cabang 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');
}
Mengirim permintaan REST ke TensorFlow Serving
Sekarang Anda dapat mengirim permintaan.
- Tambahkan kode ini tepat setelah kode di atas di cabang REST:
// Send the REST request.
xhr.send(data);
Memproses respons REST dari TensorFlow Serving
Model Inception v3 menampilkan array probabilitas bahwa gambar termasuk dalam kategori yang telah ditentukan sebelumnya. Jika prediksi berhasil, Anda harus menampilkan kategori yang paling mungkin di UI.
Anda menerapkan pemroses onload()
untuk menangani respons.
xhr.onload = () => {
}
- Tambahkan kode ini ke pemroses
onload()
:
// Process the REST response.
const response = JSON.parse(xhr.responseText);
const maxIndex = argmax(response['predictions'][0])
document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;
Sekarang, pendengar mengekstrak probabilitas prediksi dari respons, mengidentifikasi kategori objek yang paling mungkin, dan menampilkan hasilnya di UI.
Menjalankan aplikasi
- Di terminal Anda, buka folder
starter
dan gunakan webpack untuk menggabungkan semua file JavaScript ke dalam satu file yang dapat Anda sematkan di filedist/index.html
:
npm install -g npx npm install --save-dev webpack npx webpack
- Muat ulang http://localhost:8887/ di browser Anda, lalu klik REST > Classify.
Situs menampilkan 286
sebagai kategori yang diprediksi, yang dipetakan ke label Egyptian Cat
dalam set data ImageNet.
8. Menghubungkan situs dengan TensorFlow Serving melalui gRPC
Selain REST, TensorFlow Serving juga mendukung gRPC.
gRPC adalah framework Remote Procedure Call (RPC) modern, open source, dan berperforma tinggi yang dapat berjalan di lingkungan apa pun. Framework ini dapat menghubungkan layanan di dalam dan di seluruh pusat data secara efisien dengan dukungan yang dapat dicocokkan untuk load balancing, tracing, health checking, dan autentikasi. Berdasarkan pengamatan dalam praktiknya, performa gRPC lebih tinggi daripada REST.
Mengirim permintaan dan menerima respons dengan gRPC
Ada empat langkah sederhana:
- Opsional: Buat kode stub klien gRPC.
- Buat permintaan gRPC.
- Kirim permintaan gRPC ke TensorFlow Serving.
- Ekstrak hasil yang diprediksi dari respons gRPC dan tampilkan di UI.
Anda dapat menyelesaikan langkah-langkah ini di file src/index.js
.
Opsional: Membuat kode stub klien gRPC
Untuk menggunakan gRPC dengan TensorFlow Serving, Anda harus mengikuti alur kerja gRPC. Untuk mempelajari detailnya lebih lanjut, baca dokumentasi gRPC.
TensorFlow Serving dan TensorFlow menentukan file .proto
untuk Anda. Mulai dari TensorFlow dan TensorFlow Serve 2.8, file .proto
berikut adalah file yang diperlukan:
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
- Di terminal Anda, buka folder
starter/src/proto/
dan buat stub:
bash generate_grpc_stub_js.sh
Membuat permintaan gRPC
Serupa dengan permintaan REST, Anda membuat permintaan gRPC di cabang gRPC.
if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {
}
else {
print("Using gRPC")
// TODO: Add code to send a gRPC request to TensorFlow Serving.
}
- Tambahkan kode ini ke cabang 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);
Mengirim permintaan gRPC ke TensorFlow Serving
Sekarang Anda dapat mengirim permintaan.
- Tambahkan kode ini segera setelah kode di cabang gRPC dalam cuplikan kode sebelumnya:
// Send the gRPC request.
stub.predict(predictionServiceRequest, {}, function(err, response) {
// TODO: Add code to process the response.
});
Memproses respons gRPC dari TensorFlow Serving
Terakhir, Anda menerapkan fungsi callback di atas untuk menangani respons.
- Tambahkan kode ini ke isi fungsi dalam cuplikan kode sebelumnya:
// 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;
}
Sekarang, pendengar mengekstrak probabilitas prediksi dari respons, mengidentifikasi kategori objek yang paling mungkin, dan menampilkan hasilnya di UI.
Menjalankan aplikasi
- Di terminal, gunakan webpack untuk menggabungkan semua file JavaScript ke dalam satu file yang dapat Anda sematkan di file
index.html
:
npx webpack
- Muat ulang http://localhost:8887/ di browser Anda.
- Klik gRPC > Classify.
Situs ini menampilkan kategori 286
yang diprediksi, yang dipetakan ke label Egyptian Cat
di set data ImageNet.
9. Selamat
Anda telah menggunakan TensorFlow Serving untuk menambahkan kemampuan klasifikasi gambar ke situs Anda.