,

Running Your First Model with Transformers Pipelines (Hugging Face Series, Part 4)

The transformers pipeline is the shortest path from a model repo to a running inference call. Here is what actually loads onto your box, and how device, device_map and dtype decide whether a model fits your GPU.

Hugging Face Series · Part 4 of 17
TL;DR: The transformers pipeline is the shortest path from a model repo on the Hub to a running inference call. Three lines pull the weights, build the tokenizer, place the model on a device, and return predictions. What matters is not the API surface, it is what physically lands on your box: a multi gigabyte artifact pulled from a registry into RAM and then VRAM. Set device, device_map and dtype correctly and a 2.6 billion parameter model fits a single mid range GPU. Get them wrong and you run out of memory on load.
Who this is for: The infrastructure or platform engineer who can size a VM, read a registry pull log and reason about VRAM, but has not yet run a model locally. If you have pulled a container image and watched it land in layers, you already have the mental model for what happens here. Prerequisite: an access token and the CLI from Part 3, plus Python 3.9 or newer with pip.

Here is the whole thing. No training, no fine tuning, no serving stack. Just a model running on your machine:

from transformers import pipeline

clf = pipeline(task='text-classification',
               model='distilbert/distilbert-base-uncased-finetuned-sst-2-english')
print(clf('This deployment finally went smoothly.'))
# [{'label': 'POSITIVE', 'score': 0.9998}]

Those three lines did far more than they look. The call resolved a repo id, pulled the model config, the tokenizer files and the weights from the Hub, cached them on local disk, instantiated the model in memory, moved it to a device, tokenized your text, ran a forward pass, and turned raw logits back into a label. The right way to read this as an infra person: it is a registry pull followed by a process that maps a large artifact into memory and runs it. Treat it like pulling and running a container image, not like calling a stateless HTTP API. The same questions apply. How big is the artifact, where is it cached, what does it need to run, and does it fit the box.

Tested on: Python 3.11+, transformers 5.x, PyTorch and huggingface_hub 1.21, on a single NVIDIA GPU (CUDA 12). These are the versions this walkthrough was checked against in mid-2026. The Hugging Face stack moves quickly, so pin the version you install rather than pulling latest, and expect a major release to shift some defaults.

What pipeline() assembles

A pipeline is a thin wrapper around three stages: a preprocessor, the model, and a post-processor. For text that means a tokenizer turns your string into token ids, the model turns ids into logits, and a task specific post-processor turns logits into something you can use, a label and a score for classification, generated text for text generation. You can build all three by hand with AutoTokenizer and AutoModel, and later parts do exactly that when you need the control. The pipeline exists so your first run is one call instead of fifteen.

What pipeline() assemblesone call builds three stages and runs them in orderTokenizertext to token idsModel forwardweights in VRAMPost-processlogits to labels
Figure 1: pipeline() wires the preprocessor, model and post-processor into a single call.

If you skip the model argument, the pipeline silently picks a default model for the task and pulls it. That is convenient for a demo and a problem in production, because you no longer know exactly which artifact entered your environment. Name the model every time. It is the same discipline as pinning an image tag instead of running :latest.

Your first run, and what lands on the box

Install the library with the PyTorch backend, plus Accelerate for device placement:

pip install 'transformers[torch]' accelerate
# pulls transformers, the torch backend, and Accelerate for device placement

Now run a small generation model on the first GPU, in half precision:

import torch
from transformers import pipeline

gen = pipeline(
    task='text-generation',
    model='google/gemma-2-2b',
    device=0,
    dtype=torch.bfloat16,
)
out = gen('The secret to a clean rollback is', max_new_tokens=20)
print(out[0]['generated_text'])
# The secret to a clean rollback is having a tested path back to the last known good state ...

Two failure modes show up here, and both are infra problems, not model problems. The first is a 401 or a gated repo error: google/gemma-2-2b requires accepting a license and an authenticated token, which is why Part 3 set that up. The second is an out of memory error on load. That is not a bug, it is capacity. A model needs roughly its parameter count times the bytes per parameter just to hold weights, before any activations or KV cache. The worked example below makes that concrete.

Worked example

Gemma 2 2B has about 2.6 billion parameters. In float32 that is 4 bytes each, near 10.5 GB of weights, which will not fit a 8 GB card. In bfloat16 it is 2 bytes each, near 5.2 GB, which fits comfortably on a 12 GB or 16 GB GPU with room for the KV cache. Setting dtype=torch.bfloat16 is the difference between a model that loads and one that does not. After the call, run nvidia-smi and you should see roughly 5 to 6 GB used. That number, not the docs, is your source of truth for what fits.

nvidia-smi --query-gpu=memory.used --format=csv
# memory.used [MiB]
# 5312 MiB

Where the weights go: device, device_map and dtype

Three parameters decide placement, and they are the ones worth memorizing. device takes a single target: -1 for CPU, which is the default, or a GPU ordinal like 0 for the first card. device_map='auto' hands placement to Accelerate, which fills the fastest device first and then spills to CPU and disk if the model does not fit. dtype sets the precision the weights load in, and in current transformers this is the parameter name to use. Half precision, torch.float16 or torch.bfloat16, halves the VRAM footprint versus float32 with negligible quality loss on most models.

Where the weights landsame model, three placement settingsdevice=-1full model in system RAMruns on CPUsimple, slowdevice=0full model in GPU VRAMmust fit one cardfast, hard limitdevice_map=autoGPU first, thenCPU, then diskfits big, spill is slow
Figure 2: the placement setting decides which tier of memory holds the weights, and how fast inference runs.
ParameterWhat it doesInfra consequence
taskSelects the pipeline and its default modelSet it so the pull is predictable
modelThe repo id to pullThis is the artifact, pin a revision
device-1 for CPU, 0 for first GPUWhich box or accelerator runs it
device_map='auto'Splits weights across GPU, CPU, diskCapacity spillover, watch the slow tiers
dtypePrecision weights load inbfloat16 halves the VRAM bill
batch_sizeInputs per forward passThroughput versus latency versus OOM

The precision choice is the cheapest lever you have. Here is the same 2.6 billion parameter model under each common dtype, weight memory only:

PrecisionBytes per parameterApprox weights for 2.6B
float324~10.5 GB
float16 / bfloat162~5.2 GB
int81~2.6 GB
int40.5~1.3 GB

These are weight estimates, not the full runtime footprint. Activations and, for generation, the KV cache add on top and grow with sequence length and batch size. Treat the weight number as a floor. The int8 and int4 rows come from quantization, which the GenAI series covers as a concept in the Generative AI guide. Part 13 of this series does it with optimum and the Hugging Face tooling.

What this means if you run the platform: device_map='auto' looks like magic because it never refuses to load. That is the trap. When a model does not fit VRAM it silently offloads layers to CPU RAM and then to disk, and inference slows by an order of magnitude or more while every dashboard stays green. If you size GPU capacity off whether jobs complete, you will under provision and not know it. Size off VRAM headroom and watch for offload in the load logs. A model that fits is fast, a model that spills is a latency incident waiting to happen.

Choosing the device setting

The decision is short. No GPU, use the CPU default and accept it is for prototyping, not throughput. One GPU and the model fits, target it directly and set a half precision dtype. Model too big for one card, hand it to Accelerate and consider quantization to claw the footprint back down.

Pick the placementGPU available?device=-1CPU, prototypingFits one GPU?device=0 + bf16device_map=auto + quantizenoyesyesno
Figure 3: a short decision tree for the placement setting on a single host.

For the model that does not fit, the call is barely different. You swap the single device target for the map:

gen = pipeline(
    task='text-generation',
    model='google/gemma-2-2b',
    device_map='auto',
    dtype=torch.bfloat16,
)
# Accelerate places layers across GPU, then CPU, then disk as needed

When to batch, and when to skip it

Pass many inputs as a list and add batch_size to send several through the model per forward pass. On a GPU with regular input lengths this raises throughput. It is off by default for a reason: it can also do nothing, or push you into an out of memory error, depending on hardware, sequence length and the model.

prompts = ['ticket one text', 'ticket two text', 'ticket three text', 'ticket four text']
for result in clf(prompts, batch_size=2):
    print(result)
# pipeline sends two inputs through the model at a time
Gotcha: do not batch on CPU, do not batch when latency is the constraint, and do not batch when input lengths vary wildly, because the batch pads to the longest item and you pay for that padding on every row. The only honest way to set batch_size is to measure on your model, your data and your hardware, then push it up until you hit OOM and back off. There is no safe default that is also a good one.

Quantization in one extra argument

When half precision still does not fit, the pipeline accepts a quantized model through model_kwargs. With bitsandbytes installed, load in 8 bit and cut the weight footprint roughly in half again:

import torch
from transformers import pipeline, BitsAndBytesConfig

gen = pipeline(
    model='google/gemma-2-2b',
    dtype=torch.bfloat16,
    device_map='auto',
    model_kwargs={'quantization_config': BitsAndBytesConfig(load_in_8bit=True)},
)
print(gen('Summarize the incident in one line:', max_new_tokens=30)[0]['generated_text'])
# requires: pip install bitsandbytes

Eight bit loading trades a small amount of quality for a large amount of VRAM, and for most chat and classification work the difference is hard to see. Below 8 bit the trade off becomes real and model dependent, which is why quantization gets its own part later rather than a footnote here. The registry side of this, scanning and trusting what you pull before it runs, parallels exactly what a container registry does for images; the Harbor for Beginners guide covers that control plane.

Before you run this in a shared environment: the first pipeline call downloads weights to a local cache and runs arbitrary model code in your process. Pull only from repos you trust, pin a specific revision, and on shared or air gapped hosts mirror the model into an internal registry first rather than reaching out to the public Hub from production. Part 7 covers why the file format matters for that trust decision, and Part 15 covers scanning and governance.

What I’d ship

Use the pipeline for your first runs, for prototypes, for batch jobs, and as the reference implementation you compare a serving stack against. It is the right default because it is the shortest correct path: it handles tokenization and post-processing that are easy to get subtly wrong by hand, and the placement knobs, device, device_map and dtype, cover the cases that matter on a single host.

It is not the right tool for production serving under real traffic. The pipeline runs one process, holds one copy of the model, and does not do request batching, queueing, or autoscaling. For that you move to Text Generation Inference or Inference Endpoints, which Parts 11 and 12 cover, or to the NVIDIA serving stack in the NVIDIA AI guide when you own the GPUs. Before you ship anything, validate three things: run nvidia-smi after load and confirm the model fits with headroom rather than offloading, confirm the dtype you asked for is what loaded, and pin the model revision so the artifact cannot change under you.

Pull a model that fits your card, run those three lines, and keep nvidia-smi open in another pane while it loads. The number it prints is the only capacity figure you can trust.

Where the weights live after the first pull

The first pipeline call downloads weights once and caches them on local disk. Every later call for the same repo reads from that cache instead of hitting the network, the same way a container runtime reuses pulled layers. By default the cache sits under the home directory at ~/.cache/huggingface/hub, which is fine on a laptop and wrong on most servers, where home is small and often on the wrong volume. Point it at real storage with the HF_HOME environment variable before the first run.

export HF_HOME=/mnt/models/hf-cache
python run_inference.py
# weights now cache under /mnt/models/hf-cache/hub, not the home directory

This is a storage and data movement decision, not a detail. Model repos run from hundreds of megabytes to tens of gigabytes, the cache only grows, and on a fleet of identical workers each node pulling the same model independently wastes bandwidth and time. The fixes are familiar from any artifact system: put the cache on a sized, monitored volume, warm it ahead of a rollout, and on air gapped hosts mirror the repo internally so production never reaches for the public Hub. Part 6 takes this further for datasets, where the cache problem is larger and easier to get wrong.

Hugging Face Series · Part 4 of 17
« Previous: Part 3  |  Hugging Face Guide  |  Next: Part 5

References

About The Author


Discover more from Journal of Intelligent Infrastructure – By Dr Pranay Jha

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure - By Dr Pranay Jha

Subscribe now to keep reading and get access to the full archive.

Continue reading