-
Notifications
You must be signed in to change notification settings - Fork 1
feat: precision verification framework + PP dump fix — diagnostic, comparison, and baseline tools for ON/OFF prefix-sharing #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e963310
16877df
3260dc6
f8bded1
ab4943f
8794180
f6a5dd5
d9279f6
d7aab26
7f80253
c0de097
6b122af
717d994
c86ea12
f5e9d09
64fc4e4
f4be6b5
badaba3
da39661
eb9ebc3
ea76466
a4f3495
fad4c9d
b61e353
dc70f97
cc40ce6
04d06c6
68b0c44
c7dcc29
2235b1c
bfc06fa
5e68962
4cd87dc
7aa39da
5b95d70
91282bd
2d5270e
e261dca
a429b5f
18d99c1
2742f45
ea68999
21dc6e1
31d3373
a10bb08
9de6f2b
da791d8
366d1ce
ecfac35
efe5f6b
649484d
9eefb4d
abf2949
ca37506
14f6fb5
ae48d97
716b416
8b8a042
b7fe400
bbdc5ac
861be13
c6b0ecd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1354,8 +1354,26 @@ def fit(self): | |
| max_prompt_length=self.config.data.max_prompt_length, | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 开发目标:在 fit() 训练循环前注入 synthetic baseline 数据
|
||
| max_response_length=self.config.data.max_response_length, | ||
| ) | ||
| # Inject baseline synthetic data when PREFIX_SHARING_BASELINE_SYNTHETIC env is set | ||
| baseline_json = os.environ.get("PREFIX_SHARING_BASELINE_SYNTHETIC", None) | ||
| if baseline_json: | ||
| from prefix_sharing.tools.inject_baseline_synthetic import patch_baseline_synthetic | ||
|
|
||
| _num_seq = int(os.environ.get("PREFIX_SHARING_BASELINE_NUM_SEQ", "1")) | ||
| _stack = int(os.environ.get("PREFIX_SHARING_BASELINE_STACK", "1")) | ||
| _seed = int(os.environ.get("PREFIX_SHARING_BASELINE_SEED", "42")) | ||
| patch_baseline_synthetic( | ||
| self, | ||
| json_path=baseline_json, | ||
| max_prompt_length=self.config.data.max_prompt_length, | ||
| max_response_length=self.config.data.max_response_length, | ||
| num_seq=_num_seq, | ||
| stack=_stack, | ||
| seed=_seed, | ||
| ) | ||
| #####prefix-sharing:inject data######## | ||
|
|
||
|
|
||
| current_epoch = self.global_steps // len(self.train_dataloader) | ||
|
|
||
| # perform validation before training | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -106,6 +106,15 @@ def prefix_attention( | |
| f"built expanded kv: expanded_key_shape={tuple(expanded_key.shape)}, expanded_value_shape={tuple(expanded_value.shape)}" | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 开发目标 A:修复 PP dump 中 attention output 永不 flush 的 bug(核心 bug 修复) 旧逻辑用
开发目标 B:新增 SKIP_RESTORE env var 诊断 probe
|
||
| ) | ||
|
|
||
| ##### [PS-diag] ON expanded K/V dump(build_kv 输出)##### | ||
| try: | ||
| from prefix_sharing.tools.diagnostic_dump_verl080 import dump_expanded_kv_on | ||
| dump_expanded_kv_on(layer_id, expanded_key, expanded_value, | ||
| attention_module.config.num_layers) | ||
| except Exception as exc: | ||
| print(f"expanded_kv dump failed: {exc}", flush=True) | ||
| ##### [PS-diag] ON expanded K/V dump end ##### | ||
|
|
||
| # 注意力计算 | ||
| core_attn_out = attention_backend.attention( | ||
| query, | ||
|
|
@@ -119,15 +128,15 @@ def prefix_attention( | |
| core_attn_out = core_attn_out.reshape(core_attn_out.size(0), 1, -1) | ||
| output = attention_module.linear_proj(core_attn_out) # (tensor, bias) tuple | ||
|
|
||
| ######### prefix-sharing diag: ON attention_output (per-layer) ######### | ||
| ##### [PS-diag] ON attention_output (per-layer) ##### | ||
| try: | ||
| from prefix_sharing.tools.diagnostic_dump import dump_attn_on | ||
| dump_attn_on(output[0], packed_seq_params, prefix_sharing_context.prefix_sharing_plan, | ||
| attention_module.layer_number, | ||
| attention_module.config.num_layers) | ||
| except Exception as e: | ||
| print(f"last-attn dump (ON) failed: {e}") | ||
| ######### prefix-sharing diag: ON attention_output (per-layer) ######### | ||
| except Exception as exc: | ||
| print(f"last-attn dump (ON) failed: {exc}") | ||
| ##### [PS-diag] ON attention_output (per-layer) end ##### | ||
| # --- | ||
|
|
||
| return output | ||
|
|
@@ -173,6 +182,32 @@ def _apply_positioned_rope( | |
| if q_pos_emb is not None and max_needed > q_pos_emb.shape[0]: | ||
| dim_half = q_pos_emb.shape[-1] // 2 | ||
| step = q_pos_emb[1:2, :, :, :dim_half] - q_pos_emb[0:1, :, :, :dim_half] | ||
|
|
||
| # ── [PS-diag] RoPE extrapolation: 验证相邻位置 step 是否恒定 ── | ||
| _layer = getattr(attention_module, 'layer_number', -1) | ||
| _num_extra = int(max_needed - q_pos_emb.shape[0]) | ||
| _step_vals = step.detach().flatten() | ||
| # 关键诊断: step01 == step12 ? (pos_emb[p] 对 p 是否线性) | ||
| _step01_vs_step12_diff = None | ||
| if q_pos_emb.shape[0] >= 3: | ||
| _step12 = q_pos_emb[2:3, :, :, :dim_half] - q_pos_emb[1:2, :, :, :dim_half] | ||
| _step12_vals = _step12.detach().flatten() | ||
| _diff = (_step_vals - _step12_vals).abs() | ||
| _step01_vs_step12_diff = _diff.max().item() | ||
| _is_linear = (_step01_vs_step12_diff is not None and _step01_vs_step12_diff < 1e-8) | ||
| print( | ||
| f"[PS][RoPE-extrapolate-Q] layer={_layer} " | ||
| f"max_needed={max_needed} precomputed={q_pos_emb.shape[0]} extra={_num_extra} " | ||
| f"step_min={_step_vals.min().item():.8f} step_max={_step_vals.max().item():.8f} " | ||
| f"step_mean={_step_vals.mean().item():.8f} step_std={_step_vals.std().item():.8f} " | ||
| f"step01_vs_step12_maxdiff={_step01_vs_step12_diff} is_linear={_is_linear}", | ||
| flush=True, | ||
| ) | ||
| # ── [PS-diag] end ── | ||
|
|
||
| # RoPE 具有线性性质:freqs[p] = p * inv_freq。 | ||
| # 因此可以通过 pos_emb[1] - pos_emb[0] 恢复出 step(即 inv_freq), | ||
| # 从而生成缺失的高位置频率向量。 | ||
| extra_positions = torch.arange( | ||
| q_pos_emb.shape[0], max_needed, | ||
| device=q_pos_emb.device, dtype=q_pos_emb.dtype, | ||
|
|
@@ -183,6 +218,28 @@ def _apply_positioned_rope( | |
| if k_pos_emb is not None and max_needed > k_pos_emb.shape[0]: | ||
| dim_half = k_pos_emb.shape[-1] // 2 | ||
| step = k_pos_emb[1:2, :, :, :dim_half] - k_pos_emb[0:1, :, :, :dim_half] | ||
|
|
||
| # ── [PS-diag] RoPE extrapolation: 验证相邻位置 step 是否恒定 ── | ||
| _layer = getattr(attention_module, 'layer_number', -1) | ||
| _num_extra = int(max_needed - k_pos_emb.shape[0]) | ||
| _step_vals = step.detach().flatten() | ||
| _step01_vs_step12_diff = None | ||
| if k_pos_emb.shape[0] >= 3: | ||
| _step12 = k_pos_emb[2:3, :, :, :dim_half] - k_pos_emb[1:2, :, :, :dim_half] | ||
| _step12_vals = _step12.detach().flatten() | ||
| _diff = (_step_vals - _step12_vals).abs() | ||
| _step01_vs_step12_diff = _diff.max().item() | ||
| _is_linear = (_step01_vs_step12_diff is not None and _step01_vs_step12_diff < 1e-8) | ||
| print( | ||
| f"[PS][RoPE-extrapolate-K] layer={_layer} " | ||
| f"max_needed={max_needed} precomputed={k_pos_emb.shape[0]} extra={_num_extra} " | ||
| f"step_min={_step_vals.min().item():.8f} step_max={_step_vals.max().item():.8f} " | ||
| f"step_mean={_step_vals.mean().item():.8f} step_std={_step_vals.std().item():.8f} " | ||
| f"step01_vs_step12_maxdiff={_step01_vs_step12_diff} is_linear={_is_linear}", | ||
| flush=True, | ||
| ) | ||
| # ── [PS-diag] end ── | ||
|
|
||
| extra_positions = torch.arange( | ||
| k_pos_emb.shape[0], max_needed, | ||
| device=k_pos_emb.device, dtype=k_pos_emb.dtype, | ||
|
|
@@ -191,6 +248,54 @@ def _apply_positioned_rope( | |
| extra_emb = torch.cat([extra_angles, extra_angles], dim=-1) | ||
| k_pos_emb = torch.cat([k_pos_emb, extra_emb], dim=0) | ||
|
|
||
| # ── [PS-diag] RoPE ground-truth probe ── | ||
| # 当 PREFIX_SHARING_DIAG_ROPE_GROUND_TRUTH=1 时,从 inv_freq 直接计算完整 | ||
| # 频率表(跳过线性外推),用于验证外推是否引入数值偏差。 | ||
| # 如果启用此开关后 ON vs OFF 结果一致,RoPE 外推就是根因。 | ||
| import os as _os_gt | ||
| if _os_gt.environ.get("PREFIX_SHARING_DIAG_ROPE_GROUND_TRUTH"): | ||
| _layer_gt = getattr(attention_module, 'layer_number', -1) | ||
| # 尝试从 attention_module 获取 inv_freq | ||
| _inv_freq = None | ||
| _rotary_emb = getattr(attention_module, 'rotary_pos_emb', None) | ||
| if _rotary_emb is not None: | ||
| _inv_freq = getattr(_rotary_emb, 'inv_freq', None) | ||
| if _inv_freq is None: | ||
| # fallback: 从 config 读取 RoPE 参数 | ||
| _cfg = attention_module.config | ||
| _dim = getattr(_cfg, 'hidden_size', 4096) // getattr(_cfg, 'num_attention_heads', 32) | ||
| _base = getattr(_cfg, 'rope_theta', 10000.0) | ||
| _inv_freq = 1.0 / (_base ** (torch.arange( | ||
| 0, _dim, 2, device=q_pos_emb.device if q_pos_emb is not None | ||
| else k_pos_emb.device).float() / _dim)) | ||
|
|
||
| _device = q_pos_emb.device if q_pos_emb is not None else k_pos_emb.device | ||
| _dtype = q_pos_emb.dtype if q_pos_emb is not None else k_pos_emb.dtype | ||
| _all_positions = torch.arange(0, max_needed, device=_device, dtype=torch.float) | ||
| _freqs = torch.outer(_all_positions, _inv_freq.to(_device).float()) # [max_needed, dim/2] | ||
| _emb_gt = torch.cat([_freqs, _freqs], dim=-1) # [max_needed, dim] | ||
| # reshape 匹配 pos_emb 维度 [max_needed, 1, 1, dim] | ||
| _emb_gt = _emb_gt.unsqueeze(1).unsqueeze(1).to(_dtype) | ||
|
|
||
| _extra_old = max(0, int(max_needed - ( | ||
| q_pos_emb.shape[0] if q_pos_emb is not None else max_needed))) | ||
| _is_q_truncated = q_pos_emb is not None and q_pos_emb.shape[0] < max_needed | ||
| _is_k_truncated = k_pos_emb is not None and k_pos_emb.shape[0] < max_needed | ||
|
|
||
| if q_pos_emb is not None: | ||
| q_pos_emb = _emb_gt | ||
| if k_pos_emb is not None: | ||
| k_pos_emb = _emb_gt | ||
|
|
||
| print( | ||
| f"[PS][RoPE-ground-truth] layer={_layer_gt} " | ||
| f"max_needed={max_needed} emb_shape={_emb_gt.shape} " | ||
| f"was_extrapolated_q={_is_q_truncated} was_extrapolated_k={_is_k_truncated} " | ||
| f"old_extra_count={_extra_old}", | ||
| flush=True, | ||
| ) | ||
| # ── [PS-diag] end ── | ||
|
|
||
| # Build kwargs for apply_rotary_pos_emb. | ||
| # Only include version-specific params when they're provided, | ||
| # to maintain backward compat with v070 (mcore <= 0.15.x). | ||
|
|
@@ -210,14 +315,14 @@ def _rope_kwargs(_unused_cu_seqlens: Any | None) -> dict[str, Any]: | |
|
|
||
| if q_pos_emb is not None: | ||
| q_freqs = q_pos_emb.index_select(0, positions) | ||
| ######### prefix-sharing diag: ON rope_freqs (per-layer) ######### | ||
| ##### [PS-diag] ON rope_freqs (per-layer) ##### | ||
| try: | ||
| from prefix_sharing.tools.diagnostic_dump import dump_rope_freqs_on | ||
| dump_rope_freqs_on(q_freqs, attention_module.layer_number, | ||
| from prefix_sharing.tools.diagnostic_dump import dump_rope_freqs | ||
| dump_rope_freqs(q_freqs, attention_module.layer_number, | ||
| attention_module.config.num_layers) | ||
| except Exception as e: | ||
| print(f"rope_freqs_on dump failed: {e}") | ||
| ######### prefix-sharing diag: ON rope_freqs (per-layer) ######### | ||
| except Exception as exc: | ||
| print(f"rope_freqs dump failed: {exc}") | ||
| ##### [PS-diag] ON rope_freqs (per-layer) end ##### | ||
| query = apply_rotary_pos_emb( | ||
| query.unsqueeze(1), | ||
| q_freqs, | ||
|
|
@@ -230,6 +335,20 @@ def _rope_kwargs(_unused_cu_seqlens: Any | None) -> dict[str, Any]: | |
| k_freqs, | ||
| **_rope_kwargs(cu_seqlens_kv), | ||
| ).squeeze(1) | ||
|
|
||
| ##### [PS-diag] ON post-RoPE Q/K dump (per-layer) ##### | ||
| try: | ||
| from prefix_sharing.tools.diagnostic_dump_verl080 import dump_rope_postqk_verl080 | ||
| dump_rope_postqk_verl080( | ||
| attention_module.layer_number, | ||
| query, key, | ||
| attention_module.config.num_layers, | ||
| positions=packed_position_ids, | ||
| ) | ||
| except Exception as exc: | ||
| print(f"rope_postqk_layer dump failed: {exc}") | ||
| ##### [PS-diag] ON post-RoPE Q/K dump end ##### | ||
|
|
||
| return query, key | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
开发目标 A:OFF 路径插桩 pre-RoPE Q/K/V + hidden_states dump(post-squeeze、pre-rotary)
dump_rope_preqk_verl080(layer_number, query, key, num_layers)dump_build_kv_input_v_on(layer_number, value, num_layers)dump_hidden_states_on(layer_number, hidden_states, num_layers)开发目标 B:OFF 路径插桩 post-RoPE Q/K + full KV dump(rotary 后、core attention 前)
dump_rope_postqk_verl080(layer_number, query, key, num_layers)dump_full_kv_off(layer_number, key, value, num_layers)两者由
PREFIX_SHARING_DIAG_DUMPenv var 控制,未设时仅一次os.environ.get检查。try/except 保护 dump 失败不崩训练。