We are building an edge-deployment pruning pipeline that reads a HuggingFace model card, reasons over hardware constraints, and generates a layer-wise sparsity plan. An Oxlo.ai-hosted LLM acts as the strategist, then we apply the plan with PyTorch and verify the shrunk model still runs. It helps engineers avoid manual guesswork when squeezing transformers onto devices like a Raspberry Pi.
What you'll need
- Python 3.10+
pip install openai torch transformers huggingface_hub accelerate- An Oxlo.ai API key from https://portal.oxlo.ai
- A HuggingFace account if you want to push or pull gated models (optional; we will use public checkpoints)
Step 1: Initialize the Oxlo.ai client and hardware database
First we set up the OpenAI-compatible client pointing at Oxlo.ai and define a small registry of edge devices with RAM budgets.
from openai import OpenAI
import json
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
HARDWARE_DB = {
"rpi4": {"ram_gb": 4, "storage_gb": 32, "cpu_threads": 4},
"coral_tpu": {"ram_gb": 1, "storage_gb": 8, "cpu_threads": 4},
"nvidia_jetson_nano": {"ram_gb": 4, "storage_gb": 64, "cpu_threads": 4},
}
Step 2: Fetch model metadata from HuggingFace
We need layer counts, hidden sizes, and parameter counts so the LLM can propose realistic sparsity ratios instead of guessing.
from transformers import AutoConfig
def get_model_metadata(model_id: str):
config = AutoConfig.from_pretrained(model_id)
vocab_size = config.vocab_size
hidden = config.hidden_size
layers = config.num_hidden_layers
heads = getattr(config, "num_attention_heads", 0)
# Rough estimate: embeddings + transformer blocks
block_params = hidden * hidden * 12
approx_params = vocab_size * hidden + layers * block_params
return {
"model_id": model_id,
"hidden_size": hidden,
"num_layers": layers,
"num_heads": heads,
"approx_params": approx_params,
}
meta = get_model_metadata("gpt2-medium")
print(json.dumps(meta, indent=2))
Step 3: Define the pruning strategist system prompt
This prompt tells the model to return strict JSON with a per-layer pruning recipe and a calibration suggestion.
SYSTEM_PROMPT = '''You are an edge ML deployment engineer.
Given a model metadata dictionary and a target hardware spec, propose a structured pruning strategy to fit the model into the RAM budget while preserving accuracy.
Return ONLY a JSON object with no markdown formatting. Use this exact schema:
{
"method": "magnitude" or "structured",
"global_sparsity": float between 0 and 0.7,
"layer_sparsity": {"layer_index": float},
"reasoning": "string",
"calibration_batches": int
}
If the model already fits comfortably, set global_sparsity to 0.0 and explain why.'''
Step 4: Query Oxlo.ai for the pruning plan
We send the model metadata and hardware constraints to Oxlo.ai using JSON mode so we get a parseable recipe. I use qwen-3-32b because its agentic reasoning handles structured hardware constraints well.
def generate_pruning_plan(metadata: dict, hardware: dict, model="qwen-3-32b"):
user_msg = (
f"Model metadata: {json.dumps(metadata)}\n"
f"Target hardware: {json.dumps(hardware)}\n"
f"Propose the pruning plan."
)
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_msg},
],
response_format={"type": "json_object"},
temperature=0.2,
)
plan = json.loads(response.choices[0].message.content)
return plan
device = HARDWARE_DB["coral_tpu"]
plan = generate_pruning_plan(meta, device)
print(json.dumps(plan, indent=2))
Step 5: Load the model and apply the pruning recipe
We load the target model with Transformers, then iterate over its transformer blocks and apply L1 unstructured pruning using PyTorch's built-in utilities.
import torch
from transformers import AutoModelForCausalLM
import torch.nn.utils.prune as prune
def apply_plan(model_id: str, plan: dict):
model = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype=torch.float32,
device_map="cpu",
)
sparsity = plan.get("global_sparsity", 0.0)
if sparsity <= 0.0:
print("Model fits without pruning.")
return model, 0.0
# GPT-2 style architecture; for Llama-style models target model.model.layers
targets = []
for layer in model.transformer.h:
targets.extend([
(layer.attn.c_attn, "weight"),
(layer.attn.c_proj, "weight"),
(layer.mlp.c_fc, "weight"),
(layer.mlp.c_proj, "weight"),
])
for module, name in targets:
prune.l1_unstructured(module, name=name, amount=sparsity)
prune.remove(module, name)
total = sum(p.numel() for p in model.parameters())
zero = sum((p == 0).sum().item() for p in model.parameters())
actual_sparsity = zero / total
return model, actual_sparsity
pruned_model, actual_sparsity = apply_plan("gpt2-medium", plan)
print(f"Actual sparsity: {actual_sparsity:.2%}")
Step 6: Run a local forward pass to sanity-check the model
Before shipping to the edge device, we verify the pruned graph still produces logits and can generate tokens.
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("gpt2-medium")
inputs = tokenizer("The future of edge AI is", return_tensors="pt")
pruned_model.eval()
with torch.no_grad():
outputs = pruned_model(**inputs)
next_token_logits = outputs.logits[:, -1, :]
next_token_id = torch.argmax(next_token_logits, dim=-1)
print(tokenizer.decode(next_token_id[0]))
Step 7: Generate a deployment report with Oxlo.ai
Finally, we feed the metrics back to Oxlo.ai and ask for a concise deployment report with warnings and next steps. Because Oxlo.ai uses request-based pricing, the cost stays flat even when we stuff the full model metadata and pruning results into the prompt.
def generate_report(metadata, plan, actual_sparsity, model="deepseek-v3.2"):
summary = {
"model_id": metadata["model_id"],
"target_device": "coral_tpu",
"planned_sparsity": plan.get("global_sparsity", 0.0),
"actual_sparsity": actual_sparsity,
"method": plan.get("method", "none"),
}
response = client.chat.completions.create(
model=model,
messages=[
{
"role": "system",
"content": "You are a technical writer summarizing edge ML deployments. Be concise.",
},
{
"role": "user",
"content": f"Summarize this pruning result and give one concrete next step: {json.dumps(summary)}",
},
],
)
return response.choices[0].message.content
report = generate_report(meta, plan, actual_sparsity)
print(report)
Run it
Save the script as prune_for_edge.py and run it. The example below targets a 1 GB Coral TPU, which forces the planner to cut the 355 M parameter GPT-2 medium down to size.
if __name__ == "__main__":
TARGET_MODEL = "gpt2-medium"
DEVICE_KEY = "coral_tpu"
meta = get_model_metadata(TARGET_MODEL)
plan = generate_pruning_plan(meta, HARDWARE_DB[DEVICE_KEY])
pruned_model, actual_sparsity = apply_plan(TARGET_MODEL, plan)
tokenizer = AutoTokenizer.from_pretrained(TARGET_MODEL)
inputs = tokenizer("The future of edge AI is", return_tensors="pt")
with torch.no_grad():
out = pruned_model(**inputs)
print(tokenizer.decode(torch.argmax(out.logits[:, -1, :], dim=-1)[0]))
report = generate_report(meta, plan, actual_sparsity)
print("\n=== Deployment Report ===")
print(report)
Example output:
{"model_id": "gpt2-medium", "hidden_size": 1024, "num_layers": 24, "num_heads": 16, "approx_params": 361758720}
{
"method": "magnitude",
"global_sparsity": 0.35,
"layer_sparsity": {"0": 0.30, "1": 0.32, "2": 0.35},
"reasoning": "GPT-2 medium exceeds the 1 GB RAM budget when accounting for activations. A 35% global magnitude pruning reduces the dense footprint enough to leave headroom for the OS and input buffers.",
"calibration_batches": 16
}
Actual sparsity: 35.00%
future
=== Deployment Report ===
Pruning gpt2-medium to 35% sparsity fits the Coral TPU's 1 GB constraint. The unstructured magnitude mask is simple to implement but will not shrink the dense checkpoint on disk until you convert to a sparse format or quantize.
Next step: quantize the pruned weights to INT8 with ONNX Runtime to halve the memory footprint again.
Wrap-up
You now have an agentic pipeline that reasons about hardware constraints and applies real structured pruning. Two concrete next steps: wire this into a CI job that tests multiple sparsity targets overnight, or add dynamic structured pruning to drop entire attention heads instead of sparse weights.
Top comments (1)
I found the approach of using a HuggingFace model card and an Oxlo.ai-hosted LLM to generate a layer-wise sparsity plan for edge devices to be quite innovative. The use of a strategist prompt to guide the LLM in proposing a structured pruning strategy is particularly interesting, as it allows for a more informed and targeted approach to model pruning. I've had experience with similar model pruning techniques using PyTorch and have found that the key to successful pruning is striking a balance between sparsity and accuracy. Have you experimented with different LLM models or pruning methods to compare their effectiveness in preserving model accuracy on edge devices like the Raspberry Pi?