mthuy's picture
Upload 2 files
e3227e0 verified
Raw
History Blame Contribute Delete
5.08 kB
"""
app.py - Hugging Face Spaces (CPU) deployment for the fine-tuned Llama 3.2 1B LoRA.
This runs WITHOUT unsloth or bitsandbytes, because both require a GPU/CUDA.
Instead it loads a full-precision base model on CPU with `transformers` and
applies your LoRA adapter on top with `peft`, then merges the two so CPU
generation is a little faster.
The adapter repo (mthuy/dssp-llama-1b-lora) is public, so no token is needed.
"""
import os
import torch
import gradio as gr
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
# ------------------------------------------------------------
# Configuration
# ------------------------------------------------------------
# Your fine-tuned LoRA adapter (also holds the tokenizer + chat template).
ADAPTER_REPO_ID = "mthuy/dssp-llama-1b-lora"
# Full-precision base model to apply the adapter on top of.
# The adapter was trained on "unsloth/Llama-3.2-1B-Instruct-bnb-4bit" (4-bit),
# which can't load on CPU. This is the same model, un-quantized and ungated.
BASE_MODEL_ID = "unsloth/Llama-3.2-1B-Instruct"
# Keep CPU generation responsive.
torch.set_num_threads(os.cpu_count() or 2)
# ------------------------------------------------------------
# Load model + tokenizer once at startup
# ------------------------------------------------------------
print("Loading tokenizer from adapter repo...")
tokenizer = AutoTokenizer.from_pretrained(ADAPTER_REPO_ID)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
print("Loading base model on CPU (float32)...")
base_model = AutoModelForCausalLM.from_pretrained(
BASE_MODEL_ID,
torch_dtype=torch.float32, # float32 is the safe choice on CPU
low_cpu_mem_usage=True,
)
print("Applying LoRA adapter...")
model = PeftModel.from_pretrained(base_model, ADAPTER_REPO_ID)
# Merge LoRA weights into the base for faster CPU inference, then drop PEFT wrappers.
model = model.merge_and_unload()
model.eval()
print("Model ready.")
# ------------------------------------------------------------
# Inference
# ------------------------------------------------------------
def ask_model(question, max_new_tokens=60, temperature=0.0):
if not question or not question.strip():
return ""
messages = [{"role": "user", "content": question}]
inputs = tokenizer.apply_chat_template(
messages,
tokenize=True,
add_generation_prompt=True,
return_tensors="pt",
return_dict=True,
)
# Everything stays on CPU.
generation_args = {
"max_new_tokens": int(max_new_tokens),
"pad_token_id": tokenizer.eos_token_id,
"eos_token_id": tokenizer.eos_token_id,
"use_cache": True,
}
if temperature and temperature > 0:
generation_args["do_sample"] = True
generation_args["temperature"] = float(temperature)
generation_args["top_p"] = 0.9
else:
generation_args["do_sample"] = False
with torch.inference_mode():
outputs = model.generate(**inputs, **generation_args)
input_length = inputs["input_ids"].shape[-1]
new_tokens = outputs[0][input_length:]
response = tokenizer.decode(new_tokens, skip_special_tokens=True)
return response.strip()
# ------------------------------------------------------------
# Gradio UI
# ------------------------------------------------------------
with gr.Blocks(title="DSSP Fine-Tuned Llama 1B Demo") as demo:
gr.Markdown(
"""
# DSSP Fine-Tuned Llama 1B Demo (Dog Food Edition)
This Space runs a Llama 3.2 1B model fine-tuned with a LoRA adapter on a
handful of dog food / canine nutrition facts. It runs on **CPU**, so the
first answer after a cold start can take a little while.
Adapter: `mthuy/dssp-llama-1b-lora`
"""
)
question = gr.Textbox(
label="Question",
lines=3,
placeholder="Example: Is chocolate safe for dogs?"
)
with gr.Row():
max_new_tokens = gr.Slider(
minimum=20,
maximum=100,
value=60,
step=10,
label="Max new tokens"
)
temperature = gr.Slider(
minimum=0.0,
maximum=1.0,
value=0.0,
step=0.1,
label="Temperature"
)
answer = gr.Textbox(
label="Model answer",
lines=6
)
submit = gr.Button("Ask model")
submit.click(
fn=ask_model,
inputs=[question, max_new_tokens, temperature],
outputs=answer
)
gr.Examples(
examples=[
["Is chocolate safe for dogs?", 60, 0.0],
["Can dogs eat grapes?", 60, 0.0],
["Are onions safe for dogs?", 60, 0.0],
["What is the most important nutrient in a dog's diet?", 60, 0.0],
["How often should an adult dog be fed?", 60, 0.0],
],
inputs=[question, max_new_tokens, temperature],
outputs=answer,
fn=ask_model,
)
if __name__ == "__main__":
demo.launch()