Ce didacticiel vous montre comment utiliser les composants TensorFlow Serving pour créer le TensorFlow ModelServer standard qui découvre et diffuse dynamiquement de nouvelles versions d'un modèle TensorFlow entraîné. Si vous souhaitez simplement utiliser le serveur standard pour diffuser vos modèles, consultez le didacticiel de base sur TensorFlow Serving .
Ce didacticiel utilise le modèle simple de régression Softmax introduit dans le didacticiel TensorFlow pour la classification des images manuscrites (données MNIST). Si vous ne savez pas ce qu'est TensorFlow ou MNIST, consultez le didacticiel MNIST For ML Beginners .
Le code de ce didacticiel se compose de deux parties :
Un fichier Python mnist_saved_model.py qui entraîne et exporte plusieurs versions du modèle.
Un fichier C++ main.cc qui est le TensorFlow ModelServer standard qui découvre les nouveaux modèles exportés et exécute un service gRPC pour les servir.
Ce didacticiel passe en revue les tâches suivantes :
- Entraînez et exportez un modèle TensorFlow.
- Gérez la gestion des versions du modèle avec TensorFlow Serving
ServerCore
. - Configurez le traitement par lots à l’aide de
SavedModelBundleSourceAdapterConfig
. - Servir la requête avec TensorFlow Serving
ServerCore
. - Exécutez et testez le service.
Avant de commencer, installez d'abord Docker
Entraîner et exporter le modèle TensorFlow
Tout d'abord, si vous ne l'avez pas encore fait, clonez ce référentiel sur votre machine locale :
git clone https://github.com/tensorflow/serving.git
cd serving
Effacez le répertoire d'exportation s'il existe déjà :
rm -rf /tmp/models
Entraîner (avec 100 itérations) et exporter la première version du modèle :
tools/run_in_docker.sh python tensorflow_serving/example/mnist_saved_model.py \
--training_iteration=100 --model_version=1 /tmp/mnist
Entraîner (avec 2000 itérations) et exporter la deuxième version du modèle :
tools/run_in_docker.sh python tensorflow_serving/example/mnist_saved_model.py \
--training_iteration=2000 --model_version=2 /tmp/mnist
Comme vous pouvez le voir dans mnist_saved_model.py
, la formation et l'exportation se font de la même manière que dans le didacticiel de base de TensorFlow Serving . À des fins de démonstration, vous réduisez intentionnellement les itérations d'entraînement pour la première exécution et les exportez en tant que v1, tout en les entraînant normalement pour la deuxième exécution et en les exportant en tant que v2 vers le même répertoire parent - comme nous nous attendons à ce que ce dernier le fasse. une meilleure précision de classification grâce à une formation plus intensive. Vous devriez voir les données d'entraînement pour chaque exécution d'entraînement dans votre répertoire /tmp/mnist
:
$ ls /tmp/mnist
1 2
ServeurCore
Imaginez maintenant que les versions v1 et v2 du modèle soient générées dynamiquement au moment de l'exécution, au fur et à mesure que de nouveaux algorithmes sont expérimentés ou que le modèle est entraîné avec un nouvel ensemble de données. Dans un environnement de production, vous souhaiterez peut-être créer un serveur capable de prendre en charge un déploiement progressif, dans lequel la v2 peut être découverte, chargée, expérimentée, surveillée ou annulée tout en servant la v1. Alternativement, vous souhaiterez peut-être supprimer la v1 avant d’afficher la v2. TensorFlow Serving prend en charge les deux options : l'une permet de maintenir la disponibilité pendant la transition, l'autre permet de minimiser l'utilisation des ressources (par exemple, la RAM).
TensorFlow Serving Manager
fait exactement cela. Il gère le cycle de vie complet des modèles TensorFlow, y compris leur chargement, leur diffusion et leur déchargement, ainsi que les transitions de version. Dans ce didacticiel, vous allez construire votre serveur au-dessus d'un TensorFlow Serving ServerCore
, qui encapsule en interne un AspiredVersionsManager
.
int main(int argc, char** argv) {
...
ServerCore::Options options;
options.model_server_config = model_server_config;
options.servable_state_monitor_creator = &CreateServableStateMonitor;
options.custom_model_config_loader = &LoadCustomModelConfig;
::google::protobuf::Any source_adapter_config;
SavedModelBundleSourceAdapterConfig
saved_model_bundle_source_adapter_config;
source_adapter_config.PackFrom(saved_model_bundle_source_adapter_config);
(*(*options.platform_config_map.mutable_platform_configs())
[kTensorFlowModelPlatform].mutable_source_adapter_config()) =
source_adapter_config;
std::unique_ptr<ServerCore> core;
TF_CHECK_OK(ServerCore::Create(options, &core));
RunServer(port, std::move(core));
return 0;
}
ServerCore::Create()
prend un paramètre ServerCore::Options. Voici quelques options couramment utilisées :
-
ModelServerConfig
qui spécifie les modèles à charger. Les modèles sont déclarés soit viamodel_config_list
, qui déclare une liste statique de modèles, soit viacustom_model_config
, qui définit une manière personnalisée de déclarer une liste de modèles pouvant être mis à jour au moment de l'exécution. -
PlatformConfigMap
qui mappe le nom de la plate-forme (commetensorflow
) auPlatformConfig
, qui est utilisé pour créer leSourceAdapter
.SourceAdapter
adapteStoragePath
(le chemin où une version de modèle est découverte) au modelLoader
(charge la version du modèle à partir du chemin de stockage et fournit des interfaces de transition d'état auManager
). SiPlatformConfig
contientSavedModelBundleSourceAdapterConfig
, unSavedModelBundleSourceAdapter
sera créé, ce que nous expliquerons plus tard.
SavedModelBundle
est un composant clé de TensorFlow Serving. Il représente un modèle TensorFlow chargé à partir d'un chemin donné et fournit la même Session::Run
que TensorFlow pour exécuter l'inférence. SavedModelBundleSourceAdapter
adapte le chemin de stockage à Loader<SavedModelBundle>
afin que la durée de vie du modèle puisse être gérée par Manager
. Veuillez noter que SavedModelBundle
est le successeur de SessionBundle
obsolète. Les utilisateurs sont encouragés à utiliser SavedModelBundle
car la prise en charge de SessionBundle
sera bientôt supprimée.
Avec tout cela, ServerCore
effectue en interne les opérations suivantes :
- Instancie un
FileSystemStoragePathSource
qui surveille les chemins d'exportation de modèles déclarés dansmodel_config_list
. - Instancie un
SourceAdapter
à l'aide dePlatformConfigMap
avec la plate-forme de modèle déclarée dansmodel_config_list
et y connecte leFileSystemStoragePathSource
. De cette façon, chaque fois qu'une nouvelle version du modèle est découverte sous le chemin d'exportation,SavedModelBundleSourceAdapter
l'adapte à unLoader<SavedModelBundle>
. - Instancie une implémentation spécifique de
Manager
appeléeAspiredVersionsManager
qui gère toutes ces instancesLoader
créées parSavedModelBundleSourceAdapter
.ServerCore
exporte l'interfaceManager
en déléguant les appels àAspiredVersionsManager
.
Chaque fois qu'une nouvelle version est disponible, cet AspiredVersionsManager
charge la nouvelle version et, selon son comportement par défaut, décharge l'ancienne. Si vous souhaitez commencer la personnalisation, nous vous encourageons à comprendre les composants créés en interne et comment les configurer.
Il convient de mentionner que TensorFlow Serving est conçu dès le départ pour être très flexible et extensible. Vous pouvez créer divers plugins pour personnaliser le comportement du système, tout en tirant parti des composants de base génériques tels que ServerCore
et AspiredVersionsManager
. Par exemple, vous pouvez créer un plugin de source de données qui surveille le stockage dans le cloud au lieu du stockage local, ou vous pouvez créer un plugin de stratégie de version qui effectue la transition de version d'une manière différente. En fait, vous pouvez même créer un plugin de modèle personnalisé qui sert modèles non TensorFlow. Ces sujets sortent du cadre de ce didacticiel. Cependant, vous pouvez vous référer aux didacticiels sur la source personnalisée et sur le serveur personnalisé pour plus d'informations.
Traitement par lots
Une autre fonctionnalité de serveur typique que nous souhaitons dans un environnement de production est le traitement par lots. Les accélérateurs matériels modernes (GPU, etc.) utilisés pour effectuer l'inférence d'apprentissage automatique atteignent généralement la meilleure efficacité de calcul lorsque les requêtes d'inférence sont exécutées par lots importants.
Le traitement par lots peut être activé en fournissant SessionBundleConfig
approprié lors de la création du SavedModelBundleSourceAdapter
. Dans ce cas, nous définissons les BatchingParameters
avec des valeurs par défaut. Le traitement par lots peut être affiné en définissant des valeurs de délai d'attente personnalisées, de taille de lot, etc. Pour plus de détails, veuillez vous référer à BatchingParameters
.
SessionBundleConfig session_bundle_config;
// Batching config
if (enable_batching) {
BatchingParameters* batching_parameters =
session_bundle_config.mutable_batching_parameters();
batching_parameters->mutable_thread_pool_name()->set_value(
"model_server_batch_threads");
}
*saved_model_bundle_source_adapter_config.mutable_legacy_config() =
session_bundle_config;
Une fois le lot complet atteint, les requêtes d'inférence sont fusionnées en interne en une seule requête volumineuse (tenseur) et tensorflow::Session::Run()
est invoqué (d'où vient le gain d'efficacité réel sur les GPU).
Servir avec le gestionnaire
Comme mentionné ci-dessus, TensorFlow Serving Manager
est conçu pour être un composant générique capable de gérer le chargement, le service, le déchargement et la transition de version de modèles générés par des systèmes d'apprentissage automatique arbitraires. Ses API sont construites autour des concepts clés suivants :
Serviable : Servable est tout objet opaque qui peut être utilisé pour répondre aux demandes des clients. La taille et la granularité d'un servable sont flexibles, de sorte qu'un seul servable peut inclure n'importe quoi, depuis un seul fragment d'une table de recherche jusqu'à un seul modèle appris par machine ou un tuple de modèles. Un servable peut être de n’importe quel type et interface.
Version servable : les servables sont versionnés et TensorFlow Serving
Manager
peut gérer une ou plusieurs versions d'un servable. La gestion des versions permet de charger simultanément plusieurs versions d'un serveur, ce qui permet un déploiement et une expérimentation progressifs.Serviable Stream : Un flux servable est la séquence de versions d'un servable, avec des numéros de version croissants.
Modèle : Un modèle appris automatiquement est représenté par un ou plusieurs servables. Des exemples de servables sont :
- Session TensorFlow ou wrappers autour d'eux, tels que
SavedModelBundle
. - Autres types de modèles appris par machine.
- Tableaux de recherche de vocabulaire.
- Intégration de tables de recherche.
Un modèle composite peut être représenté sous la forme de plusieurs servables indépendants ou sous la forme d'un seul servable composite. Un servable peut également correspondre à une fraction d'un modèle, par exemple avec une grande table de recherche répartie sur de nombreuses instances
Manager
.- Session TensorFlow ou wrappers autour d'eux, tels que
Pour mettre tout cela dans le contexte de ce tutoriel :
Les modèles TensorFlow sont représentés par un type de servable :
SavedModelBundle
.SavedModelBundle
se compose en interne d'untensorflow:Session
associé à des métadonnées sur le graphique chargé dans la session et comment l'exécuter pour l'inférence.Il existe un répertoire du système de fichiers contenant un flux d'exportations TensorFlow, chacune dans son propre sous-répertoire dont le nom est un numéro de version. Le répertoire externe peut être considéré comme la représentation sérialisée du flux utilisable pour le modèle TensorFlow diffusé. Chaque export correspond à une servable pouvant être chargée.
AspiredVersionsManager
surveille le flux d'exportation et gère dynamiquement le cycle de vie de tous les servablesSavedModelBundle
.
TensorflowPredictImpl::Predict
alors simplement :
- Demande
SavedModelBundle
au gestionnaire (via ServerCore). - Utilise les
generic signatures
pour mapper les noms de tenseurs logiques dansPredictRequest
aux noms de tenseurs réels et lier les valeurs aux tenseurs. - Exécute l'inférence.
Testez et exécutez le serveur
Copiez la première version de l'export dans le dossier surveillé :
mkdir /tmp/monitored
cp -r /tmp/mnist/1 /tmp/monitored
Démarrez ensuite le serveur :
docker run -p 8500:8500 \
--mount type=bind,source=/tmp/monitored,target=/models/mnist \
-t --entrypoint=tensorflow_model_server tensorflow/serving --enable_batching \
--port=8500 --model_name=mnist --model_base_path=/models/mnist &
Le serveur émettra des messages de journal toutes les secondes indiquant "Version en attente pour servable...", ce qui signifie qu'il a trouvé l'exportation et suit son existence continue.
Exécutons le client avec --concurrency=10
. Cela enverra des requêtes simultanées au serveur et déclenchera ainsi votre logique de traitement par lots.
tools/run_in_docker.sh python tensorflow_serving/example/mnist_client.py \
--num_tests=1000 --server=127.0.0.1:8500 --concurrency=10
Ce qui donne un résultat qui ressemble à :
...
Inference error rate: 13.1%
Ensuite, nous copions la deuxième version de l'export dans le dossier surveillé et réexécutons le test :
cp -r /tmp/mnist/2 /tmp/monitored
tools/run_in_docker.sh python tensorflow_serving/example/mnist_client.py \
--num_tests=1000 --server=127.0.0.1:8500 --concurrency=10
Ce qui donne un résultat qui ressemble à :
...
Inference error rate: 9.5%
Cela confirme que votre serveur découvre automatiquement la nouvelle version et l'utilise pour le service !