Think of Hugging Face as GitHub for AI. Three things live on the platform: pretrained models you can download and use immediately without training, community-curated datasets for training and evaluation, and Spaces where developers host live demo applications built on top of those models.
Every model comes with a model card — the equivalent of a README — explaining what it does, how it was trained, its license, and example usage. Most models are usable in three to four lines of Python. Two libraries cover the majority of use cases.
pip install transformers datasets huggingface_hub
transformers handles loading and running models locally. datasets downloads and manages data from the Hub. huggingface_hubenables calls to hosted inference providers when you need to run models remotely.
Running Models: Local vs Inference Providers
You have two ways to run a model. Local inference downloads the model to your machine and runs it there. Inference providers send your prompt to a hosted service that runs the model on their hardware and returns the result. Local works well for small to medium models; providers handle models too large for a laptop.
Local inference with pipeline
The pipeline class is the simplest entry point. You specify a task and a model, and it handles tokenization, model loading, and output formatting.
from transformers import pipelinetext_generator = pipeline( task='text-generation', model='openai-community/gpt2')results = text_generator( 'What if AI', max_new_tokens=10, num_return_sequences=2)for result in results: print(result['generated_text'])
Think of pipeline as a vending machine. You pick a task and a model, and it handles everything behind the scenes: downloading the weights, converting your text to numbers, running the computation, and converting the output back to words. max_new_tokens=10 limits the length of each continuation, and num_return_sequences=2 asks for two different completions from the same prompt.
Inference providers
For large models that will not fit locally, route requests through InferenceClient. This requires an HF_TOKEN environment variable set to your Hugging Face access token.
import osfrom huggingface_hub import InferenceClientclient = InferenceClient( provider='together', api_key=os.environ['HF_TOKEN'],)completion = client.chat.completions.create( model='deepseek-ai/DeepSeek-V3', messages=[ {'role': 'user', 'content': 'What is the capital of France?'} ],)print(completion.choices[0].message)
Your prompt travels to a provider (Together AI here) that has the hardware to run the model, and the response comes back over the network. The chat format uses the same role/content structure as the OpenAI API, which makes it straightforward to switch between providers. Your token authenticates the request.
| Situation | Use |
|---|---|
| Small or medium model (GPT-2, DistilBERT, BART) | Local pipeline |
| Large model (70B+, DeepSeek, Llama 3 70B) | Inference Provider |
| Offline or privacy-sensitive data | Local pipeline |
| Quick prototype with no GPU | Inference Provider |
Working with Datasets
The Hub hosts thousands of datasets, filterable by modality (text, image, audio) and task type. Each dataset has a dataset card with license, size, and source details, plus a browser-based viewer for inspecting rows before committing to a download. Most datasets use Apache Arrow under the hood — a columnar format optimized for fast filtering and slicing.
Loading data
from datasets import load_datasetdataset = load_dataset('wikimedia/wikipedia', '20231101.en')dataset = load_dataset('wikimedia/wikipedia', '20231101.en', split='train')
Without a split argument, load_dataset returns a DatasetDict containing all available splits. Specifying split='train' returns just that split as a Dataset object, which is faster and cheaper on bandwidth when you only need one portion.
Filtering and slicing
filtered = dataset.filter(lambda row: 'football' in row['text'])example = filtered.select(range(1))print(example[0]['text'])
.filter walks every row and keeps only those where the lambda returns True. .select(range(1)) slices by row index — here, just the first match. Both return a Dataset object rather than a plain list, which is why you index into example[0] to read the actual row.
| Operation | Code |
|---|---|
| Load specific split | load_dataset('id', split='train') |
| Filter rows | data.filter(lambda r: condition) |
| Slice rows | data.select(range(10)) |
| Access a row | data[0] |
| Access a column value | data[0]['column_name'] |
| Get column names | data.column_names |
| Number of rows | len(data) |
Text Classification
Text classification assigns a category to a piece of text. Common uses include sentiment analysis, grammar checking, topic labeling, and natural language inference. The pipeline call is the same for all of them; the model determines what kind of classification is performed.
Standard classification
from transformers import pipelinegrammar_model = pipeline( task='text-classification', model='textattack/distilbert-base-uncased-CoLA')print(grammar_model('He eat pizza everyday'))
This model was trained on CoLA (Corpus of Linguistic Acceptability), a dataset of English sentences labeled as grammatically acceptable or not. It returns a label and a confidence score. The model does not understand English the way a person does; it has learned statistical patterns that correlate with grammatical correctness.
Question-Natural-Language-Inference
QNLI tests whether a passage actually answers a given question. Question and passage are concatenated into a single string, and the model returns entailment (the passage answers the question) or not_entailment (it does not).
qnli_model = pipeline( task='text-classification', model='cross-encoder/qnli-electra-base')output = qnli_model( "Where is the capital of France?, Brittany is known for its stunning coastline.")print(output)
A passage about coastal scenery does not answer a question about a capital city, so not_entailment is the expected result here.
Zero-shot classification
Zero-shot classification lets you define the categories at inference time without retraining.
text = "AI-powered robots assist in complex brain surgeries with precision."zero_shot_model = pipeline( task='zero-shot-classification', model='facebook/bart-large-mnli')categories = ['politics', 'science', 'sports']output = zero_shot_model(text, categories)print(f"Top label: {output['labels'][0]} with score: {output['scores'][0]}")
A standard classifier is trained on fixed categories and cannot generalize beyond them. A zero-shot model was trained on natural-language entailment, which allows it to reason about any label you provide by asking itself “does this text entail it belongs to this category?” It ranks candidates by entailment confidence and returns them sorted from highest to lowest. Labels still need to be semantically distinct from each other or the scores will be noisy.
Summarization
Summarization models fall into two types. Extractive models select key sentences verbatim from the source, making them reliable for legal, financial, or scientific documents where faithfulness matters. Abstractive models rephrase content in new words, producing more natural output for articles and blog posts. The pipeline call is identical for both; the choice of model determines which approach is used.
from transformers import pipelinesummarizer = pipeline( task='summarization', model='cnicu/t5-small-booksum')original_text = "..."summary = summarizer(original_text)print(f"Original length: {len(original_text)}")print(f"Summary length: {len(summary[0]['summary_text'])}")
Without constraints, a summarizer might produce a trivially short output or reproduce most of the input. min_new_tokensand max_new_tokens set a floor and ceiling on the generated length. Tokens are sub-word chunks — roughly 0.75 words each on average.
summarizer = pipeline( task='summarization', model='facebook/bart-large-cnn', min_new_tokens=10, max_new_tokens=150)
min_new_tokens=10 ensures the model writes at least a sentence. max_new_tokens=150 caps the output at roughly 110 words, keeping summaries readable and preventing the model from reproducing large sections of the source.
Auto Classes
pipeline is a convenience wrapper. When you need more control — raw logits, custom tokenization rules, or the ability to combine a tokenizer from one model with weights from another — you work directly with Auto classes.
Loading model and tokenizer separately
from transformers import AutoModelForSequenceClassification, AutoTokenizersentiment_model = AutoModelForSequenceClassification.from_pretrained( 'distilbert-base-uncased-finetuned-sst-2-english')sentiment_tokenizer = AutoTokenizer.from_pretrained( 'distilbert-base-uncased-finetuned-sst-2-english')tokens = sentiment_tokenizer.tokenize('Hugging Face makes AI accessible')print(tokens)
Auto classes let you hold the model and tokenizer as separate objects rather than a sealed unit. Once separated, you can inspect intermediate steps, modify either component, or wire them together in non-standard ways. Calling .tokenize() shows exactly how the model processes your input text before any numbers are involved.
Assembling a custom pipeline
You can hand a pipeline pre-loaded objects rather than a model name string.
from transformers import AutoModelForSequenceClassification, AutoTokenizer, pipelinesentiment_model = AutoModelForSequenceClassification.from_pretrained( 'distilbert-base-uncased-finetuned-sst-2-english')sentiment_tokenizer = AutoTokenizer.from_pretrained( 'distilbert-base-uncased-finetuned-sst-2-english')sentiment_pipeline = pipeline( task='sentiment-analysis', model=sentiment_model, tokenizer=sentiment_tokenizer)print(sentiment_pipeline('Hugging Face is wonderful'))
This pattern is useful when you need the same model or tokenizer in multiple places without downloading twice, or when you want to modify a component before plugging it in. The calling convention is identical to a name-based pipeline once it is assembled.
| Situation | Use |
|---|---|
| Standard task, default behavior | pipeline |
| Need raw logits or probabilities | Auto classes |
| Custom tokenization (truncation, padding) | Auto classes |
| Mix tokenizer from model A with model B | Auto classes |
| Batch inference with manual control | Auto classes |
| Quick prototype | pipeline |
Document Question Answering
Document QA takes two inputs — a question and a context — and returns a span of text extracted directly from the context. The answer is always drawn from the source; it is not generated from scratch.
Extracting text from a PDF
from pypdf import PdfReaderreader = PdfReader('hr-policies.pdf')document_text = ''for page in reader.pages: document_text += page.extract_text()
A PDF is not plain text; it is a formatted layout that may include headers, columns, and images. PdfReader walks through each page and extracts the text it finds, which gets concatenated into a single string so the QA model can search the entire document at once. This works well for text-based PDFs. Scanned or image-only PDFs need a separate OCR step.
Running the QA pipeline
doc_qa = pipeline( task='question-answering', model='distilbert-base-cased-distilled-squad')question = 'What is the notice period for resignation?'result = doc_qa( question=question, context=document_text)print(result['answer'])
The model scans the full context and identifies which span of words most likely answers the question, similar to a very fast ctrl+F with reading comprehension behind it. Both question and context are required keyword arguments. The result includes the answer string, the character positions where it starts and ends in the source, and a confidence score.
Three practical limitations apply. Most QA models cap at 512 tokens, so long documents need to be chunked before being passed in. The model will always return a span even when the answer is not present in the document — check result['score']and set a minimum threshold before trusting the output. PDF text extraction is also imperfect; scanned pages and multi-column layouts frequently produce garbled text that reduces QA accuracy.
Choosing the Right Tool
The task you want to perform determines which component to reach for. Use pipeline(task=..., model=...) for running any standard model end-to-end. For large models that will not fit locally, route requests through InferenceClient to a hosted provider. When you need raw logits, custom preprocessing, or precise control over tokenization, use AutoModel and AutoTokenizer directly. Use load_dataset() from the datasets library for any training or evaluation data.
For the task itself: text-classification handles sentiment, grammar, and topic labeling. zero-shot-classification classifies into any labels you define at inference time without retraining. summarization condenses long text, with the model choice determining whether the result is extractive or abstractive. question-answering combined with a PDF parser lets you query documents directly using natural-language questions.
See you soon.
[…] Working with Hugging Face […]
[…] Working with Hugging Face […]
[…] Working with Hugging Face […]
[…] Hugging Face article explains the landscape: a hub of pretrained models and datasets, the pipeline class that makes […]
[…] the full background, read the guide to working with Hugging Face. To practise, work through the 10 code-along […]