Files

324 lines
10 KiB
Python

#!/usr/bin/env python
# coding=utf-8
"""
测试当 LoRA B = 0 时是否会产生 NaN
假设:pt 文件中 LoRA B 全为 0(标准初始化),这可能导致 C++ 代码中的某些问题。
测试:将 LoRA B 设置为非零值后,问题是否消失。
"""
import os
import sys
sys.path.insert(0, os.path.dirname(__file__) + "/../build")
import torch
DATA_PATH = "/mnt/data/lpl/kt_nan_debug_data.pt"
try:
from kt_kernel import kt_kernel_ext
HAS_KT_KERNEL = True
except ImportError:
HAS_KT_KERNEL = False
kt_kernel_ext = None
def silu(x):
return x * torch.sigmoid(x)
def moe_sft_torch_forward(
input_data,
expert_ids,
weights,
gate_proj,
up_proj,
down_proj,
gate_lora_a,
gate_lora_b,
up_lora_a,
up_lora_b,
down_lora_a,
down_lora_b,
lora_scaling,
):
"""PyTorch reference implementation."""
qlen = input_data.shape[0]
hidden_size = input_data.shape[1]
num_experts_per_tok = expert_ids.shape[1]
expert_num = gate_proj.shape[0]
output = torch.zeros((qlen, hidden_size), dtype=input_data.dtype)
for i in range(qlen):
for j in range(num_experts_per_tok):
expert_id = expert_ids[i, j].item()
weight = weights[i, j].item()
x = input_data[i : i + 1].float()
# Gate
gate_base = x @ gate_proj[expert_id].float().T
gate_lora = (x @ gate_lora_a[expert_id].float().T) @ gate_lora_b[expert_id].float().T
gate_out = gate_base + gate_lora * lora_scaling
# Up
up_base = x @ up_proj[expert_id].float().T
up_lora = (x @ up_lora_a[expert_id].float().T) @ up_lora_b[expert_id].float().T
up_out = up_base + up_lora * lora_scaling
# Activation
intermediate = silu(gate_out) * up_out
# Down
down_base = intermediate @ down_proj[expert_id].float().T
down_lora = (intermediate @ down_lora_a[expert_id].float().T) @ down_lora_b[expert_id].float().T
expert_output = down_base + down_lora * lora_scaling
output[i] += weight * expert_output.squeeze(0).to(output.dtype)
return output
def test_with_modified_lora_b():
"""测试将 LoRA B 设置为非零值后是否还有 NaN"""
print("=" * 70)
print("测试:将 LoRA B 设置为非零值")
print("=" * 70)
data = torch.load(DATA_PATH)
# 配置
real_expert_num = data["expert_num"]
real_hidden_size = data["hidden_size"]
real_intermediate_size = data["intermediate_size"]
real_num_experts_per_tok = data["num_experts_per_tok"]
real_qlen = data["input_data"].shape[0]
real_lora_rank = data["gate_lora_a"].shape[1]
real_lora_alpha = 16.0
real_lora_scaling = real_lora_alpha / real_lora_rank
print(f"\n配置:")
print(f" expert_num: {real_expert_num}")
print(f" hidden_size: {real_hidden_size}")
print(f" intermediate_size: {real_intermediate_size}")
print(f" qlen: {real_qlen}")
print(f" lora_rank: {real_lora_rank}")
# 提取数据
input_data = data["input_data"].contiguous()
expert_ids = data["expert_ids"].contiguous()
weights = data["weights"].contiguous()
gate_proj = data["gate_proj"].contiguous()
up_proj = data["up_proj"].contiguous()
down_proj = data["down_proj"].contiguous()
# 原始 LoRA 权重(B 全为 0)
gate_lora_a = data["gate_lora_a"].contiguous()
gate_lora_b = data["gate_lora_b"].contiguous()
up_lora_a = data["up_lora_a"].contiguous()
up_lora_b = data["up_lora_b"].contiguous()
down_lora_a = data["down_lora_a"].contiguous()
down_lora_b = data["down_lora_b"].contiguous()
print(f"\n原始 LoRA B 权重检查:")
print(f" gate_lora_b: min={gate_lora_b.min().item():.6f}, max={gate_lora_b.max().item():.6f}")
print(f" up_lora_b: min={up_lora_b.min().item():.6f}, max={up_lora_b.max().item():.6f}")
print(f" down_lora_b: min={down_lora_b.min().item():.6f}, max={down_lora_b.max().item():.6f}")
# 修改 LoRA B 为非零值(与 accuracy 测试相同)
print("\n将 LoRA B 设置为非零随机值...")
torch.manual_seed(42)
gate_lora_b_nonzero = torch.randn_like(gate_lora_b) / 100
up_lora_b_nonzero = torch.randn_like(up_lora_b) / 100
down_lora_b_nonzero = torch.randn_like(down_lora_b) / 100
print(f"\n修改后 LoRA B 权重:")
print(f" gate_lora_b: min={gate_lora_b_nonzero.min().item():.6f}, max={gate_lora_b_nonzero.max().item():.6f}")
print(f" up_lora_b: min={up_lora_b_nonzero.min().item():.6f}, max={up_lora_b_nonzero.max().item():.6f}")
print(f" down_lora_b: min={down_lora_b_nonzero.min().item():.6f}, max={down_lora_b_nonzero.max().item():.6f}")
if not HAS_KT_KERNEL:
print("\n[SKIP] kt_kernel_ext 不可用")
return
# 测试 1: 原始 LoRA B (全零)
print("\n" + "=" * 70)
print("测试 1: 原始 LoRA B (全零)")
print("=" * 70)
num_threads = 60
pool_config = kt_kernel_ext.WorkerPoolConfig()
pool_config.subpool_count = 1
pool_config.subpool_numa_map = [0]
pool_config.subpool_thread_count = [num_threads]
CPUInfer = kt_kernel_ext.CPUInfer(pool_config)
config = kt_kernel_ext.moe.MOESFTConfig()
config.expert_num = real_expert_num
config.num_experts_per_tok = real_num_experts_per_tok
config.hidden_size = real_hidden_size
config.intermediate_size = real_intermediate_size
config.lora_rank = real_lora_rank
config.lora_alpha = real_lora_alpha
config.max_cache_depth = 1
config.max_len = max(real_qlen * 2, 4096)
config.layer_idx = data["layer_idx"]
config.gate_proj = gate_proj.data_ptr()
config.up_proj = up_proj.data_ptr()
config.down_proj = down_proj.data_ptr()
config.gate_lora_a = gate_lora_a.data_ptr()
config.gate_lora_b = gate_lora_b.data_ptr() # 原始全零
config.up_lora_a = up_lora_a.data_ptr()
config.up_lora_b = up_lora_b.data_ptr() # 原始全零
config.down_lora_a = down_lora_a.data_ptr()
config.down_lora_b = down_lora_b.data_ptr() # 原始全零
config.pool = CPUInfer.backend_
moe = kt_kernel_ext.moe.AMXBF16_SFT_MOE(config)
CPUInfer.submit(moe.load_weights_task())
CPUInfer.sync()
CPUInfer.submit(moe.warm_up_task())
CPUInfer.sync()
bsz_tensor = torch.tensor([real_qlen], device="cpu")
amx_output_zero = torch.zeros((real_qlen, real_hidden_size), dtype=torch.bfloat16).contiguous()
CPUInfer.submit(
moe.forward_sft_task(
bsz_tensor.data_ptr(),
real_num_experts_per_tok,
expert_ids.data_ptr(),
weights.data_ptr(),
input_data.data_ptr(),
amx_output_zero.data_ptr(),
False,
)
)
CPUInfer.sync()
nan_count_zero = torch.isnan(amx_output_zero).sum().item()
print(f"\n结果 (LoRA B = 0):")
print(f" NaN 数量: {nan_count_zero}")
# 测试 2: 修改后 LoRA B (非零)
print("\n" + "=" * 70)
print("测试 2: 修改后 LoRA B (非零)")
print("=" * 70)
# 重新创建 MOE 实例
config2 = kt_kernel_ext.moe.MOESFTConfig()
config2.expert_num = real_expert_num
config2.num_experts_per_tok = real_num_experts_per_tok
config2.hidden_size = real_hidden_size
config2.intermediate_size = real_intermediate_size
config2.lora_rank = real_lora_rank
config2.lora_alpha = real_lora_alpha
config2.max_cache_depth = 1
config2.max_len = max(real_qlen * 2, 4096)
config2.layer_idx = data["layer_idx"]
config2.gate_proj = gate_proj.data_ptr()
config2.up_proj = up_proj.data_ptr()
config2.down_proj = down_proj.data_ptr()
config2.gate_lora_a = gate_lora_a.data_ptr()
config2.gate_lora_b = gate_lora_b_nonzero.data_ptr() # 非零
config2.up_lora_a = up_lora_a.data_ptr()
config2.up_lora_b = up_lora_b_nonzero.data_ptr() # 非零
config2.down_lora_a = down_lora_a.data_ptr()
config2.down_lora_b = down_lora_b_nonzero.data_ptr() # 非零
config2.pool = CPUInfer.backend_
moe2 = kt_kernel_ext.moe.AMXBF16_SFT_MOE(config2)
CPUInfer.submit(moe2.load_weights_task())
CPUInfer.sync()
CPUInfer.submit(moe2.warm_up_task())
CPUInfer.sync()
amx_output_nonzero = torch.zeros((real_qlen, real_hidden_size), dtype=torch.bfloat16).contiguous()
CPUInfer.submit(
moe2.forward_sft_task(
bsz_tensor.data_ptr(),
real_num_experts_per_tok,
expert_ids.data_ptr(),
weights.data_ptr(),
input_data.data_ptr(),
amx_output_nonzero.data_ptr(),
False,
)
)
CPUInfer.sync()
nan_count_nonzero = torch.isnan(amx_output_nonzero).sum().item()
print(f"\n结果 (LoRA B = 非零):")
print(f" NaN 数量: {nan_count_nonzero}")
# PyTorch 参考
print("\n" + "=" * 70)
print("PyTorch 参考")
print("=" * 70)
torch_output_zero = moe_sft_torch_forward(
input_data,
expert_ids,
weights,
gate_proj,
up_proj,
down_proj,
gate_lora_a,
gate_lora_b, # 原始全零
up_lora_a,
up_lora_b,
down_lora_a,
down_lora_b,
real_lora_scaling,
)
torch_nan_zero = torch.isnan(torch_output_zero).sum().item()
torch_output_nonzero = moe_sft_torch_forward(
input_data,
expert_ids,
weights,
gate_proj,
up_proj,
down_proj,
gate_lora_a,
gate_lora_b_nonzero, # 非零
up_lora_a,
up_lora_b_nonzero,
down_lora_a,
down_lora_b_nonzero,
real_lora_scaling,
)
torch_nan_nonzero = torch.isnan(torch_output_nonzero).sum().item()
print(f" PyTorch (LoRA B = 0) NaN: {torch_nan_zero}")
print(f" PyTorch (LoRA B = 非零) NaN: {torch_nan_nonzero}")
# 结论
print("\n" + "=" * 70)
print("结论")
print("=" * 70)
print(f" AMX (LoRA B = 0): {nan_count_zero} NaN")
print(f" AMX (LoRA B = 非零): {nan_count_nonzero} NaN")
print(f" PyTorch (LoRA B = 0): {torch_nan_zero} NaN")
print(f" PyTorch (LoRA B = 非零): {torch_nan_nonzero} NaN")
if nan_count_zero > 0 and nan_count_nonzero == 0:
print("\n*** 问题与 LoRA B = 0 相关!***")
print("当 LoRA B 为全零时,C++ 代码产生 NaN")
print("当 LoRA B 为非零时,C++ 代码正常")
elif nan_count_zero > 0 and nan_count_nonzero > 0:
print("\n*** 问题与 LoRA B 值无关 ***")
print("无论 LoRA B 是否为零,都有 NaN")
else:
print("\n*** 两种情况都没有 NaN ***")
if __name__ == "__main__":
test_with_modified_lora_b()