Всем привет, меня зовут Матвей, я работаю Data Scientist-ом. Моя работа состоит из предварительной обработки данных, развития и развертывания моделей.
Сегодня я поделюсь с вами своим опытом и покажу, как развернуть модель таким образом, чтобы часть расчетов происходила на стороне клиента. Эта статья предназначена для всех, кто создал модель и желает уменьшить нагрузку на сервер, передав часть с прогнозированием клиенту. Особенно для Data Scientist-ов, которые используют Python ежедневно и плохо владеют Javascript.
Вступление
Представим, что вы создали какую-то замечательную модель, которая делает крутые вещи и помогает людям. Например, модель прогнозирует любимый емоджі человека на основе фотографии ее чашки. Вы скачали эту модель в интернет. Ежедневное использование достигает примерно 1000 запросов — немного. Простой сервер может дать с этим справиться, но однажды о нем узнает много людей, и вы начнете получать по 100 тысяч запросов ежедневно. Ваш сервер, скорее всего, «умрет». Следовательно, вы можете увеличить или сервер и добавлять каждый раз больше памяти, или переписать прогнозирования на сторону клиента. Если вы выберете второй вариант, то вот для вас туториал.
Чтобы достичь цели, нам нужны такие компоненты:
Backend: Flask, любая библиотека для предварительной обработки изображения в Python.
Frontend: TensorFlow.js
Нещодавно в TensorFlow.js з’явилася підтримка Node.js, проте ми будемо використовувати Flask, який є бібліотекою Python. Часто деяким натренованим моделям потрібна попередня обробка даних для коректної роботи. Наразі попередню обробку набагато зручніше виконувати в Python ніж в JavaScipt. Сподіваюся, що одного разу стане також можливою і попередня обробка на стороні клієнта.
Створення моделі
Ви можете тренувати модель для MNIST, запустивши train_model.py, або ж створити і натренувати будь-яку модель, яку ви хочете. Важливо зберегти топологію і навантаження. У випадку, якщо ваша модель написана на Keras, просто додайте це.
Наша первая версия будет выглядеть так. Единственная важная вещь здесь — на строке 6, где мы добавляем Tensorflow.js с CDN. Следующий шаг — это добавление тела HTML, чтобы пользователь смог загружать изображения и нажимать на кнопки :) Вот он.
<body>
<h1>TensorflowJS client side prediction</h1>
<h2>When you first time press predict it will take more time, for model to load</h2>
Следующий и последний шаг для части с HTML — добавить немного стиля к нашей страницы, соответственно присвоив классы элементам HTML, и также создать основной main.js файл который будет содержать наше магическое прогнозирования. Теперь давайте посмотрим на окончательную версию index.html.
В строке 3 — адрес нашей модели, сейчас она находится на моей локальной машине, но вообще ее можно развернуть в любом месте. Также позже мы создадим маршрут в Flask для этой модели.
Теперь добавим часть, которая загружает нашу модель, будет загруженные пользователем изображения и отправлять их на сервер для предварительной обработки.
Мы отправляем изображение на /api/prepare/, этот путь мы добавим позже. Также мы присваиваем ответа сервера на поле с изображением tf.tensor2d.
Теперь нужно добавить прогнозирования для tensor, после этого визуализировать этот прогноз и изображение для нашего пользователя. Последний шаг — написать функционал для кнопки и вызов функции.
Итак, последняя версия магического скрипта с прогнозированием на стороне клиента будет выглядеть как-то так.
Вы можете переписать часть с fetch так, чтобы отправить все файлы в одном посте. Не забудьте привести возвращен массив в того же формата, по которым тренировалась модель.
Обновление сервера Flask
Теперь нам нужно обновить наш сервер, чтобы он мог выполнять предварительную обработку изображения для /api/prepare/ и также выводить модель /model на frontend. Финальная версия сервера будет выглядеть примерно так.
from flask_cors import CORS
from flask import Flask, request, render_template, json, jsonify, send_from_directory
Для предварительной обработки у нас есть две функции:
prepare (вызывается на /api/prepare/);
preprocessing (принимает изображение и возвращает измененное изображение в numpy массива, эта функция может выполнять любую предварительную обработку, и все будет работать без проблем, если она возвращает массив numpy).
Модель:
model (вызывается для /model);
load_shards (вызывается для любого файла, который можно вызвать с /, эта функция используется для загрузки бинарных weight файлов).
Почему нам нужны две функции и два отдельных API для модели вместо одного?
В этой версии TensorFlow.js когда мы загружаем модель для какого-то API,
model = await tf.loadModel(modelURL);
она сначала загружает модель, которая является файлом JSON, с modelURL, и после этого автоматически отправляет еще несколько POST запросов к основе домена, чтобы скачать shards (просмотрите этот запрос в демо, в логах сервера). Поскольку я не хочу держать модель в основе пути вместе с сервером, мне нужны две функции: одна для model.json и другая для shards.
Результат
На этом все! Теперь вы можете передать клиенту процесс прогнозирования любимого емоджі человека на основе изображения ее чашки или, как это в моем случае, MNIST! Если все сделано правильно, то вы увидите что-то такое.
Спасибо что прочитали! Можете добавить/переписать любую часть, как вам угодно. Наслаждайтесь!