1. Trước khi bắt đầu
Trong lớp học lập trình này, bạn sẽ tìm hiểu cách suy luận phân loại hình ảnh từ một trang web bằng cách sử dụng TensorFlow Delivery với REST và gRPC.
Điều kiện tiên quyết
- Kiến thức cơ bản về việc phát triển web, chẳng hạn như HTML và JavaScript
- Kiến thức cơ bản về công nghệ máy học với TensorFlow, chẳng hạn như chương trình đào tạo và triển khai
- Kiến thức cơ bản về thiết bị đầu cuối và Docker
Kiến thức bạn sẽ học được
- Cách tìm các mô hình phân loại hình ảnh đã được đào tạo trước trên TensorFlow Hub.
- Cách xây dựng một trang web đơn giản và dự đoán bằng mô hình phân loại hình ảnh đã tải xuống thông qua việc phân phát TensorFlow (REST và gRPC).
- Cách hiển thị kết quả phát hiện trong giao diện người dùng.
Bạn cần có
- Docker
- Google Chrome
- Máy chủ web dành cho Chrome
- Node.js và Atom
- Bash
- Trình biên dịch bộ đệm giao thức (chỉ cần thiết nếu bạn muốn tự tạo lại mã trang gRPC)
- Trình bổ trợ trình tạo mã web gRPC (chỉ cần thiết nếu bạn muốn tự tạo lại mã trang gRPC)
2. Bắt đầu thiết lập
Cách tải mã xuống cho lớp học lập trình này:
- Chuyển đến kho lưu trữ này trên GitHub.
- Nhấp vào Code > Download zip để tải tất cả mã xuống cho lớp học lập trình này.
- Giải nén tệp zip đã tải xuống để giải nén thư mục gốc
codelabs
bằng tất cả tài nguyên mà bạn cần.
Đối với lớp học lập trình này, bạn chỉ cần các tệp trong thư mục con TFServing/ImageClassificationWeb
trong kho lưu trữ, nơi chứa hai thư mục:
- Thư mục
starter
chứa mã dành cho người mới bắt đầu mà bạn xây dựng cho lớp học lập trình này. - Thư mục
finished
chứa mã đã hoàn tất cho ứng dụng mẫu đã hoàn thành.
3. Cài đặt phần phụ thuộc
Cách cài đặt các phần phụ thuộc:
- Trong thiết bị đầu cuối của bạn, hãy chuyển đến thư mục
starter
rồi cài đặt các gói GTM bắt buộc:
npm install
4. Chạy trang web cho người mới bắt đầu
Sử dụng Máy chủ web dành cho Chrome để tải tệp TFServing/ImageClassificationWeb/starter/dist/index.html
:
- Nhập
Chrome://apps/
vào thanh địa chỉ của Chrome, sau đó tìm Web Server for Chrome trong danh sách ứng dụng. - Chạy Máy chủ web dành cho Chrome, sau đó chọn thư mục
TFServing/ImageClassificationWeb/starter/dist/
. - Nhấp vào nút bật/tắt Máy chủ web để bật tính năng này, sau đó chuyển đến http://localhost:8887/ trong trình duyệt của bạn.
Chạy và khám phá trang web
Bạn sẽ thấy trang web ngay bây giờ. Giao diện người dùng khá đơn giản: có một hình ảnh mèo mà bạn muốn phân loại và người dùng có thể gửi dữ liệu tới phần phụ trợ bằng REST hoặc gRPC. Phụ trợ này thực hiện việc phân loại hình ảnh trên hình ảnh và trả về kết quả phân loại cho trang web, nơi hiển thị kết quả.
Nếu bạn nhấp vào Phân loại, sẽ không có gì xảy ra vì chưa thể giao tiếp với phần phụ trợ.
5. Triển khai mô hình phân loại hình ảnh bằng TensorFlowServe
Phân loại hình ảnh là một thao tác máy học rất phổ biến nhằm phân loại hình ảnh thành các danh mục được xác định trước dựa trên nội dung chính của hình ảnh. Sau đây là ví dụ về cách phân loại hoa:
Có một số mô hình phân loại hình ảnh được đào tạo trước trên TensorFlow Hub. Bạn sử dụng mô hình Inceition v3 phổ biến cho lớp học lập trình này.
Để triển khai mô hình phân loại hình ảnh bằng TensorFlow Delivery:
- Tải tệp mô hình Inceition v3 xuống.
- Giải nén tệp
.tar.gz
đã tải xuống bằng một công cụ giải nén, chẳng hạn như 7-zip. - Tạo thư mục
inception_v3
, sau đó tạo một thư mục con123
bên trong thư mục đó. - Đặt thư mục
variables
đã trích xuất và tệpsaved_model.pb
vào thư mục con123
.
Bạn có thể tham chiếu thư mục inception_v3
dưới dạng thư mục SavedModel
. 123
là số phiên bản mẫu. Nếu muốn, bạn có thể chọn một số khác.
Cấu trúc thư mục sẽ có dạng như sau:
Bắt đầu phân phát TensorFlow
- Trên thiết bị đầu cuối, hãy bắt đầu phân phát TensorFlow bằng Docker, nhưng thay thế
PATH/TO/SAVEDMODEL
bằng đường dẫn tuyệt đối của thư mụcinception_v3
trên máy tính.
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, hệ thống sẽ tự động tải hình ảnh Phân phát TensorFlow xuống trước. Quá trình này sẽ mất một phút. Sau đó, Quá trình phân phát TensorFlow sẽ bắt đầu. Nhật ký sẽ trông giống như đoạn mã này:
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. Thiết lập proxy Envoy
Hiện tại, việc phân phát TensorFlow không đặt tiêu đề Access-Control-Allow-Origin
, vì vậy, trình duyệt sẽ chặn yêu cầu từ JavaScript của giao diện người dùng sang TensorFlowServe vì lý do bảo mật. Để giải quyết vấn đề này, bạn cần sử dụng proxy, chẳng hạn như Envoy, để ủy quyền yêu cầu từ JavaScript cho phụ trợ Phân phát TensorFlow.
Bắt đầu Envoy
- Trong thiết bị đầu cuối của bạn, hãy tải hình ảnh Envoy xuống và bắt đầu Envoy bằng Docker, nhưng thay thế trình giữ chỗ
PATH/TO/ENVOY-CUSTOM.YAML
bằng đường dẫn tuyệt đối của tệpenvoy-custom.yaml
trong thư mụcstarter
.
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 trước sẽ tự động tải hình ảnh Envoy xuống. Sau đó, Envoy sẽ bắt đầu. Nhật ký sẽ trông giống như đoạn mã này:
[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. Kết nối trang web với TensorFlow thông qua REST
Phần phụ trợ này hiện đã sẵn sàng để bạn có thể gửi các yêu cầu khách hàng đến Dịch vụ TensorFlow để phân loại hình ảnh. Có hai cách để gửi yêu cầu đến Phục vụ TensorFlow:
- Kiến trúc chuyển trạng thái đại diện (REST)
- gRPC
Gửi yêu cầu và nhận phản hồi thông qua REST
Có ba bước đơn giản để gửi và nhận yêu cầu thông qua REST:
- Tạo yêu cầu REST.
- Gửi yêu cầu REST tới TensorFlowServe.
- Trích xuất kết quả dự đoán từ phản hồi REST và hiển thị kết quả.
Bạn thực hiện các bước này trong tệp src/index.js
.
Tạo yêu cầu REST
Hiện tại, hàm classify_img()
không gửi yêu cầu Kiến trúc chuyển trạng thái đại diện (REST) đến phục vụ TensorFlow. Trước tiên, bạn cần triển khai nhánh REST này để tạo yêu cầu REST:
if (radioButtons[0].checked) {
console.log('Using REST');
// TODO: Add code to send a REST request to TensorFlow Serving.
}
TensorFlow Delivery kỳ vọng một yêu cầu POST chứa ký tự hình ảnh cho mô hình Inceition v3 mà bạn sử dụng, vì vậy, bạn cần trích xuất các giá trị RGB từ mỗi pixel của hình ảnh vào một mảng, sau đó đưa mảng vào trong JSON, đây là phần tải của yêu cầu.
- Thêm mã này vào nhánh 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');
}
Gửi yêu cầu REST đến Phục vụ TensorFlow
Bây giờ, bạn có thể gửi yêu cầu.
- Thêm mã này ngay sau mã ở trên trong nhánh REST:
// Send the REST request.
xhr.send(data);
Xử lý phản hồi REST (Phân phát) của TensorFlow
Mô hình Inceition v3 trả về một chuỗi các xác suất mà hình ảnh thuộc về các danh mục được xác định trước. Khi dự đoán thành công, bạn sẽ xuất ra danh mục có thể xảy ra nhất trong giao diện người dùng.
Bạn triển khai trình nghe onload()
để xử lý phản hồi.
xhr.onload = () => {
}
- Thêm mã này vào trình nghe
onload()
:
// Process the REST response.
const response = JSON.parse(xhr.responseText);
const maxIndex = argmax(response['predictions'][0])
document.getElementById('category').textContent = 'Predicted category: ' + maxIndex;
Bây giờ, trình nghe trích xuất xác suất dự đoán từ phản hồi, xác định danh mục có khả năng xảy ra nhất của đối tượng và hiển thị kết quả trong giao diện người dùng.
Chạy
- Trong thiết bị đầu cuối, hãy chuyển đến thư mục
starter
rồi dùng webpack để nhóm tất cả các tệp JavaScript thành một tệp mà bạn có thể nhúng vào tệpdist/index.html
:
npm install -g npx npm install --save-dev webpack npx webpack
- Làm mới http://localhost:8887/ trong trình duyệt rồi nhấp vào REST > Classify.
Trang web này hiển thị 286
dưới dạng danh mục dự đoán, ánh xạ tới nhãn Egyptian Cat
trong tập dữ liệu ImageNet.
8. Kết nối trang web với TensorFlow Delivery thông qua gRPC
Ngoài REST, việc phân phát TensorFlow cũng hỗ trợ gRPC.
gRPC là một khung lệnh gọi từ xa, nguồn mở hiện đại, hiệu suất cao, có thể chạy trong mọi môi trường. API này có thể kết nối các dịch vụ trong và trên các trung tâm dữ liệu một cách hiệu quả với khả năng hỗ trợ dễ dàng để cân bằng tải, theo dõi, kiểm tra tình trạng và xác thực. Theo quan sát, gRPC có hiệu suất cao hơn REST trong thực tế.
Gửi yêu cầu và nhận phản hồi bằng gRPC
Có 4 bước đơn giản:
- Không bắt buộc: Tạo mã mã ứng dụng gRPC.
- Tạo yêu cầu gRPC.
- Gửi yêu cầu gRPC đến TensorFlowServe.
- Trích xuất kết quả dự đoán từ phản hồi gRPC và hiển thị kết quả đó trong giao diện người dùng.
Bạn cần hoàn thành các bước này trong tệp src/index.js
.
Không bắt buộc: Tạo mã mã ứng dụng gRPC
Để sử dụng gRPC với TensorFlowServe, bạn cần phải tuân theo quy trình làm việc của gRPC. Để tìm hiểu thêm về các chi tiết, hãy xem tài liệu gRPC.
TensorFlow và TensorFlow xác định các tệp .proto
cho bạn. Kể từ TensorFlow và TensorFlow phân phát 2.8, .proto
tệp này là những tệp cần thiết:
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
- Trong thiết bị đầu cuối, hãy chuyển đến thư mục
starter/src/proto/
và tạo mã trang:
bash generate_grpc_stub_js.sh
Tạo yêu cầu gRPC
Tương tự như yêu cầu REST, bạn tạo yêu cầu gRPC trong chi nhánh gRPC.
if (connectionMode[picker.selectedRow(inComponent: 0)] == "REST") {
}
else {
print("Using gRPC")
// TODO: Add code to send a gRPC request to TensorFlow Serving.
}
- Thêm mã này vào chi nhánh 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);
Gửi yêu cầu gRPC đến TensorFlowServe
Bây giờ, bạn có thể gửi yêu cầu.
- Thêm mã này ngay sau mã trong chi nhánh gRPC trong đoạn mã trước:
// Send the gRPC request.
stub.predict(predictionServiceRequest, {}, function(err, response) {
// TODO: Add code to process the response.
});
Xử lý phản hồi gRPC từ TensorFlowServe
Cuối cùng, bạn triển khai hàm callback ở trên để xử lý phản hồi.
- Thêm mã này vào phần nội dung hàm trong đoạn mã trước đó:
// 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;
}
Bây giờ, trình nghe trích xuất xác suất dự đoán từ phản hồi, xác định danh mục có khả năng xảy ra nhất của đối tượng và hiển thị kết quả trong giao diện người dùng.
Chạy
- Trong thiết bị đầu cuối của bạn, hãy sử dụng webpack để nhóm tất cả các tệp JavaScript vào một tệp mà bạn có thể nhúng vào tệp
index.html
:
npx webpack
- Làm mới http://localhost:8887/ trong trình duyệt của bạn.
- Nhấp vào gRPC > Classify.
Trang web này hiển thị danh mục được dự đoán là 286
, danh mục này tương ứng với nhãn Egyptian Cat
trong tập dữ liệu ImageNet.
9. Xin chúc mừng
Bạn đã sử dụng tính năng Phân phát TensorFlow để thêm các chức năng phân loại hình ảnh vào trang web của bạn!