Todos los desafíos
Intermedio 0 resueltos 0% de aceptación

Autoatención causal (enmascarada)

Implementa la atención enmascarada de los modelos GPT, donde cada token solo puede ver a sí mismo y a los tokens anteriores.

Lo que aprenderás

  • Ocultar los tokens futuros con una máscara causal
  • Aplicar softmax solo a posiciones permitidas
  • Entender cómo los modelos de lenguaje generan texto

Problema

Ver original (inglés)

Un modelo de lenguaje no debe mirar las palabras futuras. La atención causal enmascara el futuro: la fila de consulta i solo puede atender a las filas de clave 0 a i.

Para cada fila i (queries, keys y values tienen n filas):

  1. Puntúa cada clave permitida j ≤ i: score = (queries[i] · keys[j]) / √d, donde d es el número de columnas de keys.
  2. Aplica softmax solo sobre esas puntuaciones permitidas (resta la mayor antes de calcular e^x).
  3. Fila de salida i = Σ weight_j × values[j] para j ≤ i.

Implementa causal_attention(queries, keys, values).

Entrada

queries, keys: n filas de d números.

values: n filas de v números.

Salida

Devuelve la matriz de salida n × v.

Restricciones

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

-10 ≤ cada valor ≤ 10

Ejemplos

Ejemplo 1
Entrada:
keys = [
    [1.0],
    [2.0],
    [3.0]
]
values = [
    [3.0],
    [6.0],
    [9.0]
]
queries = [
    [0.0],
    [0.0],
    [0.0]
]

Salida esperada:
expected = [
    [3.0],
    [4.5],
    [6.0]
]
Entrada:
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}
};

Salida esperada:
std::vector<std::vector<double>> expected = {
    {3.0},
    {4.5},
    {6.0}
};
Entrada:
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}
};

Salida esperada:
double[][] expected = new double[][] {
    new double[] {3.0},
    new double[] {4.5},
    new double[] {6.0}
};

Explicación

Todas las puntuaciones son 0, así que cada fila promedia los valores que puede ver: la fila 0 solo ve 3, la fila 1 promedia 3 y 6 (4.5), la fila 2 promedia los tres (6).

Ejemplo 2
Entrada:
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]
]

Salida esperada:
expected = [
    [1.0, 2.0],
    [2.0, 3.0]
]
Entrada:
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}
};

Salida esperada:
std::vector<std::vector<double>> expected = {
    {1.0, 2.0},
    {2.0, 3.0}
};
Entrada:
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}
};

Salida esperada:
double[][] expected = new double[][] {
    new double[] {1.0, 2.0},
    new double[] {2.0, 3.0}
};

Explicación

La fila 0 solo se ve a sí misma y copia values[0] = [1, 2]. La fila 1 da la misma puntuación a ambas claves y las promedia: [2, 3].

Referencias

Este reto está adaptado para CodeUtility. Sigue el enunciado y las restricciones indicadas arriba.

Aprende de otros

Soluciones de la comunidad

Aún no hay soluciones publicadas. Resuelve este reto para compartir la primera.