Reusable Neural-Network Blocks #
Configuration records for MLP and convolution blocks, plus residual and branching combinators over
already-built sequential models. The seeded block constructors live in NN.API.Seeded.
MLP (multi-layer perceptron) configuration.
This builder produces a sequential stack of linear layers with activations and optional dropout.
PyTorch analogue: a hand-written nn.Sequential(Linear(...), ReLU(), ..., Linear(...)).
- activation : Activation.Kind
Activation used after each hidden linear layer.
Optional dropout probability after each activation.
Instances For
Convolution followed by an activation and optional dropout.
- convolution : Convolution.Config d
Convolution configuration.
- activation : Activation.Kind
Activation applied after convolution.
Optional dropout probability applied after activation.
Instances For
Convolution/activation followed by max pooling.
- block : ConvBlock.Config d
Convolution and activation stage.
- pooling : Pooling.Config d
Pooling stage.
Instances For
Interpret an activation kind as an elementwise sequential model.
Instances For
Residual (skip) connection as a single Layer.
Given inner : Seq s s this computes $x \mapsto \operatorname{inner}(x) + x$, the shape that
appears in ResNet (He et al., Deep Residual Learning for Image Recognition, CVPR 2016) and in
every Transformer sublayer since Vaswani et al., Attention Is All You Need (NeurIPS 2017).
The Layer namespace holds the raw single-layer constructors; the same name without the namespace
returns a Sequential. That split is why there is no residualLayer: the return type belongs in
the namespace, not in the identifier.
Instances For
The same residual connection, wrapped as a one-element Sequential model.
Example:
def inner : nn.Builder (nn.Sequential [64] [64]) :=
nn.Sequential![nn.linear 64 64, nn.relu]
-- `x + inner x`, the connection that made deep stacks trainable.
def model : nn.Builder (nn.Sequential [64] [64]) := do
pure (nn.residual (← inner))
Instances For
Branching (skip connections) #
Seq is linear, but skip connections require two computations to consume the same input. The
generic constructor below owns the shared state plumbing; public blocks choose how to combine the
two outputs.
Run two sequential branches on the same input and combine their outputs.
Parameters and persistent buffers are stored as state(f) ++ state(g). The combining operation is
polymorphic in the runtime, so eager execution and graph lowering share the same branch structure.
Instances For
Two branches over one input, outputs added. The Sequential form is nn.addBranches.
Instances For
Combine two models with the same input/output shapes by summing their outputs.
This is a typed residual-add block: addBranches f g represents the model
$x \mapsto f(x) + g(x)$,
and its parameter list is the concatenation of the two branches’ parameter lists.
Example:
-- Two branches over the same input, outputs summed. Parameters are stored as the first branch's
-- list followed by the second's.
def model : nn.Builder (nn.Sequential [32] [8]) := do
let wide ← nn.Sequential![nn.linear 32 8, nn.relu]
let shortcut ← nn.linear 32 8
pure (nn.addBranches wide shortcut)
Instances For
Concatenate two branch outputs along their first axis.
Both branches consume the same input. Their outputs must agree on every remaining dimension, and
the result records the sum of their first-axis extents in its type. This is the general typed skip
connection needed by encoder-decoder models; arbitrary outer batch axes can be added with
mapLeading.
Instances For
Concatenate two typed branches along their first output axis.
Example:
-- Concatenation along the first output axis: `[4, 8]` next to `[6, 8]` gives `[10, 8]`, and the
-- addition happens in the type rather than in a runtime shape check.
def model : nn.Builder (nn.Sequential [16] [10, 8]) := do
let left ← nn.Sequential![nn.linear 16 (4 * 8), nn.reshape [4 * 8] [4, 8]]
let right ← nn.Sequential![nn.linear 16 (6 * 8), nn.reshape [6 * 8] [6, 8]]
pure (nn.concatBranches left right)