Todos os desafios
Médio 0 resolvidos 0% de aceitação

Autoatenção causal (mascarada)

Implemente a atenção mascarada dos modelos GPT, em que cada token só pode ver a si mesmo e os tokens anteriores.

O que você vai aprender

  • Esconder tokens futuros com uma máscara causal
  • Aplicar softmax só nas posições permitidas
  • Entender como os modelos de linguagem geram texto

Problema

Ver original (inglês)

Um modelo de linguagem não pode olhar para palavras futuras. A atenção causal mascara o futuro: a linha de consulta i só pode atender às linhas de chave 0 a i.

Para cada linha i (queries, keys e values têm n linhas):

  1. Pontue cada chave permitida j ≤ i: score = (queries[i] · keys[j]) / √d, onde d é o número de colunas de keys.
  2. Aplique softmax apenas sobre essas pontuações permitidas (subtraia a maior antes de calcular e^x).
  3. Linha de saída i = Σ weight_j × values[j] para j ≤ i.

Implemente causal_attention(queries, keys, values).

Entrada

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

values: n linhas de v números.

Saída

Retorne a matriz de saída n × v.

Restrições

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

-10 ≤ cada valor ≤ 10

Exemplos

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

Saída 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}
};

Saída 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}
};

Saída esperada:
double[][] expected = new double[][] {
    new double[] {3.0},
    new double[] {4.5},
    new double[] {6.0}
};

Explicação

Todas as pontuações são 0, então cada linha faz a média dos valores que pode ver: a linha 0 vê só 3, a linha 1 faz a média de 3 e 6 (4.5), a linha 2 faz a média dos três (6).

Exemplo 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]
]

Saída 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}
};

Saída 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}
};

Saída esperada:
double[][] expected = new double[][] {
    new double[] {1.0, 2.0},
    new double[] {2.0, 3.0}
};

Explicação

A linha 0 vê apenas a si mesma e copia values[0] = [1, 2]. A linha 1 dá a mesma pontuação às duas chaves e faz a média delas: [2, 3].

Referências

Este desafio foi adaptado para o CodeUtility. Siga o enunciado e as restrições acima.

Aprenda com outros

Soluções da comunidade

Ainda não há soluções publicadas. Resolva este desafio para compartilhar a primeira.