import argparse import json import logging import pathlib import sys ROOT = pathlib.Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) import config config.setup_env() import torch import transformers import jlens class ProgressHandler(logging.Handler): def emit(self, record): if not record.args: return if record.msg.startswith(" prompt"): print( json.dumps( { "event": "progress", "done": record.args[0], "total": record.args[1], "seconds": record.args[4], } ), flush=True, ) elif record.msg.startswith(" resuming"): print( json.dumps( {"event": "resume", "done": record.args[0], "total": record.args[1]} ), flush=True, ) def main(): parser = argparse.ArgumentParser() parser.add_argument("--model", required=True) parser.add_argument("--device", required=True) parser.add_argument("--dtype", default="bf16") parser.add_argument("--quant", default=None) parser.add_argument("--prompts", required=True) parser.add_argument("--checkpoint", required=True) parser.add_argument("--out", required=True) parser.add_argument("--dim-batch", type=int, default=8) parser.add_argument("--max-seq-len", type=int, default=128) parser.add_argument("--source-layers", default=None) args = parser.parse_args() logging.basicConfig(level=logging.INFO, handlers=[ProgressHandler()]) prompts = json.loads(pathlib.Path(args.prompts).read_text(encoding="utf-8")) torch_dtype = torch.bfloat16 if args.dtype == "bf16" else torch.float16 kwargs = {"dtype": torch_dtype, "device_map": {"": args.device}} model_source = args.model if args.quant == "int8": kwargs["quantization_config"] = transformers.BitsAndBytesConfig(load_in_8bit=True) elif args.quant == "nf4": kwargs["quantization_config"] = transformers.BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch_dtype, bnb_4bit_use_double_quant=True, ) print(json.dumps({"event": "loading", "model": args.model, "device": args.device}), flush=True) hf_model = transformers.AutoModelForCausalLM.from_pretrained(model_source, **kwargs) tokenizer = transformers.AutoTokenizer.from_pretrained(model_source) model = jlens.from_hf(hf_model, tokenizer) source_layers = json.loads(args.source_layers) if args.source_layers else None # large models: the checkpoint (n_layers × d_model² × 4 B) can weigh hundreds # of MB — writing it after every prompt would wear the SSD for nothing. We # space it out to target ~150 MB of average writes per prompt. n_src = len(source_layers) if source_layers else model.n_layers - 1 ckpt_bytes = n_src * model.d_model**2 * 4 checkpoint_every = max(1, round(ckpt_bytes / 150e6)) lens = jlens.fit( model, prompts, source_layers=source_layers, dim_batch=args.dim_batch, max_seq_len=args.max_seq_len, checkpoint_path=args.checkpoint, checkpoint_every=checkpoint_every, ) lens.save(args.out) print(json.dumps({"event": "done", "out": args.out, "n_prompts": lens.n_prompts}), flush=True) main()