NumPy structured arrays¶
A normal NumPy array has one value type, such as a 32-bit token ID. A structured array has named fields in each element, like a table row with a fixed binary layout. Each field can have a different type, but every row takes the same number of bytes.
See NumPy's structured arrays guide for details on defining fields, accessing values, and controlling memory layout.
Define and access fields¶
import numpy as np
row_dtype = np.dtype([("tokens", "<i4"), ("document_ids", "<u4"), ("loss_mask", "u1")])
rows = np.array([(10, 7, 1), (11, 7, 0), (12, 8, 1)], dtype=row_dtype)
assert rows.ndim == 1
assert rows.shape == (3,)
assert row_dtype.itemsize == 9
assert rows[0]["tokens"] == 10
assert rows["document_ids"].tolist() == [7, 7, 8]
encoded = rows.tobytes()
restored = np.frombuffer(encoded, dtype=row_dtype)
assert np.array_equal(restored, rows)
<i4 is a little-endian signed four-byte integer, <u4 is an unsigned four-byte
integer, and u1 is an unsigned byte. With the default packed layout, these fields
take nine bytes per row. rows is one-dimensional even though it has three
fields. Its token field is an array view with one token every nine bytes.
np.frombuffer interprets existing bytes using a dtype without copying them.
Keep the backing buffer alive while using its views, and use .copy() when you
need independent writable memory. Bytes-backed arrays are read-only.
Use fields with GuideLoom¶
StreamNumpyFormat packs a dictionary of equally sized one-dimensional arrays
into a structured array. On reading, it returns a dictionary of field views.
You can use ordinary arrays in your preprocessing and collator; the format
handles the structured representation. See the stream example.
A custom StreamStorageFormat may instead return a structured array directly.
Choose plain stream fields for small, fixed-size values such as masks or IDs.
Use stream metadata for variable-length payloads
such as lists of concepts.
Preserve the binary layout¶
Keep field names, order, types, and byte order consistent between writing and
reading. Filesystem stream storage normalizes dtypes to little endian; using
explicit little-endian types makes your schema clear. Application payloads in
DocumentStorage or document metadata use the encoding you define.
Do not use object fields for raw storage: they hold Python object references,
not a portable serialization of those objects. Field views can also be strided;
a tensor conversion or collator may need a contiguous copy. np.stack creates
a new batch array, so a buffer-based storage read does not make the whole
training pipeline zero-copy.