Apprentissage semi-supervisé: Recommandation de contenu
L’apprentissage semi-supervisé appliqué à la recommandation de contenu utilise un petit ensemble de données annotées sur les préférences des utilisateurs, combiné à un large volume de données non annotées, pour améliorer la précision des recommandations. Cette méthode permet de tirer parti des comportements non explicitement étiquetés, comme les clics ou les temps de visionnage, afin d’affiner les suggestions personnalisées. En exploitant ces données non annotées, les systèmes de recommandation deviennent plus efficaces et capables de s’adapter rapidement aux préférences changeantes des utilisateurs.
Fonctions :
-
Recommandation de contenu (exemple)
La recommandation de contenu est une technologie qui analyse les préférences et le comportement des utilisateurs pour leur proposer des articles, vidéos, musiques ou produits adaptés à leurs goûts. Par exemple, les plateformes de streaming comme Netflix suggèrent des films ou séries en se basant sur les visionnages précédents et les notes données par l’utilisateur, améliorant ainsi son expérience personnalisée.
Exemple de code :
import tensorflow as tf import numpy as np # Simulation de données utilisateurs-contenus n_users = 500 n_items = 300 embedding_dim = 16 # Données étiquetées : quelques notes (1 à 5) données par des utilisateurs sur des items def generate_labeled_data(n_samples): user_ids = np.random.randint(0, n_users, size=n_samples) item_ids = np.random.randint(0, n_items, size=n_samples) ratings = np.random.randint(1, 6, size=n_samples).astype(np.float32) # notes de 1 à 5 return user_ids, item_ids, ratings # Données non étiquetées : juste les indices utilisateur/item (sans notes) def generate_unlabeled_data(n_samples): user_ids = np.random.randint(0, n_users, size=n_samples) item_ids = np.random.randint(0, n_items, size=n_samples) return user_ids, item_ids # Génération des données n_labeled = 2000 n_unlabeled = 8000 n_val = 1000 u_labeled, i_labeled, r_labeled = generate_labeled_data(n_labeled) u_unlabeled, i_unlabeled = generate_unlabeled_data(n_unlabeled) u_val, i_val, r_val = generate_labeled_data(n_val) # Modèle simple d'embeddings pour utilisateurs et items + prédiction class RecommenderModel(tf.keras.Model): def __init__(self, n_users, n_items, embedding_dim): super().__init__() self.user_emb = tf.keras.layers.Embedding(n_users, embedding_dim, embeddings_initializer='he_normal') self.item_emb = tf.keras.layers.Embedding(n_items, embedding_dim, embeddings_initializer='he_normal') self.dense = tf.keras.layers.Dense(1) # prédiction note def call(self, inputs): u, i = inputs u_emb = self.user_emb(u) i_emb = self.item_emb(i) x = tf.concat([u_emb, i_emb], axis=1) x = self.dense(x) return x model = RecommenderModel(n_users, n_items, embedding_dim) optimizer = tf.keras.optimizers.Adam(learning_rate=0.001) loss_fn = tf.keras.losses.MeanSquaredError() batch_size = 64 consistency_weight = 0.5 epochs = 10 # Fonction d'augmentation simple : ajoute un bruit léger aux embeddings en entrée def augment_inputs(u_batch, i_batch): noise_level = 0.05 u_noise = tf.random.normal(shape=tf.shape(u_batch), mean=0.0, stddev=noise_level) i_noise = tf.random.normal(shape=tf.shape(i_batch), mean=0.0, stddev=noise_level) return u_batch + tf.cast(u_noise, tf.int32), i_batch + tf.cast(i_noise, tf.int32) # Préparation des datasets labeled_ds = tf.data.Dataset.from_tensor_slices(((u_labeled, i_labeled), r_labeled)) labeled_ds = labeled_ds.shuffle(n_labeled).batch(batch_size).repeat() unlabeled_ds = tf.data.Dataset.from_tensor_slices((u_unlabeled, i_unlabeled)).shuffle(n_unlabeled).batch(batch_size).repeat() val_ds = tf.data.Dataset.from_tensor_slices(((u_val, i_val), r_val)).batch(batch_size) labeled_iter = iter(labeled_ds) unlabeled_iter = iter(unlabeled_ds) for epoch in range(epochs): print(f"Epoch {epoch+1}/{epochs}") total_loss = 0 steps_per_epoch = n_labeled // batch_size for step in range(steps_per_epoch): (u_lab, i_lab), r_lab = next(labeled_iter) u_unlab, i_unlab = next(unlabeled_iter) # Augmentation sur données non étiquetées : ici on fait simple, pas d'augmentation des indices, # on applique augmentation au niveau embeddings (dans le call) ou on fait deux passes différentes # Pour simplicité, on fera deux passes directes sans modification d'indices. with tf.GradientTape() as tape: # Supervision sur données étiquetées preds_lab = model((u_lab, i_lab), training=True) loss_sup = loss_fn(r_lab, preds_lab) # Consistency regularization sur données non étiquetées preds_unlab_1 = model((u_unlab, i_unlab), training=True) preds_unlab_2 = model((u_unlab, i_unlab), training=True) # même entrée, mais dropout actif si activé loss_consistency = tf.reduce_mean(tf.square(preds_unlab_1 - preds_unlab_2)) loss = loss_sup + consistency_weight * loss_consistency grads = tape.gradient(loss, model.trainable_variables) optimizer.apply_gradients(zip(grads, model.trainable_variables)) total_loss += loss.numpy() print(f"Loss moyenne: {total_loss/steps_per_epoch:.4f}") # Évaluation mse_metric = tf.keras.metrics.MeanSquaredError() for (u_batch, i_batch), r_batch in val_ds: preds = model((u_batch, i_batch), training=False) mse_metric.update_state(r_batch, preds) print(f"MSE validation: {mse_metric.result().numpy():.4f}")
Explication du code :
1. Simulation des données utilisateurs-items avec notes (labels)
- 500 utilisateurs, 300 items, embeddings de dimension 16. - Données étiquetées (avec notes de 1 à 5) : 2000 exemples. - Données non étiquetées (uniquement indices utilisateur/item, sans notes) : 8000 exemples. - Jeu de validation de 1000 exemples avec notes.2. Modèle d’embeddings simple
- Deux couches embedding séparées pour utilisateurs et items, chacune produisant un vecteur de taille 16. - Les embeddings utilisateur et item sont concaténés (dimension 32) puis passés dans une couche dense (1 neurone) pour prédire la note. - Pas de fonction d’activation finale, la sortie est une valeur réelle (regression).3. Fonction d’augmentation des données (commentée)
- Une fonction d’augmentation est définie (ajout bruit gaussien sur indices), mais pas utilisée car bruit sur indices d’embedding ne fait pas sens. - L’augmentation est donc réalisée implicitement par deux passes différentes sur les mêmes entrées non étiquetées (exploitation possible de dropout).4. Préparation des datasets TensorFlow
- Dataset étiqueté : tuples ((user_id, item_id), rating), shuffle, batch, répétition infinie. - Dataset non étiqueté : tuples (user_id, item_id), shuffle, batch, répétition infinie. - Dataset validation : batch, pas de shuffle ni répétition.5. Entraînement semi-supervisé avec régularisation de consistance
Pour chaque batch : - Extraction des batches étiquetés et non étiquetés. - Calcul de la perte supervisée : MSE entre notes vraies et prédictions sur données étiquetées. - Calcul de la perte de consistance sur non-étiquetées : - Le modèle fait deux prédictions différentes sur les mêmes entrées non étiquetées. - La régularisation encourage les prédictions à être proches (différence au carré moyenne). - Cette technique exploite la variation induite par la stochasticité interne du modèle (dropout, batchnorm, etc.). - La perte totale est somme pondérée : `loss = loss_sup + consistency_weight * loss_consistency` avec `consistency_weight=0.5`. - Mise à jour des poids via backpropagation.6. Évaluation finale
- Calcul du MSE sur le jeu de validation avec les vraies notes et prédictions. - Affichage du score MSE final.