Step 1: Merge adapters
In training, you saved the tiny LoRA adapter weights separately. For deployment, merge the adapter back into the base model weights:
from peft import AutoPeftModelForCausalLM
# Load the checkpoint with LoRA adapters
model = AutoPeftModelForCausalLM.from_pretrained(
"./fine_tuned_model",
device_map="auto",
)
# Merge adapters into base weights
merged_model = model.merge_and_unload()
# Save merged model (single file, ready for deployment)
merged_model.save_pretrained("./merged_model")
tokenizer.save_pretrained("./merged_model")
Why merge? Faster inference (no adapter inference overhead). Easier deployment (single model, not base+adapter). Trade-off: larger file size.
Step 2: Inference
Use the merged model for inference:
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
base_model_id = "mistralai/Mistral-7B-Instruct-v0.2"
adapter_path = "./fine_tuned_model/adapter"
tokenizer = AutoTokenizer.from_pretrained(base_model_id)
base_model = AutoModelForCausalLM.from_pretrained(
base_model_id, torch_dtype=torch.float16, device_map="auto"
)
model = PeftModel.from_pretrained(base_model, adapter_path)
# Merge adapter into base weights for faster inference
model = model.merge_and_unload()
model.save_pretrained("./merged_model")
tokenizer.save_pretrained("./merged_model")
def chat(user_message: str) -> str:
messages = [
{"role": "system", "content": "You are a helpful customer support agent."},
{"role": "user", "content": user_message},
]
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
output = model.generate(**inputs, max_new_tokens=200, do_sample=False)
return tokenizer.decode(output[0][len(inputs["input_ids"][0]):], skip_special_tokens=True)
print(chat("How do I reset my password?"))
Serving options
Ollama (Local / on-device)
Simple, fast for local dev. Convert your model to GGUF, run ollama serve. Zero latency, zero cloud cost. Limited to single machine.
HuggingFace Inference API
Upload model, get an API endpoint. Automatic scaling, easy setup. Cost: $0.01–0.10 per 1M tokens. Good for small-to-medium scale.
vLLM (Self-hosted)
High-performance inference server. Batches requests, optimizes memory, 10x faster than naive inference. Deploy on your own servers or cloud VMs.
Modal / RunPod (Serverless)
Deploy with one command, auto-scales. Pay only for inference time. Good for variable workloads. Cost: $0.20–1/hour for GPU.
AWS / GCP / Azure VMs
Full control, integrates with your infra. Higher operational burden. Cost: $1–5/hour for GPU.
Quantization for deployment
Quantization shrinks model size and speeds up inference. Two popular formats:
GGUF (Quantized General Uniform Format)
4-bit or 8-bit quantized model. Smaller (2–4GB for 7B model), fast inference, works with Ollama. Use for local / on-device deployment.
GPTQ
Another quantization method. Slightly higher quality than GGUF but more complex. Use if GGUF quality isn't sufficient.
Cost comparison
| Option | GPU Cost | Setup | Best For |
|---|---|---|---|
| Ollama (local) | $0 (your hardware) | Trivial (1 command) | Dev, internal tools |
| HuggingFace Inference API | $0.01–0.10 / 1M tokens | Simple (upload model) | Low volume, easy |
| Modal / RunPod | $0.20–0.50/hour | Easy (code or UI) | Variable workload, scaling |
| vLLM on self-hosted GPU | $1–5/hour | Moderate (k8s, docker) | High volume, control |
| AWS / GCP VMs | $0.50–2/hour | Complex (full setup) | Enterprise, custom needs |
FastAPI wrapper pattern
Wrap your inference function in a simple REST API for easy integration:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class QueryRequest(BaseModel):
message: str
@app.post("/chat")
async def chat_endpoint(request: QueryRequest):
response = chat(request.message)
return {"response": response}
# Run: uvicorn script:app --reload
This gives you a REST API instantly. Deploy with Docker, scale with Kubernetes. Add auth, rate limiting, logging as needed.
Deployment decision rule
For hobbyists: Ollama (free, local). For startups: HuggingFace API or Modal (simple, pay-as-you-go). For scale: vLLM on your own hardware (control, cost). Never deploy the bare merged model directly; always wrap in an API or inference server.
Deployment architecture patterns
The choice between merging adapters and serving them separately has significant architectural implications. Merged models are simpler: one file, one deployment, no adapter management. But if you fine-tune frequently or serve multiple task variants, keeping adapters separate with a framework like vLLM (which supports runtime LoRA loading) reduces GPU memory costs dramatically — one base model in memory can serve ten different fine-tuned behaviors.
For production deployments, quantization is nearly always the right choice. A 4-bit quantized 7B model fits in 4GB of VRAM with negligible quality loss for most tasks. This means you can serve it on an RTX 3060 or even a laptop GPU. The path from training to deployment is: train with QLoRA, merge adapters, quantize to GGUF or GPTQ, deploy with Ollama (local) or vLLM (server). Each step reduces size and increases speed. For applications that combine fine-tuned models with live web data, pairing your deployed model with Firecrawl for real-time content extraction creates a powerful hybrid system.
Model Deployment FAQ
How do I merge LoRA adapters with the base model?
Use the merge_and_unload method from the PEFT library. This folds the adapter weights into the base model, producing a single model file with no inference overhead.
What is vLLM and why use it for serving?
vLLM is a high-throughput inference engine that uses PagedAttention for efficient memory management. It serves fine-tuned models 2-4x faster than naive HuggingFace inference.
Should I quantize my fine-tuned model for deployment?
Yes, if latency or cost matters. GPTQ or AWQ quantization to 4-bit reduces memory by 4x with minimal quality loss, letting you serve larger models on cheaper hardware.
What are the cheapest ways to deploy a fine-tuned model?
Use a quantized model on a single consumer GPU for small-scale serving. For production traffic, RunPod, Modal, or Together AI offer serverless GPU endpoints starting around $0.20 per hour.
Can I deploy a LoRA adapter without merging?
Yes. Frameworks like vLLM and text-generation-inference support loading LoRA adapters at runtime. This lets you serve multiple fine-tuned variants from a single base model.
Related tutorials
Continue learning with our evaluation tutorial and all tutorials.