Skip to main content

Shapes, layers & the NPZ format

The previous page explained that a model is a pile of numbers organised into layers. This page makes that concrete: what a layer looks like as an array, what shape means, and how BranchKey packages everything into a single .npz file.

What "shape" means

A single number is just a number: 0.42.

But model weights are usually arrays — grids of numbers. The shape of an array is simply its dimensions: how many rows, columns, and so on. It's the same idea as saying a spreadsheet is "100 rows by 5 columns".

Some examples:

ThingShapeRead as
A single number (a scalar)()just one value
A list of 32 numbers (a vector)(32,)32 values in a row
A table, 64 rows × 32 cols (a matrix)(64, 32)64 × 32 = 2048 values
A convolution filter bank(64, 32, 3, 3)a 4-dimensional grid of values

In NumPy you can always ask an array its shape:

import numpy as np

layer = np.zeros((64, 32))
print(layer.shape) # (64, 32)
print(layer.size) # 2048 (total number of values)

Shapes matter for BranchKey for one reason: your shapes and every other participant's shapes must match, layer for layer. You are all averaging the same model architecture, so layer 0 must be the same shape for everyone, layer 1 the same shape for everyone, and so on. If they don't match, the numbers can't be averaged together. (See the FAQ if you hit a shape-mismatch error.)

A model is a list of arrays

When you extract a model's weights, you get a list of arrays — one array per layer, each with its own shape. For a small network it might look like this:

parameters = [
weights_layer_0, # shape (32, 1, 3, 3) — first conv layer
bias_layer_0, # shape (32,)
weights_layer_1, # shape (64, 32, 3, 3) — second conv layer
bias_layer_1, # shape (64,)
# ... and so on
]

The key points:

  • It is an ordered list. Order is part of the meaning — layer 0 is the first step, layer 1 the second. You must keep the order consistent.
  • Each entry is a NumPy array with its own shape.
  • Both "weight" arrays and "bias" arrays are just entries in the list — BranchKey treats every entry uniformly as a numbered layer.

How BranchKey packages it: the .npz file

BranchKey uses NumPy's standard .npz format — a single compressed file that holds several named arrays. You do not build this by hand; the client library's save_weights does it for you. But it helps to know what's inside.

When you call:

weights_file = client.save_weights(
file_path="model_weights",
weighting=1000, # a single number — see below
parameters=parameters, # your list of arrays
)

the resulting model_weights.npz contains:

{
"weighting": array([1000.0]), # how much this update counts
"layer_0": <your first array>,
"layer_1": <your second array>,
"layer_2": <your third array>,
# ... one "layer_N" per entry in your parameters list, in order
}

So the two things inside every upload are:

  1. weighting — a single number saying how much influence this update should have when averaged with everyone else's. Usually you set it to your number of training samples, so that a participant who trained on more data pulls the average a little more strongly. It is not part of your model — it is metadata about your update.
  2. layer_0layer_n — your model's weights, one named entry per layer, in order.

What the aggregated result looks like

After BranchKey combines everyone's uploads, you download an aggregated .npz. It contains only the layerslayer_0, layer_1, … — and no weighting field (the weighting was already used up in computing the average).

# An aggregated result you download:
{
"layer_0": <averaged array>,
"layer_1": <averaged array>,
# ...
}

Each layer_N has the same shape you uploaded — it is the average of everyone's layer_N. That is why it drops straight back into your model. The next page shows exactly how.

layer_10 vs layer_1

Layer names are text, so naïve alphabetical sorting puts layer_10 right after layer_1. Always sort by the number, not the string, when reading them back:

layer_keys = sorted(npz.files, key=lambda k: int(k.split("_")[1]))

Recap

  • Shape = the dimensions of an array of numbers.
  • A model's weights are an ordered list of arrays, one per layer.
  • save_weights packs them into a .npz as weighting + layer_0…layer_n.
  • The aggregated download contains only layer_0…layer_n, each the same shape you sent.