DS DevShelfHub Projects · AI tools
Tutorials / Fine-tuning / Deployment
Fine-tuning Intermediate · 10 min read Page 9 of 10

Deploying Fine-tuned Models

Merge adapters, serve with vLLM, quantize for production. Deployment options and cost comparison.

By DevShelfHub

Series progress 9 / 10
Fine-tuning deployment tutorial — Deploying Fine-tuned Models

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:

python
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.

Continue learning with our evaluation tutorial and all tutorials.