Aide à protéger la Grande barrière de corail avec tensorflow sur Kaggle Rejoignez Défi

Migration de feature_columns vers les couches de prétraitement Keras de TF2

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier

La formation d'un modèle s'accompagne généralement d'un certain prétraitement des fonctionnalités, en particulier lorsqu'il s'agit de données structurées. Lors de la formation d'un tf.estimator.Estimator dans TF1, ce prétraitement de fonctionnalité est généralement effectué avec l'API tf.feature_column . Dans TF2, ce prétraitement peut se faire directement avec les couches Keras, appelées couches de prétraitement .

Dans ce guide de migration, vous effectuerez certaines transformations de fonctionnalités courantes en utilisant à la fois des colonnes de fonctionnalités et des couches de prétraitement, puis vous entraînerez un modèle complet avec les deux API.

Tout d'abord, commencez par quelques importations nécessaires,

import tensorflow as tf
import tensorflow.compat.v1 as tf1
import math

et ajoutez un utilitaire pour appeler une colonne de fonctionnalités à des fins de démonstration :

def call_feature_columns(feature_columns, inputs):
  # This is a convenient way to call a `feature_column` outside of an estimator
  # to display its output.
  feature_layer = tf1.keras.layers.DenseFeatures(feature_columns)
  return feature_layer(inputs)

Gestion des entrées

Pour utiliser des colonnes de caractéristiques avec un estimateur, les entrées du modèle doivent toujours être un dictionnaire de tenseurs :

input_dict = {
  'foo': tf.constant([1]),
  'bar': tf.constant([0]),
  'baz': tf.constant([-1])
}

Chaque colonne de caractéristiques doit être créée avec une clé à indexer dans les données source. La sortie de toutes les colonnes de caractéristiques est concaténée et utilisée par le modèle d'estimateur.

columns = [
  tf1.feature_column.numeric_column('foo'),
  tf1.feature_column.numeric_column('bar'),
  tf1.feature_column.numeric_column('baz'),
]
call_feature_columns(columns, input_dict)
<tf.Tensor: shape=(1, 3), dtype=float32, numpy=array([[ 0., -1.,  1.]], dtype=float32)>

Dans Keras, la saisie du modèle est beaucoup plus flexible. Un tf.keras.Model peut gérer une seule entrée de tenseur, une liste de caractéristiques de tenseur ou un dictionnaire de caractéristiques de tenseur. Vous pouvez gérer l'entrée du dictionnaire en transmettant un dictionnaire de tf.keras.Input lors de la création du modèle. Les entrées ne seront pas concaténées automatiquement, ce qui permet de les utiliser de manière beaucoup plus flexible. Ils peuvent être concaténés avec tf.keras.layers.Concatenate .

inputs = {
  'foo': tf.keras.Input(shape=()),
  'bar': tf.keras.Input(shape=()),
  'baz': tf.keras.Input(shape=()),
}
# Inputs are typically transformed by preprocessing layers before concatenation.
outputs = tf.keras.layers.Concatenate()(inputs.values())
model = tf.keras.Model(inputs=inputs, outputs=outputs)
model(input_dict)
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([ 1.,  0., -1.], dtype=float32)>

ID entiers d'encodage à chaud

Une transformation de caractéristique courante consiste à encoder à chaud des entrées entières d'une plage connue. Voici un exemple utilisant des colonnes de caractéristiques :

categorical_col = tf1.feature_column.categorical_column_with_identity(
    'type', num_buckets=3)
indicator_col = tf1.feature_column.indicator_column(categorical_col)
call_feature_columns(indicator_col, {'type': [0, 1, 2]})
<tf.Tensor: shape=(3, 3), dtype=float32, numpy=
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]], dtype=float32)>

En utilisant les couches de prétraitement Keras, ces colonnes peuvent être remplacées par une seule couche tf.keras.layers.CategoryEncoding avec output_mode défini sur 'one_hot' :

one_hot_layer = tf.keras.layers.CategoryEncoding(
    num_tokens=3, output_mode='one_hot')
one_hot_layer([0, 1, 2])
<tf.Tensor: shape=(3, 3), dtype=float32, numpy=
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]], dtype=float32)>

Normalisation des caractéristiques numériques

Lors de la gestion d'entités continues à virgule flottante avec des colonnes d'entités, vous devez utiliser un tf.feature_column.numeric_column . Dans le cas où l'entrée est déjà normalisée, la convertir en Keras est trivial. Vous pouvez simplement utiliser un tf.keras.Input directement dans votre modèle, comme indiqué ci-dessus.

Une numeric_column peut également être utilisée pour normaliser l'entrée :

def normalize(x):
  mean, variance = (2.0, 1.0)
  return (x - mean) / math.sqrt(variance)
numeric_col = tf1.feature_column.numeric_column('col', normalizer_fn=normalize)
call_feature_columns(numeric_col, {'col': tf.constant([[0.], [1.], [2.]])})
<tf.Tensor: shape=(3, 1), dtype=float32, numpy=
array([[-2.],
       [-1.],
       [ 0.]], dtype=float32)>

En revanche, avec Keras, cette normalisation peut être effectuée avec tf.keras.layers.Normalization .

normalization_layer = tf.keras.layers.Normalization(mean=2.0, variance=1.0)
normalization_layer(tf.constant([[0.], [1.], [2.]]))
<tf.Tensor: shape=(3, 1), dtype=float32, numpy=
array([[-2.],
       [-1.],
       [ 0.]], dtype=float32)>

Fonctions numériques de regroupement et d'encodage à chaud

Une autre transformation courante des entrées continues à virgule flottante consiste à les compartimenter ensuite en nombres entiers d'une plage fixe.

Dans les colonnes de fonctionnalités, cela peut être réalisé avec un tf.feature_column.bucketized_column :

numeric_col = tf1.feature_column.numeric_column('col')
bucketized_col = tf1.feature_column.bucketized_column(numeric_col, [1, 4, 5])
call_feature_columns(bucketized_col, {'col': tf.constant([1., 2., 3., 4., 5.])})
<tf.Tensor: shape=(5, 4), dtype=float32, numpy=
array([[0., 1., 0., 0.],
       [0., 1., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]], dtype=float32)>

Dans Keras, cela peut être remplacé par tf.keras.layers.Discretization :

discretization_layer = tf.keras.layers.Discretization(bin_boundaries=[1, 4, 5])
one_hot_layer = tf.keras.layers.CategoryEncoding(
    num_tokens=4, output_mode='one_hot')
one_hot_layer(discretization_layer([1., 2., 3., 4., 5.]))
<tf.Tensor: shape=(5, 4), dtype=float32, numpy=
array([[0., 1., 0., 0.],
       [0., 1., 0., 0.],
       [0., 1., 0., 0.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]], dtype=float32)>

Encodage à chaud des données de chaîne avec un vocabulaire

La gestion des fonctionnalités de chaîne nécessite souvent une recherche de vocabulaire pour traduire les chaînes en index. Voici un exemple utilisant des colonnes de caractéristiques pour rechercher des chaînes, puis encoder à chaud les index :

vocab_col = tf1.feature_column.categorical_column_with_vocabulary_list(
    'sizes',
    vocabulary_list=['small', 'medium', 'large'],
    num_oov_buckets=0)
indicator_col = tf1.feature_column.indicator_column(vocab_col)
call_feature_columns(indicator_col, {'sizes': ['small', 'medium', 'large']})
<tf.Tensor: shape=(3, 3), dtype=float32, numpy=
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]], dtype=float32)>

À l'aide des couches de prétraitement Keras, utilisez la couche tf.keras.layers.StringLookup avec output_mode défini sur 'one_hot' :

string_lookup_layer = tf.keras.layers.StringLookup(
    vocabulary=['small', 'medium', 'large'],
    num_oov_indices=0,
    output_mode='one_hot')
string_lookup_layer(['small', 'medium', 'large'])
<tf.Tensor: shape=(3, 3), dtype=float32, numpy=
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]], dtype=float32)>

Intégrer des données de chaîne avec un vocabulaire

Pour les vocabulaires plus volumineux, une incorporation est souvent nécessaire pour de bonnes performances. Voici un exemple d'intégration d'une fonctionnalité de chaîne à l'aide de colonnes de fonctionnalités :

vocab_col = tf1.feature_column.categorical_column_with_vocabulary_list(
    'col',
    vocabulary_list=['small', 'medium', 'large'],
    num_oov_buckets=0)
embedding_col = tf1.feature_column.embedding_column(vocab_col, 4)
call_feature_columns(embedding_col, {'col': ['small', 'medium', 'large']})
<tf.Tensor: shape=(3, 4), dtype=float32, numpy=
array([[-0.01798586, -0.2808677 ,  0.27639154,  0.06081508],
       [ 0.05771849,  0.02464074,  0.20080602,  0.50164527],
       [-0.9208247 , -0.40816694, -0.49132794,  0.9203153 ]],
      dtype=float32)>

En utilisant les couches de prétraitement Keras, cela peut être réalisé en combinant une couche tf.keras.layers.StringLookup et une couche tf.keras.layers.Embedding . La sortie par défaut pour le StringLookup sera des indices entiers qui peuvent être introduits directement dans une intégration.

string_lookup_layer = tf.keras.layers.StringLookup(
    vocabulary=['small', 'medium', 'large'], num_oov_indices=0)
embedding = tf.keras.layers.Embedding(3, 4)
embedding(string_lookup_layer(['small', 'medium', 'large']))
<tf.Tensor: shape=(3, 4), dtype=float32, numpy=
array([[ 0.04838837, -0.04014301,  0.02001903, -0.01150769],
       [-0.04580117, -0.04319514,  0.03725603, -0.00572466],
       [-0.0401094 ,  0.00997342,  0.00111955,  0.00132702]],
      dtype=float32)>

Sommation de données catégorielles pondérées

Dans certains cas, vous devez traiter des données catégorielles où chaque occurrence d'une catégorie est associée à un poids. Dans les colonnes de caractéristiques, cela est géré avec tf.feature_column.weighted_categorical_column . Lorsqu'il est associé à un indicator_column , cela a pour effet d'additionner les poids par catégorie.

ids = tf.constant([[5, 11, 5, 17, 17]])
weights = tf.constant([[0.5, 1.5, 0.7, 1.8, 0.2]])

categorical_col = tf1.feature_column.categorical_column_with_identity(
    'ids', num_buckets=20)
weighted_categorical_col = tf1.feature_column.weighted_categorical_column(
    categorical_col, 'weights')
indicator_col = tf1.feature_column.indicator_column(weighted_categorical_col)
call_feature_columns(indicator_col, {'ids': ids, 'weights': weights})
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/feature_column/feature_column_v2.py:4203: sparse_merge (from tensorflow.python.ops.sparse_ops) is deprecated and will be removed in a future version.
Instructions for updating:
No similar op available at this time.
<tf.Tensor: shape=(1, 20), dtype=float32, numpy=
array([[0. , 0. , 0. , 0. , 0. , 1.2, 0. , 0. , 0. , 0. , 0. , 1.5, 0. ,

        0. , 0. , 0. , 0. , 2. , 0. , 0. ]], dtype=float32)>

Dans Keras, cela peut être fait en passant une entrée count_weights à tf.keras.layers.CategoryEncoding avec output_mode='count' .

ids = tf.constant([[5, 11, 5, 17, 17]])
weights = tf.constant([[0.5, 1.5, 0.7, 1.8, 0.2]])

# Using sparse output is more efficient when `num_tokens` is large.
count_layer = tf.keras.layers.CategoryEncoding(
    num_tokens=20, output_mode='count', sparse=True)
tf.sparse.to_dense(count_layer(ids, count_weights=weights))
<tf.Tensor: shape=(1, 20), dtype=float32, numpy=
array([[0. , 0. , 0. , 0. , 0. , 1.2, 0. , 0. , 0. , 0. , 0. , 1.5, 0. ,

        0. , 0. , 0. , 0. , 2. , 0. , 0. ]], dtype=float32)>

Intégration de données catégorielles pondérées

Vous pouvez également intégrer des entrées catégorielles pondérées. Dans les colonnes de caractéristiques, embedding_column contient un argument combiner . Si un échantillon contient plusieurs entrées pour une catégorie, elles seront combinées en fonction du paramètre d'argument (par défaut 'mean' ).

ids = tf.constant([[5, 11, 5, 17, 17]])
weights = tf.constant([[0.5, 1.5, 0.7, 1.8, 0.2]])

categorical_col = tf1.feature_column.categorical_column_with_identity(
    'ids', num_buckets=20)
weighted_categorical_col = tf1.feature_column.weighted_categorical_column(
    categorical_col, 'weights')
embedding_col = tf1.feature_column.embedding_column(
    weighted_categorical_col, 4, combiner='mean')
call_feature_columns(embedding_col, {'ids': ids, 'weights': weights})
<tf.Tensor: shape=(1, 4), dtype=float32, numpy=
array([[ 0.02666993,  0.289671  ,  0.18065728, -0.21045178]],
      dtype=float32)>

Dans Keras, il n'y a pas d'option de tf.keras.layers.Embedding combiner mais vous pouvez obtenir le même effet avec tf.keras.layers.Dense . La embedding_column ci-dessus combine simplement de manière linéaire les vecteurs d'intégration en fonction du poids de la catégorie. Bien que cela ne soit pas évident au début, cela revient exactement à représenter vos entrées catégorielles sous la forme d'un vecteur de poids clairsemé de size (num_tokens) et de les multiplier par un noyau Dense de forme (embedding_size, num_tokens) .

ids = tf.constant([[5, 11, 5, 17, 17]])
weights = tf.constant([[0.5, 1.5, 0.7, 1.8, 0.2]])

# For `combiner='mean'`, normalize your weights to sum to 1. Removing this line
# would be eqivalent to an `embedding_column` with `combiner='sum'`.
weights = weights / tf.reduce_sum(weights, axis=-1, keepdims=True)

count_layer = tf.keras.layers.CategoryEncoding(
    num_tokens=20, output_mode='count', sparse=True)
embedding_layer = tf.keras.layers.Dense(4, use_bias=False)
embedding_layer(count_layer(ids, count_weights=weights))
<tf.Tensor: shape=(1, 4), dtype=float32, numpy=
array([[-0.03897291, -0.27131438,  0.09332469,  0.04333957]],
      dtype=float32)>

Exemple de formation complet

Pour afficher un flux de travail de formation complet, préparez d'abord des données avec trois fonctionnalités de types différents :

features = {
    'type': [0, 1, 1],
    'size': ['small', 'small', 'medium'],
    'weight': [2.7, 1.8, 1.6],
}
labels = [1, 1, 0]
predict_features = {'type': [0], 'size': ['foo'], 'weight': [-0.7]}

Définissez des constantes communes aux workflows TF1 et TF2 :

vocab = ['small', 'medium', 'large']
one_hot_dims = 3
embedding_dims = 4
weight_mean = 2.0
weight_variance = 1.0

Avec des colonnes de fonctionnalités

Les colonnes de caractéristiques doivent être transmises sous forme de liste à l'estimateur lors de la création et seront appelées implicitement lors de la formation.

categorical_col = tf1.feature_column.categorical_column_with_identity(
    'type', num_buckets=one_hot_dims)
# Convert index to one-hot; e.g. [2] -> [0,0,1].
indicator_col = tf1.feature_column.indicator_column(categorical_col)

# Convert strings to indices; e.g. ['small'] -> [1].
vocab_col = tf1.feature_column.categorical_column_with_vocabulary_list(
    'size', vocabulary_list=vocab, num_oov_buckets=1)
# Embed the indices.
embedding_col = tf1.feature_column.embedding_column(vocab_col, embedding_dims)

normalizer_fn = lambda x: (x - weight_mean) / math.sqrt(weight_variance)
# Normalize the numeric inputs; e.g. [2.0] -> [0.0].
numeric_col = tf1.feature_column.numeric_column(
    'weight', normalizer_fn=normalizer_fn)

estimator = tf1.estimator.DNNClassifier(
    feature_columns=[indicator_col, embedding_col, numeric_col],
    hidden_units=[1])

def _input_fn():
  return tf1.data.Dataset.from_tensor_slices((features, labels)).batch(1)

estimator.train(_input_fn)
INFO:tensorflow:Using default config.
WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmp8lwbuor2
INFO:tensorflow:Using config: {'_model_dir': '/tmp/tmp8lwbuor2', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true
graph_options {
  rewrite_options {
    meta_optimizer_iterations: ONE
  }
}
, '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_checkpoint_save_graph_def': True, '_service': None, '_cluster_spec': ClusterSpec({}), '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.
Instructions for updating:
Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.
INFO:tensorflow:Calling model_fn.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/training/adagrad.py:77: calling Constant.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.
Instructions for updating:
Call initializer instance with the dtype argument instead of passing it to the constructor
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Create CheckpointSaverHook.
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 0...
INFO:tensorflow:Saving checkpoints for 0 into /tmp/tmp8lwbuor2/model.ckpt.
INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 0...
INFO:tensorflow:loss = 0.54634213, step = 0
INFO:tensorflow:Calling checkpoint listeners before saving checkpoint 3...
INFO:tensorflow:Saving checkpoints for 3 into /tmp/tmp8lwbuor2/model.ckpt.
INFO:tensorflow:Calling checkpoint listeners after saving checkpoint 3...
INFO:tensorflow:Loss for final step: 0.7308526.
<tensorflow_estimator.python.estimator.canned.dnn.DNNClassifier at 0x7f90685d53d0>

Les colonnes de caractéristiques seront également utilisées pour transformer les données d'entrée lors de l'exécution de l'inférence sur le modèle.

def _predict_fn():
  return tf1.data.Dataset.from_tensor_slices(predict_features).batch(1)

next(estimator.predict(_predict_fn))
INFO:tensorflow:Calling model_fn.
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Restoring parameters from /tmp/tmp8lwbuor2/model.ckpt-3
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
{'logits': array([0.5172372], dtype=float32),
 'logistic': array([0.6265015], dtype=float32),
 'probabilities': array([0.37349847, 0.6265015 ], dtype=float32),
 'class_ids': array([1]),
 'classes': array([b'1'], dtype=object),
 'all_class_ids': array([0, 1], dtype=int32),
 'all_classes': array([b'0', b'1'], dtype=object)}

Avec les couches de prétraitement Keras

Les couches de prétraitement Keras sont plus flexibles quant à l'endroit où elles peuvent être appelées. Une couche peut être appliquée directement aux tenseurs, utilisée dans un pipeline d'entrée tf.data ou intégrée directement dans un modèle Keras entraînable.

Dans cet exemple, vous appliquerez des couches de prétraitement dans un pipeline d'entrée tf.data . Pour ce faire, vous pouvez définir un tf.keras.Model distinct pour prétraiter vos entités d'entrée. Ce modèle ne peut pas être entraîné, mais constitue un moyen pratique de regrouper les couches de prétraitement.

inputs = {
  'type': tf.keras.Input(shape=(), dtype='int64'),
  'size': tf.keras.Input(shape=(), dtype='string'),
  'weight': tf.keras.Input(shape=(), dtype='float32'),
}
# Convert index to one-hot; e.g. [2] -> [0,0,1].
type_output = tf.keras.layers.CategoryEncoding(
      one_hot_dims, output_mode='one_hot')(inputs['type'])
# Convert size strings to indices; e.g. ['small'] -> [1].
size_output = tf.keras.layers.StringLookup(vocabulary=vocab)(inputs['size'])
# Normalize the numeric inputs; e.g. [2.0] -> [0.0].
weight_output = tf.keras.layers.Normalization(
      axis=None, mean=weight_mean, variance=weight_variance)(inputs['weight'])
outputs = {
  'type': type_output,
  'size': size_output,
  'weight': weight_output,
}
preprocessing_model = tf.keras.Model(inputs, outputs)

Vous pouvez maintenant appliquer ce modèle dans un appel à tf.data.Dataset.map . Veuillez noter que la fonction transmise à map sera automatiquement convertie en tf.function , et les mises en garde habituelles pour l'écriture du code tf.function s'appliquent (pas d'effets secondaires).

# Apply the preprocessing in tf.data.Dataset.map.
dataset = tf.data.Dataset.from_tensor_slices((features, labels)).batch(1)
dataset = dataset.map(lambda x, y: (preprocessing_model(x), y),
                      num_parallel_calls=tf.data.AUTOTUNE)
# Display a preprocessed input sample.
next(dataset.take(1).as_numpy_iterator())
({'type': array([[1., 0., 0.]], dtype=float32),
  'size': array([1]),
  'weight': array([0.70000005], dtype=float32)},
 array([1], dtype=int32))

Ensuite, vous pouvez définir un Model séparé contenant les couches entraînables. Notez comment les entrées de ce modèle reflètent désormais les types et formes d'entités prétraités.

inputs = {
  'type': tf.keras.Input(shape=(one_hot_dims,), dtype='float32'),
  'size': tf.keras.Input(shape=(), dtype='int64'),
  'weight': tf.keras.Input(shape=(), dtype='float32'),
}
# Since the embedding is trainable, it needs to be part of the training model.
embedding = tf.keras.layers.Embedding(len(vocab), embedding_dims)
outputs = tf.keras.layers.Concatenate()([
  inputs['type'],
  embedding(inputs['size']),
  tf.expand_dims(inputs['weight'], -1),
])
outputs = tf.keras.layers.Dense(1)(outputs)
training_model = tf.keras.Model(inputs, outputs)

Vous pouvez maintenant former le training_model avec tf.keras.Model.fit .

# Train on the preprocessed data.
training_model.compile(
    loss=tf.keras.losses.BinaryCrossentropy(from_logits=True))
training_model.fit(dataset)
3/3 [==============================] - 0s 3ms/step - loss: 0.7248
<keras.callbacks.History at 0x7f9041a294d0>

Enfin, au moment de l'inférence, il peut être utile de combiner ces étapes distinctes en un seul modèle qui gère les entrées d'entités brutes.

inputs = preprocessing_model.input
outpus = training_model(preprocessing_model(inputs))
inference_model = tf.keras.Model(inputs, outpus)

predict_dataset = tf.data.Dataset.from_tensor_slices(predict_features).batch(1)
inference_model.predict(predict_dataset)
array([[0.936637]], dtype=float32)

Ce modèle composé peut être enregistré en tant que SavedModel pour une utilisation ultérieure.

inference_model.save('model')
restored_model = tf.keras.models.load_model('model')
restored_model.predict(predict_dataset)
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
2021-10-27 01:23:25.649967: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: model/assets
WARNING:tensorflow:No training configuration found in save file, so the model was *not* compiled. Compile it manually.
array([[0.936637]], dtype=float32)

Tableau d'équivalence des colonnes de fonctions

Pour référence, voici une correspondance approximative entre les colonnes de caractéristiques et les couches de prétraitement :

Colonne de fonctionnalités Couche Keras
feature_column.bucketized_column layers.Discretization
feature_column.categorical_column_with_hash_bucket layers.Hashing
feature_column.categorical_column_with_identity layers.CategoryEncoding
feature_column.categorical_column_with_vocabulary_file layers.StringLookup ou layers.IntegerLookup
feature_column.categorical_column_with_vocabulary_list layers.StringLookup ou layers.IntegerLookup
feature_column.crossed_column Pas mis en œuvre.
feature_column.embedding_column layers.Embedding
feature_column.indicator_column output_mode='one_hot' ou output_mode='multi_hot' *
feature_column.numeric_column layers.Normalization
feature_column.sequence_categorical_column_with_hash_bucket layers.Hashing
feature_column.sequence_categorical_column_with_identity layers.CategoryEncoding
feature_column.sequence_categorical_column_with_vocabulary_file layers.StringLookup , layers.IntegerLookup ou layer.TextVectorization
feature_column.sequence_categorical_column_with_vocabulary_list layers.StringLookup , layers.IntegerLookup ou layer.TextVectorization
feature_column.sequence_numeric_column layers.Normalization
feature_column.weighted_categorical_column layers.CategoryEncoding

* output_mode peut être transmis à layers.CategoryEncoding , layers.StringLookup , layers.IntegerLookup et layers.TextVectorization .

layers.TextVectorization peut gérer directement la saisie de texte libre (par exemple, des phrases entières ou des paragraphes). Il ne s'agit pas d'un remplacement un à un pour la gestion des séquences catégorielles dans TF1, mais peut offrir un remplacement pratique pour le prétraitement de texte ad hoc.

Prochaines étapes