Repository navigation
Quantization - #37
Merged
Merged
Quantization#37
Conversation
…M/ADAMW gradient path needed new MatMulAccum/SumRowsAccum backend methods (the old MatMul/SumRows only worked by accident when T==AccumT), and GetRequiredElements()'s arena-capacity math assumed 16-element padding, which undercounts for any T smaller than 4 bytes. - Phase 3 (hwy::float16_t, correctness-first wrapper per your choice): replaced ~79 generic cast sites in CPUBackendT.cpp with hwy::ConvertScalarTo for cross-compiler portability; fixed a CMakeLists.txt gap (hwy was linked PRIVATE, breaking any external consumer of the now-public hwy::float16_t type). - Phase 4 (3 posit formats — Posit4_0Value, Posit4_1Value, Posit4_2Value): mechanically identical to Fp4E2M1Value's proven pattern, as the plan predicted — all three compiled and passed verification on the first try. - Verification harness (tests/tFullPCLayerTemplatingVerify.cpp) grew to 15 passing checks across all phases; unregistered from CMakeLists.txt now that every gate has passed, with the source file kept on disk per this project's existing convention for one-time verification tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Three things, all discovered/fixed while validating whether 4-bit quantized
FullPCNtraining works end to end, then extending Python access to match:Fixed a real gradient-killing bug in the
fp4_e2m1quantized path.derivScratch/feedbackError/proj/feedbackScratchwereT*(the lossy 4-bit storage type), but sigmoid's derivative range ([0, 0.25]) is entirely belowFp4E2M1Value's smallest nonzero grid magnitude (0.5) -- every derivative silently rounded to0, so gradient flow through the hidden layer was a complete no-op at any learning rate. Changed those four buffers toAccumT(added 5 new mixed-precision backend primitives to support it:MatMulAccumMixed,AxpyIntoAccum,AxpyIntoFromAccum,ActivationDerivativeIntoAccum,MultiplyIntoAccum). Verified bit-exact against the pre-fixfloat32baseline (the fix must not changeT == AccumTbehavior at all), and verified via direct belief/weight-movement diagnostics thatfp4_e2m1now trains for real -- separately proven via a controlled toy classification task reaching 100% accuracy, matchingfloat32, onceinference_rateis scaled for sigmoid's smaller derivative magnitude. (Real-task accuracy on MNIST at this storage width is still an open question -- not what this PR is about; the mechanism is proven correct, hyperparameter search for a specific downstream task is separate work.)Added Python bindings for
ModelIOsave/load, across every bound network type (FullPCNetwork_f32/f64/fp4_e2m1, the originalFullPCNetwork,DiscriminativePCNetwork,SimplePCNetwork,GaussSeidelPCNetwork,DirectKPPCNetwork,FullConvPCNetwork,ConvPCNetwork,SimpleConvPCNetwork) -- there was previously no way to persist a trained model from Python at all. Found and fixed a real bug inModelIO.hitself while doing this:Save()/Load()never called theSyncStateDictFromDevice()/SyncStateDictToDevice()hooks thatLayer.h's own doc comments say are required, soFullConvPCNetwork.save()/.load()on a CUDA-device network would have silently written/read stale host staging buffers instead of the real device weights.Fixed a systemic crash bug found while checking whether a device-resident
FullPCLayerT(the quantization family) could even exist: every network constructor taking adeviceparameter (8 of them --FullPCNetwork,FullConvPCNetwork,ConvPCNetwork,SimpleConvPCNetwork,GaussSeidelPCNetwork,DirectKPPCNetwork,SimplePCNetwork,FullPCNetworkT) was markednoexceptwhile its body callsCreateBackend/CreateBackendT, which can throw for an unsupported GPU request. Throwing out ofnoexceptisstd::terminate()-- confirmed live:FullPCNetwork_fp4_e2m1(4, device='cuda')from Python crashed the whole interpreter (SIGABRT) instead of raising. Removednoexceptfrom all 8; verified each now raises a clean, catchableRuntimeErrorinstead.Added Python bindings for the quantization formats themselves (
Fp4E2M1Value,Posit4_0/1/2Value, theirdecode_*/encode_*functions, and their grid tables as module constants) -- previously only reachable transitively through aFullPCNetwork_fp4_e2m1instance'strain_step()/predict(), never directly inspectable or callable.Closes #
Type of change
(Not checking "Breaking change": removing
noexceptfrom 8 constructors is source-compatible for every existing caller -- it only expands what's allowed to happen (a clean exception instead of a crash), it doesn't change any call site. Flagged for reviewers below anyway, since it's a signature-level change worth knowing about.)Testing
python build.py Releasebuilds andDeepityTestspassespython build.py Debugbuilds andDeepityTestspasses (required for anything correctness-sensitive)pyrightpasses ondeepity/(11 files, 0 errors/0 warnings) -- see reviewer notes onexamples/belowPlatform(s) and compiler(s) tested: Fedora 44, GCC 16.2.1 (
/usr/bin/c++), Ninja generator, CUDA toolkit not present on this machine (so the CUDA-path fixes are verified via clean-exception behavior, not a real GPU run -- see reviewer notes).Beyond the two
build.pypasses above:ninja -t clean+ rebuild, 267/267 targets) -- zero errors, zero new warnings (all pre-existing, confined toCPUBackendT.h's deliberate "out of scope"throwstubs for unsupported T/AccumT combinations).fp4_e2m1round-trip through the high-levelFullPCNPython wrapper (not just the raw bound class): train → save → load into a fresh instance, weights match exactly.RuntimeErrorwith a clear message.device='cuda'through the fullFullPCN(..., dtype='fp4_e2m1', device='cuda')path raises cleanly; process stays alive (previously SIGABRT).encode(-0.0)doesn't set the sign bit, matching the documented C++ behavior).Performance impact
Not applicable to the Python-binding/bug-fix work (items 2-4). Item 1's new mixed-precision backend primitives (
MatMulAccumMixedetc.) only run whenT != AccumT(i.e. only for the quantized formats) -- the existingT == AccumT(float/double) fast paths were verified bit-exact and untouched; no perf regression expected or measured for those.Checklist
[Unreleased](for any user-facing change) -- not done yet, flagging rather than guessing at wording.save()/load()/quantization-format API surface..clang-formatstyle -- for files fully authored this session (QuantFormatBindings.{h,cpp},tBitPackedGemmPrototype.cpp, theModelIO.hdiff): ranclang-format -i, now 0 diff.NetworkBindings.cpp/LayerBindings.cpphave pre-existing formatting drift from before this PR (confirmed:main's own versions of those files have 1000+ line clang-format diffs already) -- not reformatted here, since doing so would bloat this diff with unrelated whole-file reformatting.Notes for reviewers
examples/mnist_fullpcn.pywas not actually type-checked. The project's ownpyrightconfig.jsonglobally excludesexamples/, and that exclude wins over an explicit file argument on the pyright command line -- confirmed directly (pyright examples/mnist_fullpcn.pyreports "files analyzed: 0"). Not something this PR fixes; flagging since the PR template calls outexamples/as something to check.noexceptfix is verified by the exception path (every constructor now raises cleanly instead of crashing), but I could not verify the success path (an actualCUDABackendconstructing and running) on this machine.FullPCLayerT(the quantization family) has no CUDA backend implementation at all yet regardless (CreateBackendTunconditionally throws forDEVICE_GPU) -- this PR does not add one, it only makes the rejection clean instead of fatal.ConvPCLayer/SimpleConvPCLayer's CUDA save/load safety is unverified. UnlikeFullConvPCLayer, they never gotSyncStateDictFromDevice/ToDeviceoverrides -- theModelIO.hfix in this PR (item 2) handles sync correctly where those hooks exist and is a no-op where they don't, so it doesn't make this worse, but it also doesn't prove those two layer types are CUDA-safe to save/load. Separate, pre-existing gap.tests/tBitPackedGemmPrototype.cppis included -- a standalone, deliberately-unintegrated proof-of-concept for generic sub-byte bit-packing (not wired intoCMakeLists.txt, compiles standalone viag++ -O2 -std=c++20). It's parked exploratory work, not a feature this PR claims to ship; happy to drop it from this PR if you'd rather it not ride along.v(momentum) andadjoint(ePC) asT*, matching the original templating plan's own stated scope -- momentum/ePC precision at low bit-width wasn't separately validated here (both are off by default).