Создайте простой веб-сайт, который классифицирует изображения

1. Прежде чем начать

В этой лабораторной работе вы узнаете, как выполнить вывод о классификации изображений с веб-сайта, используя TensorFlow Serving с REST и gRPC.

Предпосылки

  • Базовые знания веб-разработки, такие как HTML и JavaScript
  • Базовые знания машинного обучения с TensorFlow, такие как обучение и развертывание
  • Базовые знания терминалов и Docker

Что вы узнаете

  • Как найти предварительно обученные модели классификации изображений в TensorFlow Hub.
  • Как создать простой веб-сайт и делать прогнозы с помощью загруженной модели классификации изображений через TensorFlow Serving (REST и gRPC).
  • Как отобразить результат обнаружения в пользовательском интерфейсе.

Что вам понадобится

2. Настройте

Чтобы скачать код для этой кодлабы:

  1. Перейдите к этому репозиторию GitHub .
  2. Нажмите « Код» > «Загрузить zip» , чтобы загрузить весь код для этой лаборатории кода.

a72f2bb4caa9a96.png

  1. Разархивируйте загруженный zip-файл, чтобы распаковать корневую папку codelabs со всеми необходимыми ресурсами.

Для этой лаборатории кода вам нужны только файлы в подкаталоге 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

Если вы нажмете Classify , ничего не произойдет, потому что он еще не может взаимодействовать с серверной частью.

5. Разверните модель классификации изображений с помощью TensorFlow Serving.

Классификация изображений — очень распространенная задача машинного обучения, которая классифицирует изображение по предопределенным категориям на основе основного содержимого изображения. Вот пример классификации цветов:

a6da16b4a7665db0.png

На TensorFlow Hub есть ряд предварительно обученных моделей классификации изображений. Вы используете популярную модель Inception v3 для этой лаборатории кода.

Чтобы развернуть модель классификации изображений с помощью TensorFlow Serving:

  1. Загрузите файл модели Inception v3 .
  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

Docker сначала автоматически загружает образ TensorFlow Serving, что занимает минуту. После этого должен запуститься TensorFlow Serving. Журнал должен выглядеть как этот фрагмент кода:

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. Настройте прокси Envoy

В настоящее время TensorFlow Serving не устанавливает заголовок Access-Control-Allow-Origin , поэтому браузер блокирует запрос от интерфейса JavaScript к TensorFlow Serving по соображениям безопасности. Чтобы обойти это, вам нужно использовать прокси-сервер, например Envoy , для передачи запроса от JavaScript к серверной части TensorFlow Serving.

Начать посланника

  • В терминале загрузите образ Envoy и запустите Envoy с помощью Docker, но замените заполнитель 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

Docker автоматически сначала загружает образ 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:

  • ОТДЫХАТЬ
  • gRPC

Отправляйте запросы и получайте ответы через REST

Есть три простых шага для отправки и получения запросов через REST:

  1. Создайте REST-запрос.
  2. Отправьте запрос REST в TensorFlow Serving.
  3. Извлеките прогнозируемый результат из ответа REST и отобразите результат.

Вы выполняете эти шаги в файле src/index.js .

Создайте REST-запрос

Прямо сейчас функция classify_img() не отправляет запрос REST в TensorFlow Serving. Вам нужно реализовать эту ветку REST, чтобы сначала создать запрос REST:

if (radioButtons[0].checked) {
    console.log('Using REST');
    // TODO: Add code to send a REST request to TensorFlow Serving.

} 

TensorFlow Serving ожидает запрос POST, который содержит тензор изображения для модели Inception 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 Serving.

Теперь вы можете отправить запрос.

  • Добавьте этот код сразу после кода выше в ветке REST:
// Send the REST request.
xhr.send(data);

Обработка ответа REST от TensorFlow Serving

Модель Inception v3 возвращает массив вероятностей принадлежности изображения к предопределенным категориям. Когда прогноз будет успешным, вы должны вывести наиболее вероятную категорию в пользовательском интерфейсе.

Вы реализуете прослушиватель onload() для обработки ответа.

xhr.onload = () => {

}
  • Добавьте этот код в прослушиватель 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 папку и используйте webpack для объединения всех файлов JavaScript в один файл, который вы можете встроить в файл dist/index.html :
npm install -g npx
npm install --save-dev webpack
npx webpack
  1. Обновите http://localhost:8887/ в браузере и нажмите REST > Classify .

Веб-сайт отображает 286 в качестве прогнозируемой категории, которая соответствует метке Egyptian Cat в наборе данных ImageNet .

c865a93b9b58335d.png

8. Подключите веб-сайт к TensorFlow Serving через gRPC.

Помимо REST, TensorFlow Serving также поддерживает gRPC .

b6f4449c2c850b0e.png

gRPC — это современная высокопроизводительная платформа удаленного вызова процедур (RPC) с открытым исходным кодом, которая может работать в любой среде. Он может эффективно подключать службы в центрах обработки данных и между ними с подключаемой поддержкой балансировки нагрузки, трассировки, проверки работоспособности и аутентификации. Было замечено, что на практике gRPC более эффективен, чем REST.

Отправляйте запросы и получайте ответы с помощью gRPC

Всего четыре простых шага:

  1. Необязательно: сгенерируйте код-заглушку клиента gRPC.
  2. Создайте запрос gRPC.
  3. Отправьте запрос gRPC в TensorFlow Serving.
  4. Извлеките прогнозируемый результат из ответа gRPC и отобразите его в пользовательском интерфейсе.

Вы выполняете эти шаги в файле src/index.js .

Необязательно: сгенерируйте код-заглушку клиента gRPC.

Чтобы использовать gRPC с TensorFlow Serving, вам необходимо следовать рабочему процессу gRPC. Чтобы узнать больше о деталях, см. документацию по gRPC .

a9d0e5cb543467b4.png

TensorFlow Serving и TensorFlow определяют для вас файлы .proto . Начиная с TensorFlow и TensorFlow Serving 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 Serving.

Теперь вы можете отправить запрос.

  • Добавьте этот код сразу после кода в ветке 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 > Классифицировать .

Веб-сайт отображает прогнозируемую категорию 286 , которая соответствует метке Egyptian Cat в наборе данных ImageNet .

9. Поздравления

Вы использовали TensorFlow Serving, чтобы добавить возможности классификации изображений на свой веб-сайт!

Учить больше