Tạo một trang web đơn giản để phân loại hình ảnh

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ó

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:

  1. Chuyển đến kho lưu trữ này trên GitHub.
  2. 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.

a72f2bb4caa9a96.png

  1. 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:

  1. 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.
  2. Chạy Máy chủ web dành cho Chrome, sau đó chọn thư mục TFServing/ImageClassificationWeb/starter/dist/.
  3. 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.

f7b43cd44ebf1f1b.png

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ả.

837d97a27c59a0b3.png

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:

a6da16b4a7665db0.png

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:

  1. Tải tệp mô hình Inceition v3 xuống.
  2. 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.
  3. Tạo thư mục inception_v3, sau đó tạo một thư mục con 123 bên trong thư mục đó.
  4. Đặt thư mục variables đã trích xuất và tệp saved_model.pb vào thư mục con 123.

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:

21a8675ac8d31907.png

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ục inception_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ệp envoy-custom.yaml trong thư mục 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

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:

  1. Tạo yêu cầu REST.
  2. Gửi yêu cầu REST tới TensorFlowServe.
  3. 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

  1. 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ệp dist/index.html:
npm install -g npx
npm install --save-dev webpack
npx webpack
  1. 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.

c865a93b9b58335d.png

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.

b6f4449c2c850b0e.png

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:

  1. Không bắt buộc: Tạo mã mã ứng dụng gRPC.
  2. Tạo yêu cầu gRPC.
  3. Gửi yêu cầu gRPC đến TensorFlowServe.
  4. 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.

a9d0e5cb543467b4.png

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

  1. 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
  1. Làm mới http://localhost:8887/ trong trình duyệt của bạn.
  2. 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!

Tìm hiểu thêm