Ressource technique · RUMBA.EX

PCA, embeddings et recommandation pédagogique avec IA

Comprendre comment le PCA améliore la recommandation pédagogique avec IA en réduisant la dimension des embeddings, en stabilisant les similarités et en renforçant la personnalisation.

Résumé exécutif

Dans notre système de personnalisation pédagogique, les exercices et les profils d’apprenants sont représentés sous forme d’embeddings. Ces vecteurs denses permettent de rapprocher des contenus, d’identifier des similarités fines et de soutenir un moteur de recommandation plus précis. Leur richesse constitue un avantage méthodologique important, mais elle crée aussi une difficulté. Plus l’espace est dimensionnellement vaste, plus il devient coûteux à manipuler et difficile à interpréter.

L’Analyse en Composantes Principales, ou PCA, intervient pour rendre cet espace plus lisible. Elle réduit la dimension des embeddings tout en conservant ce qui structure réellement les différences entre exercices, profils et trajectoires d’apprentissage. Dans notre architecture, le PCA ne relève donc pas d’un simple choix technique. Il joue un rôle central dans la stabilisation du calcul, dans la visualisation des proximités et dans la qualité de la recommandation pédagogique.

Pourquoi utiliser le PCA dans un système de recommandation pédagogique

Le PCA permet de condenser l’information utile dans un espace plus compact. Les embeddings initiaux peuvent contenir plusieurs centaines de dimensions. Une telle profondeur rend les calculs plus lourds et masque souvent les regroupements réellement significatifs. En réduisant la dimension, le PCA facilite l’identification de structures cohérentes. Des familles d’exercices proches se regroupent plus nettement, tandis que certains contenus atypiques deviennent plus visibles.

Cette réduction améliore aussi l’interprétabilité. Lorsqu’un profil d’apprenant est projeté dans le même espace que les exercices, il devient plus facile de comprendre quelles activités lui ressemblent le plus sur le plan cognitif ou pédagogique. Le PCA aide ainsi à transformer un ensemble d’embeddings abstraits en une cartographie plus exploitable pour la recommandation.

Comment les embeddings soutiennent la personnalisation des apprentissages

Les embeddings traduisent des contenus pédagogiques et des caractéristiques d’apprentissage dans un langage vectoriel. Ils servent à comparer des exercices entre eux, à rapprocher certains profils d’élèves de certaines tâches et à organiser le moteur de recommandation autour de similarités calculables. Sans réduction, ces comparaisons restent possibles, mais elles deviennent plus opaques et plus coûteuses. Avec le PCA, elles gagnent en stabilité et en clarté.

Un espace plus lisible pour les exercices

La projection réduite permet de mieux distinguer des groupes d’exercices qui mobilisent des compétences proches. Cette lisibilité est utile non seulement pour recommander, mais aussi pour contrôler la cohérence du corpus pédagogique.

Un espace partagé pour les profils d’apprenants

Lorsqu’un profil est placé dans le même espace réduit, il devient possible d’estimer plus finement la proximité entre les besoins d’un élève et les exercices disponibles. C’est cette mise en relation qui nourrit ensuite le classement des recommandations.

Du diagnostic au classement des exercices

La recommandation s’inscrit dans un processus complet. Lorsqu’un apprenant répond à une série d’exercices, ses réponses sont analysées. Le système ne se contente pas d’une logique binaire. Il prend en compte à la fois l’exactitude stricte et la proximité textuelle avec la réponse attendue. Une réponse presque correcte n’est donc pas traitée comme une erreur totale.

Ces scores alimentent ensuite un diagnostic de profil. Les résultats sont agrégés, pondérés puis normalisés afin d’estimer la probabilité que l’élève corresponde à un ou plusieurs profils cognitifs. À partir de là, le moteur compare les embeddings réduits des exercices à l’embedding associé au profil estimé. Chaque exercice reçoit alors un score de pertinence qui permet d’ordonner les recommandations.

Une étape supplémentaire garantit la diversité des propositions. Le système ne cherche pas à enfermer l’apprenant dans un seul type d’exercice. Il veille à couvrir plusieurs domaines de compétence afin que la personnalisation reste précise sans devenir étroite.

Code et logique du pipeline de recommandation

Les scripts ci-dessous illustrent le fonctionnement du pipeline. Ils montrent comment les données sont chargées, comment les réponses sont évaluées, comment les scores de profil sont agrégés et comment la recommandation finale est produite avec une contrainte de diversité.

dataset_io.py

dataset_io.pyextrait technique
$ python dataset_io.py
→ Chargement des similarités profil↔exercice et des métadonnées d'exercices
import re, ast, json, pathlib

def _extract_rows(stmt: str):
    rows, cur, depth, on = [], "", 0, False
    for c in stmt:
        if c == "(" and not on:
            on = True; depth = 1; cur = "("
        elif on:
            cur += c
            if c == "(": depth += 1
            elif c == ")":
                depth -= 1
                if depth == 0:
                    rows.append(cur); on = False
    return rows

def parse_dataset(path: str):
    """
    Retourne
      M : dict[profil][exo_id] = similarité (float)
      meta : dict[exo_id] = {"title": str, "domain": str, "answer": str}
    """
    p = pathlib.Path(path)
    text = p.read_text(encoding="utf-8")

    try:
        if text.strip().startswith("["):
            blob = json.loads(text)
            return _from_json(blob)
        if "\n{" in text and "\"id\"" in text:
            blob = [json.loads(line) for line in text.splitlines() if line.strip().startswith("{")]
            return _from_json(blob)
    except Exception:
        pass

    M, meta = {}, {}
    m = re.search(r"INSERT INTO profile_exercise_similarity.*?;", text, re.S)
    if m:
        for row in _extract_rows(m.group(0)):
            prof, exo, sim = ast.literal_eval(row)
            M.setdefault(prof, {})[exo] = float(sim)

    for m in re.finditer(r"INSERT INTO exercise.*?;", text, re.S):
        for row in _extract_rows(m.group(0)):
            parts = ast.literal_eval(row)
            exo_id, title, domain, answer = parts[0], parts[1], parts[3], parts[7]
            meta[exo_id] = {"title": title, "domain": domain, "answer": answer}
    return M, meta

def _from_json(blob):
    M, meta = {}, {}
    for item in blob:
        if item.get("type") == "similarity":
            prof = item["profile"]; exo = item["exercise"]; sim = float(item["sim"])
            M.setdefault(prof, {})[exo] = sim
        elif item.get("type") == "exercise":
            exo = item["id"]
            meta[exo] = {
              "title": item.get("title",""),
              "domain": item.get("domain",""),
              "answer": item.get("answer","")
            }
    return M, meta
[OK] Données prêtes pour le diagnostic et la recommandation
        

Évaluer les réponses avec exactitude et similarité

L’évaluation combine une logique stricte et une logique graduée. Cela permet de mieux distinguer les erreurs franches des réponses proches mais imparfaites.

scoring.py

scoring.pyextrait technique
$ python scoring.py
→ Évaluation nuancée des réponses avec exactitude et similarité textuelle
from difflib import SequenceMatcher

def evaluate_response(correct: str, answer: str) -> float:
    """
    Score ∈ [0,1]. 0 = "réponse parfaite", 1 = "erreur nette".
    """
    exact = 1 if answer.strip() != correct.strip() else 0
    ratio = SequenceMatcher(None, answer, correct).ratio()
    return 0.7 * exact + 0.3 * (1 - ratio)

def batch_error(exercise_ids, answers, meta):
    errors = {}
    for exo_id, answer in zip(exercise_ids, answers):
        expected = meta[exo_id]["answer"]
        errors[exo_id] = evaluate_response(expected, answer)
    return errors
[OK] Scores par exercice disponibles
        

Agréger les scores pour classer les exercices

Une fois les profils normalisés, les similarités sont agrégées afin de produire un classement initial des exercices les plus pertinents.

recommender_core.py

recommender_core.pyextrait technique
$ python recommender_core.py
→ Normalisation des profils puis agrégation des similarités profil↔exercice
def normalize(scores: dict):
    total = sum(scores.values()) or 1.0
    return {k: v/total for k, v in scores.items()}

S_norm = normalize(scores)

RJ = {}
for profil, row in M.items():
    w = S_norm.get(profil, 0.0)
    for exo, sim in row.items():
        RJ[exo] = RJ.get(exo, 0.0) + w * sim

sorted_exos = sorted(RJ.items(), key=lambda x: x[1], reverse=True)
[OK] Classement initial obtenu
        

Garantir la diversité des recommandations

La sélection finale veille à ne pas concentrer toutes les recommandations sur un seul domaine. Cette étape est essentielle pour éviter une personnalisation trop fermée.

diversity_filter.py

diversity_filter.pyextrait technique
$ python diversity_filter.py
→ Sélection finale avec contrainte de diversité
N_RECOMMEND = 6
recs, domains = [], set()
for exo, score in sorted_exos:
    dom = meta[exo]["domain"]
    if len(recs) < N_RECOMMEND and (dom not in domains or len(domains) >= 2):
        recs.append(exo); domains.add(dom)
    if len(recs) >= N_RECOMMEND:
        break
[OK] Top exercices équilibrés sur plusieurs domaines
        

Où le PCA agit concrètement dans le pipeline

Le PCA intervient au moment où les embeddings sont préparés et projetés dans un espace plus compact. Ce sont ensuite ces vecteurs réduits qui servent de base pour les calculs de similarité, pour le classement des exercices et pour le stockage. En pratique, cela signifie que la recommandation repose sur des représentations plus légères, plus stables et plus interprétables.

Cette étape peut aussi être rejouée dans un cadre de recherche ou de développement afin de comparer plusieurs niveaux de réduction, d’étudier la variance expliquée ou de vérifier la stabilité des voisins les plus proches dans l’espace réduit. Le PCA devient alors un levier de contrôle méthodologique autant qu’un outil de performance.

Conclusion

Le PCA joue un rôle fondamental dans notre système de recommandation pédagogique avec IA. Il améliore la lisibilité des embeddings, renforce la stabilité des similarités et facilite la personnalisation des apprentissages. Couplé à une évaluation nuancée des réponses et à un filtre de diversité, il permet de transformer des données complexes en propositions pédagogiques réellement exploitables. L’enjeu n’est donc pas seulement computationnel. Il touche directement à la qualité de l’accompagnement proposé aux apprenants.