Composants personnalisés TensorFlow
Les composants personnalisés TensorFlow permettent aux développeurs d'étendre les fonctionnalités de TensorFlow en fonction d'exigences spécifiques. Lorsque les opérations intégrées ne répondent pas à vos besoins, vous pouvez créer :
- Couches personnalisées- Implémenter de nouvelles structures de couches de réseaux de neurones
- Fonctions de perte personnalisées- Concevoir des objectifs d'optimisation pour des tâches spécifiques
- Métriques d'évaluation personnalisées- Définir des critères uniques de mesure de performance
- Boucles d'entraînement personnalisées- Implémenter une logique d'entraînement spéciale
Exemple
# Exemple simple de couche personnalisée
class SimpleDense(tf.keras.layers.Layer):
def __init__(self, units=32):
super().__init__()
self.units = units
def build(self, input_shape):
self.w = self.add_weight(shape=(input_shape[-1], self.units))
self.b = self.add_weight(shape=(self.units,))
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
class SimpleDense(tf.keras.layers.Layer):
def __init__(self, units=32):
super().__init__()
self.units = units
def build(self, input_shape):
self.w = self.add_weight(shape=(input_shape[-1], self.units))
self.b = self.add_weight(shape=(self.units,))
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
Pourquoi les composants personnalisés sont nécessaires
Résolution de problèmes spécifiques à un domaine
- Opérations de convolution spéciales en vision par ordinateur
- Variantes de mécanismes d’attention en NLP
- Méthodes de croisement de caractéristiques dans les systèmes de recommandation
Besoins d'optimisation des performances
- Noyaux de calcul optimisés pour le matériel
- Gestion spéciale de l'entraînement en précision mixte
Innovation en recherche
- Implémenter de nouvelles structures de réseau à partir d'articles de recherche
- Expérimenter avec des méthodes de régularisation personnalisées
Détails du développement de couches personnalisées
Structure de base
Chaque couche personnalisée doit héritertf.keras.layers.Layeret implémenter :
__init__()- Initialiser les paramètres de configurationbuild()- Créer des variables de poids (recommandé)call()- Définir la logique de calcul avantget_config()- Prendre en charge la sérialisation (facultatif)
Exemple
class CustomLayer(tf.keras.layers.Layer):
def __init__(self, units=32, **kwargs):
super().__init__(**kwargs)
self.units = units
def build(self, input_shape):
self.kernel = self.add_weight(
name="kernel",
shape=(input_shape[-1], self.units),
initializer="glorot_uniform"
)
self.bias = self.add_weight(
name="bias",
shape=(self.units,),
initializer="zeros"
)
def call(self, inputs):
return tf.matmul(inputs, self.kernel) + self.bias
def get_config(self):
return {"units": self.units}
def __init__(self, units=32, **kwargs):
super().__init__(**kwargs)
self.units = units
def build(self, input_shape):
self.kernel = self.add_weight(
name="kernel",
shape=(input_shape[-1], self.units),
initializer="glorot_uniform"
)
self.bias = self.add_weight(
name="bias",
shape=(self.units,),
initializer="zeros"
)
def call(self, inputs):
return tf.matmul(inputs, self.kernel) + self.bias
def get_config(self):
return {"units": self.units}
Bonnes pratiques pour la gestion des poids
| Méthode | Description | Cas d'utilisation |
|---|---|---|
add_weight() |
Gérer automatiquement les poids | La plupart des cas |
| Créer des variables directement | Contrôle plus flexible | Lorsqu'une initialisation spéciale est nécessaire |
| Réutiliser les poids existants | Partage de paramètres | Mécanismes d'attention, etc. |
Développement d'une fonction de perte personnalisée
Deux approches d'implémentation
Approche 1 : forme fonctionnelle
Exemple
def custom_mse(y_true, y_pred):
squared_diff = tf.square(y_true - y_pred)
return tf.reduce_mean(squared_diff, axis=-1)
squared_diff = tf.square(y_true - y_pred)
return tf.reduce_mean(squared_diff, axis=-1)
Approche 2 : forme de classe (héritant de la classe Loss)
Exemple
class CustomLoss(tf.keras.losses.Loss):
def __init__(self, regularization_factor=0.1):
super().__init__()
self.reg_factor = regularization_factor
def call(self, y_true, y_pred):
mse = tf.reduce_mean(tf.square(y_true - y_pred))
reg = tf.reduce_sum(self.reg_factor * tf.abs(y_pred))
return mse + reg
def __init__(self, regularization_factor=0.1):
super().__init__()
self.reg_factor = regularization_factor
def call(self, y_true, y_pred):
mse = tf.reduce_mean(tf.square(y_true - y_pred))
reg = tf.reduce_sum(self.reg_factor * tf.abs(y_pred))
return mse + reg
Considérations courantes
- S'assurer que le calcul est différentiable
- Gérer les entrées de formes différentes (par exemple, traitement par lots)
- Tenir compte de la stabilité numérique (par exemple, ajouter un petit epsilon)
Intégration d'une boucle d'entraînement personnalisée
Exemple complet de pipeline d'entraînement
Exemple
model = tf.keras.Sequential([...])
optimizer = tf.keras.optimizers.Adam()
loss_fn = CustomLoss()
@tf.function # Améliorer l'efficacité d'exécution
def train_step(x, y):
with tf.GradientTape() as tape:
preds = model(x)
loss = loss_fn(y, preds)
grads = tape.gradient(loss, model.trainable_weights)
optimizer.apply_gradients(zip(grads, model.trainable_weights))
return loss
for epoch in range(epochs):
for x_batch, y_batch in train_dataset:
loss = train_step(x_batch, y_batch)
print(f"Époque {epoch}, Perte : {loss.numpy()}")
optimizer = tf.keras.optimizers.Adam()
loss_fn = CustomLoss()
@tf.function # Améliorer l'efficacité d'exécution
def train_step(x, y):
with tf.GradientTape() as tape:
preds = model(x)
loss = loss_fn(y, preds)
grads = tape.gradient(loss, model.trainable_weights)
optimizer.apply_gradients(zip(grads, model.trainable_weights))
return loss
for epoch in range(epochs):
for x_batch, y_batch in train_dataset:
loss = train_step(x_batch, y_batch)
print(f"Époque {epoch}, Perte : {loss.numpy()}")
Description des composants clés
- GradientTape- Enregistreur de différenciation automatique
- apply_gradients- Méthode de mise à jour des poids
- @tf.function- Décorateur d'exécution de graphe
Conseils d'optimisation des performances
Optimisation du graphe de calcul
Exemple
graph LR
A[Fonction Python] -->|@tf.function| B(Graphe de calcul TensorFlow)
B --> C[Optimisation automatique]
C --> D[Exécution de graphe statique]
A[Fonction Python] -->|@tf.function| B(Graphe de calcul TensorFlow)
B --> C[Optimisation automatique]
C --> D[Exécution de graphe statique]
Entraînement en précision mixte
Exemple
policy = tf.keras.mixed_precision.Policy('mixed_float16')
tf.keras.mixed_precision.set_global_policy(policy)
tf.keras.mixed_precision.set_global_policy(policy)
Accélération de la compilation XLA
Exemple
# Activer XLA sur GPU/TPU
tf.config.optimizer.set_jit(True)
tf.config.optimizer.set_jit(True)
Débogage et tests
Tableau de dépannage courant
| Symptôme du problème | Cause possible | Solution |
|---|---|---|
| Perte NaN | Instabilité numérique | Ajouter un petit epsilon |
| Gradients explosifs | Taux d'apprentissage trop élevé | Écrêtage des gradients |
| Performances médiocres | Exécution du graphe non utilisée | Ajouter @tf.function |
Exemple de test unitaire
Exemple
class TestCustomLayer(tf.test.TestCase):
def test_output_shape(self):
layer = CustomLayer(units=64)
input_tensor = tf.random.normal([32, 128])
output = layer(input_tensor)
self.assertEqual(output.shape, [32, 64])
def test_output_shape(self):
layer = CustomLayer(units=64)
input_tensor = tf.random.normal([32, 128])
output = layer(input_tensor)
self.assertEqual(output.shape, [32, 64])
Cas d'application pratiques
Couche d'amélioration de la super-résolution d'image
Exemple
class PixelShuffle(tf.keras.layers.Layer):
def __init__(self, upscale_factor):
super().__init__()
self.upscale_factor = upscale_factor
def call(self, inputs):
return tf.nn.depth_to_space(inputs, self.upscale_factor)
def __init__(self, upscale_factor):
super().__init__()
self.upscale_factor = upscale_factor
def call(self, inputs):
return tf.nn.depth_to_space(inputs, self.upscale_factor)
Perte de prévision de séries temporelles
Exemple
class QuantileLoss(tf.keras.losses.Loss):
def __init__(self, quantiles=[0.1, 0.5, 0.9]):
super().__init__()
self.quantiles = quantiles
def call(self, y_true, y_pred):
errors = y_true - y_pred
losses = []
for i, q in enumerate(self.quantiles):
losses.append(tf.reduce_mean(tf.maximum(q*errors, (q-1)*errors)))
return tf.reduce_sum(losses)
def __init__(self, quantiles=[0.1, 0.5, 0.9]):
super().__init__()
self.quantiles = quantiles
def call(self, y_true, y_pred):
errors = y_true - y_pred
losses = []
for i, q in enumerate(self.quantiles):
losses.append(tf.reduce_mean(tf.maximum(q*errors, (q-1)*errors)))
return tf.reduce_sum(losses)