Il 30 novembre 2020 Hugging Face ha rilasciato transformers 4.0.0, una major release che cambia il comportamento predefinito in modi che rompono il codice scritto per la serie 3.x. Per chi mantiene pipeline in produzione i tre punti più rilevanti sono: tokenizer veloci attivi di default, return_dict=True come nuovo standard e la rimozione di sentencepiece dalle dipendenze obbligatorie. Le note di rilascio li elencano tutti come breaking change espliciti (release v4.0.0).
Contesto
transformers è una libreria Python sotto licenza Apache 2.0 che offre un’API uniforme per i modelli con architettura Transformer — BERT, RoBERTa, GPT-2, T5, BART e altri — con interoperabilità fra PyTorch e TensorFlow 2.0 e un supporto sperimentale a Flax (documentazione 4.0). Il progetto nasce nel 2018 come pytorch-pretrained-bert, diventa pytorch-transformers e prende infine il nome transformers quando smette di limitarsi a PyTorch.
Quello che lo separa da un semplice repository di implementazioni è la distribuzione dei pesi tramite l’Hub: i pesi pre-addestrati non viaggiano insieme al codice, ma si scaricano a runtime a partire da identificatori come bert-base-uncased. Questo lega strettamente la versione della libreria al formato dei modelli pubblicati, ed è il motivo per cui un cambio di default nella 4.0 ha effetti che vanno oltre il singolo progetto.
I tre default che cambiano
Tokenizer veloci. In 3.x AutoTokenizer restituiva di default l’implementazione Python (slow). In 4.0 restituisce, quando esiste, l’implementazione della libreria tokenizers, scritta in Rust. Le note di rilascio ricordano che il vecchio comportamento si recupera con use_fast=False. La differenza non è solo di velocità: tokenizer veloci e lenti possono produrre offset diversi e gestire diversamente i casi limite, quindi la sostituzione silenziosa va controllata sui task sensibili all’allineamento token-carattere, come il named-entity recognition.
return_dict. Il forward dei modelli ora restituisce oggetti simili a named-tuple invece di tuple semplici. Il codice che indicizza l’output per posizione — outputs[0] per i logit, per dire — continua a funzionare, ma dove serve compatibilità rigorosa return_dict=False riporta al comportamento 3.x.
Separazione di sentencepiece. La dipendenza da sentencepiece è uscita da setup.py. La richiedono i tokenizer di XLNet, ALBERT, CamemBERT, mBART, Pegasus, T5, Reformer e XLM-RoBERTa, e ora si installa come extra: pip install transformers[sentencepiece]. Un ambiente che contava sull’installazione transitiva si rompe al primo import di uno di questi tokenizer.
A tutto questo si aggiunge la riorganizzazione del package: i file dei modelli sono finiti in sottodirectory per architettura, e gli import diretti passano da transformers.modeling_bert a transformers.models.bert.modeling_bert. Gli import dal namespace top-level (from transformers import BertModel) restano com’erano; si rompe solo chi importava i moduli interni.
Punto critico
La distribuzione dei pesi via Hub e i default che cambiano, messi insieme, aprono un punto di rottura poco evidente. Un modello pubblicato sull’Hub porta con sé i file del tokenizer, ma non vincola la versione di libreria con cui verrà caricato. Chi pubblica un checkpoint provato con tokenizer slow e chi lo carica sotto la 4.0 ottengono, di default, due tokenizer diversi. Quasi sempre l’output coincide; quando non coincide, il sintomo è uno scostamento di accuratezza difficile da attribuire, perché niente nel codice segnala che il tokenizer è cambiato.
È il prezzo strutturale di un ecosistema dove codice e pesi hanno cicli di vita separati e versioning indipendente. La comodità — un classificatore di sentiment in tre righe — e il rischio — un comportamento che dipende da quale versione risolve quale default — nascono dallo stesso disaccoppiamento.
from transformers import pipeline
classifier = pipeline("sentiment-analysis")
classifier("open source si presta bene alla revisione")
# in 4.0 questo carica un tokenizer veloce per default
L’ecosistema attorno
Accanto alla 4.0 stanno librerie con versioning proprio. tokenizers fornisce le implementazioni Rust ed è la dipendenza che abilita il default veloce. datasets, arrivata alla 1.0 a settembre 2020, gestisce caricamento e streaming dei dataset appoggiandosi ad Apache Arrow per non tenere tutto in memoria (repository datasets). Ognuna ha il proprio numero di versione e il proprio calendario, quindi la compatibilità fra transformers 4.0 e una specifica minor di datasets o tokenizers va fissata esplicitamente nel file dei requisiti, non data per scontata.
La 4.0 porta anche modelli nuovi: mT5 e T5 v1.1, l’implementazione TensorFlow di DPR, Longformer in TensorFlow con teste di task aggiuntive. L’elenco delle architetture nella documentazione 4.0 ne conta circa 34.
Implicazioni per chi aggiorna
La migrazione 3.x → 4.0 sta in poche verifiche concrete. Fissare le versioni delle quattro librerie collegate nel file dei requisiti. Aggiungere l’extra [sentencepiece] se si usano i modelli che lo richiedono. Decidere caso per caso se il passaggio ai tokenizer veloci è sicuro per il proprio task e, nel dubbio, scrivere use_fast=False invece di affidarsi al default. Aggiornare ai nuovi percorsi per architettura gli import dei moduli interni, se ce ne sono.
Sotto a tutti questi punti c’è lo stesso principio: in un sistema dove i default cambiano fra major version e i pesi viaggiano separati dal codice, la riproducibilità si ottiene pinnando esplicitamente ciò che altrimenti decide la libreria. Un requirements file con le versioni esatte di transformers, tokenizers, datasets e sentencepiece dice in che condizioni è stato ottenuto un risultato meglio di qualsiasi nota a margine.
Limiti
Quanto scritto qui vale per la 4.0.0 e per il comportamento documentato nelle sue note di rilascio; le minor successive possono spostare altri default. Il supporto Flax è dichiarato sperimentale e copre solo una parte dei modelli, quindi non regge un carico di produzione. E la verifica della divergenza fra tokenizer veloce e lento resta empirica: dipende dal modello e dal task, e l’unico modo affidabile per escluderla è misurarla sul proprio dataset.
- https://github.com/huggingface/transformers/releases/tag/v4.0.0
- https://huggingface.co/transformers/v4.0.1/index.html
- https://github.com/huggingface/datasets
- https://github.com/huggingface/tokenizers
Immagine di copertina: Logo di Hugging Face: faccina gialla che abbraccia con le mani aperte, accanto alla scritta “Hugging Face” — logo di Victor, Apache 2.0 — https://commons.wikimedia.org/wiki/File:Hf-logo-with-title.svg