1. Прежде чем начать
В этой лабораторной работе вы узнаете, как выполнить вывод классификации изображений на веб-сайте с использованием TensorFlow Serving с REST и gRPC.
Предпосылки
- Базовые знания веб-разработки, такие как HTML и JavaScript
- Базовые знания машинного обучения с TensorFlow, такие как обучение и развертывание
- Базовые знания терминалов и Docker
Чему вы научитесь
- Как найти предварительно обученные модели классификации изображений на TensorFlow Hub.
- Как создать простой веб-сайт и делать прогнозы с помощью загруженной модели классификации изображений через TensorFlow Serving (REST и gRPC).
- Как отобразить результат обнаружения в пользовательском интерфейсе.
Что вам понадобится
- Докер
- Гугл Хром
- Веб-сервер для Chrome
- Node.js и NPM
- Баш
- Компилятор буфера протокола (необходим только в том случае, если вы хотите самостоятельно повторно сгенерировать заглушку gRPC)
- Плагин генератора кода gRPC-web (нужен только в том случае, если вы хотите самостоятельно повторно сгенерировать заглушку gRPC)
2. Настройте
Чтобы загрузить код для этой лабораторной работы:
- Перейдите в этот репозиторий GitHub .
- Нажмите Код > Загрузить zip , чтобы загрузить весь код для этой лабораторной работы.
- Разархивируйте загруженный zip-файл, чтобы распаковать корневую папку
codelabs
со всеми необходимыми вам ресурсами.
Для этой лабораторной работы вам понадобятся только файлы в подкаталоге TFServing/ImageClassificationWeb
в репозитории, который содержит две папки:
- В
starter
папке содержится стартовый код, на основе которого вы будете строить эту лабораторную работу. -
finished
папка содержит готовый код для готового примера приложения.
3. Установите зависимости
Чтобы установить зависимости:
- В терминале перейдите в
starter
папку и установите необходимые пакеты NPM:
npm install
4. Запустите стартовый сайт.
Используйте веб-сервер для Chrome для загрузки файла TFServing/ImageClassificationWeb/starter/dist/index.html
:
- Введите
Chrome://apps/
в адресной строке Chrome, а затем найдите «Веб-сервер для Chrome» в списке приложений. - Запустите веб-сервер для Chrome, а затем выберите папку
TFServing/ImageClassificationWeb/starter/dist/
. - Включите переключатель «Веб-сервер» , а затем перейдите по адресу http://localhost:8887/ в браузере.
Запустите и изучите сайт
Теперь вы должны увидеть сайт. Интерфейс довольно прост: есть изображение кошки, которое нужно классифицировать, и пользователь может отправить данные на бэкенд с помощью REST или gRPC. Бэкенд выполняет классификацию изображения и возвращает результат классификации на сайт, который его отображает.
Если нажать кнопку «Классифицировать» , ничего не произойдет, поскольку система пока не может связаться с бэкэндом.
5. Разверните модель классификации изображений с помощью TensorFlow Serving
Классификация изображений — очень распространённая задача машинного обучения, которая классифицирует изображение по предопределённым категориям на основе его основного содержания. Вот пример классификации цветов:
На TensorFlow Hub представлено несколько предобученных моделей классификации изображений. Для этой лабораторной работы вы используете популярную модель Inception v3 .
Чтобы развернуть модель классификации изображений с помощью TensorFlow Serving:
- Загрузите файл модели Inception v3 .
- Распакуйте загруженный файл
.tar.gz
с помощью инструмента распаковки, например, 7-Zip. - Создайте папку
inception_v3
, а затем создайте внутри нее подпапку123
. - Поместите извлеченную папку
variables
и файлsaved_model.pb
в подпапку123
.
Папку inception_v3
можно называть папкой SavedModel
. 123
— это пример номера версии. При желании вы можете выбрать другой номер.
Структура папок должна выглядеть так, как показано на рисунке:
Запустить TensorFlow Serving
- В терминале запустите 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 необходимо выполнить три простых шага:
- Создайте REST-запрос.
- Отправьте REST-запрос в TensorFlow Serving.
- Извлеките прогнозируемый результат из ответа 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;
Теперь прослушиватель извлекает прогнозируемые вероятности из ответа, определяет наиболее вероятную категорию объекта и отображает результат в пользовательском интерфейсе.
Запусти это
- В терминале перейдите в папку
starter
и используйте webpack для объединения всех файлов JavaScript в один файл, который можно встроить в файлdist/index.html
:
npm install -g npx npm install --save-dev webpack npx webpack
- Обновите http://localhost:8887/ в своем браузере и нажмите REST > Classify .
На сайте отображается прогнозируемая категория 286
, которая соответствует метке Egyptian Cat
в наборе данных ImageNet .
8. Подключите веб-сайт к TensorFlow Serving через gRPC.
Помимо REST, TensorFlow Serving также поддерживает gRPC .
gRPC — это современная высокопроизводительная платформа удалённого вызова процедур (RPC) с открытым исходным кодом, которая может работать в любой среде. Она обеспечивает эффективное подключение сервисов внутри и между центрами обработки данных благодаря подключаемым модулям для балансировки нагрузки, трассировки, проверки работоспособности и аутентификации. На практике gRPC демонстрирует более высокую производительность, чем REST.
Отправка запросов и получение ответов с помощью gRPC
Четыре простых шага:
- Необязательно: сгенерируйте заглушку кода клиента gRPC.
- Создайте запрос gRPC.
- Отправьте запрос gRPC в TensorFlow Serving.
- Извлеките прогнозируемый результат из ответа gRPC и отобразите его в пользовательском интерфейсе.
Эти шаги выполняются в файле src/index.js
.
Необязательно: сгенерируйте заглушку кода клиента gRPC.
Чтобы использовать gRPC с TensorFlow Serving, необходимо следовать рабочему процессу gRPC. Подробнее см. в документации gRPC .
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;
}
Теперь прослушиватель извлекает прогнозируемые вероятности из ответа, определяет наиболее вероятную категорию объекта и отображает результат в пользовательском интерфейсе.
Запусти это
- В терминале используйте webpack для объединения всех файлов JavaScript в один файл, который можно встроить в файл
index.html
:
npx webpack
- Обновите http://localhost:8887/ в вашем браузере.
- Нажмите gRPC > Классифицировать .
На сайте отображается прогнозируемая категория 286
, которая соответствует метке Egyptian Cat
в наборе данных ImageNet .
9. Поздравления
Вы использовали TensorFlow Serving для добавления возможностей классификации изображений на свой веб-сайт!