Языковая модель не должна заглядывать в будущие слова. Каузальное внимание маскирует будущее: строка запроса i может обращать внимание только на строки ключей от 0 до i.
Для каждой строки i (у queries, keys и values по n строк):
- Оцените каждый допустимый ключ j ≤ i: score = (queries[i] · keys[j]) / √d, где d — число столбцов keys.
- Примените softmax только к этим допустимым оценкам (вычтите наибольшую перед вычислением e^x).
- Строка выхода i = Σ weight_j × values[j] по j ≤ i.
Реализуйте causal_attention(queries, keys, values).
Входные данные
queries, keys: n строк по d чисел.
values: n строк по v чисел.
Выходные данные
Верните выходную матрицу n × v.
Ограничения
1 ≤ n ≤ 50, 1 ≤ d, v ≤ 32
-10 ≤ каждое значение ≤ 10
Примеры
Ввод:
keys = [
[1.0],
[2.0],
[3.0]
]
values = [
[3.0],
[6.0],
[9.0]
]
queries = [
[0.0],
[0.0],
[0.0]
]
Ожидаемый вывод:
expected = [
[3.0],
[4.5],
[6.0]
]
Ввод:
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}
};
Ожидаемый вывод:
std::vector<std::vector<double>> expected = {
{3.0},
{4.5},
{6.0}
};
Ввод:
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}
};
Ожидаемый вывод:
double[][] expected = new double[][] {
new double[] {3.0},
new double[] {4.5},
new double[] {6.0}
};
Пояснение
Все оценки равны 0, поэтому каждая строка усредняет видимые ей значения: строка 0 видит только 3, строка 1 усредняет 3 и 6 (4.5), строка 2 усредняет все три (6).
Ввод:
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]
]
Ожидаемый вывод:
expected = [
[1.0, 2.0],
[2.0, 3.0]
]
Ввод:
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}
};
Ожидаемый вывод:
std::vector<std::vector<double>> expected = {
{1.0, 2.0},
{2.0, 3.0}
};
Ввод:
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}
};
Ожидаемый вывод:
double[][] expected = new double[][] {
new double[] {1.0, 2.0},
new double[] {2.0, 3.0}
};
Пояснение
Строка 0 видит только себя и копирует values[0] = [1, 2]. Строка 1 даёт обоим ключам одинаковую оценку и усредняет их: [2, 3].
Ссылки
Это задание адаптировано для CodeUtility. Следуйте условию и ограничениям выше.