Skip to content

deepbox/nn

Attention & Transformer

MultiheadAttention, positional encoding, transformer encoder and decoder layers and stacks, and causalMask.

  • Transformer

Overview

  • Inputs are [batch, seqLen, dModel], or [seqLen, dModel] for one sequence. The model dimension must be divisible by the number of heads. mha.forward(query, key?, value?, attnMask?, options?) takes an additive float mask, where a large negative number blocks a position, or a boolean mask where true blocks it. causalMask(seqLen) builds the mask for autoregressive attention.
  • The options object takes needWeights (returns [output, weights]), averageAttnWeights and keyPaddingMask. Transformer layers take activation ("relu" or "gelu") and normFirst.
  • Dropout is 0.1 in the transformer layers and 0 in MultiheadAttention. Call model.eval() for inference, and wrap it in noGrad to stop recording.
  • Defaults: the feed-forward width is 2048, FullTransformer has 6 encoder and 6 decoder layers with dModel: 512, and it has no final LayerNorm unless you pass finalNorm: true, where PyTorch's nn.Transformer has one. The decoder uses a causal mask when you give no tgtMask.

Types

2

MultiheadAttentionForwardOptions

type

Options of MultiheadAttention.forward, passed as the last argument.

  • readonly needWeights?: boolean

    Also return the attention weights, as [output, weights] (default: false).

  • readonly averageAttnWeights?: boolean

    With needWeights, average the weights over the heads (default: true).

  • readonly keyPaddingMask?: AnyTensor

    Marks the key positions to ignore, shape (batch, seqLenK) (or (seqLenK) for inputs without a batch axis).

TransformerActivation

type

Activation of the feed-forward network inside a Transformer layer: "relu" or the exact (erf) "gelu".

type TransformerActivation = "relu" | "gelu";

Classes

7

FullTransformer

extends Module

Full Transformer model with encoder and decoder stacks.

Post-norm layers with ReLU feed-forward networks by default (see the normFirst and activation options). Unlike PyTorch's nn.Transformer, the encoder and decoder stacks have no final LayerNorm unless you ask for it with options.finalNorm. Parameters follow the dtype option (default: the global default dtype, float32 unless changed).

Example
import { FullTransformer } from 'deepbox/nn';import { randn } from 'deepbox/ndarray'; const transformer = new FullTransformer(64, 8, 2, 2, 256);const src = randn([2, 10, 64]); // (batch, srcLen, dModel)const tgt = randn([2, 7, 64]); // (batch, tgtLen, dModel)const output = transformer.forward(src, tgt); // shape [2, 7, 64]
Members (5)
  • constructor(dModel?: number, nHead?: number, numEncoderLayers?: number, numDecoderLayers?: number, dFF?: number, options?: { readonly dropout?: number; readonly eps?: number; readonly dtype?: LayerDType; readonly finalNorm?: boolean; readonly activation?: TransformerActivation; readonly normFirst?: boolean; })
  • forward(src: GradTensor, tgt?: AnyTensor, srcMask?: AnyTensor, tgtMask?: AnyTensor, memoryMask?: AnyTensor): GradTensor

    Forward pass of the full Transformer.

  • forward(src: Tensor, tgt?: AnyTensor, srcMask?: AnyTensor, tgtMask?: AnyTensor, memoryMask?: AnyTensor): AnyTensor
  • forward(src: AnyTensor, tgt?: AnyTensor, srcMask?: AnyTensor, tgtMask?: AnyTensor, memoryMask?: AnyTensor): AnyTensor
  • toString(): string

MultiheadAttention

extends Module

Multi-Head Attention mechanism.

Allows the model to jointly attend to information from different representation subspaces at different positions. This is the core building block of Transformers.

Mathematical Formulation:

Attention(Q, K, V) = softmax(Q * K^T / sqrt(d_k)) * VMultiHead(Q, K, V) = Concat(head_1, ..., head_h) * W_Owhere head_i = Attention(Q * W_Q^i, K * W_K^i, V * W_V^i)

Parameters are float32 unless the dtype option (or the global default dtype) says otherwise. The layer computes in the parameter dtype and casts its inputs to it. A GradTensor input gives a GradTensor; plain tensors give a GradTensor that tracks the weights while they require grad and gradient tracking is on, and a plain Tensor otherwise (inside noGrad() or with frozen weights).

Example
import { MultiheadAttention, causalMask } from 'deepbox/nn';import { randn } from 'deepbox/ndarray'; const mha = new MultiheadAttention(64, 8);const x = randn([2, 10, 64]); // (batch, seqLen, embedDim)const out = mha.forward(x, x, x); // shape [2, 10, 64]const masked = mha.forward(x, x, x, causalMask(10)); // each step sees only earlier steps
Members (8)
  • constructor(embedDim: number, numHeads: number, options?: { readonly bias?: boolean; readonly dropout?: number; readonly dtype?: LayerDType; })

    Create a new MultiheadAttention layer.

  • forward<Q extends AnyTensor, O extends MultiheadAttentionForwardOptions>(query: Q, key: AnyTensor | undefined, value: AnyTensor | undefined, attnMask: AnyTensor | undefined, options: O): ForwardResult<Q, O>

    Forward pass of multi-head attention.

  • forward<Q extends AnyTensor, O extends MultiheadAttentionForwardOptions>(query: Q, key: AnyTensor | undefined, value: AnyTensor | undefined, options: O): ForwardResult<Q, O>
  • forward<Q extends AnyTensor, O extends MultiheadAttentionForwardOptions>(query: Q, options: O): ForwardResult<Q, O>
  • forward(query: GradTensor, ...rest: AnyTensor[]): GradTensor
  • forward(query: Tensor, ...rest: AnyTensor[]): AnyTensor
  • forward(...inputs: AnyTensor[]): AnyTensor
  • toString(): string

PositionalEncoding

extends Module

Sinusoidal Positional Encoding.

Adds sinusoidal positional information to input embeddings using the formula from "Attention Is All You Need":

PE(pos, 2i)   = sin(pos / 10000^(2i/d_model))PE(pos, 2i+1) = cos(pos / 10000^(2i/d_model))

For an odd dModel the last column pairs with the same frequency as the one before it. The table is stored as the pe buffer and cast to the input's float dtype on each call; integer inputs are cast to float32.

Example
import { PositionalEncoding } from 'deepbox/nn';import { randn } from 'deepbox/ndarray'; const pe = new PositionalEncoding(64, { maxLen: 500 });const x = randn([2, 10, 64]); // (batch, seqLen, dModel)const output = pe.forward(x); // x + positional encoding, then dropout (training mode)
Members (5)
  • constructor(dModel: number, options?: { readonly dropout?: number; readonly maxLen?: number; })
  • forward(x: GradTensor): GradTensor
  • forward(x: Tensor): Tensor
  • forward(x: AnyTensor): AnyTensor
  • toString(): string

TransformerDecoder

extends Module

Transformer Decoder, a stack of N decoder layers.

The first layer is the decoderLayer you pass in; the other N - 1 layers are independent copies of it (see TransformerDecoderLayer.clone).

Example
import { TransformerDecoder, TransformerDecoderLayer } from 'deepbox/nn';import { randn } from 'deepbox/ndarray'; const decoderLayer = new TransformerDecoderLayer(64, 8, 256);const decoder = new TransformerDecoder(decoderLayer, 6);const output = decoder.forward(randn([2, 7, 64]), randn([2, 10, 64])); // shape [2, 7, 64]
Members (5)
  • constructor(decoderLayer: TransformerDecoderLayer, numLayers: number, options?: { readonly norm?: LayerNorm; })
  • forward(tgt: GradTensor, memory?: AnyTensor, tgtMask?: AnyTensor, memoryMask?: AnyTensor): GradTensor
  • forward(tgt: Tensor, memory?: AnyTensor, tgtMask?: AnyTensor, memoryMask?: AnyTensor): AnyTensor
  • forward(tgt: AnyTensor, memory?: AnyTensor, tgtMask?: AnyTensor, memoryMask?: AnyTensor): AnyTensor
  • toString(): string

TransformerDecoderLayer

extends Module

Transformer Decoder Layer.

A single layer of the Transformer decoder, consisting of: 1. Masked multi-head self-attention 2. Add & Norm 3. Multi-head cross-attention (attending to encoder output) 4. Add & Norm 5. Feed-forward network 6. Add & Norm

By default the self-attention is causal: position i only attends to positions up to i. Pass tgtMask to forward to use a different mask. The layout is post-norm with a ReLU feed-forward network unless normFirst and activation say otherwise.

Parameters are float32 unless the dtype option (or the global default dtype) says otherwise; inputs of other dtypes are cast to the parameter dtype. Gradient tracking follows the rule of MultiheadAttention.

Example
import { TransformerDecoderLayer } from 'deepbox/nn';import { randn } from 'deepbox/ndarray'; const layer = new TransformerDecoderLayer(64, 8, 256);const tgt = randn([2, 7, 64]); // (batch, tgtLen, dModel)const memory = randn([2, 10, 64]); // encoder output (batch, srcLen, dModel)const output = layer.forward(tgt, memory); // shape [2, 7, 64]
Members (6)
  • constructor(dModel: number, nHead: number, dFF?: number, options?: { readonly dropout?: number; readonly eps?: number; readonly dtype?: LayerDType; readonly activation?: TransformerActivation; readonly normFirst?: boolean; })
  • clone(): TransformerDecoderLayer

    Create an independent deep copy: same architecture, copied weights, buffers, trainability and train/eval mode.

  • forward(tgt: GradTensor, memory?: AnyTensor, tgtMask?: AnyTensor, memoryMask?: AnyTensor): GradTensor

    Forward pass of the Transformer decoder layer.

  • forward(tgt: Tensor, memory?: AnyTensor, tgtMask?: AnyTensor, memoryMask?: AnyTensor): AnyTensor
  • forward(tgt: AnyTensor, memory?: AnyTensor, tgtMask?: AnyTensor, memoryMask?: AnyTensor): AnyTensor
  • toString(): string

TransformerEncoder

extends Module

Transformer Encoder, a stack of N encoder layers.

The first layer is the encoderLayer you pass in; the other N - 1 layers are independent copies of it (see TransformerEncoderLayer.clone), so every layer starts from the same weights and then trains separately.

Example
import { TransformerEncoder, TransformerEncoderLayer } from 'deepbox/nn';import { randn } from 'deepbox/ndarray'; const encoderLayer = new TransformerEncoderLayer(64, 8, 256);const encoder = new TransformerEncoder(encoderLayer, 6);const output = encoder.forward(randn([2, 10, 64])); // shape [2, 10, 64]
Members (5)
  • constructor(encoderLayer: TransformerEncoderLayer, numLayers: number, options?: { readonly norm?: LayerNorm; })
  • forward(src: GradTensor, mask?: AnyTensor): GradTensor
  • forward(src: Tensor, mask?: AnyTensor): AnyTensor
  • forward(src: AnyTensor, mask?: AnyTensor): AnyTensor
  • toString(): string

TransformerEncoderLayer

extends Module

Transformer Encoder Layer.

A single layer of the Transformer encoder, consisting of: 1. Multi-head self-attention 2. Add & Norm (residual connection + layer normalization) 3. Feed-forward network (FFN) 4. Add & Norm

Uses the post-norm layout and a ReLU feed-forward network by default, like PyTorch. Pass activation: 'gelu' and normFirst: true for a GELU network and the pre-norm layout. Parameters are float32 unless the dtype option (or the global default dtype) says otherwise; inputs of other dtypes are cast to the parameter dtype. Gradient tracking follows the rule of MultiheadAttention.

Example
import { TransformerEncoderLayer } from 'deepbox/nn';import { randn } from 'deepbox/ndarray'; const layer = new TransformerEncoderLayer(64, 8, 256);const x = randn([2, 10, 64]); // (batch, seqLen, dModel)const output = layer.forward(x); // shape [2, 10, 64]
Members (6)
  • constructor(dModelOrOpts: number | { readonly dModel: number; readonly nHead: number; readonly dimFeedforward?: number; readonly dFF?: number; readonly dropout?: number; readonly eps?: number; readonly dtype?: LayerDType; readonly activation?: TransformerActivation; readonly normFirst?: boolean; }, nHead?: number, dFF…
  • clone(): TransformerEncoderLayer

    Create an independent deep copy: same architecture, copied weights, buffers, trainability and train/eval mode.

  • forward(src: GradTensor, mask?: AnyTensor): GradTensor

    Forward pass of the Transformer encoder layer.

  • forward(src: Tensor, mask?: AnyTensor): AnyTensor
  • forward(src: AnyTensor, mask?: AnyTensor): AnyTensor
  • toString(): string

Functions

1

causalMask

function causalMask(seqLen: number): Tensor;

Build an additive causal (autoregressive) attention mask of shape [L, L]: 0 on and below the diagonal, -1e9 above it. Adding this to attention scores before softmax prevents each position from attending to future positions.

Parameters

seqLen
number
Sequence length L (non-negative integer)

Returns

Tensorfloat64 tensor of shape [seqLen, seqLen]

Throws

  • InvalidParameterErrorIf seqLen is not a non-negative integer
Example
import { causalMask } from 'deepbox/nn'; causalMask(3).toArray();// [[0, -1e9, -1e9], [0, 0, -1e9], [0, 0, 0]]
nn-attention.ts
import { randn } from "deepbox/ndarray";import {  FullTransformer,  MultiheadAttention,  PositionalEncoding,  TransformerEncoderLayer,  causalMask,} from "deepbox/nn"; const x = randn([2, 10, 64]); // [batch, seqLen, dModel] const attention = new MultiheadAttention(64, 8);const [out, weights] = attention.forward(x, x, x, causalMask(10), { needWeights: true });console.log(out.shape, weights.shape); // [2, 10, 64] [2, 10, 10] const encoder = new TransformerEncoderLayer(64, 8, 128, { activation: "gelu" });console.log(encoder.forward(x).shape); console.log(new PositionalEncoding(64).forward(x).shape); const transformer = new FullTransformer(64, 8, 2, 2, 128);console.log(transformer.forward(x, randn([2, 7, 64])).shape); // [2, 7, 64]