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

Attention par produit scalaire mis à l'échelle

Implémentez l'opération d'attention, au cœur des Transformers et des grands modèles de langage.

Ce que vous allez apprendre

  • Noter les requêtes par rapport aux clés
  • Appliquer softmax pour obtenir les poids d'attention
  • Mélanger les valeurs avec ces poids

Problème

L'attention permet à chaque position d'une séquence de regarder toutes les autres et de mélanger leurs informations. C'est le cœur de l'architecture Transformer utilisée par les grands modèles de langage :

Attention(Q, K, V) = softmax(Q × Kᵀ / √d) × V

Pour chaque ligne de requête q :

  1. Calculez le score de chaque ligne de clé k : score = (q · k) / √d, où d est le nombre de colonnes de K.
  2. Transformez les scores en poids avec softmax (soustrayez le plus grand score avant de calculer e^x).
  3. La ligne de sortie est la somme pondérée des lignes de valeurs : Σ weight_j × values[j].

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

Entrée

queries : n lignes de d nombres chacune.

keys : m lignes de d nombres chacune.

values : m lignes (une par clé) de v nombres chacune.

Sortie

Renvoyez la matrice de sortie n × v.

Contraintes

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

-10 ≤ chaque valeur ≤ 10

Exemples

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

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

Sortie attendue :
std::vector<std::vector<double>> expected = {
    {4.0, 6.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[] {2.0, 4.0},
    new double[] {6.0, 8.0}
};
double[][] queries = new double[][] {
    new double[] {1.0, 0.0}
};

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

Explication

Les deux clés donnent le même score, donc les poids softmax valent 0.5 et 0.5. La sortie est la moyenne des lignes de valeurs : [(2 + 6) / 2, (4 + 8) / 2] = [4, 6].

Exemple 2
Entrée :
keys = [
    [3.0, 1.0],
    [-2.0, 5.0],
    [7.0, 7.0]
]
values = [
    [1.0],
    [2.0],
    [6.0]
]
queries = [
    [0.0, 0.0]
]

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

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

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

Explication

Une requête nulle obtient un score de 0 avec chaque clé, donc l'attention est répartie également : (1 + 2 + 6) / 3 = 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.