Fix EK-FAC gradient store overflow above ~537M tracked params#317
Fix EK-FAC gradient store overflow above ~537M tracked params#317davidoj wants to merge 2 commits into
Conversation
The EK-FAC influence pipeline (`bergson ekfac`) stores the query gradient unprojected in full model space as a numpy structured record, one field per tracked module. numpy keeps a structured dtype's record itemsize in a C int, so the record cannot exceed 2**31 - 1 bytes; at fp32 this overflows once a model has more than 2**31 / 4 = ~537M tracked params, raising "integer won't fit into a C int" in apply_hessian (load_gradients / create_index) before any work is done. This is hardware-independent. The structured record layout is byte-identical to a flat 2D (num_grads, total_grad_dim) memmap, whose shape is intp-sized and therefore has no such ceiling (the score path already reads the same bytes with structured=False). For oversized stores only, create_index and load_gradients now serve the data as a flat 2D memmap wrapped in FlatGradientView, a small adapter that preserves the structured field-access interface the codebase uses (view[module], view.dtype.names, len, flush). Sub-cap models are unaffected and still get a genuine structured memmap. gradients_loader in the FAISS path is routed through the same overflow-safe loader. Adds CPU-only regression tests in tests/test_data.py: an oversized store (> 2**31/4 fp32 elements, allocated sparsely) now round-trips through create_index + load_gradients(structured=True), and a sub-cap store still returns a structured np.memmap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01866exUnUq9z5uPTFofcmmj
Please merge whenever makes sense to you to maximize development speed. Long term it seems like we could maybe just pass structured=False here rather than writing a bunch of new code unless we need the structure for something. |
|
@claude are you still wired up here haha |
|
Claude finished @luciaquirke's task in 15s —— View job 👋 Yep, still here!Just confirming I'm wired up and watching this PR. Nothing to act on from your "haha" ping specifically — but happy to help with anything on this PR. A couple of things worth flagging while I'm here:
Just say the word ( |
|
@claude I'm happy with this work, can you fix the typecheck so we can merge |
|
Claude encountered an error after 2m 11s —— View job Working on it
|
Narrow the three unstructured-layout call sites (load_gradient_dataset, builder, score) to np.memmap via isinstance asserts, since with_structure/ structured=False never takes the overflow path. Widen write_grads to accept np.memmap | FlatGradientView, since gradient collection uses with_structure= True and oversized models legitimately flow a FlatGradientView through it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Fix EK-FAC gradient store overflow above ~537M tracked params
Problem
bergson ekfac(the EK-FAC influence pipeline) cannot attribute any model withmore than ~537M tracked-linear parameters. The apply-Hessian stage loads a structured memmap which contains unprojected grads so is beyond the structured memmap size limit and aborts with
ValueError: integer won't fit into a C int.So
sum(grad_sizes.values())equalsthe full per-example gradient dimension, and at fp32 the record crosses
2**31bytes once the model has more than
2**31 / 4 = 536,870,912tracked params.The exact failing calls are in the apply stage:
bergson/hessians/apply_hessian.py:79—load_gradients(self.gradient_path)(structured) maps the unprojected query gradient → overflow.
bergson/hessians/apply_hessian.py:83—create_index(...)(structured,default) allocates the transformed-gradient output → overflow.
Fix
You can avoid the crash by loading with the flag structured=False.
For oversized stores only,
create_index/load_gradientsnow serve the dataas a flat 2D memmap wrapped in a small field-access view that preserves the
structured interface the rest of the codebase uses. Small models are completely
unaffected — they still get a genuine numpy structured memmap.
bergson/data.py— addedFlatGradientView: exposesview[module_name](returns the module's
(num_grads, size)column block as a live, writable viewinto the memmap),
view.dtype.names,len(view), andview.flush().bergson/data.py— added_structured_dtype_fits();create_index(
with_structure=True) andload_gradients(structured=True) fall back toFlatGradientViewwhen the record would overflow the C-int cap, and areotherwise unchanged. The persisted
info.jsonis unchanged (still records thestructured
dtype,grad_sizes, andbase_dtype), so existing stores andmetadata readers keep working.
bergson/query/faiss_index.py—gradients_loadernow routes throughload_gradients(so the approximate-query path is overflow-safe too), and theloader loop skips the
structured_to_unstructuredcopy for an already-flatview.
No public API or signature changes.
apply_hessian's field-access reads/writes(
mmap[k],grad_buffer[k][:] = ...,len,.flush) work unchanged againstFlatGradientView.Test
tests/test_data.py(CPU-only, no CUDA, no model):test_large_gradient_store_roundtrips— builds a store whose combined fp32record exceeds
2**31bytes (> 2**31 / 4float32 elements across twomodules), writes sentinels at each module's start/end via structured field
access, and asserts they round-trip through
create_index+load_gradients(structured=True). It also cross-checks the flat(
structured=False) view of the same bytes to confirm the byte-identicallayout. The store is a sparse file (
truncateonly sets the size), so thetest touches a handful of pages instead of materializing ~2 GB.
test_small_gradient_store_stays_structured— asserts sub-cap stores stillreturn a genuine
np.memmapstructured array with unchanged behavior.Before this fix the oversized case raises
ValueError: integer won't fit into a C intincreate_index; after, it round-trips. The previoustest_data.py(which asserted the failure as expected behavior) is replaced accordingly.
Verified in the pinned venv (numpy 2.5.1, torch 2.9.1+cu128):
Scope and limitations
~537M params; it does not change results for models below the old ceiling.
address: the query gradient is materialized unprojected in full model
space on the GPU during
apply_hessian(and again when reading it back forscoring). That is an architectural memory ceiling (order ~7B params on an 80 GB
GPU, and it also implies a large host/disk footprint), independent of the
numpy itemsize limit fixed here. Projecting or streaming the unprojected query
gradient to lift that ceiling is out of scope for this bug fix.
FlatGradientViewimplements only the subset of the structured-arrayinterface Bergson actually uses (field access,
dtype.names,len,flush,and integer/slice row indexing on the underlying flat array). It is not a
general drop-in for every numpy structured-array operation.
🤖 Generated with Claude Code
https://claude.ai/code/session_016rrSNbhfYPhfuxuBrdJeFG