Fase 5: 21_quantize_nvfp4.py - soporte para calibracion mezclada con ultrachat_200k
Nueva variable NUM_ULTRACHAT_SAMPLES (default 0, sin cambio de comportamiento): cuando > 0, mezcla esa cantidad de muestras de HuggingFaceH4/ultrachat_200k (split train_sft, el mismo corpus/split que uso RedHatAI en su receta de referencia) con (NUM_CALIBRATION_SAMPLES - NUM_ULTRACHAT_SAMPLES) muestras de TRAIN_DATA_PATH, concatenadas y mezcladas (shuffle, seed=42) antes de tokenizar para calibracion. Hipotesis a probar (decision del usuario tras ver que el aislamiento sin --speculative-config descarto al speculative decoding como causante de la regresion, y antes de simplemente aumentar la cantidad de muestras propias): la regresion podria venir de poca DIVERSIDAD tematica en la calibracion (solo conversaciones angostas de los 5 MCPs/skills del proyecto) en vez de poca cantidad de muestras. La porcion de TRAIN_DATA_PATH sigue usando el mismo seed=42, asi que con NUM_ULTRACHAT_SAMPLES=256 y NUM_CALIBRATION_SAMPLES=512, las 256 muestras propias son identicas a las del primer intento (256 solo propias).
This commit is contained in:
@@ -13,6 +13,14 @@ una muestra de data/train.jsonl (el propio dataset de fine-tuning) en vez del
|
|||||||
corpus generico ultrachat_200k, aplicando el chat template de PRODUCCION (el que
|
corpus generico ultrachat_200k, aplicando el chat template de PRODUCCION (el que
|
||||||
ya trae MODEL_PATH, no el de masking de training).
|
ya trae MODEL_PATH, no el de masking de training).
|
||||||
|
|
||||||
|
Soporta ademas NUM_ULTRACHAT_SAMPLES (default 0) para mezclar N muestras de
|
||||||
|
HuggingFaceH4/ultrachat_200k (split train_sft, el mismo corpus/split que uso
|
||||||
|
RedHatAI) con (NUM_CALIBRATION_SAMPLES - NUM_ULTRACHAT_SAMPLES) muestras de
|
||||||
|
TRAIN_DATA_PATH, concatenadas y mezcladas (shuffle, mismo seed=42) antes de
|
||||||
|
tokenizar -- experimento para probar si la regresion de calidad viene de poca
|
||||||
|
DIVERSIDAD tematica en la calibracion (solo conversaciones angostas de los 5
|
||||||
|
MCPs/skills) en vez de poca CANTIDAD de muestras.
|
||||||
|
|
||||||
Algoritmo:
|
Algoritmo:
|
||||||
1. Cargar Qwen3_5MoeForConditionalGeneration.from_pretrained(MODEL_PATH,
|
1. Cargar Qwen3_5MoeForConditionalGeneration.from_pretrained(MODEL_PATH,
|
||||||
dtype="auto") + AutoProcessor.from_pretrained(MODEL_PATH).
|
dtype="auto") + AutoProcessor.from_pretrained(MODEL_PATH).
|
||||||
@@ -77,6 +85,16 @@ MODEL_PATH = Path(os.environ.get("MODEL_PATH", "/workspace/ft-models/Qwen3.6-35B
|
|||||||
OUTPUT_PATH = Path(os.environ.get("OUTPUT_PATH", "/workspace/ft-models/Qwen3.6-35B-A3B-mcp-NVFP4"))
|
OUTPUT_PATH = Path(os.environ.get("OUTPUT_PATH", "/workspace/ft-models/Qwen3.6-35B-A3B-mcp-NVFP4"))
|
||||||
TRAIN_DATA_PATH = Path(os.environ.get("TRAIN_DATA_PATH", str(REPO_ROOT / "data" / "train.jsonl")))
|
TRAIN_DATA_PATH = Path(os.environ.get("TRAIN_DATA_PATH", str(REPO_ROOT / "data" / "train.jsonl")))
|
||||||
NUM_CALIBRATION_SAMPLES = int(os.environ.get("NUM_CALIBRATION_SAMPLES", "256"))
|
NUM_CALIBRATION_SAMPLES = int(os.environ.get("NUM_CALIBRATION_SAMPLES", "256"))
|
||||||
|
# Muestras adicionales de un corpus generico y amplio (mismo dataset/split que uso
|
||||||
|
# RedHatAI en su receta de referencia), mezcladas con las de TRAIN_DATA_PATH.
|
||||||
|
# Hipotesis a probar: la regresion de calidad no es por CANTIDAD de muestras sino
|
||||||
|
# por DIVERSIDAD -- calibrar solo con conversaciones angostas de los 5 MCPs/skills
|
||||||
|
# del proyecto podria dejar a los 256 expertos MoE con una vision demasiado
|
||||||
|
# estrecha. NUM_CALIBRATION_SAMPLES sigue siendo el TOTAL; la porcion de
|
||||||
|
# TRAIN_DATA_PATH se reduce a (NUM_CALIBRATION_SAMPLES - NUM_ULTRACHAT_SAMPLES).
|
||||||
|
NUM_ULTRACHAT_SAMPLES = int(os.environ.get("NUM_ULTRACHAT_SAMPLES", "0"))
|
||||||
|
ULTRACHAT_DATASET = "HuggingFaceH4/ultrachat_200k"
|
||||||
|
ULTRACHAT_SPLIT = "train_sft"
|
||||||
MAX_SEQUENCE_LENGTH = int(os.environ.get("MAX_SEQUENCE_LENGTH", "8192"))
|
MAX_SEQUENCE_LENGTH = int(os.environ.get("MAX_SEQUENCE_LENGTH", "8192"))
|
||||||
CALIBRATION_SEED = 42
|
CALIBRATION_SEED = 42
|
||||||
|
|
||||||
@@ -95,7 +113,7 @@ QUANTIZATION_IGNORE = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def load_calibration_dataset(tokenizer):
|
def load_train_examples(n):
|
||||||
print(f"[INFO] cargando ejemplos de calibracion desde {TRAIN_DATA_PATH}")
|
print(f"[INFO] cargando ejemplos de calibracion desde {TRAIN_DATA_PATH}")
|
||||||
examples = []
|
examples = []
|
||||||
with open(TRAIN_DATA_PATH, encoding="utf-8") as f:
|
with open(TRAIN_DATA_PATH, encoding="utf-8") as f:
|
||||||
@@ -107,21 +125,55 @@ def load_calibration_dataset(tokenizer):
|
|||||||
|
|
||||||
rng = random.Random(CALIBRATION_SEED)
|
rng = random.Random(CALIBRATION_SEED)
|
||||||
rng.shuffle(examples)
|
rng.shuffle(examples)
|
||||||
sampled = examples[:NUM_CALIBRATION_SAMPLES]
|
sampled = examples[:n]
|
||||||
if len(sampled) < NUM_CALIBRATION_SAMPLES:
|
if len(sampled) < n:
|
||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
f"se pidieron {NUM_CALIBRATION_SAMPLES} muestras de calibracion pero "
|
f"se pidieron {n} muestras de {TRAIN_DATA_PATH} pero solo tiene {len(examples)} ejemplos"
|
||||||
f"{TRAIN_DATA_PATH} solo tiene {len(examples)} ejemplos"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
bucket_counts = Counter(ex.get("meta", {}).get("bucket", "?") for ex in sampled)
|
bucket_counts = Counter(ex.get("meta", {}).get("bucket", "?") for ex in sampled)
|
||||||
print(f"[INFO] distribucion de buckets en la muestra de calibracion: {dict(bucket_counts)}")
|
print(f"[INFO] distribucion de buckets (train.jsonl): {dict(bucket_counts)}")
|
||||||
|
return sampled
|
||||||
|
|
||||||
|
|
||||||
|
def load_ultrachat_examples(n):
|
||||||
|
from datasets import load_dataset
|
||||||
|
|
||||||
|
print(f"[INFO] cargando {n} muestras de {ULTRACHAT_DATASET} (split={ULTRACHAT_SPLIT})")
|
||||||
|
ds = load_dataset(ULTRACHAT_DATASET, split=ULTRACHAT_SPLIT)
|
||||||
|
ds = ds.shuffle(seed=CALIBRATION_SEED).select(range(n))
|
||||||
|
examples = [{"messages": row["messages"]} for row in ds]
|
||||||
|
print(f"[INFO] {len(examples)} muestras de {ULTRACHAT_DATASET} cargadas")
|
||||||
|
return examples
|
||||||
|
|
||||||
|
|
||||||
|
def load_calibration_dataset(tokenizer):
|
||||||
|
num_ultrachat = NUM_ULTRACHAT_SAMPLES
|
||||||
|
num_train = NUM_CALIBRATION_SAMPLES - num_ultrachat
|
||||||
|
if num_train < 0:
|
||||||
|
raise AssertionError(
|
||||||
|
f"NUM_ULTRACHAT_SAMPLES ({num_ultrachat}) no puede superar "
|
||||||
|
f"NUM_CALIBRATION_SAMPLES ({NUM_CALIBRATION_SAMPLES})"
|
||||||
|
)
|
||||||
|
|
||||||
|
examples = []
|
||||||
|
if num_train > 0:
|
||||||
|
examples.extend(load_train_examples(num_train))
|
||||||
|
if num_ultrachat > 0:
|
||||||
|
examples.extend(load_ultrachat_examples(num_ultrachat))
|
||||||
|
|
||||||
|
rng = random.Random(CALIBRATION_SEED)
|
||||||
|
rng.shuffle(examples)
|
||||||
|
print(
|
||||||
|
f"[INFO] muestra de calibracion mezclada: {num_train} de {TRAIN_DATA_PATH.name} + "
|
||||||
|
f"{num_ultrachat} de {ULTRACHAT_DATASET}, {len(examples)} total, orden mezclado (seed={CALIBRATION_SEED})"
|
||||||
|
)
|
||||||
|
|
||||||
input_ids_list = []
|
input_ids_list = []
|
||||||
attention_mask_list = []
|
attention_mask_list = []
|
||||||
for ex in sampled:
|
for ex in examples:
|
||||||
text = tokenizer.apply_chat_template(
|
text = tokenizer.apply_chat_template(
|
||||||
ex["messages"],
|
ex["messages"],
|
||||||
tools=ex.get("tools"),
|
tools=ex.get("tools"),
|
||||||
@@ -327,6 +379,7 @@ def main():
|
|||||||
else:
|
else:
|
||||||
print(f"[INFO] TRAIN_DATA_PATH={TRAIN_DATA_PATH}")
|
print(f"[INFO] TRAIN_DATA_PATH={TRAIN_DATA_PATH}")
|
||||||
print(f"[INFO] NUM_CALIBRATION_SAMPLES={NUM_CALIBRATION_SAMPLES} MAX_SEQUENCE_LENGTH={MAX_SEQUENCE_LENGTH}")
|
print(f"[INFO] NUM_CALIBRATION_SAMPLES={NUM_CALIBRATION_SAMPLES} MAX_SEQUENCE_LENGTH={MAX_SEQUENCE_LENGTH}")
|
||||||
|
print(f"[INFO] NUM_ULTRACHAT_SAMPLES={NUM_ULTRACHAT_SAMPLES}")
|
||||||
|
|
||||||
print(f"[INFO] cargando processor desde {MODEL_PATH}")
|
print(f"[INFO] cargando processor desde {MODEL_PATH}")
|
||||||
processor = AutoProcessor.from_pretrained(str(MODEL_PATH), trust_remote_code=True)
|
processor = AutoProcessor.from_pretrained(str(MODEL_PATH), trust_remote_code=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user