deepbox/nn
Recurrent & Packed Sequences
RNN, LSTM and GRU layers, and helpers that pack and unpack variable-length sequences.
- Sequences
Overview
- The input layout is
[batch, seq, feature]by default (batchFirst: true). PyTorch defaults to sequence first. Other options arenumLayers,bidirectional,bias, andnonlinearityforRNN. forward(x)returns the outputs of every step.forwardWithState(x, hx?)also returns the final hidden state, as[output, hN], and forLSTMas[output, [hN, cN]].- Layers compute in their parameter dtype, so integer input is accepted. The recurrent layers run host kernels, so their weights stay on the CPU after
model.to(device). packSequencestores sequences of different lengths in one flat tensor sorted by length, andunpackSequenceandpadPackedSequencereverse it. Packed data is plain tensors outside autograd, and the recurrent layers take padded tensors, notPackedSequenceobjects.
PackedSequence
typeA packed representation of variable-length sequences.
Stores sequence data sorted by length (longest first) in a flat tensor, along with batch sizes at each timestep.
readonly data: TensorPacked data tensor of shape (totalElements, features)
readonly batchSizes: number[]Number of active sequences at each timestep
readonly sortedIndices: number[]Original index of each sequence, listed in sorted (longest first) order
readonly unsortedIndices: number[]Position of each original sequence within the sorted order (inverse of
sortedIndices)readonly featureSize: numberFeature dimension size
readonly squeezeFeature?: booleanTrue when the packed sequences were 1D, so unpackSequence returns 1D tensors of shape
(seqLen)instead of(seqLen, 1).
RNNNonlinearity
typetype RNNNonlinearity = "tanh" | "relu";GRU
extends ModuleGRU (Gated Recurrent Unit) layer.
Gate weights are stacked in PyTorch's order (reset, update, new) and the reset gate is applied to the hidden projection only, as in PyTorch. Input and output layouts, parameter names, initialization and dtype handling follow RNN.
import { GRU } from 'deepbox/nn'; const gru = new GRU(4, 8);const output = gru.forward(x);Members (13)
constructor(inputSize: number, hiddenSize: number, options?: { readonly numLayers?: number; readonly bias?: boolean; readonly batchFirst?: boolean; readonly bidirectional?: boolean; readonly dtype?: "float32" | "float64"; })readonly inputSize: numberNumber of features of each input step.
readonly hiddenSize: numberNumber of features of the hidden state.
readonly numLayers: numberNumber of stacked layers.
readonly batchFirst: booleanWhether input and output are
(batch, seq, feature)rather than(seq, batch, feature).readonly bidirectional: booleanWhether each layer also runs over the sequence backwards.
forward(input: GradTensor, hx?: AnyTensor): GradTensorforward(input: Tensor, hx?: AnyTensor): AnyTensorforward(...inputs: AnyTensor[]): AnyTensorforwardWithState(input: GradTensor, hx?: AnyTensor): [ GradTensor, GradTensor ]Run the layer and also return the final hidden state.
forwardWithState(input: Tensor, hx?: AnyTensor): [ AnyTensor, AnyTensor ]forwardWithState(input: AnyTensor, hx?: AnyTensor): [ AnyTensor, AnyTensor ]toString(): string
LSTM
extends ModuleLSTM (Long Short-Term Memory) layer.
Gate weights are stacked in PyTorch's order (input, forget, cell, output). Input and output layouts, parameter names, initialization and dtype handling follow RNN. forwardWithState returns the output and the final hidden and cell states [output, [hN, cN]].
import { LSTM } from 'deepbox/nn'; const lstm = new LSTM(4, 8);const [output, [hN, cN]] = lstm.forwardWithState(x);Members (13)
constructor(inputSize: number, hiddenSize: number, options?: { readonly numLayers?: number; readonly bias?: boolean; readonly batchFirst?: boolean; readonly bidirectional?: boolean; readonly dtype?: "float32" | "float64"; })readonly inputSize: numberNumber of features of each input step.
readonly hiddenSize: numberNumber of features of the hidden state.
readonly numLayers: numberNumber of stacked layers.
readonly batchFirst: booleanWhether input and output are
(batch, seq, feature)rather than(seq, batch, feature).readonly bidirectional: booleanWhether each layer also runs over the sequence backwards.
forward(input: GradTensor, hx?: AnyTensor, cx?: AnyTensor): GradTensorforward(input: Tensor, hx?: AnyTensor, cx?: AnyTensor): AnyTensorforward(...inputs: AnyTensor[]): AnyTensorforwardWithState(input: GradTensor, hx?: AnyTensor, cx?: AnyTensor): [ GradTensor, [ GradTensor, GradTensor ] ]Run the layer and also return the final hidden and cell states.
forwardWithState(input: Tensor, hx?: AnyTensor, cx?: AnyTensor): [ AnyTensor, [ AnyTensor, AnyTensor ] ]forwardWithState(input: AnyTensor, hx?: AnyTensor, cx?: AnyTensor): [ AnyTensor, [ AnyTensor, AnyTensor ] ]toString(): string
RNN
extends ModuleElman RNN layer with tanh or relu nonlinearity.
Computes h_t = act(W_ih x_t + b_ih + W_hh h_(t-1) + b_hh) for every layer and direction. Input is (batch, seq, feature) (or (seq, batch, feature) with batchFirst: false), or unbatched (seq, feature). As in PyTorch, all weights and biases are drawn from the uniform distribution U(-1/sqrt(hiddenSize), 1/sqrt(hiddenSize)). Parameters are named like PyTorch's (weight_ih_l0, weight_hh_l0, bias_ih_l0, bias_hh_l0, and a _reverse suffix for the backward direction). The layer computes in the parameter dtype and casts the input (and the initial state) to it.
forward returns the output sequence (batch, seq, hidden * directions); forwardWithState also returns the final hidden state (layers * directions, batch, hidden). A GradTensor input gives GradTensor results. A plain Tensor input gives GradTensor results that track the weights while they require grad and gradient tracking is on, and plain tensors otherwise (inside noGrad() or with frozen weights).
import { RNN } from 'deepbox/nn';import { randn } from 'deepbox/ndarray'; const rnn = new RNN(4, 8, { numLayers: 2 });const x = randn([2, 5, 4]); // (batch, seq, feature)const output = rnn.forward(x);Members (14)
constructor(inputSize: number, hiddenSize: number, options?: { readonly numLayers?: number; readonly nonlinearity?: RNNNonlinearity; readonly bias?: boolean; readonly batchFirst?: boolean; readonly bidirectional?: boolean; readonly dtype?: "float32" | "float64"; })readonly inputSize: numberNumber of features of each input step.
readonly hiddenSize: numberNumber of features of the hidden state.
readonly numLayers: numberNumber of stacked layers.
readonly nonlinearity: RNNNonlinearityActivation applied at every step.
readonly batchFirst: booleanWhether input and output are
(batch, seq, feature)rather than(seq, batch, feature).readonly bidirectional: booleanWhether each layer also runs over the sequence backwards.
forward(input: GradTensor, hx?: AnyTensor): GradTensorforward(input: Tensor, hx?: AnyTensor): AnyTensorforward(...inputs: AnyTensor[]): AnyTensorforwardWithState(input: GradTensor, hx?: AnyTensor): [ GradTensor, GradTensor ]Run the layer and also return the final hidden state.
forwardWithState(input: Tensor, hx?: AnyTensor): [ AnyTensor, AnyTensor ]forwardWithState(input: AnyTensor, hx?: AnyTensor): [ AnyTensor, AnyTensor ]toString(): string
packPaddedSequence
function packPaddedSequence(input: Tensor, lengths: readonly number[], enforcesSorted?: boolean): PackedSequence;Pack a padded 3D tensor into a PackedSequence.
Takes a padded tensor of shape (batch, maxLen, features) and corresponding lengths, and creates a packed representation. The input dtype is kept.
Parameters
inputTensor- Padded tensor of shape (batch, maxLen, features)
lengthsreadonly number[]- Actual lengths of each sequence in the batch
enforcesSortedoptionalboolean- If true, the lengths must already be sorted descending (an error is thrown otherwise)
Returns
PackedSequencePacked sequence representation
Throws
ShapeErrorIfinputis not 3DInvalidParameterErrorIflengthshas the wrong number of entries or an entry is not an integer in[1, maxLen]
import { packPaddedSequence } from 'deepbox/nn';import { tensor } from 'deepbox/ndarray'; const padded = tensor([[[1, 2], [3, 4]], [[5, 6], [0, 0]]]);const packed = packPaddedSequence(padded, [2, 1]);packSequence
function packSequence(sequences: readonly Tensor[], enforcesSorted?: boolean): PackedSequence;Pack a list of variable-length tensors into a PackedSequence.
Sequences are sorted by length (longest first) and interleaved so that at each timestep only active sequences are present. All sequences are converted to the dtype of the first one.
Parameters
sequencesreadonly Tensor[]- Array of 1D or 2D tensors. If 2D, shape is (seqLen, features). If 1D, treated as (seqLen, 1).
enforcesSortedoptionalboolean- If true, the sequences must already be sorted by descending length (an error is thrown otherwise) and no sorting is done. Default: false.
Returns
PackedSequencePacked sequence representation
Throws
InvalidParameterErrorIfsequencesis empty, a sequence has zero length, orenforcesSortedis set but the lengths are not descendingShapeErrorIf a sequence is not 1D or 2D, or feature sizes differDTypeErrorIf a sequence is a string tensor
import { packSequence } from 'deepbox/nn';import { tensor } from 'deepbox/ndarray'; const seqs = [ tensor([[1, 2], [3, 4], [5, 6]]), tensor([[7, 8]]),];const packed = packSequence(seqs);padPackedSequence
function padPackedSequence(packed: PackedSequence, totalLength?: number, paddingValue?: number): [ Tensor, number[] ];Pad a PackedSequence to a dense tensor.
Creates a padded 3D tensor of shape (batchSize, maxLen, features) filled with paddingValue after the end of shorter sequences. The returned tensor has sequences in their original order.
Parameters
packedPackedSequence- Packed sequence to pad
totalLengthoptionalnumber- Optional total length to pad to (defaults to max sequence length). Must be at least the length of the longest sequence.
paddingValueoptionalnumber- Value written after the end of each sequence (default: 0)
Returns
[ Tensor, number[] ]Tuple of [paddedTensor, lengths]
Throws
InvalidParameterErrorIftotalLengthis not an integer or is shorter than the longest sequence, or the packed sequence is inconsistent
import { packSequence, padPackedSequence } from 'deepbox/nn';import { tensor } from 'deepbox/ndarray'; const seqs = [tensor([[1, 2], [3, 4]]), tensor([[5, 6]])];const packed = packSequence(seqs);const [padded, lengths] = padPackedSequence(packed);// padded.shape = [2, 2, 2], lengths = [2, 1]unpackSequence
function unpackSequence(packed: PackedSequence): [ Tensor[], number[] ];Unpack a PackedSequence back to a list of tensors.
Returns the sequences in the original order (before sorting). Each tensor has shape (seqLen, features), or (seqLen) when the packed sequences were 1D.
Parameters
packedPackedSequence- Packed sequence to unpack
Returns
[ Tensor[], number[] ]Tuple of [sequences, lengths] where sequences are in original order
Throws
InvalidParameterErrorIf the packed sequence is inconsistentShapeErrorIf the packed data size does not matchbatchSizes
import { packSequence, unpackSequence } from 'deepbox/nn';import { tensor } from 'deepbox/ndarray'; const seqs = [tensor([[1, 2], [3, 4]]), tensor([[5, 6]])];const packed = packSequence(seqs);const [unpacked, lengths] = unpackSequence(packed);import { randn, tensor } from "deepbox/ndarray";import { GRU, LSTM, RNN, packSequence, unpackSequence } from "deepbox/nn"; const x = randn([2, 5, 3]); // [batch, seq, feature] const lstm = new LSTM(3, 4);const [output, [hN, cN]] = lstm.forwardWithState(x);console.log(output.shape, hN.shape, cN.shape); // [2, 5, 4] [1, 2, 4] [1, 2, 4] console.log(new RNN(3, 4, { bidirectional: true }).forward(x).shape); // [2, 5, 8]console.log(new GRU(3, 4, { numLayers: 2 }).forward(x).shape); // [2, 5, 4] const packed = packSequence([tensor([[1], [2], [3]]), tensor([[4], [5]])]);console.log(packed.batchSizes);console.log(unpackSequence(packed)[1]); // original lengthsFound a mistake? Open an issue.