Problema
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):
- Puntúa cada clave permitida j ≤ i: score = (queries[i] · keys[j]) / √d, donde d es el número de columnas de keys.
- Aplica softmax solo sobre esas puntuaciones permitidas (resta la mayor antes de calcular e^x).
- 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
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).
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.