""" FAIE Full Model Converter -- Path 2. Converts ALL 9,600 experts (75 MoE layers x 128 experts) from GLM-5.2-REAM-Q2_K.gguf to packed ternary format on disk. Also converts attention, embeddings, and output layers. Output format: directory of .bin files per layer + metadata JSON. Each expert file contains: packed_ternary_bytes + scales_float32. """ import os import sys import json import time import struct import shutil import numpy as np from pathlib import Path from gguf import GGUFReader from faie import TernaryCompressor, GLM_CONFIG from fast_dequant import dequant_q2k_fast, dequant_q3k_fast, QK_K, Q2_K_BLOCK_SIZE, Q3_K_BLOCK_SIZE MODEL_PATH = r"E:\ABU\MODELS\llm\glm-5.2-ream\GLM-5.2-REAM-Q2_K.gguf" OUTPUT_DIR = r"E:\ABU\NEOCORTEX\faie_model" GROUP_SIZE = 64 def convert_expert(reader, layer_idx, expert_idx, output_dir): """Convert one expert's gate+up+down projections to ternary format (SwiGLU).""" gate_name = f"blk.{layer_idx}.ffn_gate_exps.weight" up_name = f"blk.{layer_idx}.ffn_up_exps.weight" down_name = f"blk.{layer_idx}.ffn_down_exps.weight" gate_tensor = None up_tensor = None down_tensor = None for t in reader.tensors: if t.name == gate_name: gate_tensor = t elif t.name == up_name: up_tensor = t elif t.name == down_name: down_tensor = t if gate_tensor and up_tensor and down_tensor: break if gate_tensor is None or up_tensor is None or down_tensor is None: return None gate_shape = [int(s) for s in gate_tensor.shape] up_shape = [int(s) for s in up_tensor.shape] down_shape = [int(s) for s in down_tensor.shape] # GGUF memmap is (n_experts, ...) -- index by expert_idx directly gate_elements = gate_shape[0] * gate_shape[1] gate_raw = gate_tensor.data[expert_idx].tobytes() gate_f32 = dequant_q2k_fast(gate_raw, gate_elements).reshape(gate_shape[0], gate_shape[1]).T up_elements = up_shape[0] * up_shape[1] up_raw = up_tensor.data[expert_idx].tobytes() up_f32 = dequant_q2k_fast(up_raw, up_elements).reshape(up_shape[0], up_shape[1]).T down_qtype = down_tensor.tensor_type.name if hasattr(down_tensor.tensor_type, 'name') else str(down_tensor.tensor_type) down_elements = down_shape[0] * down_shape[1] down_raw = down_tensor.data[expert_idx].tobytes() if "Q3_K" in down_qtype: down_f32 = dequant_q3k_fast(down_raw, down_elements).reshape(down_shape[0], down_shape[1]).T else: down_f32 = dequant_q2k_fast(down_raw, down_elements).reshape(down_shape[0], down_shape[1]).T # Ternary quantize all three projections q_gate = TernaryCompressor.quantize(gate_f32, group_size=GROUP_SIZE) q_up = TernaryCompressor.quantize(up_f32, group_size=GROUP_SIZE) q_down = TernaryCompressor.quantize(down_f32, group_size=GROUP_SIZE) # Pack gate_packed, gate_n = TernaryCompressor.pack_ternary(q_gate["ternary"]) up_packed, up_n = TernaryCompressor.pack_ternary(q_up["ternary"]) down_packed, down_n = TernaryCompressor.pack_ternary(q_down["ternary"]) # Write binary file (v2 format: gate + up + down) expert_path = os.path.join(output_dir, f"expert_{expert_idx:03d}.bin") with open(expert_path, 'wb') as f: # Magic + version f.write(struct.pack(' 5_000_000: file_size = os.path.getsize(expert_path) results.append({ "gate_shape": [6144, 2048], "down_shape": [2048, 6144], "gate_sparsity": 0.5, "down_sparsity": 0.5, "file_bytes": file_size, "_cached": True, }) continue result = convert_expert(reader, layer_idx, exp_idx, layer_dir) if result is None: return None results.append(result) total_bytes = sum(r["file_bytes"] for r in results) avg_gate_sparsity = np.mean([r.get("gate_sparsity", 0.5) for r in results]) avg_up_sparsity = np.mean([r.get("up_sparsity", 0.5) for r in results]) avg_down_sparsity = np.mean([r.get("down_sparsity", 0.5) for r in results]) meta = { "layer_idx": layer_idx, "n_experts": n_experts, "total_bytes": total_bytes, "avg_gate_sparsity": float(avg_gate_sparsity), "avg_up_sparsity": float(avg_up_sparsity), "avg_down_sparsity": float(avg_down_sparsity), "experts": results, } with open(os.path.join(layer_dir, "meta.json"), 'w') as f: json.dump(meta, f, indent=2) return meta def convert_full_model(): """Convert the entire GLM-5.2 GGUF to FAIE ternary format.""" print("=" * 70) print(" FAIE FULL MODEL CONVERSION") print(f" Source: {MODEL_PATH}") print(f" Output: {OUTPUT_DIR}") print("=" * 70) if not os.path.exists(MODEL_PATH): print(f" ERROR: Model not found: {MODEL_PATH}") return False os.makedirs(OUTPUT_DIR, exist_ok=True) print(f"\n Loading GGUF reader (memory-mapped)...") t_start = time.perf_counter() reader = GGUFReader(MODEL_PATH, 'r') # Identify MoE layers (layers 3-77 are MoE) moe_layers = list(range(3, 78)) # 75 MoE layers n_experts = GLM_CONFIG["expert_count"] # 128 total_experts = len(moe_layers) * n_experts # 9,600 print(f" MoE layers: {moe_layers[0]}-{moe_layers[-1]} ({len(moe_layers)} layers)") print(f" Experts per layer: {n_experts}") print(f" Total experts to convert: {total_experts:,}") # Convert all MoE layers (with resume support) layer_metas = [] total_bytes = 0 total_converted = 0 skipped = 0 for i, layer_idx in enumerate(moe_layers): if is_layer_complete(OUTPUT_DIR, layer_idx): meta_path = os.path.join(OUTPUT_DIR, f"layer_{layer_idx:02d}", "meta.json") with open(meta_path, 'r') as f: meta = json.load(f) total_bytes += meta["total_bytes"] total_converted += n_experts layer_metas.append(meta) skipped += 1 print(f"\n Layer {layer_idx:2d} ({i+1}/{len(moe_layers)})... SKIP (already done)", flush=True) continue t0 = time.perf_counter() print(f"\n Layer {layer_idx:2d} ({i+1}/{len(moe_layers)})...", end="", flush=True) meta = convert_layer(reader, layer_idx, OUTPUT_DIR) if meta is None: print(f" FAILED (tensors not found)") continue t1 = time.perf_counter() layer_bytes = meta["total_bytes"] total_bytes += layer_bytes total_converted += n_experts elapsed = t1 - t0 remaining = len(moe_layers) - i - 1 - skipped eta = elapsed * max(0, remaining) print(f" {layer_bytes/(1024**2):.1f} MB, " f"sparsity gate={meta['avg_gate_sparsity']*100:.0f}% down={meta['avg_down_sparsity']*100:.0f}%, " f"{elapsed:.0f}s (ETA: {eta/60:.0f}m)") layer_metas.append(meta) t_total = time.perf_counter() - t_start # Write global metadata global_meta = { "model": "GLM-5.2-REAM", "source_gguf": MODEL_PATH, "format_version": 1, "group_size": GROUP_SIZE, "quantization": "ternary_rms_0.7", "n_moe_layers": len(moe_layers), "n_experts_per_layer": n_experts, "total_experts": total_converted, "total_bytes": total_bytes, "total_gb": total_bytes / (1024**3), "conversion_time_s": t_total, "config": GLM_CONFIG, "layers": [m["layer_idx"] for m in layer_metas], } with open(os.path.join(OUTPUT_DIR, "model_meta.json"), 'w') as f: json.dump(global_meta, f, indent=2) print(f"\n{'=' * 70}") print(f" CONVERSION COMPLETE") print(f" Experts converted: {total_converted:,} / {total_experts:,}") print(f" Total size: {total_bytes/(1024**3):.2f} GB") print(f" Time: {t_total/60:.1f} minutes") print(f" Output: {OUTPUT_DIR}") print(f"{'=' * 70}") return True def read_faie_expert(expert_path): """Read a v2 FAIE expert binary file.""" with open(expert_path, 'rb') as f: magic = struct.unpack(' 10 else ''}") results.append(("V2: All 75 layer directories", v2_pass, f"{75 - len(missing_layers)}/75")) # V3: Check all 128 expert files per layer (sample 7 layers) print("\n [V3] Expert file count (sample 7 layers)...") sample_layers = [3, 10, 20, 35, 50, 65, 77] all_128 = True for layer_idx in sample_layers: layer_dir = os.path.join(OUTPUT_DIR, f"layer_{layer_idx:02d}") if os.path.isdir(layer_dir): expert_files = [f for f in os.listdir(layer_dir) if f.startswith("expert_") and f.endswith(".bin")] if len(expert_files) != 128: all_128 = False print(f" Layer {layer_idx}: {len(expert_files)}/128 experts") else: all_128 = False v3_pass = all_128 print(f" All sampled layers have 128 experts: {v3_pass}") results.append(("V3: 128 experts per layer (7 samples)", v3_pass, "128/128" if v3_pass else "MISSING")) # V4: Read and dequantize a random expert, compare to GGUF original print("\n [V4] Roundtrip quality: read converted expert vs GGUF original...") test_layer = 20 test_expert = 42 expert_path = os.path.join(OUTPUT_DIR, f"layer_{test_layer:02d}", f"expert_{test_expert:03d}.bin") v4_pass = False cos = 0.0 if os.path.exists(expert_path): data = read_faie_expert(expert_path) if data: gate_ternary = TernaryCompressor.unpack_ternary(data["gate_packed"], data["gate_n"]) gate_recon = TernaryCompressor.dequantize( gate_ternary.reshape(data["gate_shape"]), data["gate_scales"], data["gate_shape"], data["group_size"]) reader = GGUFReader(MODEL_PATH, 'r') gate_name = f"blk.{test_layer}.ffn_gate_exps.weight" for t in reader.tensors: if t.name == gate_name: gate_shape_orig = [int(s) for s in t.shape] gate_elements = gate_shape_orig[0] * gate_shape_orig[1] gate_raw_orig = t.data[test_expert].tobytes() gate_orig = dequant_q2k_fast(gate_raw_orig, gate_elements).reshape(gate_shape_orig[0], gate_shape_orig[1]).T break del reader cos = np.dot(gate_orig.ravel(), gate_recon.ravel()) / ( np.linalg.norm(gate_orig) * np.linalg.norm(gate_recon) + 1e-10) v4_pass = cos > 0.85 print(f" Layer {test_layer}, Expert {test_expert}") print(f" Gate cosine (FAIE vs GGUF original): {cos:.6f}") results.append(("V4: Roundtrip quality > 0.85", v4_pass, f"cos={cos:.6f}" if v4_pass else "FAILED")) # V5: Total size matches projection (~67 GB for experts) print("\n [V5] Total size validation...") total_size = 0 for root, dirs, files in os.walk(OUTPUT_DIR): for f in files: if f.endswith('.bin'): total_size += os.path.getsize(os.path.join(root, f)) total_gb = total_size / (1024**3) v5_pass = 70 < total_gb < 130 # gate+up+down = ~100 GB print(f" Total expert data: {total_gb:.2f} GB") print(f" Expected: ~100 GB (gate+up+down)") results.append(("V5: Size 50-90 GB", v5_pass, f"{total_gb:.1f} GB")) # V6: Sparsity distribution across layers print("\n [V6] Sparsity distribution (7 layer samples)...") sparsities = [] for layer_idx in sample_layers: meta_path_l = os.path.join(OUTPUT_DIR, f"layer_{layer_idx:02d}", "meta.json") if os.path.exists(meta_path_l): with open(meta_path_l, 'r') as f: lmeta = json.load(f) sparsities.append(lmeta["avg_gate_sparsity"]) v6_pass = len(sparsities) >= 5 and all(0.3 < s < 0.8 for s in sparsities) if sparsities: print(f" Range: {min(sparsities)*100:.0f}% - {max(sparsities)*100:.0f}%") print(f" Mean: {np.mean(sparsities)*100:.0f}%") results.append(("V6: Sparsity in 30-80% range", v6_pass, f"{np.mean(sparsities)*100:.0f}%" if sparsities else "N/A")) # V7: Load 7 random experts from different layers, SwiGLU forward pass, non-zero output print("\n [V7] SwiGLU forward pass test (7 experts from 7 layers)...") import torch DEVICE = torch.device('cuda:0') all_nonzero = True for layer_idx in sample_layers: expert_idx = layer_idx % 128 expert_path = os.path.join(OUTPUT_DIR, f"layer_{layer_idx:02d}", f"expert_{expert_idx:03d}.bin") if not os.path.exists(expert_path): all_nonzero = False continue data = read_faie_expert(expert_path) if data is None: all_nonzero = False continue gs0, gs1 = data["gate_shape"] us0, us1 = data["up_shape"] ds0, ds1 = data["down_shape"] group_size = data["group_size"] gate_ternary = TernaryCompressor.unpack_ternary(data["gate_packed"], data["gate_n"]).reshape(gs0, gs1) gate_recon = TernaryCompressor.dequantize(gate_ternary, data["gate_scales"], (gs0, gs1), group_size) up_ternary = TernaryCompressor.unpack_ternary(data["up_packed"], data["up_n"]).reshape(us0, us1) up_recon = TernaryCompressor.dequantize(up_ternary, data["up_scales"], (us0, us1), group_size) down_ternary = TernaryCompressor.unpack_ternary(data["down_packed"], data["down_n"]).reshape(ds0, ds1) down_recon = TernaryCompressor.dequantize(down_ternary, data["down_scales"], (ds0, ds1), group_size) x = torch.randn(1, gs1, dtype=torch.float16, device=DEVICE) * 0.1 W_gate = torch.from_numpy(gate_recon).to(torch.float16).to(DEVICE) W_up = torch.from_numpy(up_recon).to(torch.float16).to(DEVICE) W_down = torch.from_numpy(down_recon).to(torch.float16).to(DEVICE) hidden = F.silu(F.linear(x, W_gate)) * F.linear(x, W_up) y = F.linear(hidden, W_down) y_norm = y.norm().item() if y_norm < 1e-10: all_nonzero = False print(f" Layer {layer_idx}, Expert {expert_idx}: ZERO OUTPUT") del W_gate, W_up, W_down, x, y, hidden torch.cuda.empty_cache() v7_pass = all_nonzero print(f" All 7 experts produce non-zero output: {v7_pass}") results.append(("V7: Forward pass non-zero (7 experts)", v7_pass, "all non-zero" if v7_pass else "ZERO FOUND")) # Verdict print(f"\n{'=' * 70}") all_pass = all(r[1] for r in results) for label, passed, detail in results: status = "PASS" if passed else "FAIL" print(f" [{status}] {label} ({detail})") print(f"\n VERDICT: {'ALL 7 CHECKS PASSED' if all_pass else 'SOME CHECKS FAILED'}") print(f"{'=' * 70}") return all_pass, results if __name__ == "__main__": import torch import torch.nn.functional as F if "--verify-only" in sys.argv: verify_conversion() else: success = convert_full_model() if success: verify_conversion()