Je souhaite inclure ma logique de prétraitement personnalisée dans mon modèle Keras exporté pour une utilisation dans Tensorflow Serving.

Mon prétraitement effectue une segmentation de chaîne et utilise un dictionnaire externe pour convertir chaque jeton en un index à saisir dans la couche d'intégration:

from keras.preprocessing import sequence

token_to_idx_dict = ... #read from file

# Custom Pythonic pre-processing steps on input_data
tokens = [tokenize(s) for s in input_data]
token_idxs = [[token_to_idx_dict[t] for t in ts] for ts in tokens]
tokens_padded = sequence.pad_sequences(token_idxs, maxlen=maxlen)

Architecture du modèle et formation:

model = Sequential()
model.add(Embedding(max_features, 128, input_length=maxlen))
model.add(LSTM(128, activation='sigmoid'))
model.add(Dense(n_classes, activation='softmax'))
model.compile(loss='sparse_categorical_crossentropy', optimizer='adam')

model.fit(x_train, y_train)

Étant donné que le modèle sera utilisé dans Tensorflow Serving, je souhaite incorporer toute la logique de prétraitement dans le modèle lui-même (codé dans le fichier de modèle exporté).

Q: Comment puis-je le faire en utilisant uniquement la bibliothèque Keras?

J'ai trouvé ce guide explique comment combinez Keras et Tensorflow. Mais je ne sais toujours pas comment exporter tout comme un modèle.

Je sais que Tensorflow a un fractionnement de chaîne intégré, fichier E / S et recherche de dictionnaire opérations.

Logique de prétraitement utilisant les opérations Tensorflow:

# Get input text
input_string_tensor = tf.placeholder(tf.string, shape={1})
# Split input text by whitespace
splitted_string = tf.string_split(input_string_tensor, " ")
# Read index lookup dictionary
token_to_idx_dict = tf.contrib.lookup.HashTable(tf.contrib.lookup.TextFileInitializer("vocab.txt", tf.string, 0, tf.int64, 1, delimiter=","), -1)
# Convert tokens to indexes
token_idxs = token_to_idx_dict.lookup(splitted_string)
# Pad zeros to fixed length
token_idxs_padded = tf.pad(token_idxs, ...)

Q: Comment puis-je utiliser ces opérations de prétraitement prédéfinies Tensorflow et mes couches Keras ensemble pour entraîner et exporter le modèle sous forme de "boîte noire" à utiliser dans Tensorflow Serving?

18
Qululu 16 janv. 2017 à 11:25

2 réponses

Meilleure réponse

Je l'ai compris, alors je vais répondre à ma propre question ici.

Voici l'essentiel:

Tout d'abord, (dans un fichier de code séparé), j'ai formé le modèle à l'aide de Keras uniquement avec mes propres fonctions de prétraitement, exporté le fichier de pondérations du modèle Keras et mon dictionnaire token-to-index.

Ensuite, j'ai copié uniquement l'architecture du modèle Keras, défini l'entrée comme sortie du tenseur prétraité, chargé le fichier de poids du modèle Keras précédemment formé et l'ai mis en sandwich entre les opérations de prétraitement Tensorflow et l'exportateur Tensorflow.

Produit final:

import tensorflow as tf
from keras import backend as K
from keras.models import Sequential, Embedding, LSTM, Dense
from tensorflow.contrib.session_bundle import exporter
from tensorflow.contrib.lookup import HashTable, TextFileInitializer

# Initialize Keras with Tensorflow session
sess = tf.Session()
K.set_session(sess)

# Token to index lookup dictionary
token_to_idx_path = '...'
token_to_idx_dict = HashTable(TextFileInitializer(token_to_idx_path, tf.string, 0, tf.int64, 1, delimiter='\t'), 0)

maxlen = ...

# Pre-processing sub-graph using Tensorflow operations
input = tf.placeholder(tf.string, name='input')
sparse_tokenized_input = tf.string_split(input)
tokenized_input = tf.sparse_tensor_to_dense(sparse_tokenized_input, default_value='')
token_idxs = token_to_idx_dict.lookup(tokenized_input)
token_idxs_padded = tf.pad(token_idxs, [[0,0],[0,maxlen]])
token_idxs_embedding = tf.slice(token_idxs_padded, [0,0], [-1,maxlen])

# Initialize Keras model
model = Sequential()
e = Embedding(max_features, 128, input_length=maxlen)
e.set_input(token_idxs_embedding)
model.add(e)
model.add(LSTM(128, activation='sigmoid'))
model.add(Dense(num_classes, activation='softmax'))

# Load weights from previously trained Keras model
weights_path = '...'
model.load_weights(weights_path)

K.set_learning_phase(0)

# Export model in Tensorflow format
# (Official tutorial: https://github.com/tensorflow/serving/blob/master/tensorflow_serving/g3doc/serving_basic.md)
saver = tf.train.Saver(sharded=True)
model_exporter = exporter.Exporter(saver)
signature = exporter.classification_signature(input_tensor=model.input, scores_tensor=model.output)
model_exporter.init(sess.graph.as_graph_def(), default_graph_signature=signature)
model_dir = '...'
model_version = 1
model_exporter.export(model_dir, tf.constant(model_version), sess)

# Input example
with sess.as_default():
    token_to_idx_dict.init.run()
    sess.run(model.output, feed_dict={input: ["this is a raw input example"]})
11
Qululu 29 janv. 2017 à 15:04

La réponse acceptée est très utile, mais elle utilise une API Keras obsolète comme @Qululu l'a mentionné, et une API de serveur TF (exportateur) obsolète, et elle ne montre pas comment exporter le modèle afin que son entrée soit l'espace réservé tf d'origine (par rapport à Keras model.input, qui est le post-prétraitement). Voici une version qui fonctionne bien que de TF v1.4 et Keras 2.1.2:

sess = tf.Session()
K.set_session(sess)

K._LEARNING_PHASE = tf.constant(0)
K.set_learning_phase(0)

max_features = 5000
max_lens = 500

dict_table = tf.contrib.lookup.HashTable(tf.contrib.lookup.TextFileInitializer("vocab.txt",tf.string, 0, tf.int64, TextFileIndex.LINE_NUMBER, vocab_size=max_features, delimiter=" "), 0)

x_input = tf.placeholder(tf.string, name='x_input', shape=(None,))
sparse_tokenized_input = tf.string_split(x_input)
tokenized_input = tf.sparse_tensor_to_dense(sparse_tokenized_input, default_value='')
token_idxs = dict_table.lookup(tokenized_input)
token_idxs_padded = tf.pad(token_idxs, [[0,0],[0, max_lens]])
token_idxs_embedding = tf.slice(token_idxs_padded, [0,0], [-1, max_lens])

model = Sequential()
model.add(InputLayer(input_tensor=token_idxs_embedding, input_shape=(None, max_lens)))

 ...REST OF MODEL...

model.load_weights("model.h5")

x_info = tf.saved_model.utils.build_tensor_info(x_input)
y_info = tf.saved_model.utils.build_tensor_info(model.output)

prediction_signature = tf.saved_model.signature_def_utils.build_signature_def(inputs={"text": x_info}, outputs={"prediction":y_info}, method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME)

builder = saved_model_builder.SavedModelBuilder("/path/to/model")

legacy_init_op = tf.group(tf.tables_initializer(), name='legacy_init_op')

init_op = tf.group(tf.global_variables_initializer(), tf.local_variables_initializer())
sess.run(init_op)


# Add the meta_graph and the variables to the builder
builder.add_meta_graph_and_variables(
  sess, [tag_constants.SERVING],
  signature_def_map={
       signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:
           prediction_signature,
  },
  legacy_init_op=legacy_init_op)

builder.save()  

MISE À JOUR Le prétraitement pour l'inférence avec Tensorflow est une opération du processeur et n'est pas effectué efficacement si le modèle est déployé sur un serveur GPU. Le GPU cale vraiment mal et le débit est très faible. Par conséquent, nous avons abandonné cela pour un prétraitement efficace dans le processus client.

9
Daniel Nitzan 11 janv. 2019 à 17:58