Hugging Face Transformers: jak używać modeli NLP w Pythonie i w chmurze

0
76
2/5 - (1 vote)

Nawigacja:

Od czego zacząć z Hugging Face Transformers – kontekst i podstawowe pojęcia

Kiedy Transformers ma sens, a kiedy wystarczą prostsze metody

Modele Transformers z biblioteki Hugging Face świetnie sprawdzają się wtedy, gdy liczy się kontekst, niuanse językowe i elastyczność. Jeżeli Twoje zadanie to np. klasyfikacja opinii klientów, odpowiedzi na pytania z dokumentów, automatyczne podsumowania czy tłumaczenia – klasyczne metody NLP bardzo szybko dochodzą do ściany. Transformers potrafią uchwycić znaczenie całych zdań, a nawet fragmentów rozmów, zamiast patrzeć tylko na pojedyncze słowa.

Dla kontrastu, prostsze podejścia jak regexy, TF‑IDF + klasyfikator czy ręczne słowniki nadal wygrywają w kilku kategoriach: są przewidywalne, łatwe do audytu, nie wymagają GPU i zwykle są dużo tańsze. Jeśli potrzebujesz tylko wyłapać numer PESEL z dokumentu, rozpoznać NIP lub złapać konkretny pattern w logach – modele Transformers będą przesadą. Podobnie przy bardzo prostych klasyfikacjach, gdzie liczy się głównie obecność kilku słów kluczowych, klasyczny model może być lżejszy, stabilniejszy i wystarczający.

Dobrym filtrem decyzyjnym jest pytanie: czy człowiek, który zna język, ale nie zna domeny, dałby sobie radę? Jeśli tak, Transformer bardzo prawdopodobnie poradzi sobie lepiej niż prosta reguła. Jeśli nie – być może potrzebne są bardziej domenowe reguły, a model językowy będzie tylko wsparciem.

Kluczowe pojęcia: model, tokenizer, checkpoint, pipeline, Hugging Face Hub

Aby korzystać z Hugging Face Transformers bez frustracji, przydaje się krótki słownik pojęć:

  • Model – wytrenowana sieć neuronowa przyjmująca sekwencję tokenów i zwracająca przewidywania. Np. BERT do klasyfikacji tekstu, GPT‑2 do generowania, T5 do tłumaczeń i podsumowań.
  • Tokenizer – komponent, który zamienia tekst na tokeny (liczby) zrozumiałe dla modelu. Odpowiada też za odwrotny proces: dekodowanie tokenów na tekst.
  • Checkpoint – konkretna „wersja” modelu: zestaw wag i konfiguracji. Np. bert-base-multilingual-cased albo distilbert-base-uncased-finetuned-sst-2-english.
  • Pipeline – wysokopoziomowa nakładka, która łączy w sobie model, tokenizer i logikę konkretnego zadania (np. analiza sentymentu), tak aby wystarczył jeden wiersz kodu.
  • Hugging Face Hub – serwis z repozytoriami modeli, zbiorów danych i przestrzeni demo. Działa jak GitHub dla modeli ML: możesz pobierać, udostępniać i wersjonować modele.

Zrozumienie relacji: tekst → tokenizer → model → wynik → dekoder pozwala później łatwo przejść od prostych pipeline’ów do zaawansowanych scenariuszy, np. fine-tuningu czy wdrożeń w chmurze.

Gotowe modele vs trenowanie i fine-tuning

Większość praktycznych zastosowań NLP z Transformers zaczyna się od gotowych checkpointów. Hugging Face udostępnia tysiące modeli wytrenowanych na ogromnych zbiorach danych. W wielu przypadkach wystarczy je pobrać i użyć wprost, bez dodatkowego trenowania. Przykładowo: analiza sentymentu angielskich opinii, tłumaczenia EN↔PL, klasyczne QA na tekstach po angielsku.

Kiedy gotowy model przestaje wystarczać, wchodzą w grę dwie opcje:

  • Fine-tuning – dopasowanie istniejącego modelu do własnych danych (np. polskie recenzje, słownictwo z Twojej branży, specyficzne etykiety).
  • Trenowanie od zera – rzadko potrzebne i bardzo kosztowne. Stosowane głównie przez duże firmy lub zespoły badawcze.

W praktyce fine-tuning jest złotym środkiem: korzystasz z ogólnej wiedzy modelu, a jednocześnie uczysz go specyfiki własnej domeny, oszczędzając zasoby obliczeniowe i czas.

Najczęstsze zadania NLP dostępne w Transformers

Biblioteka Transformers obsługuje szerokie spektrum zadań. Najpopularniejsze kategorie to:

  • Klasyfikacja tekstu – przypisanie etykiety: spam/nie-spam, pozytywna/negatywna, temat wiadomości itd.
  • Analiza sentymentu – specjalny przypadek klasyfikacji, skupiony na emocjach/opinii.
  • Rozpoznawanie jednostek nazwanych (NER) – wykrywanie nazw firm, osób, miejsc, produktów, kwot itp.
  • Question Answering (QA) – odpowiedzi na pytania w oparciu o podany kontekst, np. fragment dokumentu.
  • Tłumaczenie maszynowe – konwersja tekstu z jednego języka na inny.
  • Podsumowanie (summarization) – streszczanie długich tekstów.
  • Generowanie tekstu – kontynuacja promptu, kreatywne pisanie, chat‑boty.

Wszystkie te zadania można uruchomić w Pythonie przy użyciu kilku linijek kodu, a później przenieść do chmury jako API, mikroserwis lub endpoint serverless.

Sprzęt, GPU i rozsądne oczekiwania

Początkujący często obawiają się, że bez potężnej karty graficznej nie ma co podchodzić do Hugging Face Transformers. Do samego korzystania z modeli (inference) większość mniejszych checkpointów da się uruchomić na zwykłym laptopie z 8–16 GB RAM. Będzie wolniej niż na GPU, ale do eksperymentów to wystarcza.

GPU staje się kluczowe głównie przy:

  • fine-tuningu modeli na własnych danych,
  • przetwarzaniu dużych batchy w czasie rzeczywistym,
  • pracy z bardzo dużymi modelami (np. GPT‑J, BLOOM, LLaMA‑klasy).

Jeśli lokalne zasoby są ograniczone, dobrym kompromisem są: Google Colab, instancje GPU w chmurze (AWS, GCP, Azure) lub gotowe endpointy / Inference API na Hugging Face Hub. Daje to elastyczność: kod piszesz lokalnie, a ciężkie obliczenia odpalasz tam, gdzie masz GPU.

Kobieta przegląda książki na półce w nowoczesnej bibliotece
Źródło: Pexels | Autor: cottonbro studio

Instalacja i konfiguracja środowiska – od pustego katalogu do działającego kodu

Wybór środowiska pracy: lokalnie, Colab, Docker, notebook w chmurze

Najważniejsze na początku to wybrać środowisko, które nie będzie walczyć z Tobą przy każdej instalacji pakietu. Najczęściej używane opcje:

  • Lokalne środowisko (venv/conda) – dobra opcja, jeśli masz stabilny system i nie boisz się Pythona. Każdy projekt ma własne środowisko, wersje bibliotek izolowane od reszty.
  • Google Colab – idealny, gdy nie masz lokalnego GPU. Wiele przykładów Hugging Face jest przygotowanych właśnie pod Colaba. Minusem jest „ulotność” środowiska.
  • VS Code + Docker – dobry wybór dla zespołów lub gdy chcesz mieć powtarzalne środowisko na wielu maszynach.
  • Notebooki w chmurze (SageMaker Studio, Vertex AI, Azure ML) – sensowne przy większych projektach, gdzie od razu planujesz wdrożenie w chmurze.

Dla pierwszych kroków najprościej zacząć od virtualenv + lokalny Python albo od razu od Google Colab. W obu przypadkach komendy instalacyjne są niemal identyczne.

Instalacja transformers i powiązanych bibliotek

W nowym katalogu projektu możesz utworzyć i aktywować wirtualne środowisko (przykład dla venv):

python -m venv .venv
source .venv/bin/activate  # Linux / macOS
# .venvScriptsactivate   # Windows (PowerShell / CMD)

Następnie instalujesz bazowe biblioteki:

pip install "transformers[torch]" datasets accelerate sentencepiece

Najważniejsze elementy:

  • transformers[torch] – instaluje transformers razem z PyTorchem (możesz użyć też TensorFlow, ale PyTorch jest lepiej wspierany przez ekosystem HF).
  • datasets – wygodne ładowanie i przetwarzanie zbiorów danych.
  • accelerate – ułatwia trenowanie na wielu GPU, TPU lub w trybie mixed precision.
  • sentencepiece – wymagane przez wiele tokenizerów (np. T5, Marian, mBART).

Jeśli korzystasz z Google Colab, zwykle wystarczy jedna komórka:

!pip install "transformers[torch]" datasets accelerate sentencepiece

Konfiguracja GPU i sprawdzanie, czy model z niego korzysta

Jeżeli masz zainstalowaną wersję PyTorcha z obsługą CUDA i kartę graficzną NVIDIA, możesz sprawdzić dostępność GPU w Pythonie:

import torch
print(torch.cuda.is_available())  # True jeśli GPU widoczne
print(torch.cuda.device_count())  # ile GPU jest dostępnych

Aby przenieść model na GPU, stosuje się metodę .to("cuda") lub skrót .cuda(). Przykładowo:

from transformers import AutoModelForSequenceClassification, AutoTokenizer

model_name = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name)

if torch.cuda.is_available():
    model = model.to("cuda")

Przy manualnym wywołaniu modelu trzeba też zadbać, by tensory wejściowe były na tym samym urządzeniu:

inputs = tokenizer("To jest test.", return_tensors="pt")

if torch.cuda.is_available():
    inputs = {k: v.to("cuda") for k, v in inputs.items()}

with torch.no_grad():
    outputs = model(**inputs)

W przypadku pipeline’ów Transformers obsługują GPU automatycznie, jeśli podasz parametr device=0 (pierwsze GPU):

from transformers import pipeline

clf = pipeline("sentiment-analysis", device=0)  # 0 = pierwsze GPU

Minimalny przykład „Hello, Transformers”

Od prostego działającego przykładu łatwiej ruszyć dalej. Kompletny, minimalny skrypt analizy sentymentu wygląda np. tak:

from transformers import pipeline

def main():
    classifier = pipeline("sentiment-analysis")
    texts = [
        "I love using Hugging Face Transformers!",
        "This is the worst experience I've ever had."
    ]
    results = classifier(texts)
    for text, res in zip(texts, results):
        print(f"Tekst: {text}")
        print(f"Label: {res['label']}, score: {res['score']:.4f}")
        print("-" * 40)

if __name__ == "__main__":
    main()

Przy pierwszym uruchomieniu pipeline pobierze domyślny model z Hugging Face Hub i zapisze go w lokalnej pamięci podręcznej. Kolejne wywołania będą korzystać z cache, więc start będzie znacznie szybszy.

Jak unikać konfliktów wersji bibliotek

Przy pracy z ML dość łatwo wpaść w „piekło wersji”: jeden projekt wymaga starszego Pythona, inny nowego Torch, trzeci ma swoją kombinację. Zwykle pomaga trzymanie się kilku prostych zasad:

  • Każdy większy projekt NLP w osobnym środowisku (venv/conda).
  • W requirements.txt lub pyproject.toml zapisujesz użyte wersje, np.: transformers==4.41.0.
  • Nie miksujesz na ślepo pip install transformers --upgrade w istniejącym środowisku produkcyjnym.
  • Do odtwarzania środowiska używasz pip freeze > requirements.txt i później pip install -r requirements.txt na nowych maszynach.

Przy projektach zespołowych przydaje się Docker albo przynajmniej dokładny opis wersji w repozytorium. Oszczędza to wiele godzin polowania na błędy typu „u mnie działa”.

Pierwsze kroki z pipeline – szybkie wyniki bez zagłębiania się w szczegóły

Pipeline jako skrót do gotowego NLP

Pipeline w Transformers to wysokopoziomowy interfejs, który sprawia, że wiele zadań NLP da się uruchomić w jednym wierszu. Nie trzeba osobno inicjalizować tokenizera, modelu ani pisać kodu do dekodowania. Wystarczy określić rodzaj zadania (np. "sentiment-analysis", "translation", "question-answering") i ewentualnie konkretny checkpoint.

Pod spodem pipeline:

  • pobiera odpowiedni domyślny model z Hub (jeśli nie podasz innego),
  • tworzy tokenizer,
  • obsługuje tokenizację, batching, dekodowanie,
  • zwraca gotowe, przyjazne struktury danych (słowniki, listy).

Dzięki temu możesz skupić się na logice aplikacji, a nie na detalach architektury modelu. Gdy potrzebujesz bardziej precyzyjnej kontroli – łatwo zejść poziom niżej.

Analiza sentymentu – pierwszy praktyczny pipeline

Podstawowy przykład analizy sentymentu w języku angielskim wygląda tak:

from transformers import pipeline

sentiment = pipeline("sentiment-analysis")
result = sentiment("I really enjoy working with Transformers!")
print(result)
# [{'label': 'POSITIVE', 'score': 0.999...}]

Pipeline radzi sobie także z batchami tekstów:

Przetwarzanie wielu tekstów naraz (batching) i sterowanie parametrami

Gdy tylko wyjdziesz poza zabawy w konsoli, szybko pojawia się potrzeba przetwarzania wielu tekstów jednocześnie. Pipeline bez problemu przyjmuje listę wejść:

from transformers import pipeline

sentiment = pipeline("sentiment-analysis")

texts = [
    "I really enjoy working with Transformers!",
    "I'm not sure if this model understands my feelings.",
    "This is terrible, I'll never use it again."
]

results = sentiment(texts)
for text, res in zip(texts, results):
    print(f"{text[:40]!r} → {res['label']} ({res['score']:.3f})")

Domyślnie pipeline sam dobiera batch_size, ale przy większym ruchu lepiej go ustawić ręcznie, balansując między szybkością a zużyciem pamięci:

results = sentiment(texts, batch_size=8)

W przypadku modeli generatywnych (np. tłumaczenie, podsumowanie, generacja tekstu) znaczenie mają też parametry kontrolujące sampling, takie jak max_length, temperature, top_k czy top_p. Przykład dla generacji:

from transformers import pipeline

generator = pipeline("text-generation", model="gpt2")

prompt = "In the near future, AI systems will"
outputs = generator(
    prompt,
    max_length=60,
    num_return_sequences=2,
    temperature=0.7,
    top_p=0.9,
)
for i, out in enumerate(outputs, 1):
    print(f"=== Generacja {i} ===")
    print(out["generated_text"])
    print()

Wybór konkretnego modelu i języka w pipeline

Domyślny model jest wygodny, ale przy poważniejszych projektach zwykle trzeba wybrać coś bardziej dopasowanego: inny język, lżejszy checkpoint albo model trenowany na podobnym typie danych.

Dobrym punktem startu dla języka polskiego są m.in. modele z rodziny herbert, polbert, polish-roberta czy wielojęzyczne xlm-roberta. Załóżmy, że korzystasz z modelu do klasyfikacji sentymentu po polsku:

from transformers import pipeline

model_name = "allegro/herbert-base-cased-sentiment"
sentiment_pl = pipeline("sentiment-analysis", model=model_name)

print(sentiment_pl("To rozwiązanie naprawdę ułatwia mi pracę."))

Jeżeli nie masz pewności, jaki model wybrać, sensowna strategia to:

  • zacząć od mniejszego (szybszego) modelu i sprawdzić jakość,
  • jeśli wyniki są za słabe – przetestować większy lub bardziej wyspecjalizowany checkpoint,
  • gdy dostępne modele ogólne nie dają rady – przejść do fine-tuningu na swoich danych.

Do eksploracji modeli przydaje się wyszukiwarka na Hugging Face Hub z filtrami według języka, typu zadania i rozmiaru.

Pipeline dla innych typów danych: audio, obraz, multimodalne

Nazwa „Transformers” bywa myląca – to nie tylko tekst. Ten sam interfejs pipeline obsługuje także zadania:

  • ASR (automatic speech recognition) – rozpoznawanie mowy,
  • audio classification – klasyfikacja dźwięków, emocji w głosie,
  • image classification, object detection, segmentation,
  • modele multimodalne (tekst + obraz).

Prosty przykład transkrypcji audio (ASR):

from transformers import pipeline

asr = pipeline("automatic-speech-recognition",
               model="openai/whisper-small")

result = asr("sciezka/do/pliku_audio.wav")
print(result["text"])

W przypadku obrazów wejściem jest ścieżka do pliku, URL lub obiekt PIL.Image:

from transformers import pipeline

img_clf = pipeline("image-classification",
                   model="google/vit-base-patch16-224")

preds = img_clf("cat.jpg")
for p in preds:
    print(f"{p['label']}: {p['score']:.3f}")

Jeśli pojawia się myśl „czy to nie jest za dużo jak na start?”, normalna reakcja. Nie trzeba opanować wszystkiego naraz – pipeline sprawia, że wejście w kolejne typy danych jest tylko kwestią zmiany tasku i modelu.

Wydajność pipeline: CPU vs GPU, batch_size, model_kwargs

Gdy aplikacja zaczyna przetwarzać setki lub tysiące żądań, różnica między „działa” a „działa sensownie szybko” staje się wyczuwalna. Kilka praktycznych dźwigni:

  • urządzeniedevice=0 (pierwsze GPU) lub device=-1 (CPU),
  • batch_size – im większy, tym zwykle lepsze wykorzystanie GPU, kosztem pamięci,
  • model_kwargs – parametry przekazywane bezpośrednio do modelu (np. torch_dtype).
from transformers import pipeline
import torch

dtype = torch.float16 if torch.cuda.is_available() else torch.float32

clf = pipeline(
    "sentiment-analysis",
    model="distilbert-base-uncased-finetuned-sst-2-english",
    device=0,  # albo -1
    model_kwargs={"torch_dtype": dtype}
)

texts = ["text " + str(i) for i in range(100)]
results = clf(texts, batch_size=32)

Dla dużych modeli na GPU opłaca się użyć torch.float16 lub bfloat16 – znacząco zmniejsza to zużycie pamięci, przy niewielkiej utracie jakości w typowych zadaniach NLP.

Studenci programujący na laptopach w bibliotece podczas nauki NLP
Źródło: Pexels | Autor: Mikhail Nilov

Tokenizacja i praca z modelami na niższym poziomie

Dlaczego tokenizacja jest tak ważna

Większość modeli Transformerów nie operuje bezpośrednio na „surowym” tekście, tylko na tokenach – liczbach odpowiadających słowom, pod-słowom lub znakom. To, jak tekst zostanie pocięty na tokeny, ma wpływ na jakość, długość sekwencji, koszt obliczeń.

Dla polskiego (i innych języków z fleksją) tokenizery subword zwykle dzielą wyrazy na mniejsze elementy, np. "samochodami""sam", "ochod", "ami". Dzięki temu model może ogarniać odmiany słów, nie trzymając każdej formy w słowniku.

Podstawowe użycie AutoTokenizer

Do pracy na niższym poziomie przydaje się bezpośredni dostęp do tokenizera. Najczęściej korzysta się z AutoTokenizer, który sam dobiera odpowiedni typ na podstawie checkpointu:

from transformers import AutoTokenizer

model_name = "allegro/herbert-base-cased"
tokenizer = AutoTokenizer.from_pretrained(model_name)

text = "Transformery świetnie radzą sobie z językiem naturalnym."
enc = tokenizer(text)
print(enc.keys())          # dict_keys(['input_ids', 'token_type_ids', 'attention_mask'])
print(enc["input_ids"][:10])

Dla treningu i inference wygodniej od razu zwracać tensory:

enc = tokenizer(
    text,
    return_tensors="pt",   # PyTorch
    padding=True,
    truncation=True
)

print(enc["input_ids"].shape)       # [1, sequence_length]
print(enc["attention_mask"].shape)  # [1, sequence_length]

Batchowanie i maski uwagi

Większość modeli oczekuje, że wszystkie sekwencje w batchu będą miały ten sam rozmiar. Tokenizer może automatycznie je wypłaszczyć przez padding:

texts = [
    "Krótki tekst.",
    "Trochę dłuższe zdanie, które wymaga więcej tokenów."
]

batch = tokenizer(
    texts,
    return_tensors="pt",
    padding=True,     # wyrównanie długości
    truncation=True   # ucięcie zbyt długich sekwencji
)

print(batch["input_ids"].shape)       # [2, max_seq_len]
print(batch["attention_mask"].shape)  # [2, max_seq_len]

attention_mask mówi modelowi, które pozycje są prawdziwymi tokenami, a które tylko wypełniaczem (0 = padding, 1 = „prawdziwy” token). Dzięki temu model nie „marnuje uwagi” na sztuczne tokeny.

Od tokenów do tekstu i z powrotem

Przy modelach generatywnych trzeba często dekodować tokeny z powrotem do tekstu. Tokenizer dostarcza wygodne metody:

ids = batch["input_ids"][0].tolist()
print("Token IDs:", ids)
print("Tokens:", tokenizer.convert_ids_to_tokens(ids))
print("Decoded:", tokenizer.decode(ids))

# Można też zignorować tokeny specialne (CLS, SEP, PAD...)
print("Decoded (skip_special_tokens):",
      tokenizer.decode(ids, skip_special_tokens=True))

Przy debugowaniu sekwencji wygodne bywa podglądanie tokenów dla konkretnych słów. Często pomaga to w zrozumieniu, czemu model „dziwnie” reaguje na neologizmy, błędy ortograficzne czy slang.

Ręczne wołanie modelu: AutoModel i AutoModelFor*

Pipeline jest wygodny, ale czasami ogranicza elastyczność: trudniej podejrzeć wewnętrzne reprezentacje, połączyć kilka modeli naraz albo zaimplementować własną logikę scoringu. Wtedy przydaje się bezpośrednie wywołanie modelu.

Do klasyfikacji sekwencji używa się np. AutoModelForSequenceClassification:

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
import torch.nn.functional as F

model_name = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name)

text = "I really like how transparent this API is."
inputs = tokenizer(text, return_tensors="pt")

with torch.no_grad():
    outputs = model(**inputs)
    logits = outputs.logits
    probs = F.softmax(logits, dim=-1)

print("Logits:", logits)
print("Probs:", probs)
print("Predicted class:", probs.argmax(dim=-1).item())

Dzięki temu masz pełną kontrolę nad tym, co dzieje się między wejściem a decyzją: możesz np. ręcznie ustawiać progi, łączyć wyniki kilku modeli, modyfikować logity przed softmaxem.

Pobieranie embeddingów (reprezentacji) tekstu

Częsty scenariusz: chcesz nie tyle klasy, co gęstą reprezentację zdań, dokumentów czy produktów, aby później liczyć podobieństwa, wyszukiwać „podobne” opisy, grupować dane. Do tego służą tzw. embeddingi.

from transformers import AutoTokenizer, AutoModel
import torch

model_name = "sentence-transformers/all-MiniLM-L6-v2"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)

texts = [
    "Transformers make NLP easier.",
    "Using pre-trained models speeds up development."
]

enc = tokenizer(texts, padding=True, truncation=True,
                return_tensors="pt")

with torch.no_grad():
    outputs = model(**enc)
    last_hidden = outputs.last_hidden_state  # [batch, seq_len, hidden_size]

# Prosty pooling: średnia po wymiarze sekwencji z maską uwagi
attention_mask = enc["attention_mask"].unsqueeze(-1)  # [batch, seq_len, 1]
sum_embeddings = (last_hidden * attention_mask).sum(dim=1)
sum_mask = attention_mask.sum(dim=1)
sentence_embeddings = sum_embeddings / sum_mask

print(sentence_embeddings.shape)  # [batch, hidden_size]

Tego typu wektory można następnie trzymać w bazie (np. wektorowej) i używać do wyszukiwania semantycznego, deduplikacji treści czy rekomendacji podobnych wpisów.

Specjalne tokeny, maksymalna długość i przycinanie

Modele mają ograniczoną długość kontekstu, np. 512 lub 1024 tokeny. Przy dłuższych dokumentach trzeba zdecydować, co z nadmiarem:

  • uciąć z końca (truncation="only_second" / domyślne) – sensowne przy klasyfikacji krótkich opinii,
  • uciąć z początku – przy analizie ostatnich wiadomości w wątku,
  • sliding window – rozbicie długiego tekstu na „okna” kontekstu z nakładką,
  • streścić dokument wcześniej modelem do podsumowań, a dopiero potem stosować kolejny model.
enc = tokenizer(
    long_text,
    max_length=512,
    truncation=True,
    return_tensors="pt"
)

Przy aplikacjach produkcyjnych dobrze jest logować długość sekwencji i liczbę przycięć. Dzięki temu łatwiej wyłapać przypadki, w których model „gubi” ważne informacje z końca długiego dokumentu.

Łączenie własnej logiki aplikacyjnej z modelami

Gdy dochodzisz do momentu, w którym proste pipeline już nie wystarczają, naturalnym krokiem jest połączenie modeli z własnym kodem biznesowym. Kilka typowych wzorców:

  • pre‑filtracja dokumentów regułami (np. regex, proste słowniki), a dopiero potem klasyfikacja modelami,
  • łączenie kilku modeli: np. najpierw wykrycie języka, potem wybór odpowiedniego modelu do klasyfikacji,
  • agregacja wyników z wielu checkpointów (ensemble), jeśli ważniejsza jest jakość niż koszt obliczeń.

Dobrym podejściem na start jest potraktowanie modeli jak „funkcji scoringu”, które można łatwo wymienić. Logika aplikacyjna – kolejki, retry, obsługa błędów – leży wtedy po Twojej stronie, a Transformers zapewnia powtarzalne, przewidywalne wyjścia.

Osoba sięga po książkę z plenerowej biblioteczki przy ulicy
Źródło: Pexels | Autor: Sebastian Luna

Typowe zadania NLP z Transformers – przegląd z konkretnymi przykładami kodu

Klasyfikacja tekstu: sentyment, tematy, intencje

Najczęściej pierwszym realnym use case’em jest klasyfikacja: tagowanie maili wsparcia, ocena opinii, wykrywanie intencji w chatbotach.

Podstawowy pipeline już znasz. Dla własnych etykiet (np. ["support", "sales", "spam"]) używa się modeli fine‑tunowanych na takim zadaniu. Przykład dla trzech klas:

Przykład wieloklasowej klasyfikacji opinii

Załóżmy, że masz trzy typy zgłoszeń klientów: pytania sprzedażowe, zgłoszenia do supportu i spam. Model fine‑tunowany na takim zadaniu może od razu zwrócić odpowiednią etykietę, a Twój system tylko przekierowuje maila w odpowiednie miejsce.

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
import torch.nn.functional as F

model_name = "mrm8488/bert-mini-finetuned-age_news-classification"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name)

id2label = model.config.id2label  # {0: 'world', 1: 'sports', ...}

texts = [
    "Potrzebuję faktury za ostatnią usługę.",
    "Mam pytanie o integrację z waszym API.",
    "Wygrałeś nowego iPhone'a! Kliknij tutaj."
]

enc = tokenizer(texts, padding=True, truncation=True,
                return_tensors="pt")

with torch.no_grad():
    logits = model(**enc).logits
    probs = F.softmax(logits, dim=-1)

pred_ids = probs.argmax(dim=-1).tolist()
for text, cls_id in zip(texts, pred_ids):
    print(text, "→", id2label[cls_id])

Własne etykiety (np. "support", "sales", "spam") pojawią się po Twoim treningu – dokładnie w tym polu id2label. Nikt nie broni też dodać logiki „jeśli model ma małą pewność, to pokaż człowiekowi do ręcznego zaklasyfikowania”.

Wielkoetykietowa klasyfikacja (multi‑label)

Częsty scenariusz: jeden tekst może dostać kilka tagów jednocześnie, np. ["płatności", "błąd techniczny"]. To już nie jest proste „wybierz jedną z klas”, tylko niezależne decyzje dla każdej etykiety.

Na etapie inferencji najprościej zmienić post‑processing: zamiast zwycięzcy softmax, robisz sigmoid i progi na każdej etykiecie osobno.

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch

model_name = "joeddav/xlm-roberta-large-xnli"  # przykład; do multi-label lepiej własny
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name)

labels = ["billing", "technical", "cancellation", "spam"]

text = "Chciałbym anulować subskrypcję, bo ciągle pojawia się błąd płatności."

enc = tokenizer(text, return_tensors="pt", truncation=True)

with torch.no_grad():
    logits = model(**enc).logits
    probs = torch.sigmoid(logits).squeeze(0)  # [num_labels]

threshold = 0.5
predicted = [label for label, p in zip(labels, probs) if p >= threshold]

print("Prawdopodobieństwa:", {l: float(p) for l, p in zip(labels, probs)})
print("Wybrane etykiety:", predicted)

W realnym projekcie model trenuje się od zera na własnym zbiorze multi‑label (odpowiedni loss, np. BCEWithLogitsLoss), ale sam wzorzec inferencji zostaje taki jak powyżej.

Zero‑shot classification: etykiety bez treningu

Jeśli dopiero startujesz i nie masz jeszcze danych do treningu, dynamiczne przypisywanie etykiet sprawdza się z modelami NLI (natural language inference). Opisujesz etykiety krótkimi zdaniami i podpinasz je jako „hypothesis” w zadaniu „entailment vs contradiction”.

Hugging Face ma gotowy pipeline, który to opakowuje:

from transformers import pipeline

classifier = pipeline("zero-shot-classification",
                      model="facebook/bart-large-mnli")

text = "Klient nie może się zalogować do panelu po zmianie hasła."
candidate_labels = ["błąd techniczny", "pytanie o fakturę",
                    "oferta handlowa", "spam"]

result = classifier(
    text,
    candidate_labels=candidate_labels,
    hypothesis_template="Ten tekst dotyczy: {}."
)

print(result["labels"])
print(result["scores"])

To rozwiązanie bywa powolniejsze i droższe niż docelowy model fine‑tunowany, ale bardzo pomaga na etapie eksploracji problemu, gdy etykiety jeszcze „pływają”.

Token classification: NER, tagowanie encji i slotów

Kolejna rodzina zadań to przewidywanie etykiet dla poszczególnych tokenów, np. nazw firm, kwot, numerów umów. Przydaje się w ekstrakcji danych z dokumentów czy maili.

from transformers import AutoTokenizer, AutoModelForTokenClassification
from transformers import pipeline

model_name = "dslim/bert-base-NER"
ner_tokenizer = AutoTokenizer.from_pretrained(model_name)
ner_model = AutoModelForTokenClassification.from_pretrained(model_name)

ner_pipe = pipeline("ner", model=ner_model, tokenizer=ner_tokenizer,
                    aggregation_strategy="simple")

text = "Jan Kowalski pracuje w Acme Sp. z o.o. w Warszawie."
entities = ner_pipe(text)

for ent in entities:
    print(ent["word"], ent["entity_group"], ent["score"])

Przy własnych formularzach (np. dane z faktur) NER bywa lepszy niż regularne wyrażenia – model radzi sobie z drobnymi błędami i różnymi formatami danych.

Question Answering (QA) na własnych dokumentach

Modele QA przyjmują pytanie i kontekst, a zwracają fragment tekstu jako odpowiedź. Używane są w wyszukiwarkach tematycznych, chatbotach na bazie dokumentów czy systemach wsparcia pracowników.

from transformers import pipeline

qa_pipe = pipeline("question-answering",
                   model="deepset/roberta-base-squad2")

context = """
Hugging Face Transformers to biblioteka Pythona ułatwiająca korzystanie
z modeli NLP. Pozwala ładować gotowe checkpointy, trenować nowe modele
i korzystać z nich zarówno lokalnie, jak i w chmurze.
"""

question = "Do czego służy biblioteka Transformers?"

result = qa_pipe(question=question, context=context)
print(result["answer"], " (score =", result["score"], ")")

Jeżeli kontekst jest dłuższy niż limit modelu, trzeba dodać warstwę wyszukiwania (np. BM25, wektorowe wyszukiwanie embeddingami) i przepuszczać do QA tylko kilka najbardziej pasujących paragrafów.

Podsumowywanie tekstu (summarization)

Przy długich raportach czy korespondencji wiele osób chce dostać najpierw zwięzłe podsumowanie, a dopiero potem wchodzić w szczegóły. Modele abstrakcyjnego podsumowywania generują skróconą wersję tekstu własnymi słowami.

from transformers import pipeline

summarizer = pipeline("summarization",
                      model="facebook/bart-large-cnn")

long_text = """
(duży artykuł, raport lub długi opis produktu)
"""

summary = summarizer(
    long_text,
    max_length=120,
    min_length=40,
    do_sample=False
)

print(summary[0]["summary_text"])

Parametry max_length i min_length dobrze jest zestroić z typową długością Twoich tekstów. Na produkcji przydaje się też filtr słów kluczowych, aby sprawdzić, czy w podsumowaniu nie zniknęły krytyczne informacje (np. kwoty, daty).

Generowanie tekstu: modele językowe

Dla generatywnych zastosowań (tworzenie odpowiedzi, draftów maili, opisów produktów) używa się modeli typu decoder‑only lub encoder‑decoder w trybie generacji. Transformers ma ujednolicone API generate().

from transformers import AutoTokenizer, AutoModelForCausalLM

model_name = "gpt2"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)

prompt = "Narzędzia oparte na Transformerach pomagają programistom"

inputs = tokenizer(prompt, return_tensors="pt")

gen_tokens = model.generate(
    **inputs,
    max_length=60,
    num_return_sequences=1,
    do_sample=True,
    top_p=0.9,
    temperature=0.8
)

generated = tokenizer.decode(gen_tokens[0],
                             skip_special_tokens=True)
print(generated)

Gdy boisz się zbyt „kreatywnych” odpowiedzi, możesz wyłączyć sampling (do_sample=False) i użyć deterministicznego beam search.

Translacja maszynowa

Do tłumaczeń wygodne są modele Marian lub nowsze wielojęzyczne check‑pointy. Dla prostych zastosowań wystarczy pipeline, bez ręcznego wywoływania generate.

from transformers import pipeline

translator = pipeline("translation", model="Helsinki-NLP/opus-mt-pl-en")

text = "Transformery znacząco uprościły korzystanie z NLP w Pythonie."
result = translator(text, max_length=60)

print(result[0]["translation_text"])

Jeśli tworzysz narzędzie do lokalizacji produktu, możesz połączyć translację z QA: najpierw tłumaczenie, potem model odpowiadający na pytania o przetłumaczoną treść.

Własne dane i fine-tuning – jak dostosować model do swojego problemu

Skąd wziąć dane i jak je przygotować

Najczęstsza obawa: „nie mam wystarczająco dużo danych”. Do wielu zadań klasyfikacyjnych wystarczy kilkaset, czasem parę tysięcy przykładów – zwłaszcza jeśli korzystasz z dobrego modelu bazowego.

Prosty przepis na start przy klasyfikacji sekwencji:

  • zbierz realne przykłady (maile, zgłoszenia, komentarze),
  • oznacz je ręcznie w prostym narzędziu (arkusz, proste UI),
  • podziel na train/validation (np. 80/20),
  • upewnij się, że etykiety są w miarę zbalansowane (lub świadomie zostawiasz niezbalansowanie, ale potem używasz wag).

Przy danych historycznych dobrze jest usunąć duplikaty i przynajmniej pobieżnie zanonimizować treści (imiona, numery umów), jeśli pracujesz z danymi wrażliwymi.

Dataset i DataCollator z Transformers

Do treningu używa się obiektów Dataset (z biblioteki datasets) i wbudowanych data collatorów. Dzięki temu padding i tworzenie batchy dzieje się automatycznie.

from datasets import Dataset
from transformers import AutoTokenizer, DataCollatorWithPadding

texts = [
    "Nie mogę zalogować się do aplikacji.",
    "Chcę zmienić plan na droższy.",
    "To jest spam, nie wysyłajcie mi tego."
]
labels = [0, 1, 2]  # 0=support, 1=sales, 2=spam

raw_ds = Dataset.from_dict({"text": texts, "label": labels})

model_name = "allegro/herbert-base-cased"
tokenizer = AutoTokenizer.from_pretrained(model_name)

def tokenize_fn(batch):
    return tokenizer(
        batch["text"],
        truncation=True
    )

tokenized_ds = raw_ds.map(tokenize_fn, batched=True)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

Na prawdziwym zbiorze rozdzielasz jeszcze dane na train / validation (np. train_test_split z datasets).

Prosty fine‑tuning z Trainer

Najprostsza droga do fine‑tuningu to klasa Trainer, która opakowuje pętlę treningową PyTorcha i logikę ewaluacji.

from transformers import (AutoModelForSequenceClassification,
                          TrainingArguments, Trainer)
import numpy as np
from datasets import load_metric

num_labels = 3
model = AutoModelForSequenceClassification.from_pretrained(
    model_name,
    num_labels=num_labels
)

metric = load_metric("accuracy")

def compute_metrics(eval_pred):
    logits, labels = eval_pred
    preds = np.argmax(logits, axis=-1)
    return metric.compute(predictions=preds, references=labels)

training_args = TrainingArguments(
    output_dir="./checkpoints/support-classifier",
    evaluation_strategy="epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=32,
    num_train_epochs=3,
    weight_decay=0.01,
    logging_steps=50
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_ds,      # w praktyce: tokenized_ds["train"]
    eval_dataset=tokenized_ds,       # w praktyce: tokenized_ds["validation"]
    tokenizer=tokenizer,
    data_collator=data_collator,
    compute_metrics=compute_metrics
)

trainer.train()

Jeżeli boisz się „przeuczenia” na małym zbiorze, zacznij od 1–2 epok i małego learning rate. Najwyżej potem podniesiesz liczbę epok, jeśli wyniki na walidacji nadal rosną.

Zapisywanie i używanie wytrenowanego modelu

Po treningu checkpoint możesz zapisać lokalnie lub wrzucić na Hugging Face Hub, aby łatwo wczytywać go z dowolnej maszyny.

trainer.save_model("./models/support-classifier")
tokenizer.save_pretrained("./models/support-classifier")

W aplikacji inference wygląda potem jak zwykłe from_pretrained:

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch.nn.functional as F
import torch

model_path = "./models/support-classifier"
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForSequenceClassification.from_pretrained(model_path)

def classify(text: str):
    enc = tokenizer(text, return_tensors="pt", truncation=True)
    with torch.no_grad():
        logits = model(**enc).logits
        probs = F.softmax(logits, dim=-1)
    return probs.squeeze(0)

print(classify("Proszę o anulowanie subskrypcji."))

To samo zadziała, jeśli jako model_path podasz nazwę repozytorium z Huba, np. "twoja-organizacja/support-classifier-pl".

Fine‑tuning do token classification (NER na własnych polach)

Jeżeli zamiast klasy całego tekstu potrzebujesz etykiet na poziomie tokenów (np. pola z umów, nazwy produktów), konfiguracja jest bardzo podobna, tylko zmienia się typ modelu i format danych.

Przykładowy rekord w zbiorze danych NER (upraszczając) może wyglądać tak:

{
  "tokens": ["Umowa", "nr", "123/2023", "zawarta", "w", "Warszawie"],
  "labels": ["O", "O", "B-NUMER_UMOWY", "O", "O", "B-MIASTO"]
}

Przy mapowaniu w datasets tokenizujesz sekwencje, wyrównujesz etykiety do subtokenów i korzystasz z AutoModelForTokenClassification. Hugging Face ma gotowe tutoriale krok po kroku – dobrze się na nich oprzeć przy pierwszym projekcie.

Freeze’owanie warstw i trenowanie „głowy”

Najczęściej zadawane pytania (FAQ)

Od czego zacząć z Hugging Face Transformers w Pythonie?

Najprościej zacząć od małego, działającego przykładu w nowym katalogu projektu. Utwórz wirtualne środowisko (venv lub conda), zainstaluj pakiety transformers[torch], datasets, accelerate i sentencepiece, a następnie uruchom pierwszy pipeline, np. do analizy sentymentu czy tłumaczenia. To pozwala sprawdzić, czy wszystko działa, zanim wejdziesz w bardziej zaawansowane funkcje.

Jeśli konfiguracja lokalna brzmi groźnie, dobrym startem jest Google Colab. Wkładasz jedną komórkę z !pip install "transformers[torch]" datasets accelerate sentencepiece, kopiujesz przykładowy kod z dokumentacji Hugging Face i od razu widzisz wynik. Dopiero gdy poczujesz się pewniej, przenieś to do własnego środowiska na laptopie czy w Dockerze.

Kiedy używać Transformers, a kiedy wystarczą prostsze metody NLP?

Transformers opłacają się wtedy, gdy liczy się kontekst i „mięso” języka, a nie tylko kilka słów kluczowych. Przykłady: analiza opinii klientów, odpowiedzi na pytania na podstawie długich dokumentów, podsumowanie raportów, tłumaczenia całych akapitów. W takich zadaniach klasyczne metody szybko się wysycają i zaczynają popełniać dużo błędów.

Po drugiej stronie są przypadki, gdzie reguły i prostsze modele nadal wygrywają: wyciąganie numeru PESEL, dopasowanie NIP-u, wykrywanie konkretnego patternu w logach czy prosta klasyfikacja po obecności 2–3 słów. Dobrą „zasadą kciuka” jest pytanie: czy człowiek, który zna język, ale nie zna domeny, poradziłby sobie z zadaniem? Jeśli tak – Transformer ma sens. Jeśli nie – często lepiej zacząć od reguł i prostych klasyfikatorów, a model językowy traktować jako dodatek.

Czym różni się gotowy model Hugging Face od fine-tuningu na własnych danych?

Gotowy model (checkpoint) to sieć wytrenowana ogólnie, np. na dużych zbiorach w wielu językach. Można go użyć „z pudełka” do popularnych zadań: klasyfikacji sentymentu po angielsku, tłumaczeń EN↔PL, odpowiedzi na pytania w oparciu o krótki tekst. W wielu biznesowych scenariuszach to już daje akceptowalną jakość bez żadnego trenowania.

Fine-tuning polega na dalszym trenowaniu takiego modelu na Twoich danych: opinii klientów z konkretnej branży, dokumentach prawnych, ticketach z helpdesku itd. Dzięki temu model „łapie” słownictwo domenowe i specyficzne etykiety. Zwykle nie ma potrzeby trenować modelu od zera – to drogie, skomplikowane i sensowne głównie dla dużych zespołów badawczych lub gdy budujesz zupełnie nową architekturę.

Czy do używania Transformers potrzebuję GPU?

Do samego korzystania z modeli (inference) nie. Większość mniejszych checkpointów bez problemu uruchomisz na laptopie z 8–16 GB RAM. Będzie wolniej niż na GPU, ale do testów, proof-of-concept czy wewnętrznych narzędzi to w zupełności wystarcza. Dobrym kompromisem jest praca lokalnie na CPU, a cięższe eksperymenty odpalanie okresowo w chmurze.

GPU robi się potrzebne głównie wtedy, gdy:

  • planujesz fine-tuning na większych zbiorach danych,
  • musisz obsłużyć dużo zapytań w czasie rzeczywistym,
  • korzystasz z bardzo dużych modeli (GPT‑J, BLOOM, rodzina LLaMA).

Przy ograniczonych zasobach warto rozważyć Google Colab, instancje GPU w AWS/GCP/Azure albo gotowe endpointy i Inference API na Hugging Face Hub.

Jakie są najczęstsze zastosowania Hugging Face Transformers w praktyce?

Najczęściej modele Transformers wykorzystuje się do:

  • klasyfikacji tekstu (spam/nie-spam, kategorie zgłoszeń, typ dokumentu),
  • analizy sentymentu opinii, recenzji, komentarzy,
  • rozpoznawania jednostek nazwanych (osoby, firmy, miejsca, kwoty),
  • Question Answering – odpowiedzi na pytania na podstawie treści dokumentów, FAQ, regulaminów,
  • tłumaczeń maszynowych między językami,
  • podsumowania długich tekstów, np. raportów, ticketów, transkryptów,
  • generowania tekstu: od autouzupełniania po proste chatboty.

Wspólny mianownik: model bierze tekst wejściowy, przetwarza go tokenizacją, a następnie zwraca wynik, który dekodujesz do czytelnej postaci – etykiety, odpowiedzi, streszczenia czy wygenerowanej kontynuacji.

Jak uruchomić model Hugging Face Transformers w chmurze jako API?

Najłatwiej zacząć od gotowych usług: Hugging Face Inference API lub dedykowanych endpointów na Hugging Face Hub. Wtedy nie martwisz się o infrastrukturę – wysyłasz żądanie HTTP z tekstem, dostajesz wynik. To dobre wyjście na etapie prototypu lub dla zespołów bez doświadczenia w DevOps.

Jeśli potrzebujesz większej kontroli, możesz:

  • opakować model w prosty serwer (np. FastAPI, Flask) i wdrożyć jako mikroserwis w Dockerze na AWS/GCP/Azure,
  • skorzystać z funkcji serverless (Lambda, Cloud Functions) w połączeniu z lekkim modelem lub modelem trzymanym na osobnym serwerze GPU,
  • użyć notebooków w chmurze (SageMaker Studio, Vertex AI, Azure ML) i z nich wystawić endpoint HTTP.

Na początek wystarczy działający kod w Pythonie, potem dokładasz warstwę API i infrastrukturę dopasowaną do Twojej skali.

Co to jest pipeline w Transformers i kiedy warto wyjść poza pipeline?

Pipeline to wysokopoziomowa nakładka, która łączy w sobie model, tokenizer i logikę zadania. Dzięki temu np. do analizy sentymentu wystarczy kilka linijek kodu – podajesz tekst, a pipeline sam zadba o tokenizację, uruchomienie modelu i dekodowanie wyniku. To idealne narzędzie na start, gdy chcesz szybko zobaczyć efekt i nie zagłębiać się w szczegóły.

Wyjście poza pipeline ma sens, gdy potrzebujesz większej kontroli: niestandardowego pre‑processing’u danych, własnego loopa treningowego (fine-tuning), work‑flowu na wielu krokach (np. NER + QA + podsumowanie) albo integracji z istniejącą infrastrukturą ML. Wtedy operujesz bezpośrednio na klasach AutoModel*, AutoTokenizer i korzystasz np. z biblioteki datasets oraz accelerate.