[fix](kt-kernel): fix AMX BF16 Full-FT expert gradients#2086
[fix](kt-kernel): fix AMX BF16 Full-FT expert gradients#2086Illumination111 wants to merge 18 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements Full Fine-Tuning (Full FT) support for KTransformers CPU/AMX MoE expert base weights (gate_proj, up_proj, down_proj). It establishes a complete data pipeline from Python SFT training to C++ AMX kernels, including autograd support, parallelized base weight gradient computation using AMX BF16 tiles, and an optimized re-quantization path that avoids full C++ object recreation. Additionally, it introduces GLM4 MoE architecture compatibility and replaces redundant expert weights in the model tree with zero-storage placeholders to prevent double-counting of parameters. The review feedback highlights a memory management issue in kt-kernel/python/sft/amx.py where using del old_moe fails to release the old C++ MOE object because self.moe still retains a reference. The reviewer suggests explicitly setting self.moe = None to ensure immediate garbage collection and prevent memory spikes.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| # Release old C++ MOE object before creating a new one to avoid memory leak | ||
| old_moe = getattr(self, "moe", None) | ||
| if old_moe is not None: | ||
| del old_moe | ||
| import gc | ||
| gc.collect() |
There was a problem hiding this comment.
Using del old_moe only deletes the local variable reference, but self.moe still holds a reference to the old C++ MOE object. Consequently, the reference count of the old object does not drop to zero, and gc.collect() cannot release its memory before the new self.moe is instantiated. This causes both the old and new MOE objects to temporarily coexist in memory, which can lead to a significant memory spike and potential Out-Of-Memory (OOM) errors. Setting self.moe = None first will properly release the reference.
| # Release old C++ MOE object before creating a new one to avoid memory leak | |
| old_moe = getattr(self, "moe", None) | |
| if old_moe is not None: | |
| del old_moe | |
| import gc | |
| gc.collect() | |
| # Release old C++ MOE object before creating a new one to avoid memory leak | |
| if getattr(self, "moe", None) is not None: | |
| self.moe = None | |
| import gc | |
| gc.collect() |
| old_moe = getattr(self, "moe", None) | ||
| if old_moe is not None: | ||
| del old_moe |
There was a problem hiding this comment.
Similar to the previous reference counting issue, del old_moe only deletes the local variable name, leaving self.moe still referencing the old C++ MOE object. To ensure the old object is immediately released and its memory can be reclaimed before loading the new weights, explicitly set self.moe = None instead.
| old_moe = getattr(self, "moe", None) | |
| if old_moe is not None: | |
| del old_moe | |
| if getattr(self, "moe", None) is not None: | |
| self.moe = None |
Coarsen base-weight gradient work from individual output tiles to fixed-intermediate strips so each task reuses packed panels across the hidden dimension. Keep aligned thread-local BF16 panels across tasks and retain FP32 AMX accumulator tiles for the full K reduction. Gate and up run separate K passes while sharing the packed input panel. On the matched Qwen3-30B-A3B 1-GPU test, stable Full-FT backward drops from 9.281s to 6.792s (-26.82%), step time drops from 19.252s to 16.140s, and TPS rises from 212.76 to 253.78. The LoRA-only backward control changes by -2.74%. Validated with clang-format, the Release AMX/CUDA extension build, TP1/TP2 reference gradients across boundary token counts, and the 15-step Full-then-LoRA performance run.
Use one expert-aggregated tile driver for AVX512-BF16 and AMX base-weight gradients, and pack updated full-precision weights directly into TP BufferB layouts without temporary partitions. Add worker-local profiling and focused dWeight/strided-repack coverage.
Retain the first CPU MoE forward state across non-reentrant checkpoint recomputation, write BF16 activations directly into the backward cache, and reduce dWeight packing and gradient-clear traffic. Extend staged profiling and cover checkpoint reuse plus AMX/AVX dWeight paths.
What does this PR do?
Fixes the AMX BF16 Full-FT path for CPU MoE expert base weights and removes a CPU-threading performance trap in SFT.
Root cause
dYsnapshot needed by weight-gradient computation could be overwritten.OMP_NUM_THREADS=1for a GPU launch, serializing CPU-side gradient accumulation, fused AdamW, zeroing, and requantization work.Changes
Parameterthrough C++ gradient buffers, autograd, optimizer update, and requantization.dY.ACCELERATE_KT_OMP_NUM_THREADShas highest precedence; a user-providedOMP_NUM_THREADS > 1remains respected. This host resolves to 96.Validation
pytest -q kt-kernel/test/per_commit/test_sft_omp_threads.py: 4 passed.git diff --check: passed.Known validation gap
The current end-to-end evidence covers finite/nonzero gradients, parameter updates, loss reduction, and stable performance. A full elementwise comparison against a PyTorch reference, including TP configurations, remains follow-up validation.
Before submitting