Spaces:
Running on Zero
Running on Zero
File size: 3,536 Bytes
b1aba72 51e9502 b1aba72 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | """
Model loading for HuggingFace Spaces ZeroGPU deployment.
"""
import torch
from transformers import (
AutoTokenizer,
AutoModelForCausalLM,
BitsAndBytesConfig,
pipeline,
)
from langchain_huggingface import HuggingFaceEmbeddings
from config import MODEL_ID, EMBEDDING_MODEL_ID, MAX_NEW_TOKENS
from logging_config import get_logger
logger = get_logger(__name__)
# --- Tokenizer -------------------------------
logger.info(f"Loading tokenizer: {MODEL_ID}")
tokenizer = AutoTokenizer.from_pretrained(
MODEL_ID,
trust_remote_code=True, # Qwen has a custom tokenizer class
clean_up_tokenization_spaces=False, # BPE tokenizers must NOT strip spaces before punctuation — it
# corrupts output. This flag is meant for WordPiece (BERT) only.
)
# --- Quantization config -----------------------------------
# 4-bit NF4 (Normal Float 4-bit) is the most accurate 4-bit format for LLM weights.
# double_quant saves an additional ~0.4 bits per parameter on the quantization constants themselves.
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
)
# --- Model --------------------------------------------------
# device_map={"": 0} pins the ENTIRE model onto GPU slot 0 at load time.
# This is the module-scope CUDA placement ZeroGPU requires — the
# `spaces` package (imported first in app.py) intercepts this and
# handles it correctly even though no physical GPU is attached yet
# during this import.
logger.info(f"Loading model (4-bit NF4 quantized) — device_map={{'':0}}…")
model = AutoModelForCausalLM.from_pretrained(
MODEL_ID,
quantization_config=quantization_config,
device_map={"": 0}, # Module-scope GPU placement (see docstring)
low_cpu_mem_usage=True, # Streams weights from disk, avoids RAM spike
trust_remote_code=True,
)
model.eval() # Disable dropout — we're doing inference only
# NOTE: No torch.compile() here.
# ZeroGPU does not support torch.compile. Calling it would either
# silently fail to provide speedup or raise an error when the first
# @spaces.GPU-decorated call tries to actually run on a real GPU worker.
# --- Text generation pipeline --------------------------------
# The pipeline object itself does not run any CUDA kernels at
# construction time — it only wraps tokenizer + model + generation
# config. Building it here at module scope is safe. The ACTUAL
# inference call (invoking this pipeline) happens inside the
# @spaces.GPU-decorated function in generation.py — never here.
logger.info("Building text generation pipeline…")
text_gen_pipeline = pipeline(
task="text-generation",
model=model,
tokenizer=tokenizer,
max_new_tokens=MAX_NEW_TOKENS,
max_length=None, # Override model's default max_length (often 20) to silence the HF conflict warning
do_sample=False, # Greedy decoding: deterministic, faithful to context
return_full_text=False, # Return only newly generated tokens, not the prompt
)
# --- Embedding model ---------------------------------------------
# MiniLM stays on CPU — it's fast enough there and doesn't need the
# GPU slot, which should be reserved for the LLM's generation calls.
logger.info("Loading embedding model…")
embedding_model = HuggingFaceEmbeddings(
model_name=EMBEDDING_MODEL_ID,
model_kwargs={"device": "cpu"},
encode_kwargs={"normalize_embeddings": True},
)
logger.info("All models ready.")
|