2017-09-07 2 views
3

j'enregistrer le modèle à l'aide tf.estimator .Méthode export_savedmodel comme suit:Comment importer un modèle de train tensorflow enregistré à l'aide tf.estimator et prévoir des données d'entrée

export_dir="exportModel/" 

feature_spec = tf.feature_column.make_parse_example_spec(feature_columns) 

input_receiver_fn = tf.estimator.export.build_parsing_serving_input_receiver_fn(feature_spec) 

classifier.export_savedmodel(export_dir, input_receiver_fn, as_text=False, checkpoint_path="Model/model.ckpt-400") 

Comment puis-je importer ce modèle enregistré et utiliser pour les prédictions?

+0

Pouvez-vous commenter un peu plus sur l'environnement dans lequel vous souhaitez effectuer des prédictions? Voulez-vous juste écrire une application Python qui charge le modèle dans le même processus et effectue une prédiction? Voulez-vous exécuter votre propre service de production pour servir votre modèle? Voulez-vous utiliser un service géré dans le cloud? – rhaertel80

+0

Maintenant, j'essaye d'écrire un script python pour charger le modèle et effectuer la prédiction. – nayan

+0

[Exemple de fonction export_savedmodel] (https://stackoverflow.com/a/48329456/4268517) –

Répondre

9

J'ai essayé de rechercher un bon exemple de base, mais il semble que la documentation et les exemples soient un peu dispersés pour ce sujet. Commençons donc avec un exemple de base: le tf.estimator .

Cet exemple particulier ne fait pas exporter un modèle, donc nous allons le faire (pas besoin pour le cas 1):

def serving_input_receiver_fn(): 
    """Build the serving inputs.""" 
    # The outer dimension (None) allows us to batch up inputs for 
    # efficiency. However, it also means that if we want a prediction 
    # for a single instance, we'll need to wrap it in an outer list. 
    inputs = {"x": tf.placeholder(shape=[None, 4], dtype=tf.float32)} 
    return tf.estimator.export.ServingInputReceiver(inputs, inputs) 

export_dir = classifier.export_savedmodel(
    export_dir_base="/path/to/model", 
    serving_input_receiver_fn=serving_input_receiver_fn) 

astérisque énorme sur ce code: il semble y avoir un bug dans TensorFlow 1.3 qui ne vous permet pas de faire l'exportation ci-dessus sur un estimateur "en conserve" (tel que DNNClassifier). Pour une solution de contournement, consultez la section «Annexe: solution de contournement».

Le code ci-dessous les références export_dir (valeur de retour de l'étape d'exportation) à souligner qu'il est pas «/chemin/vers/modèle », mais plutôt un sous-répertoire de ce répertoire dont le nom est un horodatage.

Cas d'utilisation 1: Effectuer la prédiction dans le même processus que la formation

C'est une science-kit apprendre type d'expérience, et est déjà illustrée par l'échantillon. Par souci d'exhaustivité, vous appelez simplement predict sur le modèle formé:

classifier.train(input_fn=train_input_fn, steps=2000) 
# [...snip...] 
predictions = list(classifier.predict(input_fn=predict_input_fn)) 
predicted_classes = [p["classes"] for p in predictions] 

Cas d'utilisation 2: Charger un SavedModel en Python/Java/C++ et effectuer des prévisions

Python client

Peut-être la chose la plus facile à utiliser si vous voulez faire une prédiction en Python est SavedModelPredictor. Dans le programme Python qui utilisera le SavedModel, nous avons besoin de code comme ceci:

from tensorflow.contrib import predictor 

predict_fn = predictor.from_saved_model(export_dir) 
predictions = predict_fn(
    {"x": [[6.4, 3.2, 4.5, 1.5], 
      [5.8, 3.1, 5.0, 1.7]]}) 
print(predictions['scores']) 

Client Java

package dummy; 

import java.nio.FloatBuffer; 
import java.util.Arrays; 
import java.util.List; 

import org.tensorflow.SavedModelBundle; 
import org.tensorflow.Session; 
import org.tensorflow.Tensor; 

public class Client { 

    public static void main(String[] args) { 
    Session session = SavedModelBundle.load(args[0], "serve").session(); 

    Tensor x = 
     Tensor.create(
      new long[] {2, 4}, 
      FloatBuffer.wrap(
       new float[] { 
        6.4f, 3.2f, 4.5f, 1.5f, 
        5.8f, 3.1f, 5.0f, 1.7f 
       })); 

    // Doesn't look like Java has a good way to convert the 
    // input/output name ("x", "scores") to their underlying tensor, 
    // so we hard code them ("Placeholder:0", ...). 
    // You can inspect them on the command-line with saved_model_cli: 
    // 
    // $ saved_model_cli show --dir $EXPORT_DIR --tag_set serve --signature_def serving_default 
    final String xName = "Placeholder:0"; 
    final String scoresName = "dnn/head/predictions/probabilities:0"; 

    List<Tensor> outputs = session.runner() 
     .feed(xName, x) 
     .fetch(scoresName) 
     .run(); 

    // Outer dimension is batch size; inner dimension is number of classes 
    float[][] scores = new float[2][3]; 
    outputs.get(0).copyTo(scores); 
    System.out.println(Arrays.deepToString(scores)); 
    } 
} 

C++ client

Vous voudrez probablement utiliser tensorflow::LoadSavedModel avec Session.

#include <unordered_set> 
#include <utility> 
#include <vector> 

#include "tensorflow/cc/saved_model/loader.h" 
#include "tensorflow/core/framework/tensor.h" 
#include "tensorflow/core/public/session.h" 

namespace tf = tensorflow; 

int main(int argc, char** argv) { 
    const string export_dir = argv[1]; 

    tf::SavedModelBundle bundle; 
    tf::Status load_status = tf::LoadSavedModel(
     tf::SessionOptions(), tf::RunOptions(), export_dir, {"serve"}, &bundle); 
    if (!load_status.ok()) { 
    std::cout << "Error loading model: " << load_status << std::endl; 
    return -1; 
    } 

    // We should get the signature out of MetaGraphDef, but that's a bit 
    // involved. We'll take a shortcut like we did in the Java example. 
    const string x_name = "Placeholder:0"; 
    const string scores_name = "dnn/head/predictions/probabilities:0"; 

    auto x = tf::Tensor(tf::DT_FLOAT, tf::TensorShape({2, 4})); 
    auto matrix = x.matrix<float>(); 
    matrix(0, 0) = 6.4; 
    matrix(0, 1) = 3.2; 
    matrix(0, 2) = 4.5; 
    matrix(0, 3) = 1.5; 
    matrix(0, 1) = 5.8; 
    matrix(0, 2) = 3.1; 
    matrix(0, 3) = 5.0; 
    matrix(0, 4) = 1.7; 

    std::vector<std::pair<string, tf::Tensor>> inputs = {{x_name, x}}; 
    std::vector<tf::Tensor> outputs; 

    tf::Status run_status = 
     bundle.session->Run(inputs, {scores_name}, {}, &outputs); 
    if (!run_status.ok()) { 
    cout << "Error running session: " << run_status << std::endl; 
    return -1; 
    } 

    for (const auto& tensor : outputs) { 
    std::cout << tensor.matrix<float>() << std::endl; 
    } 
} 

Cas d'utilisation 3: Servir un modèle en utilisant tensorflow service

Exportation de modèles d'une manière prête à servir une Classification model exige que l'entrée soit un objet tf.Example.Voici comment nous pourrions exporter un modèle pour tensorflow service:

def serving_input_receiver_fn(): 
    """Build the serving inputs.""" 
    # The outer dimension (None) allows us to batch up inputs for 
    # efficiency. However, it also means that if we want a prediction 
    # for a single instance, we'll need to wrap it in an outer list. 
    example_bytestring = tf.placeholder(
     shape=[None], 
     dtype=tf.string, 
) 
    features = tf.parse_example(
     example_bytestring, 
     tf.feature_column.make_parse_example_spec(feature_columns) 
) 
    return tf.estimator.export.ServingInputReceiver(
     features, {'examples': example_bytestring}) 

export_dir = classifier.export_savedmodel(
    export_dir_base="/path/to/model", 
    serving_input_receiver_fn=serving_input_receiver_fn) 

Le lecteur est renvoyé à tensorflow Au service de documentation pour plus d'instructions sur la façon de configurer tensorflow service, donc je vais seulement fournir le code client ici:

# Omitting a bunch of connection/initialization code... 
    # But at some point we end up with a stub whose lifecycle 
    # is generally longer than that of a single request. 
    stub = create_stub(...) 

    # The actual values for prediction. We have two examples in this 
    # case, each consisting of a single, multi-dimensional feature `x`. 
    # This data here is the equivalent of the map passed to the 
    # `predict_fn` in use case #2. 
    examples = [ 
    tf.train.Example(
     features=tf.train.Features(
     feature={"x": tf.train.Feature(
      float_list=tf.train.FloatList(value=[6.4, 3.2, 4.5, 1.5]))})), 
    tf.train.Example(
     features=tf.train.Features(
     feature={"x": tf.train.Feature(
      float_list=tf.train.FloatList(value=[5.8, 3.1, 5.0, 1.7]))})), 
    ] 

    # Build the RPC request. 
    predict_request = predict_pb2.PredictRequest() 
    predict_request.model_spec.name = "default" 
    predict_request.inputs["examples"].CopyFrom(
     tensor_util.make_tensor_proto(examples, tf.float32)) 

    # Perform the actual prediction. 
    stub.Predict(request, PREDICT_DEADLINE_SECS) 

Notez que la clé, examples, qui est référencé dans le predict_request.inputs doit correspondre à la clé utilisée dans le serving_input_receiver_fn au moment de l'exportation (voir le constructeur à ServingInputReceiver dans ce code).

Annexe: Contourner Les exportations de modèles en conserve dans TF 1.3

Il semble y avoir un bug dans tensorflow 1.3 où les modèles en boîte n'exportent pas correctement pour le cas 2 (le problème n'existe pas pour " "estimateurs personnalisés"). Voici une solution de contournement qui enveloppe un DNNClassifier pour faire fonctionner les choses, en particulier pour l'exemple Iris:

# Build 3 layer DNN with 10, 20, 10 units respectively. 
class Wrapper(tf.estimator.Estimator): 
    def __init__(self, **kwargs): 
    dnn = tf.estimator.DNNClassifier(**kwargs) 

    def model_fn(mode, features, labels): 
     spec = dnn._call_model_fn(features, labels, mode) 
     export_outputs = None 
     if spec.export_outputs: 
     export_outputs = { 
      "serving_default": tf.estimator.export.PredictOutput(
        {"scores": spec.export_outputs["serving_default"].scores, 
        "classes": spec.export_outputs["serving_default"].classes})} 

     # Replace the 3rd argument (export_outputs) 
     copy = list(spec) 
     copy[4] = export_outputs 
     return tf.estimator.EstimatorSpec(mode, *copy) 

    super(Wrapper, self).__init__(model_fn, kwargs["model_dir"], dnn.config) 

classifier = Wrapper(feature_columns=feature_columns, 
        hidden_units=[10, 20, 10], 
        n_classes=3, 
        model_dir="/tmp/iris_model") 
+0

Merci beaucoup pour l'explication détaillée. Je suis capable d'obtenir les scores pour chaque classe en utilisant print (predictions ['scores']) et print (predictions ['classes']). pouvons-nous être en mesure d'obtenir la classe prévue. – nayan

+0

@nayan La sortie de DNNClassifier est conçue pour prendre en charge un très grand espace de sortie dans lequel vous pouvez prédire les classes top-n. L'idée est que la clé 'classes' contient les noms des classes correspondant aux scores de la sortie' scores'.Cependant, je ne crois pas que vous puissiez vraiment faire top-n, pour le moment. Donc ce que vous obtenez dans 'classes' est juste la liste des classes, dans l'ordre, répété pour chaque sortie. Pour obtenir la classe prédite, vous avez deux options: (1) écrire un estimateur personnalisé (éventuellement envelopper DNNClassifier ou similaire pour faire le travail) (2) demander au client de prendre l'argmax de 'scores' – rhaertel80

+0

Merci. Je suis en mesure d'obtenir le top 1 prédire la classe en utilisant argmax des scores. S'il existe un équivalent api c/C++ pour la fonction de prédiction python comme dans le cas d'utilisation 2, afin qu'il puisse être intégré à la plate-forme iOS/Android. – nayan

0

Il semble que l'équipe de tensorflow ne sont pas d'accord qu'il ya un bogue dans la version 1.3 en utilisant des estimateurs en conserve pour l'exportation d'un modèle sous le cas d'utilisation n ° 2. J'ai soumis un rapport de bogue ici: https://github.com/tensorflow/tensorflow/issues/13477

La réponse que j'ai reçue de TensorFlow est que l'entrée ne doit être qu'un seul tenseur de chaîne. Il semble qu'il existe un moyen de consolider plusieurs entités dans un seul tenseur de chaîne à l'aide de TF.examples sérialisés, mais je n'ai pas trouvé de méthode claire pour le faire. Si quelqu'un a un code indiquant comment faire cela, je serais reconnaissant.

0

Je ne pense pas qu'il y ait un bug avec Estimators en conserve (ou plutôt s'il y en avait un, il a été corrigé). J'ai réussi à exporter un modèle d'estimateur en conserve en utilisant Python et à l'importer en Java.

Voici mon code pour exporter le modèle:

a = tf.feature_column.numeric_column("a"); 
b = tf.feature_column.numeric_column("b"); 
feature_columns = [a, b]; 

model = tf.estimator.DNNClassifier(feature_columns=feature_columns ...); 

# To export 
feature_spec = tf.feature_column.make_parse_example_spec(feature_columns); 
export_input_fn = tf.estimator.export.build_parsing_serving_input_receiver_fn(feature_spec); 
servable_model_path = model.export_savedmodel(servable_model_dir, export_input_fn, as_text=True); 

Pour importer le modèle en Java, je le code client Java fourni par rhaertel80 ci-dessus et il fonctionne. J'espère que cela répond également à la question de Ben Fowler ci-dessus.

0

Vous devez exporter le modèle enregistré à l'aide de tf.contrib.export_savedmodel et vous devez définir la fonction du récepteur d'entrée pour transmettre l'entrée. Plus tard, vous pourrez charger le modèle enregistré (généralement saved.model.pb) à partir du disque et le servir.

TensorFlow: How to predict from a SavedModel?