RSVP pour votre événement TensorFlow Everywhere local dès aujourd'hui!
Cette page a été traduite par l'API Cloud Translation.
Switch to English

Création d'un serveur de modèle TensorFlow standard

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:

  1. Entraînez et exportez un modèle TensorFlow.
  2. Gérez la gestion des versions des modèles avec TensorFlow Serving ServerCore .
  3. Configurez le traitement par lots à l'aide de SavedModelBundleSourceAdapterConfig .
  4. Servir la demande avec TensorFlow Serving ServerCore .
  5. 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 via model_config_list , qui déclare une liste statique de modèles, soit via custom_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 que tensorflow ) à PlatformConfig , qui est utilisé pour créer le SourceAdapter . SourceAdapter adapte StoragePath (le chemin où une version de modèle est découverte) au Loader modèle (charge la version du modèle à partir du chemin de stockage et fournit des interfaces de transition d'état au Manager ). Si PlatformConfig contient SavedModelBundleSourceAdapterConfig , un SavedModelBundleSourceAdapter 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 dans model_config_list .
  • Instancie un SourceAdapter à l'aide de PlatformConfigMap avec la plate-forme de modèle déclarée dans model_config_list et y connecte FileSystemStoragePathSource . De cette façon, chaque fois qu'une nouvelle version de modèle est découverte sous le chemin d'exportation, le SavedModelBundleSourceAdapter adapte à un Loader<SavedModelBundle> .
  • Instancie une implémentation spécifique de Manager appelée AspiredVersionsManager qui gère toutes ces instances de Loader créées par SavedModelBundleSourceAdapter . ServerCore exporte l'interface Manager 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 .

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'un tensorflow: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 les SavedModelBundle 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 dans PredictRequest à 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!