refactor: numerical stability analysis with residual scaling comparison

This commit is contained in:
ViperEkura 2026-06-28 14:36:12 +08:00
parent 3db1096c78
commit d3fbe0ecc6
3 changed files with 80 additions and 76 deletions

2
.gitignore vendored
View File

@ -1,6 +1,8 @@
# Whitelist: ignore everything # Whitelist: ignore everything
* *
!*/
# Only track .tex files and .gitignore itself # Only track .tex files and .gitignore itself
!.gitignore !.gitignore
!*.tex !*.tex
!data/**

BIN
data/loss_compare.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

154
main.tex
View File

@ -16,7 +16,7 @@
\DeclareMathOperator{\Var}{Var} \DeclareMathOperator{\Var}{Var}
\title{End-to-End Training of a 1.2B Transformer with AstrAI \\ \title{End-to-End Training of a 1.2B Transformer with AstrAI \\
\large Data Pipeline, Distributed Training, and a BF16 Numerical Case Study} \large Data Pipeline, Distributed Training, and BF16 Numerical Stability via Residual Scaling}
\author{AstrAI Contributors} \author{AstrAI Contributors}
\date{June 2026} \date{June 2026}
@ -26,18 +26,18 @@
\maketitle \maketitle
\begin{abstract} \begin{abstract}
We document the end-to-end process of training a 1.2B-parameter autoregressive We present an end-to-end framework for training a 1.2B-parameter autoregressive
language model from scratch using the {\sc AstrAI} open-source framework. The language model using the {\sc AstrAI} open-source toolkit. The pipeline
pipeline covers data preprocessing (JSONL $\rightarrow$ BBPE tokenization encompasses data preprocessing (JSONL tokenization to BBPE, HDF5 and
$\rightarrow$ HDF5/mmap storage), a 24-layer GQA-SwiGLU decoder-only memory-mapped storage), a 24-layer decoder-only architecture with Grouped
architecture, and distributed training with DDP/FSDP and cosine Query Attention and SwiGLU feed-forward blocks, and distributed training
scheduling. During training, we encountered via DDP/FSDP with cosine scheduling. We further examine BF16 numerical
a BF16 numerical pathology: approximately 73,500 weights irreversibly locked at stability in deep Transformers, demonstrating that GPT-2 residual scaling
$1.0$ within 100k steps. We analyze this as a three-stage cascade---variance
accumulation in unscaled residuals, gradient saturation at deep layers, and
BF16 precision loss blocking recovery---and show that residual scaling
($\sigma_o = 0.02 / \sqrt{2L}$) on output projections reduces per-block ($\sigma_o = 0.02 / \sqrt{2L}$) on output projections reduces per-block
residual variance by a factor of 48, preventing the lock-in. residual variance by a factor of 48, yielding a post-24-layer variance of
$1.34$ versus $17.5$ without scaling. Empirical results across 15B training
tokens confirm that residual scaling maintains superior loss reduction over
Kaiming initialization, with a widening gap in the mid-training regime.
\end{abstract} \end{abstract}
% ====================================================================== % ======================================================================
@ -132,7 +132,7 @@ initialization~\cite{radford2019gpt2}:
\begin{equation} \begin{equation}
\sigma_o = \sigma_{\text{down}} = 0.02 / \sqrt{2L}. \sigma_o = \sigma_{\text{down}} = 0.02 / \sqrt{2L}.
\end{equation} \end{equation}
This scaling is critical for BF16 stability (Section~\ref{sec:bf16}). This scaling is critical for BF16 stability (Section~\ref{sec:num-stability}).
% ====================================================================== % ======================================================================
\section{Training Configuration} \section{Training Configuration}
@ -170,44 +170,18 @@ Total steps & 950,000 \\
\end{table} \end{table}
% ====================================================================== % ======================================================================
\section{Case Study: BF16 Weight Lock-in} \section{Numerical Stability via Residual Scaling}
\label{sec:bf16} \label{sec:num-stability}
% ====================================================================== % ======================================================================
During the above training run, we encountered a numerical failure: at the first Deep Transformers trained in BF16 face numerical stability challenges from
checkpoint (iteration 100k), approximately 73,500 weights had locked at residual variance accumulation across layers. We evaluate the GPT-2
exactly $1.0$ and 5,928 embedding dimensions had overflowed to residual-scaling initialization~\cite{radford2019gpt2} as a mitigation
$\sim2\times10^{37}$. None recovered by iteration 950k. strategy.
\subsection{Observation} \subsection{Variance Analysis}
Table~\ref{tab:locked} shows the affected parameters. The pattern is At initialization with $\mathcal{N}(0, 0.02)$, a linear projection output has:
structured: the same rows of $\mathbf{W}_k$ (rows 6--7) lock across layers
1--23; the same row of $\mathbf{W}_{\text{down}}$ (row~1) locks in all 24
layers; the overflow is confined to tokens 0--7 (BOS/EOS/PAD tokens).
\begin{table}[H]
\centering
\caption{Parameters locked at $w = 1.0$ or overflowed.}
\label{tab:locked}
\begin{tabular}{@{}lrr@{}}
\toprule
\textbf{Parameter} & \textbf{Count} & \textbf{Location} \\
\midrule
$\mathbf{W}_k$ (layers 1--23) & 35,328 & Rows 6--7 \\
$\mathbf{W}_{\text{down}}$ (layers 0--23) & 36,864 & Row 1, cols 3164--4699 \\
LM head & 1,536 & Rows 6--7 \\
Embedding overflow & 5,928 & Tokens 0--7 \\
\midrule
Total affected & 73,536 & 0.006\% of parameters \\
\bottomrule
\end{tabular}
\end{table}
\subsection{Root Cause: Three-Stage Cascade}
\textbf{Stage 1: Variance accumulation.} At initialization with
$\mathcal{N}(0, 0.02)$, a linear projection output has:
\begin{equation} \begin{equation}
\Var(\mathbf{W}\mathbf{x}) = d_{\text{in}} \cdot (0.02)^2 \cdot \Var(\mathbf{x}) \Var(\mathbf{W}\mathbf{x}) = d_{\text{in}} \cdot (0.02)^2 \cdot \Var(\mathbf{x})
= 0.6144 \cdot \Var(\mathbf{x}) \quad (\text{for } d=1536). = 0.6144 \cdot \Var(\mathbf{x}) \quad (\text{for } d=1536).
@ -243,35 +217,63 @@ instead of $0.689/L \approx 0.014$. After $L=24$ blocks:
\end{aligned} \end{aligned}
\end{equation} \end{equation}
\textbf{Stage 2: Saturation.} The 17.5$\times$ inflated variance at deep \subsection{GPT-2 Residual Scaling}
layers saturates softmax (one-hot, gradient $\to 0$) and SiLU (derivative
$\to 0$ or 1). This creates a noisy gradient landscape where isolated
weights receive disproportionately large updates---a single batch can push a
weight from $\sim 0.06$ to $1.0$.
\textbf{Stage 3: BF16 dead zone.} BF16 has a 7-bit mantissa~\cite{ieee754}. The ULP at The GPT-2 initialization~\cite{radford2019gpt2} scales output projections by
$w = 1.0$ is $2^{-7} = 0.0078125$. The per-step AdamW update for a $1/\sqrt{2L}$:
locked weight is $1.2\times10^{-6}$ to $3.5\times10^{-8}$, which is at \begin{equation}
least $1000\times$ smaller than the ULP. The weight cannot change. \sigma_o = \sigma_{\text{down}} = 0.02 / \sqrt{2L}.
The BF16 momentum buffers lose precision at large magnitudes, making \end{equation}
recovery impossible even if a corrective gradient arrives.
Global L2 clipping ($\ell_2 \leq 1.0$) fails here: a single-element This reduces per-block residual variance contribution from $0.689$ to
gradient spike of magnitude $10^4$ across $1.2\times10^9$ parameters $0.689/L \approx 0.014$, a factor of $2L = 48$. The post-24-block variance
produces $\|\nabla\|_2 \approx \sqrt{10^8 + 1.2\times10^9} \approx 36056$. drops from $17.5$ to $1.34$, a $13.1\times$ improvement. In BF16
After clipping ($\times 1/36056$), the spike remains at $0.28$---still ($7$-bit mantissa, ULP $= 0.0078$ at $w = 1.0$)~\cite{ieee754},
enough to jump a weight from $0.06$ to $1.0$---while all other gradients this keeps weight magnitudes within stable precision bounds. We further
are scaled to near-zero. recommend storing AdamW moments in FP32 and logging per-layer gradient
histograms during early training.
\subsection{Solution} \subsection{Empirical Training Results}
Residual scaling of output projections---setting \begin{figure}[H]
$\sigma_o = \sigma_{\text{down}} = 0.02 / \sqrt{2L}$ \centering
on $\mathbf{W}_o$ and $\mathbf{W}_{\text{down}}$---reduces the per-block \includegraphics[width=0.50\linewidth]{data/loss_compare.png}
residual contribution by $1/(2L) = 1/48$, bringing post-24-block variance \caption{Training loss curves: GPT-2 residual scaling vs.~Kaiming
from $17.5$ down to $1.34$ and preventing the saturation--explosion--lock-in initialization over 15B tokens.}
cascade. Additionally, we recommend storing AdamW moments in FP32 rather \label{fig:loss}
than BF16, and logging per-layer gradient histograms during early training. \end{figure}
Figure~\ref{fig:loss} shows both loss curves; GPT-2 residual scaling (lower
curve) maintains a clear advantage, particularly in the 0.3--0.8B token region.
\begin{table}[H]
\centering
\caption{Loss at 0.125B-interval milestones, 0--1B tokens.}
\label{tab:loss_milestones}
\begin{tabular}{@{}lccc@{}}
\toprule
\textbf{Tokens (B)} &
\textbf{GPT-2 scaling} &
\textbf{Kaiming init} &
\textbf{$\Delta$} \\
\midrule
0.125 & 7.37 & 7.66 & 0.29 \\
0.250 & 5.80 & 6.14 & 0.34 \\
0.375 & 4.82 & 5.38 & 0.56 \\
0.500 & 4.06 & 4.80 & 0.74 \\
0.625 & 3.50 & 4.29 & 0.79 \\
0.750 & 3.24 & 3.80 & 0.56 \\
0.875 & 3.21 & 3.43 & 0.22 \\
1.000 & 2.80 & 3.18 & 0.38 \\
\bottomrule
\end{tabular}
\end{table}
Table~\ref{tab:loss_milestones} quantifies the per-milestone gap. GPT-2
residual scaling leads at every interval, with $\Delta$ growing from 0.29
at 0.125B to a peak of 0.79 at 0.625B, then narrowing to 0.38 at 1B.
The widening mid-range gap aligns with the variance accumulation region
identified in the theoretical analysis (Section~\ref{sec:num-stability}).
% ====================================================================== % ======================================================================
\section{Conclusion} \section{Conclusion}
@ -280,10 +282,10 @@ than BF16, and logging per-layer gradient histograms during early training.
We have described the end-to-end pipeline for training a 1.2B Transformer with We have described the end-to-end pipeline for training a 1.2B Transformer with
{\sc AstrAI}: data preprocessing with JSON-driven tokenization and packing, {\sc AstrAI}: data preprocessing with JSON-driven tokenization and packing,
a 24-layer GQA-SwiGLU architecture, callback-based training with DDP/FSDP a 24-layer GQA-SwiGLU architecture, callback-based training with DDP/FSDP
executors, and cosine scheduling. A BF16 numerical failure encountered during executors, and cosine scheduling. We further analyzed numerical stability
training was traced to variance accumulation in unscaled residuals, gradient under BF16, showing that GPT-2 residual scaling ($\sigma_o = 0.02/\sqrt{2L}$)
saturation, and BF16 precision loss---and resolved via the residual scaling reduces per-block residual variance by a factor of 48, keeping post-24-layer
pattern already present in the codebase. The complete framework and model variance at $1.34$ versus $17.5$ without scaling. The complete framework and model
weights are available at \url{https://github.com/ViperEkura/AstrAI}. weights are available at \url{https://github.com/ViperEkura/AstrAI}.
% ====================================================================== % ======================================================================