Ce didacticiel vous montre comment utiliser les composants TensorFlow Serving pour créer le TensorFlow ModelServer standard qui détecte et sert de manière dynamique les nouvelles versions d'un modèle TensorFlow entraîné. Si vous souhaitez simplement utiliser le serveur standard pour servir vos modèles, consultez le didacticiel de base de TensorFlow Serving .
Ce didacticiel utilise le modèle de régression Softmax simple introduit dans le didacticiel TensorFlow pour la classification d'images manuscrites (données MNIST). Si vous ne savez pas ce qu'est TensorFlow ou MNIST, consultez le didacticiel MNIST pour les débutants en ML .
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 décrit les tâches suivantes:
- Entraînez et exportez un modèle TensorFlow.
- Gérez la gestion des versions des modèles avec TensorFlow Serving
ServerCore
. - Configurez le traitement par lots à l'aide de
SavedModelBundleSourceAdapterConfig
. - Servir la demande avec TensorFlow Serving
ServerCore
. - Exécutez et testez le service.
Avant de commencer, installez d' abord Docker
Former 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înez-vous (avec 100 itérations) et exportez 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înez (avec 2000 itérations) et exportez 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 composez intentionnellement les itérations d'entraînement pour la première exécution et l'exportez en tant que v1, tout en l'entraînant normalement pour la deuxième exécution et en l'exportant en tant que v2 vers le même répertoire parent - comme nous nous attendons à ce que ce dernier réalise meilleure précision de classification grâce à une formation plus intensive. Vous devriez voir les données d'entraînement pour chaque entraînement exécuté dans votre /tmp/mnist
:
$ ls /tmp/mnist
1 2
ServerCore
Imaginez maintenant que les versions v1 et v2 du modèle sont 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 le déploiement progressif, dans lequel la v2 peut être découverte, chargée, expérimentée, surveillée ou rétablie tout en servant la v1. Vous pouvez également supprimer la v1 avant de lancer la v2. TensorFlow Serving prend en charge les deux options - alors que l'une est bonne pour maintenir la disponibilité pendant la transition, l'autre est bonne pour minimiser l'utilisation des ressources (par exemple, la RAM).
C'est exactement ce que fait TensorFlow Serving Manager
. Il gère le cycle de vie complet des modèles TensorFlow, y compris leur chargement, leur service et leur déchargement, ainsi que les transitions de version. Dans ce didacticiel, vous allez créer votre serveur au-dessus d'un serveur TensorFlow Serving ServerCore
, qui AspiredVersionsManager
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 susceptibles d'être mis à jour lors de l'exécution. -
PlatformConfigMap
qui mappe du nom de la plate-forme (tel quetensorflow
) àPlatformConfig
, qui est utilisé pour créer leSourceAdapter
.SourceAdapter
adapteStoragePath
(le chemin où une version de modèle est découverte) auLoader
modèle (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 interface Session::Run
que TensorFlow pour exécuter l'inférence. SavedModelBundleSourceAdapter
adapte le chemin de stockage au 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
interne les opérations suivantes:
- Instancie un
FileSystemStoragePathSource
qui surveille les chemins d'exportation de modèle 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 connecteFileSystemStoragePathSource
. De cette façon, chaque fois qu'une nouvelle version de modèle est découverte sous le chemin d'exportation, leSavedModelBundleSourceAdapter
adapte à unLoader<SavedModelBundle>
. - Instancie une implémentation spécifique de
Manager
appeléeAspiredVersionsManager
qui gère toutes ces instances deLoader
créées parSavedModelBundleSourceAdapter
.ServerCore
exporte l'interfaceManager
en déléguant les appels àAspiredVersionsManager
.
Chaque fois qu'une nouvelle version est disponible, ce AspiredVersionsManager
charge la nouvelle version et, sous 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 à les configurer.
Il est à noter que TensorFlow Serving est conçu à partir de zéro pour être très flexible et extensible. Vous pouvez construire différents plugins pour personnaliser le comportement du système, tout en tirant parti des composants génériques de base comme ServerCore
et AspiredVersionsManager
. Par exemple, vous pouvez créer un plugin de source de données qui surveille le stockage cloud au lieu du stockage local, ou vous pouvez créer un plugin de politique 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 rubriques sont hors de portée de ce didacticiel. Cependant, vous pouvez vous référer aux didacticiels source personnalisée et service 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 faire des inférences d'apprentissage automatique atteignent généralement la meilleure efficacité de calcul lorsque les demandes d'inférence sont exécutées par lots importants.
Le SessionBundleConfig
par lots peut être activé en fournissant SessionBundleConfig
appropriée lors de la création de SavedModelBundleSourceAdapter
. Dans ce cas, nous définissons les paramètres BatchingParameters
avec à peu près des valeurs par défaut. Le traitement par lots peut être affiné en définissant des valeurs de timeout, batch_size, etc. personnalisées. Pour plus de détails, reportez-vous à 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 demandes d'inférence sont fusionnées en interne en une seule grande demande (tensor) et tensorflow::Session::Run()
est invoqué (d'où provient 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, la diffusion, le déchargement et la transition de version des modèles générés par des systèmes d'apprentissage automatique arbitraires. Ses API sont construites autour des concepts clés suivants:
Servable : 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, d'une seule partition d'une table de recherche à un seul modèle appris par machine en passant par 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 servable, ce qui prend en charge le déploiement progressif et l'expérimentation.Stream Servable : 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 par machine 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.
- Tables de recherche de vocabulaire.
- Incorporation de tables de recherche.
Un modèle composite peut être représenté comme plusieurs servables indépendants ou comme un seul servable composite. Un servable peut également correspondre à une fraction d'un modèle, par exemple avec une grande table de recherche partagée entre de nombreuses instances de
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 service -
SavedModelBundle
.SavedModelBundle
compose en interne d'untensorflow:Session
associée à des métadonnées sur le graphique chargé dans la session et sur la manière de l'exécuter pour l'inférence.Il existe un répertoire du système de fichiers contenant un flux d'exportations TensorFlow, chacun 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 servable pour le modèle TensorFlow servi. Chaque export correspond à un servable qui peut être chargé.
AspiredVersionsManager
surveille le flux d'exportation et gère le cycle de vie de tous lesSavedModelBundle
SavedModelBundle de manière dynamique.
TensorflowPredictImpl::Predict
alors juste:
- Requêtes
SavedModelBundle
du gestionnaire (via ServerCore). - Utilise les
generic signatures
pour mapper les noms de tenseurs logiques dansPredictRequest
à des noms de tenseurs réels et lier les valeurs aux tenseurs. - Exécute l'inférence.
Tester et exécuter le serveur
Copiez la première version de l'exportation dans le dossier surveillé:
mkdir /tmp/monitored
cp -r /tmp/mnist/1 /tmp/monitored
Puis démarrez 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 qui disent "Version à venir pour servable ...", ce qui signifie qu'il a trouvé l'exportation et qu'il suit son existence.
--concurrency=10
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 une sortie qui ressemble à:
...
Inference error rate: 13.1%
Ensuite, nous copions la deuxième version de l'exportation 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 une sortie qui ressemble à:
...
Inference error rate: 9.5%
Cela confirme que votre serveur découvre automatiquement la nouvelle version et l'utilise pour la diffusion!