Text Generation
Transformers
Safetensors
llama
tiny-random
quantized
text-generation-inference
8-bit precision
modelopt
Instructions to use IlyasMoutawwakil/tiny-random-Llama-NVFP4 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use IlyasMoutawwakil/tiny-random-Llama-NVFP4 with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="IlyasMoutawwakil/tiny-random-Llama-NVFP4")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("IlyasMoutawwakil/tiny-random-Llama-NVFP4") model = AutoModelForCausalLM.from_pretrained("IlyasMoutawwakil/tiny-random-Llama-NVFP4", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use IlyasMoutawwakil/tiny-random-Llama-NVFP4 with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "IlyasMoutawwakil/tiny-random-Llama-NVFP4" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "IlyasMoutawwakil/tiny-random-Llama-NVFP4", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/IlyasMoutawwakil/tiny-random-Llama-NVFP4
- SGLang
How to use IlyasMoutawwakil/tiny-random-Llama-NVFP4 with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "IlyasMoutawwakil/tiny-random-Llama-NVFP4" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "IlyasMoutawwakil/tiny-random-Llama-NVFP4", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "IlyasMoutawwakil/tiny-random-Llama-NVFP4" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "IlyasMoutawwakil/tiny-random-Llama-NVFP4", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use IlyasMoutawwakil/tiny-random-Llama-NVFP4 with Docker Model Runner:
docker model run hf.co/IlyasMoutawwakil/tiny-random-Llama-NVFP4
Add tiny random checkpoint
Browse files- README.md +19 -0
- config.json +73 -0
- generation_config.json +11 -0
- hf_quant_config.json +14 -0
- model.safetensors +3 -0
- reference/config.json +38 -0
- reference/generation_config.json +11 -0
- reference/model.safetensors +3 -0
- scripts/build_dense_bf16.py +54 -0
- scripts/calibrate_experts.py +60 -0
- scripts/extract_layout.py +59 -0
- scripts/quantize_dense_nvfp4.py +56 -0
README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
library_name: transformers
|
| 3 |
+
tags:
|
| 4 |
+
- tiny-random
|
| 5 |
+
- quantized
|
| 6 |
+
---
|
| 7 |
+
|
| 8 |
+
# tiny-random-Llama-NVFP4
|
| 9 |
+
|
| 10 |
+
A tiny random model for testing, shrunk from [`nvidia/Llama-3.1-8B-Instruct-FP4`](https://huggingface.co/nvidia/Llama-3.1-8B-Instruct-FP4): the same
|
| 11 |
+
architecture, quantization config and checkpoint layout at test sizes. Its key patterns, dtypes and tensor ranks match
|
| 12 |
+
the real checkpoint's (`scripts/extract_layout.py`).
|
| 13 |
+
|
| 14 |
+
modelopt NVFP4 on every decoder linear (packed U8, E4M3 `weight_scale` per 16, F32 `weight_scale_2`, calibrated F32 `input_scale`) plus the FP8 KV cache's `k_scale` / `v_scale`. Quantized with modelopt's `NVFP4QTensor`.
|
| 15 |
+
|
| 16 |
+
`reference/` holds the same weights dequantized to bf16, under the unquantized model's keys: the reference to compare
|
| 17 |
+
logits against, so a test measures what the load path and kernels add, not the quantization itself.
|
| 18 |
+
|
| 19 |
+
The weights are random; the outputs mean nothing. `scripts/` rebuilds it from the real checkpoint's `config.json`.
|
config.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"LlamaForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 1,
|
| 8 |
+
"dtype": "bfloat16",
|
| 9 |
+
"eos_token_id": [
|
| 10 |
+
1
|
| 11 |
+
],
|
| 12 |
+
"head_dim": 64,
|
| 13 |
+
"hidden_act": "silu",
|
| 14 |
+
"hidden_size": 256,
|
| 15 |
+
"initializer_range": 0.02,
|
| 16 |
+
"intermediate_size": 512,
|
| 17 |
+
"max_position_embeddings": 4096,
|
| 18 |
+
"mlp_bias": false,
|
| 19 |
+
"model_type": "llama",
|
| 20 |
+
"num_attention_heads": 4,
|
| 21 |
+
"num_hidden_layers": 2,
|
| 22 |
+
"num_key_value_heads": 2,
|
| 23 |
+
"pad_token_id": null,
|
| 24 |
+
"pretraining_tp": 1,
|
| 25 |
+
"rms_norm_eps": 1e-05,
|
| 26 |
+
"rope_parameters": {
|
| 27 |
+
"factor": 8.0,
|
| 28 |
+
"high_freq_factor": 4.0,
|
| 29 |
+
"low_freq_factor": 1.0,
|
| 30 |
+
"original_max_position_embeddings": 8192,
|
| 31 |
+
"rope_theta": 500000.0,
|
| 32 |
+
"rope_type": "llama3"
|
| 33 |
+
},
|
| 34 |
+
"tie_word_embeddings": false,
|
| 35 |
+
"transformers_version": "5.18.0.dev0",
|
| 36 |
+
"use_cache": true,
|
| 37 |
+
"vocab_size": 1024,
|
| 38 |
+
"quantization_config": {
|
| 39 |
+
"config_groups": {
|
| 40 |
+
"group_0": {
|
| 41 |
+
"input_activations": {
|
| 42 |
+
"dynamic": false,
|
| 43 |
+
"num_bits": 4,
|
| 44 |
+
"type": "float",
|
| 45 |
+
"group_size": 16
|
| 46 |
+
},
|
| 47 |
+
"weights": {
|
| 48 |
+
"dynamic": false,
|
| 49 |
+
"num_bits": 4,
|
| 50 |
+
"type": "float",
|
| 51 |
+
"group_size": 16
|
| 52 |
+
},
|
| 53 |
+
"targets": [
|
| 54 |
+
"Linear"
|
| 55 |
+
]
|
| 56 |
+
}
|
| 57 |
+
},
|
| 58 |
+
"ignore": [
|
| 59 |
+
"lm_head"
|
| 60 |
+
],
|
| 61 |
+
"quant_algo": "NVFP4",
|
| 62 |
+
"kv_cache_scheme": {
|
| 63 |
+
"dynamic": false,
|
| 64 |
+
"num_bits": 8,
|
| 65 |
+
"type": "float"
|
| 66 |
+
},
|
| 67 |
+
"producer": {
|
| 68 |
+
"name": "modelopt",
|
| 69 |
+
"version": "0.37.0.dev5+g76fb12d47.d20250905"
|
| 70 |
+
},
|
| 71 |
+
"quant_method": "modelopt"
|
| 72 |
+
}
|
| 73 |
+
}
|
generation_config.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_from_model_config": true,
|
| 3 |
+
"bos_token_id": 1,
|
| 4 |
+
"eos_token_id": [
|
| 5 |
+
1
|
| 6 |
+
],
|
| 7 |
+
"output_attentions": false,
|
| 8 |
+
"output_hidden_states": false,
|
| 9 |
+
"transformers_version": "5.18.0.dev0",
|
| 10 |
+
"use_cache": true
|
| 11 |
+
}
|
hf_quant_config.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"producer": {
|
| 3 |
+
"name": "modelopt",
|
| 4 |
+
"version": "0.37.0.dev5+g76fb12d47.d20250905"
|
| 5 |
+
},
|
| 6 |
+
"quantization": {
|
| 7 |
+
"quant_algo": "NVFP4",
|
| 8 |
+
"kv_cache_quant_algo": "FP8",
|
| 9 |
+
"group_size": 16,
|
| 10 |
+
"exclude_modules": [
|
| 11 |
+
"lm_head"
|
| 12 |
+
]
|
| 13 |
+
}
|
| 14 |
+
}
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8a96839c1a64d95f02e40b69ac4126a34f26832ea9c9836f9a4c5ee3f3e556a4
|
| 3 |
+
size 1721712
|
reference/config.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"LlamaForCausalLM"
|
| 4 |
+
],
|
| 5 |
+
"attention_bias": false,
|
| 6 |
+
"attention_dropout": 0.0,
|
| 7 |
+
"bos_token_id": 1,
|
| 8 |
+
"dtype": "bfloat16",
|
| 9 |
+
"eos_token_id": [
|
| 10 |
+
1
|
| 11 |
+
],
|
| 12 |
+
"head_dim": 64,
|
| 13 |
+
"hidden_act": "silu",
|
| 14 |
+
"hidden_size": 256,
|
| 15 |
+
"initializer_range": 0.02,
|
| 16 |
+
"intermediate_size": 512,
|
| 17 |
+
"max_position_embeddings": 4096,
|
| 18 |
+
"mlp_bias": false,
|
| 19 |
+
"model_type": "llama",
|
| 20 |
+
"num_attention_heads": 4,
|
| 21 |
+
"num_hidden_layers": 2,
|
| 22 |
+
"num_key_value_heads": 2,
|
| 23 |
+
"pad_token_id": null,
|
| 24 |
+
"pretraining_tp": 1,
|
| 25 |
+
"rms_norm_eps": 1e-05,
|
| 26 |
+
"rope_parameters": {
|
| 27 |
+
"factor": 8.0,
|
| 28 |
+
"high_freq_factor": 4.0,
|
| 29 |
+
"low_freq_factor": 1.0,
|
| 30 |
+
"original_max_position_embeddings": 8192,
|
| 31 |
+
"rope_theta": 500000.0,
|
| 32 |
+
"rope_type": "llama3"
|
| 33 |
+
},
|
| 34 |
+
"tie_word_embeddings": false,
|
| 35 |
+
"transformers_version": "5.18.0.dev0",
|
| 36 |
+
"use_cache": true,
|
| 37 |
+
"vocab_size": 1024
|
| 38 |
+
}
|
reference/generation_config.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_from_model_config": true,
|
| 3 |
+
"bos_token_id": 1,
|
| 4 |
+
"eos_token_id": [
|
| 5 |
+
1
|
| 6 |
+
],
|
| 7 |
+
"output_attentions": false,
|
| 8 |
+
"output_hidden_states": false,
|
| 9 |
+
"transformers_version": "5.18.0.dev0",
|
| 10 |
+
"use_cache": true
|
| 11 |
+
}
|
reference/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d605548fe78a18c4d21b9b17c050dab60e3d8411a0b52ed270ac9b0daf70f973
|
| 3 |
+
size 3412672
|
scripts/build_dense_bf16.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""A tiny random dense model in bf16, from a real checkpoint's config.json shrunk in its own fields:
|
| 2 |
+
Qwen3, Llama or Mistral3 (Ministral-3, with its vision tower)."""
|
| 3 |
+
|
| 4 |
+
import argparse
|
| 5 |
+
import json
|
| 6 |
+
import tempfile
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from transformers import AutoConfig, AutoModelForCausalLM, AutoModelForImageTextToText
|
| 10 |
+
|
| 11 |
+
parser = argparse.ArgumentParser()
|
| 12 |
+
parser.add_argument("--real-config", required=True)
|
| 13 |
+
parser.add_argument("--out", required=True)
|
| 14 |
+
args = parser.parse_args()
|
| 15 |
+
|
| 16 |
+
text_sizes = dict(
|
| 17 |
+
vocab_size=1024,
|
| 18 |
+
hidden_size=256,
|
| 19 |
+
intermediate_size=512,
|
| 20 |
+
num_hidden_layers=2,
|
| 21 |
+
num_attention_heads=4,
|
| 22 |
+
num_key_value_heads=2,
|
| 23 |
+
head_dim=64,
|
| 24 |
+
max_position_embeddings=4096,
|
| 25 |
+
)
|
| 26 |
+
real = json.load(open(args.real_config))
|
| 27 |
+
real.pop("quantization_config", None)
|
| 28 |
+
for key in ("architectures", "transformers_version", "torch_dtype", "dtype"):
|
| 29 |
+
real.pop(key, None)
|
| 30 |
+
if "text_config" in real: # a VLM: shrink the decoder and the vision tower in their own sections
|
| 31 |
+
text = {k: v for k, v in real["text_config"].items() if k not in ("architectures", "quantization_config")}
|
| 32 |
+
shrunk = dict(
|
| 33 |
+
real,
|
| 34 |
+
text_config=dict(text, **text_sizes),
|
| 35 |
+
vision_config=dict(
|
| 36 |
+
real["vision_config"], hidden_size=64, intermediate_size=128, num_hidden_layers=2, num_attention_heads=2,
|
| 37 |
+
head_dim=32, image_size=112,
|
| 38 |
+
),
|
| 39 |
+
image_token_index=1000,
|
| 40 |
+
)
|
| 41 |
+
else:
|
| 42 |
+
shrunk = dict(real, **text_sizes)
|
| 43 |
+
for token in ("bos_token_id", "eos_token_id", "pad_token_id"):
|
| 44 |
+
for section in (shrunk, shrunk.get("text_config", {})):
|
| 45 |
+
if section.get(token) is not None:
|
| 46 |
+
section[token] = [1] if isinstance(section[token], list) else 1
|
| 47 |
+
with tempfile.TemporaryDirectory() as tmp: # parsed from disk, the way the real config.json is
|
| 48 |
+
json.dump(shrunk, open(f"{tmp}/config.json", "w"))
|
| 49 |
+
config = AutoConfig.from_pretrained(tmp)
|
| 50 |
+
model_cls = AutoModelForImageTextToText if "vision_config" in config.to_dict() else AutoModelForCausalLM
|
| 51 |
+
torch.manual_seed(0)
|
| 52 |
+
model = model_cls.from_config(config).to(torch.bfloat16)
|
| 53 |
+
model.save_pretrained(args.out)
|
| 54 |
+
print(type(model).__name__)
|
scripts/calibrate_experts.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Per-expert activation amax of a bf16 MoE, as a modelopt calibration run collects it: the routed
|
| 2 |
+
hidden rows each expert's gate/up sees, and the intermediate its down sees. Writes
|
| 3 |
+
{"<layer>.<expert>.gate_up": amax, "<layer>.<expert>.down": amax, "<linear name>": input amax, "<linear name>:out": output amax}."""
|
| 4 |
+
|
| 5 |
+
import argparse
|
| 6 |
+
import json
|
| 7 |
+
import re
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
from transformers import AutoConfig, AutoModelForCausalLM, AutoModelForImageTextToText
|
| 11 |
+
|
| 12 |
+
parser = argparse.ArgumentParser()
|
| 13 |
+
parser.add_argument("--bf16", required=True)
|
| 14 |
+
parser.add_argument("--out", required=True)
|
| 15 |
+
args = parser.parse_args()
|
| 16 |
+
|
| 17 |
+
vlm = "vision_config" in AutoConfig.from_pretrained(args.bf16).to_dict()
|
| 18 |
+
model_cls = AutoModelForImageTextToText if vlm else AutoModelForCausalLM
|
| 19 |
+
model = model_cls.from_pretrained(args.bf16, dtype=torch.bfloat16, device_map="cuda").eval()
|
| 20 |
+
amax = {}
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def hook(name):
|
| 24 |
+
layer = re.search(r"layers\.(\d+)\.", name).group(1)
|
| 25 |
+
|
| 26 |
+
def record(module, inputs, _output):
|
| 27 |
+
hidden, top_k_index = inputs[0], inputs[1]
|
| 28 |
+
for expert in range(module.num_experts):
|
| 29 |
+
rows = hidden[(top_k_index == expert).any(-1)]
|
| 30 |
+
if rows.numel() == 0:
|
| 31 |
+
continue
|
| 32 |
+
gate, up = (rows @ module.gate_up_proj[expert].T).chunk(2, dim=-1)
|
| 33 |
+
inter = module.act_fn(gate) * up
|
| 34 |
+
for key, value in ((f"{layer}.{expert}.gate_up", rows), (f"{layer}.{expert}.down", inter)):
|
| 35 |
+
amax[key] = max(amax.get(key, 0.0), value.abs().max().item())
|
| 36 |
+
|
| 37 |
+
return record
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def linear_hook(name):
|
| 41 |
+
def record(module, inputs, output):
|
| 42 |
+
amax[name] = max(amax.get(name, 0.0), inputs[0].abs().max().item())
|
| 43 |
+
# the output too: a k/v projection's is what an FP8 KV cache's `k_scale`/`v_scale` cover
|
| 44 |
+
amax[f"{name}:out"] = max(amax.get(f"{name}:out", 0.0), output.abs().max().item())
|
| 45 |
+
|
| 46 |
+
return record
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
for name, module in model.named_modules():
|
| 50 |
+
if name.endswith("mlp.experts"):
|
| 51 |
+
module.register_forward_hook(hook(name))
|
| 52 |
+
elif isinstance(module, torch.nn.Linear): # dense linears, for exports that quantize them too
|
| 53 |
+
module.register_forward_hook(linear_hook(name))
|
| 54 |
+
|
| 55 |
+
torch.manual_seed(0)
|
| 56 |
+
with torch.no_grad():
|
| 57 |
+
for _ in range(8):
|
| 58 |
+
model(torch.randint(3, model.config.get_text_config().vocab_size, (4, 128), device="cuda"))
|
| 59 |
+
json.dump(amax, open(args.out, "w"), indent=1)
|
| 60 |
+
print(f"{len(amax)} amax values")
|
scripts/extract_layout.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The key layout of a safetensors checkpoint, read from shard headers only (no tensor data).
|
| 2 |
+
|
| 3 |
+
Keys are normalized by replacing every integer path component with `N`, so a layout is the set of
|
| 4 |
+
distinct patterns with their dtypes, the number of keys behind each, and the layer indices each appears
|
| 5 |
+
at. `compare(real, tiny)` is what the tiny-checkpoint builders assert against.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import json
|
| 9 |
+
import re
|
| 10 |
+
import struct
|
| 11 |
+
import sys
|
| 12 |
+
from collections import defaultdict
|
| 13 |
+
from pathlib import Path
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def read_header(path):
|
| 17 |
+
with open(path, "rb") as f:
|
| 18 |
+
(n,) = struct.unpack("<Q", f.read(8))
|
| 19 |
+
header = json.loads(f.read(n))
|
| 20 |
+
header.pop("__metadata__", None)
|
| 21 |
+
return header
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def normalize(key):
|
| 25 |
+
return re.sub(r"(?<=\.)\d+(?=\.|$)", "N", key)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def headers(source):
|
| 29 |
+
"""`{key: {"dtype", "shape"}}` of a local checkpoint directory, or of a hub repo read remotely."""
|
| 30 |
+
if Path(source).is_dir():
|
| 31 |
+
for shard in sorted(Path(source).glob("*.safetensors")):
|
| 32 |
+
yield from read_header(shard).items()
|
| 33 |
+
return
|
| 34 |
+
from huggingface_hub import HfApi
|
| 35 |
+
|
| 36 |
+
for shard in HfApi().get_safetensors_metadata(source).files_metadata.values():
|
| 37 |
+
for key, tensor in shard.tensors.items():
|
| 38 |
+
yield key, {"dtype": tensor.dtype, "shape": tensor.shape}
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def layout(source):
|
| 42 |
+
patterns = defaultdict(lambda: {"dtypes": set(), "count": 0, "layers": set(), "ndims": set()})
|
| 43 |
+
for key, info in headers(source):
|
| 44 |
+
entry = patterns[normalize(key)]
|
| 45 |
+
entry["dtypes"].add(info["dtype"])
|
| 46 |
+
entry["ndims"].add(len(info["shape"]))
|
| 47 |
+
entry["count"] += 1
|
| 48 |
+
if m := re.search(r"layers\.(\d+)\.", key):
|
| 49 |
+
entry["layers"].add(int(m.group(1)))
|
| 50 |
+
return {
|
| 51 |
+
p: {"dtypes": sorted(e["dtypes"]), "count": e["count"], "layers": sorted(e["layers"]), "ndims": sorted(e["ndims"])}
|
| 52 |
+
for p, e in sorted(patterns.items())
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
if __name__ == "__main__":
|
| 57 |
+
out = layout(sys.argv[1])
|
| 58 |
+
json.dump(out, open(sys.argv[2], "w"), indent=1)
|
| 59 |
+
print(f"{len(out)} patterns")
|
scripts/quantize_dense_nvfp4.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""A tiny bf16 Llama written as nvidia/Llama-3.1-8B-Instruct-FP4 ships it, with modelopt's own NVFP4
|
| 2 |
+
quantizer: every decoder linear NVFP4 (packed U8 weight, E4M3 `weight_scale` per 16, F32 `weight_scale_2`),
|
| 3 |
+
a calibrated F32 `input_scale` per linear, and the FP8 KV cache's `k_proj.k_scale` / `v_proj.v_scale`
|
| 4 |
+
(output amax / 448). The real `quantization_config` and `hf_quant_config.json`; also <out>/dequantized,
|
| 5 |
+
the logits reference. Run in the producer venv (nvidia-modelopt)."""
|
| 6 |
+
|
| 7 |
+
import argparse
|
| 8 |
+
import json
|
| 9 |
+
import re
|
| 10 |
+
import shutil
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
+
import torch
|
| 14 |
+
from modelopt.torch.quantization.qtensor import NVFP4QTensor
|
| 15 |
+
from safetensors.torch import load_file, save_file
|
| 16 |
+
|
| 17 |
+
parser = argparse.ArgumentParser()
|
| 18 |
+
parser.add_argument("--bf16", required=True)
|
| 19 |
+
parser.add_argument("--real", required=True, help="directory with the real config.json + hf_quant_config.json")
|
| 20 |
+
parser.add_argument("--calibration", required=True)
|
| 21 |
+
parser.add_argument("--out", required=True)
|
| 22 |
+
args = parser.parse_args()
|
| 23 |
+
|
| 24 |
+
source = load_file(f"{args.bf16}/model.safetensors")
|
| 25 |
+
calibration = json.load(open(args.calibration))
|
| 26 |
+
linear = re.compile(r"model\.layers\.\d+\.(self_attn|mlp)\.\w+_proj\.weight")
|
| 27 |
+
quantized, dequantized = {}, dict(source)
|
| 28 |
+
for key, tensor in source.items():
|
| 29 |
+
if not linear.fullmatch(key):
|
| 30 |
+
quantized[key] = tensor
|
| 31 |
+
continue
|
| 32 |
+
module = key.removesuffix(".weight")
|
| 33 |
+
qtensor, block_scale, global_scale = NVFP4QTensor.quantize(tensor.float(), 16)
|
| 34 |
+
quantized[key] = qtensor._quantized_data
|
| 35 |
+
quantized[f"{module}.weight_scale"] = block_scale
|
| 36 |
+
quantized[f"{module}.weight_scale_2"] = global_scale.reshape(()).float()
|
| 37 |
+
quantized[f"{module}.input_scale"] = torch.tensor(calibration[module] / (6.0 * 448.0), dtype=torch.float32)
|
| 38 |
+
for proj in ("k", "v"):
|
| 39 |
+
if module.endswith(f".{proj}_proj"):
|
| 40 |
+
quantized[f"{module}.{proj}_scale"] = torch.tensor(calibration[f"{module}:out"] / 448.0, dtype=torch.float32)
|
| 41 |
+
dequantized[key] = NVFP4QTensor(tensor.shape, torch.float32, qtensor._quantized_data).dequantize(
|
| 42 |
+
dtype=torch.float32, scale=block_scale, double_scale=global_scale, block_sizes={-1: 16}
|
| 43 |
+
).to(torch.bfloat16)
|
| 44 |
+
|
| 45 |
+
out = Path(args.out)
|
| 46 |
+
real = json.load(open(f"{args.real}/config.json"))
|
| 47 |
+
for name, tensors in (("nvfp4", quantized), ("dequantized", dequantized)):
|
| 48 |
+
(out / name).mkdir(parents=True, exist_ok=True)
|
| 49 |
+
save_file({k: v.contiguous() for k, v in tensors.items()}, out / name / "model.safetensors", metadata={"format": "pt"})
|
| 50 |
+
config = json.load(open(f"{args.bf16}/config.json"))
|
| 51 |
+
if name == "nvfp4":
|
| 52 |
+
config["quantization_config"] = real["quantization_config"]
|
| 53 |
+
shutil.copy(f"{args.real}/hf_quant_config.json", out / name / "hf_quant_config.json")
|
| 54 |
+
json.dump(config, open(out / name / "config.json", "w"), indent=2)
|
| 55 |
+
shutil.copy(f"{args.bf16}/generation_config.json", out / name / "generation_config.json")
|
| 56 |
+
print(f"wrote {len(quantized)} tensors")
|