Tous les défis
Moyen 0 résolus 0 % de réussite

Auto-attention causale (masquée)

Implémentez l'attention masquée des modèles GPT, où chaque token ne voit que lui-même et les tokens précédents.

Ce que vous allez apprendre

  • Masquer les jetons futurs avec un masque causal
  • Appliquer softmax uniquement aux positions autorisées
  • Comprendre comment les modèles de langage génèrent du texte

Problème

Un modèle de langage ne doit pas regarder les mots futurs. L'attention causale masque le futur : la ligne de requête i ne peut s'intéresser qu'aux lignes de clé 0 à i.

Pour chaque ligne i (queries, keys et values ont toutes n lignes) :

  1. Notez chaque clé autorisée j ≤ i : score = (queries[i] · keys[j]) / √d, où d est le nombre de colonnes de keys.
  2. Appliquez softmax uniquement sur ces scores autorisés (soustrayez le plus grand avant de calculer e^x).
  3. Ligne de sortie i = Σ weight_j × values[j] pour j ≤ i.

Implémentez causal_attention(queries, keys, values).

Entrée

queries, keys : n lignes de d nombres.

values : n lignes de v nombres.

Sortie

Renvoyez la matrice de sortie n × v.

Contraintes

1 ≤ n ≤ 50, 1 ≤ d, v ≤ 32

-10 ≤ chaque valeur ≤ 10

Exemples

Exemple 1
Entrée :
keys = [
    [1.0],
    [2.0],
    [3.0]
]
values = [
    [3.0],
    [6.0],
    [9.0]
]
queries = [
    [0.0],
    [0.0],
    [0.0]
]

Sortie attendue :
expected = [
    [3.0],
    [4.5],
    [6.0]
]
Entrée :
std::vector<std::vector<double>> keys = {
    {1.0},
    {2.0},
    {3.0}
};
std::vector<std::vector<double>> values = {
    {3.0},
    {6.0},
    {9.0}
};
std::vector<std::vector<double>> queries = {
    {0.0},
    {0.0},
    {0.0}
};

Sortie attendue :
std::vector<std::vector<double>> expected = {
    {3.0},
    {4.5},
    {6.0}
};
Entrée :
double[][] keys = new double[][] {
    new double[] {1.0},
    new double[] {2.0},
    new double[] {3.0}
};
double[][] values = new double[][] {
    new double[] {3.0},
    new double[] {6.0},
    new double[] {9.0}
};
double[][] queries = new double[][] {
    new double[] {0.0},
    new double[] {0.0},
    new double[] {0.0}
};

Sortie attendue :
double[][] expected = new double[][] {
    new double[] {3.0},
    new double[] {4.5},
    new double[] {6.0}
};

Explication

Tous les scores valent 0, donc chaque ligne fait la moyenne des valeurs qu'elle peut voir : la ligne 0 ne voit que 3, la ligne 1 fait la moyenne de 3 et 6 (4.5), la ligne 2 fait la moyenne des trois (6).

Exemple 2
Entrée :
keys = [
    [1.0, 0.0],
    [1.0, 0.0]
]
values = [
    [1.0, 2.0],
    [3.0, 4.0]
]
queries = [
    [1.0, 0.0],
    [1.0, 0.0]
]

Sortie attendue :
expected = [
    [1.0, 2.0],
    [2.0, 3.0]
]
Entrée :
std::vector<std::vector<double>> keys = {
    {1.0, 0.0},
    {1.0, 0.0}
};
std::vector<std::vector<double>> values = {
    {1.0, 2.0},
    {3.0, 4.0}
};
std::vector<std::vector<double>> queries = {
    {1.0, 0.0},
    {1.0, 0.0}
};

Sortie attendue :
std::vector<std::vector<double>> expected = {
    {1.0, 2.0},
    {2.0, 3.0}
};
Entrée :
double[][] keys = new double[][] {
    new double[] {1.0, 0.0},
    new double[] {1.0, 0.0}
};
double[][] values = new double[][] {
    new double[] {1.0, 2.0},
    new double[] {3.0, 4.0}
};
double[][] queries = new double[][] {
    new double[] {1.0, 0.0},
    new double[] {1.0, 0.0}
};

Sortie attendue :
double[][] expected = new double[][] {
    new double[] {1.0, 2.0},
    new double[] {2.0, 3.0}
};

Explication

La ligne 0 ne voit qu'elle-même et copie values[0] = [1, 2]. La ligne 1 donne le même score aux deux clés et en fait la moyenne : [2, 3].

Références

Ce défi a été adapté pour CodeUtility. Suivez l'énoncé et les contraintes ci-dessus.

Apprenez des autres

Solutions de la communauté

Aucune solution publiée pour l'instant. Résolvez ce défi pour partager la première.