Comme indiqué dans le document officiel ci-dessous, lors de l'exécution de la prédiction avec Batch, le traitement distribué est effectué du côté de ML Engine et l'ordre d'entrée et de sortie ne correspond pas. Vous devez donc utiliser quelque chose appelé une ** clé d'instance ** pour rendre les entrées et les sorties uniques.
En d'autres termes, il est nécessaire d'écrire explicitement la clé d'instance en entrée, mais dans le traitement de Prediction, elle doit être implémentée de sorte qu'elle soit sortie avec la valeur prédite sans rien faire. Je n'ai pas écrit soigneusement comment l'implémenter dans le document officiel, et je n'avais pas beaucoup de littérature japonaise, donc je la posterai sur qiita avec un mémorandum.
AI Platform utilise le traitement distribué pour exécuter des tâches de prédiction par lots. Autrement dit, les données sont distribuées sur n'importe quel cluster de machines virtuelles et traitées dans un ordre imprévisible. Vous devez avoir une clé d'instance définie pour pouvoir faire correspondre les prédictions renvoyées avec l'instance d'entrée. La clé d'instance est une valeur que chaque instance possède et est unique dans un ensemble d'instances de données. La clé la plus simple est le numéro d'index.
https://cloud.google.com/ml-engine/docs/prediction-overview?hl=ja#instance_keys
J'ai essayé deux méthodes de mise en œuvre.
Cette fois, nous partirons de l'hypothèse que l'évaluation est prédite à partir de userId et movieId en utilisant l'ensemble de données MovieLens (rating.csv). Pour la clé d'index, utilisez le numéro d'index des données tel que décrit dans le document officiel.
userId | movieId | rating | key |
---|---|---|---|
1 | 1 | 1.5 | 1 |
1 | 2 | 2.5 | 2 |
2 | 1 | 3.5 | 3 |
2 | 2 | 4.5 | 4 |
Premièrement, [fonction forward_features] Jetez un œil à la description (https://github.com/tensorflow/tensorflow/blob/r1.8/tensorflow/contrib/estimator/python/estimator/extenders.py#L143). J'écris sur Batch Prediction. (Comme mentionné au début, il existe également une déclaration indiquant que l'ordre de sortie n'est pas garanti, vous devez donc vous connecter à l'aide de la clé d'entrée.)
def forward_features(estimator, keys=None):
"""Forward features to predictions dictionary.
In some cases, user wants to see some of the features in estimators prediction
output. As an example, consider a batch prediction service: The service simply
runs inference on the users graph and returns the results. Keys are essential
because there is no order guarantee on the outputs so they need to be rejoined
to the inputs via keys or transclusion of the inputs in the outputs.
Le modèle que j'ai écrit cette fois est un modèle simple comme suit. Comme vous pouvez le voir sur la figure, la clé d'instance de la couche d'entrée du modèle n'est connectée à aucun nœud. Cependant, dans l'implémentation de forward_features, le processus d'inclusion de la clé d'instance dans la sortie de l'estimateur est terminé, donc je pense qu'il y sera connecté. (Probablement ici) Dans la mise en œuvre du modèle, il n'est pas particulièrement difficile d'écrire la clé de manière explicite.
MAX_MOVIES=100000
MAX_USERS=100000
DIM_EMBEDDING=10
def get_model():
key = tf.keras.layers.Input(shape=(), name='key', dtype='int32')
w_inputs = tf.keras.layers.Input(shape=(1,), dtype='int32', name='movieId')
w = tf.keras.layers.Embedding(MAX_MOVIES, DIM_EMBEDDING, name='movie')(w_inputs)
u_inputs = tf.keras.layers.Input(shape=(1,), dtype='int32', name='userId')
u = tf.keras.layers.Embedding(MAX_USERS, DIM_EMBEDDING, name='user')(u_inputs)
o = tf.keras.layers.Multiply()([w, u])
o = tf.keras.layers.Dropout(rate=0.5)(o)
o = tf.keras.layers.Flatten()(o)
o = tf.keras.layers.Dense(10, activation='relu')(o)
o = tf.keras.layers.Dense(1, name='predictions')(o)
model = tf.keras.Model(inputs=[w_inputs, u_inputs, key], outputs=o)
model.summary()
return model
La fonction forward_features
est utilisée dans la fonction principale ( train_and_evaluate
) qui compile le modèle / crée un estimateur à partir du modèle.
def train_and_evaluate(outdir, train_step):
tf.logging.set_verbosity(v=tf.logging.INFO)
model = get_model()
model.compile(optimizer='adam', loss="mae", metrics=["mae"])
estimator = tf.keras.estimator.model_to_estimator(keras_model = model,
model_dir = outdir,
config = tf.estimator.RunConfig(save_checkpoints_secs=100))
train_spec = tf.estimator.TrainSpec(input_fn=lambda: read_dataset(filename='train.csv',
mode=tf.estimator.ModeKeys.TRAIN),
max_steps=train_step)
exporter = tf.estimator.LatestExporter(name='exporter',
serving_input_receiver_fn=serving_input_fn)
#Estimateur avant ici_Emballé dans les fonctionnalités
estimator = tf.contrib.estimator.forward_features(estimator, 'key')
eval_spec = tf.estimator.EvalSpec(input_fn=lambda: read_dataset(filename='test.csv',
mode=tf.estimator.ModeKeys.EVAL),
exporters=exporter,
steps=None,
throttle_secs=15)
tf.estimator.train_and_evaluate(estimator, train_spec, eval_spec)
La valeur prédite est étrange car je n'ai pas entré beaucoup de données dans la formation. Vous pouvez voir que la clé d'instance est renvoyée telle quelle avec la valeur prédite.
Il y a une description dans ce problème, mais dans le système tensorflow 2, contrib.estimator auquel appartient forward_features est obsolète, donc dans le problème Vous devrez peut-être définir votre propre fonction feature_forward comme décrit.
Comme le montre la figure ci-dessous, la valeur sortie du dernier Dense est la valeur prédite, mais la clé d'instance est concatée telle quelle et utilisée comme sortie finale. De plus, comme la clé d'instance entre en tant que valeur scalaire, elle est remodelée pour la façonner. En faisant cela, il est possible de sortir la clé d'instance sans aucun traitement.
MAX_MOVIES=100000
MAX_USERS=100000
DIM_EMBEDDING=10
def get_model():
key_raw = tf.keras.layers.Input(shape=(), name='key', dtype='int32')
key = tf.keras.layers.Reshape((1,), input_shape=(), dtype='int32')(key_raw)
w_inputs = tf.keras.layers.Input(shape=(1,), dtype='int32', name='movieId')
w = tf.keras.layers.Embedding(MAX_MOVIES, DIM_EMBEDDING, name='movie')(w_inputs)
u_inputs = tf.keras.layers.Input(shape=(1,), dtype='int32', name='userId')
u = tf.keras.layers.Embedding(MAX_USERS, DIM_EMBEDDING, name='user')(u_inputs)
o = tf.keras.layers.Multiply()([w, u])
o = tf.keras.layers.Dropout(rate=0.5)(o)
o = tf.keras.layers.Flatten()(o)
o = tf.keras.layers.Dense(10, activation='relu')(o)
o = tf.keras.layers.Dense(1, name='predictions')(o)
#Ici, nous concilions la prédiction et la clé d'instance
#Puisque la clé d'instance est de type int, je la fais flotter de force pour qu'elle corresponde au type
pred = tf.keras.layers.Concatenate()([o, tf.cast(key, tf.float32)])
model = tf.keras.Model(inputs=[w_inputs, u_inputs, key_raw], outputs=pred)
model.summary()
return model
Cependant, avec cette méthode, vous devez écrire une fonction de perte personnalisée car la clé d'instance est incluse lors du calcul de la perte. Je ne pense pas que ce soit si difficile car il suffit d'obtenir la valeur de la prédiction dans la fonction comme indiqué ci-dessous.
def my_rmse(y_true, y_pred):
return tf.keras.backend.sqrt(tf.keras.backend.mean(
tf.keras.backend.square(y_pred[1][0] - y_true))) # y_pred[1][0]Pour n'obtenir que des prédictions avec
Aucun autre changement n'a été apporté concernant les autres implémentations. Si vous essayez réellement la prédiction comme indiqué ci-dessous, elle sera sortie comme ceci.
J'ai essayé deux méthodes cette fois, mais la différence est qu'il n'y a presque aucune différence dans le contenu du traitement, selon que vous utilisez la fonction (forward_features) préparée côté tensorflow ou que vous écrivez la structure du graphique par vous-même. Je vais. (Bien sûr, le premier est recommandé) Je n'avais pas beaucoup de littérature japonaise sur la prédiction par lots sur ML Engine, alors j'espère que cela aidera ceux qui ont des problèmes avec la même chose.
Recommended Posts