676 lines
28 KiB
Markdown
676 lines
28 KiB
Markdown
<!--Copyright 2020 The HuggingFace Team. All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
|
the License. You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
|
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
specific language governing permissions and limitations under the License.
|
|
|
|
⚠️ Note that this file is in Markdown but contain specific syntax for our doc-builder (similar to MDX) that may not be
|
|
rendered properly in your Markdown viewer.
|
|
|
|
-->
|
|
|
|
# Esporta modelli 🤗 Transformers
|
|
|
|
Se devi implementare 🤗 modelli Transformers in ambienti di produzione, noi
|
|
consigliamo di esportarli in un formato serializzato che può essere caricato ed eseguito
|
|
su runtime e hardware specializzati. In questa guida ti mostreremo come farlo
|
|
esporta 🤗 Modelli Transformers in due formati ampiamente utilizzati: ONNX e TorchScript.
|
|
|
|
Una volta esportato, un modello può essere ottimizato per l'inferenza tramite tecniche come
|
|
la quantizzazione e soppressione. Se sei interessato a ottimizzare i tuoi modelli per l'esecuzione
|
|
con la massima efficienza, dai un'occhiata a [🤗 Optimum
|
|
library](https://github.com/huggingface/optimum).
|
|
|
|
## ONNX
|
|
|
|
Il progetto [ONNX (Open Neural Network eXchange)](http://onnx.ai) Il progetto onnx è un open
|
|
standard che definisce un insieme comune di operatori e un formato di file comune a
|
|
rappresentano modelli di deep learning in un'ampia varietà di framework, tra cui
|
|
PyTorch e TensorFlow. Quando un modello viene esportato nel formato ONNX, questi
|
|
operatori sono usati per costruire un grafico computazionale (often called an
|
|
_intermediate representation_) che rappresenta il flusso di dati attraverso la
|
|
rete neurale.
|
|
|
|
Esponendo un grafico con operatori e tipi di dati standardizzati, ONNX rende
|
|
più facile passare da un framework all'altro. Ad esempio, un modello allenato in PyTorch può
|
|
essere esportato in formato ONNX e quindi importato in TensorFlow (e viceversa).
|
|
|
|
🤗 Transformers fornisce un pacchetto `transformers.onnx` che ti consente di
|
|
convertire i checkpoint del modello in un grafico ONNX sfruttando gli oggetti di configurazione.
|
|
Questi oggetti di configurazione sono già pronti per una serie di architetture di modelli,
|
|
e sono progettati per essere facilmente estensibili ad altre architetture.
|
|
|
|
Le configurazioni pronte includono le seguenti architetture:
|
|
|
|
<!--This table is automatically generated by `make fix-copies`, do not fill manually!-->
|
|
|
|
- ALBERT
|
|
- BART
|
|
- BEiT
|
|
- BERT
|
|
- BigBird
|
|
- BigBird-Pegasus
|
|
- Blenderbot
|
|
- BlenderbotSmall
|
|
- CamemBERT
|
|
- ConvBERT
|
|
- Data2VecText
|
|
- Data2VecVision
|
|
- DeiT
|
|
- DistilBERT
|
|
- ELECTRA
|
|
- FlauBERT
|
|
- GPT Neo
|
|
- GPT-J
|
|
- I-BERT
|
|
- LayoutLM
|
|
- M2M100
|
|
- Marian
|
|
- mBART
|
|
- MobileBERT
|
|
- OpenAI GPT-2
|
|
- Perceiver
|
|
- PLBart
|
|
- RoBERTa
|
|
- RoFormer
|
|
- SqueezeBERT
|
|
- T5
|
|
- ViT
|
|
- XLM
|
|
- XLM-RoBERTa
|
|
- XLM-RoBERTa-XL
|
|
|
|
Nelle prossime due sezioni, ti mostreremo come:
|
|
|
|
* Esporta un modello supportato usando il pacchetto `transformers.onnx`.
|
|
* Esporta un modello personalizzato per un'architettura non supportata.
|
|
|
|
### Esportazione di un modello in ONNX
|
|
|
|
Per esportare un modello 🤗 Transformers in ONNX, dovrai prima installarne alcune
|
|
dipendenze extra:
|
|
|
|
```bash
|
|
pip install transformers[onnx]
|
|
```
|
|
|
|
Il pacchetto `transformers.onnx` può essere usato come modulo Python:
|
|
|
|
```bash
|
|
python -m transformers.onnx --help
|
|
|
|
usage: Hugging Face Transformers ONNX exporter [-h] -m MODEL [--feature {causal-lm, ...}] [--opset OPSET] [--atol ATOL] output
|
|
|
|
positional arguments:
|
|
output Path indicating where to store generated ONNX model.
|
|
|
|
optional arguments:
|
|
-h, --help show this help message and exit
|
|
-m MODEL, --model MODEL
|
|
Model ID on huggingface.co or path on disk to load model from.
|
|
--feature {causal-lm, ...}
|
|
The type of features to export the model with.
|
|
--opset OPSET ONNX opset version to export the model with.
|
|
--atol ATOL Absolute difference tolerance when validating the model.
|
|
```
|
|
|
|
L'esportazione di un checkpoint utilizzando una configurazione già pronta può essere eseguita come segue:
|
|
|
|
```bash
|
|
python -m transformers.onnx --model=distilbert/distilbert-base-uncased onnx/
|
|
```
|
|
|
|
che dovrebbe mostrare i seguenti log:
|
|
|
|
```bash
|
|
Validating ONNX model...
|
|
-[✓] ONNX model output names match reference model ({'last_hidden_state'})
|
|
- Validating ONNX Model output "last_hidden_state":
|
|
-[✓] (2, 8, 768) matches (2, 8, 768)
|
|
-[✓] all values close (atol: 1e-05)
|
|
All good, model saved at: onnx/model.onnx
|
|
```
|
|
|
|
Questo esporta un grafico ONNX del checkpoint definito dall'argomento `--model`.
|
|
In questo esempio è `distilbert/distilbert-base-uncased`, ma può essere qualsiasi checkpoint
|
|
Hugging Face Hub o uno memorizzato localmente.
|
|
|
|
Il file risultante `model.onnx` può quindi essere eseguito su uno dei [tanti
|
|
acceleratori](https://onnx.ai/supported-tools.html#deployModel) che supportano il
|
|
lo standard ONNX. Ad esempio, possiamo caricare ed eseguire il modello con [ONNX
|
|
Runtime](https://onnxruntime.ai/) come segue:
|
|
|
|
```python
|
|
>>> from transformers import AutoTokenizer
|
|
>>> from onnxruntime import InferenceSession
|
|
|
|
>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased")
|
|
>>> session = InferenceSession("onnx/model.onnx")
|
|
>>> # ONNX Runtime expects NumPy arrays as input
|
|
>>> inputs = tokenizer("Using DistilBERT with ONNX Runtime!", return_tensors="np")
|
|
>>> outputs = session.run(output_names=["last_hidden_state"], input_feed=dict(inputs))
|
|
```
|
|
|
|
I nomi di output richiesti (cioè `["last_hidden_state"]`) possono essere ottenuti
|
|
dando un'occhiata alla configurazione ONNX di ogni modello. Ad esempio, per
|
|
DistilBERT abbiamo:
|
|
|
|
```python
|
|
>>> from transformers.models.distilbert import DistilBertConfig, DistilBertOnnxConfig
|
|
|
|
>>> config = DistilBertConfig()
|
|
>>> onnx_config = DistilBertOnnxConfig(config)
|
|
>>> print(list(onnx_config.outputs.keys()))
|
|
["last_hidden_state"]
|
|
```
|
|
|
|
Il processo è identico per i checkpoint TensorFlow sull'hub. Ad esempio, noi
|
|
possiamo esportare un checkpoint TensorFlow puro da [Keras
|
|
organizzazione](https://huggingface.co/keras-io) come segue:
|
|
|
|
```bash
|
|
python -m transformers.onnx --model=keras-io/transformers-qa onnx/
|
|
```
|
|
|
|
Per esportare un modello memorizzato localmente, devi disporre dei pesi del modello
|
|
e file tokenizer memorizzati in una directory. Ad esempio, possiamo caricare e salvare un
|
|
checkpoint come segue:
|
|
|
|
<frameworkcontent>
|
|
<pt>
|
|
```python
|
|
>>> from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
|
|
|
>>> # Load tokenizer and PyTorch weights form the Hub
|
|
>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased")
|
|
>>> pt_model = AutoModelForSequenceClassification.from_pretrained("distilbert/distilbert-base-uncased")
|
|
>>> # Save to disk
|
|
>>> tokenizer.save_pretrained("local-pt-checkpoint")
|
|
>>> pt_model.save_pretrained("local-pt-checkpoint")
|
|
```
|
|
|
|
Una volta salvato il checkpoint, possiamo esportarlo su ONNX puntando l'argomento `--model`
|
|
del pacchetto `transformers.onnx` nella directory desiderata:
|
|
|
|
```bash
|
|
python -m transformers.onnx --model=local-pt-checkpoint onnx/
|
|
```
|
|
</pt>
|
|
<tf>
|
|
```python
|
|
>>> from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
|
|
|
|
>>> # Load tokenizer and TensorFlow weights from the Hub
|
|
>>> tokenizer = AutoTokenizer.from_pretrained("distilbert/distilbert-base-uncased")
|
|
>>> tf_model = TFAutoModelForSequenceClassification.from_pretrained("distilbert/distilbert-base-uncased")
|
|
>>> # Save to disk
|
|
>>> tokenizer.save_pretrained("local-tf-checkpoint")
|
|
>>> tf_model.save_pretrained("local-tf-checkpoint")
|
|
```
|
|
|
|
Once the checkpoint is saved, we can export it to ONNX by pointing the `--model`
|
|
argument of the `transformers.onnx` package to the desired directory:
|
|
|
|
```bash
|
|
python -m transformers.onnx --model=local-tf-checkpoint onnx/
|
|
```
|
|
</tf>
|
|
</frameworkcontent>
|
|
|
|
### Selezione delle caratteristiche per diverse topologie di modello
|
|
|
|
Ogni configurazione già pronta viene fornita con una serie di _caratteristiche_ che ti consentono di
|
|
esportare modelli per diversi tipi di topologie o attività. Come mostrato nella tabella
|
|
di seguito, ogni caratteristica è associata a una diversa Auto Class:
|
|
|
|
| Caratteristica | Auto Class |
|
|
| ------------------------------------ | ------------------------------------ |
|
|
| `causal-lm`, `causal-lm-with-past` | `AutoModelForCausalLM` |
|
|
| `default`, `default-with-past` | `AutoModel` |
|
|
| `masked-lm` | `AutoModelForMaskedLM` |
|
|
| `question-answering` | `AutoModelForQuestionAnswering` |
|
|
| `seq2seq-lm`, `seq2seq-lm-with-past` | `AutoModelForSeq2SeqLM` |
|
|
| `sequence-classification` | `AutoModelForSequenceClassification` |
|
|
| `token-classification` | `AutoModelForTokenClassification` |
|
|
|
|
Per ciascuna configurazione, puoi trovare l'elenco delle funzionalità supportate tramite il
|
|
`FeaturesManager`. Ad esempio, per DistilBERT abbiamo:
|
|
|
|
```python
|
|
>>> from transformers.onnx.features import FeaturesManager
|
|
|
|
>>> distilbert_features = list(FeaturesManager.get_supported_features_for_model_type("distilbert").keys())
|
|
>>> print(distilbert_features)
|
|
["default", "masked-lm", "causal-lm", "sequence-classification", "token-classification", "question-answering"]
|
|
```
|
|
|
|
Puoi quindi passare una di queste funzionalità all'argomento `--feature` nel
|
|
pacchetto `transformers.onnx`. Ad esempio, per esportare un modello di classificazione del testo
|
|
possiamo scegliere un modello ottimizzato dall'Hub ed eseguire:
|
|
|
|
```bash
|
|
python -m transformers.onnx --model=distilbert/distilbert-base-uncased-finetuned-sst-2-english \
|
|
--feature=sequence-classification onnx/
|
|
```
|
|
|
|
che visualizzerà i seguenti registri:
|
|
|
|
```bash
|
|
Validating ONNX model...
|
|
-[✓] ONNX model output names match reference model ({'logits'})
|
|
- Validating ONNX Model output "logits":
|
|
-[✓] (2, 2) matches (2, 2)
|
|
-[✓] all values close (atol: 1e-05)
|
|
All good, model saved at: onnx/model.onnx
|
|
```
|
|
|
|
Puoi notare che in questo caso, i nomi di output del modello ottimizzato sono
|
|
`logits` invece di `last_hidden_state` che abbiamo visto con il
|
|
checkpoint `distilbert/distilbert-base-uncased` precedente. Questo è previsto dal
|
|
modello ottimizato visto che ha una testa di e.
|
|
|
|
<Tip>
|
|
|
|
Le caratteristiche che hanno un suffisso `wtih-past` (ad es. `causal-lm-with-past`)
|
|
corrispondono a topologie di modello con stati nascosti precalcolati (chiave e valori
|
|
nei blocchi di attenzione) che possono essere utilizzati per la decodifica autoregressiva veloce.
|
|
|
|
</Tip>
|
|
|
|
|
|
### Esportazione di un modello per un'architettura non supportata
|
|
|
|
Se desideri esportare un modello la cui architettura non è nativamente supportata dalla
|
|
libreria, ci sono tre passaggi principali da seguire:
|
|
|
|
1. Implementare una configurazione ONNX personalizzata.
|
|
2. Esportare il modello in ONNX.
|
|
3. Convalidare gli output di PyTorch e dei modelli esportati.
|
|
|
|
In questa sezione, vedremo come DistilBERT è stato implementato per mostrare cosa è
|
|
coinvolto in ogni passaggio.
|
|
|
|
#### Implementazione di una configurazione ONNX personalizzata
|
|
|
|
Iniziamo con l'oggetto di configurazione ONNX. Forniamo tre classi
|
|
astratte da cui ereditare, a seconda del tipo di archittettura
|
|
del modello che desideri esportare:
|
|
|
|
* I modelli basati su encoder ereditano da [`~onnx.config.OnnxConfig`]
|
|
* I modelli basati su decoder ereditano da [`~onnx.config.OnnxConfigWithPast`]
|
|
* I modelli encoder-decoder ereditano da[`~onnx.config.OnnxSeq2SeqConfigWithPast`]
|
|
|
|
<Tip>
|
|
|
|
Un buon modo per implementare una configurazione ONNX personalizzata è guardare l'implementazione
|
|
esistente nel file `configuration_<model_name>.py` di un'architettura simile.
|
|
|
|
</Tip>
|
|
|
|
Poiché DistilBERT è un modello basato su encoder, la sua configurazione eredita da
|
|
`OnnxConfig`:
|
|
|
|
```python
|
|
>>> from typing import Mapping, OrderedDict
|
|
>>> from transformers.onnx import OnnxConfig
|
|
|
|
|
|
>>> class DistilBertOnnxConfig(OnnxConfig):
|
|
... @property
|
|
... def inputs(self) -> Mapping[str, Mapping[int, str]]:
|
|
... return OrderedDict(
|
|
... [
|
|
... ("input_ids", {0: "batch", 1: "sequence"}),
|
|
... ("attention_mask", {0: "batch", 1: "sequence"}),
|
|
... ]
|
|
... )
|
|
```
|
|
|
|
Ogni oggetto di configurazione deve implementare la proprietà `inputs` e restituire una
|
|
mappatura, dove ogni chiave corrisponde a un input previsto e ogni valore
|
|
indica l'asse di quell'input. Per DistilBERT, possiamo vedere che sono richiesti
|
|
due input: `input_ids` e `attention_mask`. Questi inputs hanno la stessa forma di
|
|
`(batch_size, sequence_length)` per questo motivo vediamo gli stessi assi usati nella
|
|
configurazione.
|
|
|
|
<Tip>
|
|
|
|
Puoi notare che la proprietà `inputs` per `DistilBertOnnxConfig` restituisce un
|
|
`OrdinatoDict`. Ciò garantisce che gli input corrispondano alla loro posizione
|
|
relativa all'interno del metodo `PreTrainedModel.forward()` durante il tracciamento del grafico.
|
|
Raccomandiamo di usare un `OrderedDict` per le proprietà `inputs` e `outputs`
|
|
quando si implementano configurazioni ONNX personalizzate.
|
|
|
|
</Tip>
|
|
|
|
Dopo aver implementato una configurazione ONNX, è possibile istanziarla
|
|
fornendo alla configurazione del modello base come segue:
|
|
|
|
```python
|
|
>>> from transformers import AutoConfig
|
|
|
|
>>> config = AutoConfig.from_pretrained("distilbert/distilbert-base-uncased")
|
|
>>> onnx_config = DistilBertOnnxConfig(config)
|
|
```
|
|
|
|
L'oggetto risultante ha diverse proprietà utili. Ad esempio è possibile visualizzare il
|
|
Set operatore ONNX che verrà utilizzato durante l'esportazione:
|
|
|
|
```python
|
|
>>> print(onnx_config.default_onnx_opset)
|
|
11
|
|
```
|
|
|
|
È inoltre possibile visualizzare gli output associati al modello come segue:
|
|
|
|
```python
|
|
>>> print(onnx_config.outputs)
|
|
OrderedDict([("last_hidden_state", {0: "batch", 1: "sequence"})])
|
|
```
|
|
|
|
Puoi notare che la proprietà degli output segue la stessa struttura degli input; esso
|
|
restituisce un `OrderedDict` di output con nome e le loro forme. La struttura di output
|
|
è legato alla scelta della funzione con cui viene inizializzata la configurazione.
|
|
Per impostazione predefinita, la configurazione ONNX viene inizializzata con la funzione 'predefinita'
|
|
che corrisponde all'esportazione di un modello caricato con la classe `AutoModel`. Se tu
|
|
desideri esportare una topologia di modello diversa, è sufficiente fornire una funzionalità diversa a
|
|
l'argomento `task` quando inizializzi la configurazione ONNX. Ad esempio, se
|
|
volevamo esportare DistilBERT con una testa di classificazione per sequenze, potremmo
|
|
usare:
|
|
|
|
```python
|
|
>>> from transformers import AutoConfig
|
|
|
|
>>> config = AutoConfig.from_pretrained("distilbert/distilbert-base-uncased")
|
|
>>> onnx_config_for_seq_clf = DistilBertOnnxConfig(config, task="sequence-classification")
|
|
>>> print(onnx_config_for_seq_clf.outputs)
|
|
OrderedDict([('logits', {0: 'batch'})])
|
|
```
|
|
|
|
<Tip>
|
|
|
|
Tutte le proprietà e i metodi di base associati a [`~onnx.config.OnnxConfig`] e le
|
|
altre classi di configurazione possono essere sovrascritte se necessario. Guarda
|
|
[`BartOnnxConfig`] per un esempio avanzato.
|
|
|
|
</Tip>
|
|
|
|
#### Esportazione del modello
|
|
|
|
Una volta implementata la configurazione ONNX, il passaggio successivo consiste nell'esportare il
|
|
modello. Qui possiamo usare la funzione `export()` fornita dal
|
|
pacchetto `transformers.onnx`. Questa funzione prevede la configurazione ONNX, insieme
|
|
con il modello base e il tokenizer e il percorso per salvare il file esportato:
|
|
|
|
```python
|
|
>>> from pathlib import Path
|
|
>>> from transformers.onnx import export
|
|
>>> from transformers import AutoTokenizer, AutoModel
|
|
|
|
>>> onnx_path = Path("model.onnx")
|
|
>>> model_ckpt = "distilbert/distilbert-base-uncased"
|
|
>>> base_model = AutoModel.from_pretrained(model_ckpt)
|
|
>>> tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
|
|
|
|
>>> onnx_inputs, onnx_outputs = export(tokenizer, base_model, onnx_config, onnx_config.default_onnx_opset, onnx_path)
|
|
```
|
|
|
|
Gli `onnx_inputs` e `onnx_outputs` restituiti dalla funzione `export()` sono
|
|
liste di chiavi definite nelle proprietà di `input` e `output` della
|
|
configurazione. Una volta esportato il modello, puoi verificare che il modello sia ben
|
|
formato come segue:
|
|
|
|
```python
|
|
>>> import onnx
|
|
|
|
>>> onnx_model = onnx.load("model.onnx")
|
|
>>> onnx.checker.check_model(onnx_model)
|
|
```
|
|
|
|
<Tip>
|
|
|
|
Se il tuo modello è più largo di 2 GB, vedrai che molti file aggiuntivi sono
|
|
creati durante l'esportazione. Questo è _previsto_ perché ONNX utilizza [Protocol
|
|
Buffer](https://developers.google.com/protocol-buffers/) per memorizzare il modello e
|
|
questi hanno un limite di dimensione 2 GB. Vedi la [Documentazione
|
|
ONNX](https://github.com/onnx/onnx/blob/master/docs/ExternalData.md)
|
|
per istruzioni su come caricare modelli con dati esterni.
|
|
|
|
</Tip>
|
|
|
|
#### Convalida degli output del modello
|
|
|
|
Il passaggio finale consiste nel convalidare gli output dal modello di base e quello esportato
|
|
corrispondere entro una soglia di tolleranza assoluta. Qui possiamo usare la
|
|
Funzione `validate_model_outputs()` fornita dal pacchetto `transformers.onnx`
|
|
come segue:
|
|
|
|
```python
|
|
>>> from transformers.onnx import validate_model_outputs
|
|
|
|
>>> validate_model_outputs(
|
|
... onnx_config, tokenizer, base_model, onnx_path, onnx_outputs, onnx_config.atol_for_validation
|
|
... )
|
|
```
|
|
|
|
Questa funzione usa il metodo `OnnxConfig.generate_dummy_inputs()` per generare
|
|
input per il modello di base e quello esportato e la tolleranza assoluta può essere
|
|
definita nella configurazione. Generalmente troviamo una corrispondenza numerica nell'intervallo da 1e-6
|
|
a 1e-4, anche se è probabile che qualsiasi cosa inferiore a 1e-3 vada bene.
|
|
|
|
### Contribuire con una nuova configurazione a 🤗 Transformers
|
|
|
|
Stiamo cercando di espandere l'insieme di configurazioni già pronte e di accettare
|
|
contributi della community! Se vuoi contribuire con la tua aggiunta
|
|
nella libreria, dovrai:
|
|
|
|
* Implementare la configurazione ONNX nella corrispondente `configuration file
|
|
_<model_name>.py`
|
|
* Includere l'architettura del modello e le funzioni corrispondenti in [`~onnx.features.FeatureManager`]
|
|
* Aggiungere la tua architettura del modello ai test in `test_onnx_v2.py`
|
|
|
|
Scopri come stato contribuito la configurazione per [IBERT](https://github.com/huggingface/transformers/pull/14868/files) per
|
|
avere un'idea di cosa è coinvolto.
|
|
|
|
## TorchScript
|
|
|
|
<Tip>
|
|
|
|
Questo è l'inizio dei nostri esperimenti con TorchScript e stiamo ancora esplorando le sue capacità con
|
|
modelli con variable-input-size. È una nostra priorità e approfondiremo le nostre analisi nelle prossime versioni,
|
|
con più esempi di codici, un'implementazione più flessibile e benchmark che confrontano i codici basati su Python con quelli compilati con
|
|
TorchScript.
|
|
|
|
</Tip>
|
|
|
|
Secondo la documentazione di Pytorch: "TorchScript è un modo per creare modelli serializzabili e ottimizzabili da codice
|
|
Pytorch". I due moduli di Pytorch [JIT e TRACE](https://pytorch.org/docs/stable/jit.html) consentono allo sviluppatore di esportare
|
|
il loro modello da riutilizzare in altri programmi, come i programmi C++ orientati all'efficienza.
|
|
|
|
Abbiamo fornito un'interfaccia che consente l'esportazione di modelli 🤗 Transformers in TorchScript in modo che possano essere riutilizzati
|
|
in un ambiente diverso rispetto a un programma Python basato su Pytorch. Qui spieghiamo come esportare e utilizzare i nostri modelli utilizzando
|
|
TorchScript.
|
|
|
|
Esportare un modello richiede due cose:
|
|
|
|
- Un passaggio in avanti con input fittizzi.
|
|
- Istanziazione del modello con flag `torchscript`.
|
|
|
|
Queste necessità implicano diverse cose a cui gli sviluppatori dovrebbero prestare attenzione. Questi dettagli mostrati sotto.
|
|
|
|
### Flag TorchScript e pesi legati
|
|
|
|
Questo flag è necessario perché la maggior parte dei modelli linguistici in questo repository hanno pesi legati tra il loro
|
|
strato "Embedding" e lo strato "Decoding". TorchScript non consente l'esportazione di modelli che hanno pesi
|
|
legati, quindi è necessario prima slegare e clonare i pesi.
|
|
|
|
Ciò implica che i modelli istanziati con il flag `torchscript` hanno il loro strato `Embedding` e strato `Decoding`
|
|
separato, il che significa che non dovrebbero essere addestrati in futuro. L'allenamento de-sincronizza i due
|
|
strati, portando a risultati inaspettati.
|
|
|
|
Questo non è il caso per i modelli che non hanno una testa del modello linguistico, poiché quelli non hanno pesi legati. Questi modelli
|
|
può essere esportato in sicurezza senza il flag `torchscript`.
|
|
|
|
### Input fittizi e standard lengths
|
|
|
|
Gli input fittizzi sono usati per fare un modello passaggio in avanti . Mentre i valori degli input si propagano attraverso i strati,
|
|
Pytorch tiene traccia delle diverse operazioni eseguite su ciascun tensore. Queste operazioni registrate vengono quindi utilizzate per
|
|
creare la "traccia" del modello.
|
|
|
|
La traccia viene creata relativamente alle dimensioni degli input. È quindi vincolato dalle dimensioni dell'input
|
|
fittizio e non funzionerà per altre lunghezze di sequenza o dimensioni batch. Quando si proverà con una dimensione diversa, ci sarà errore
|
|
come:
|
|
|
|
`La dimensione espansa del tensore (3) deve corrispondere alla dimensione esistente (7) nella dimensione non singleton 2`
|
|
|
|
will be raised. Si consiglia pertanto di tracciare il modello con una dimensione di input fittizia grande almeno quanto il più grande
|
|
input che verrà fornito al modello durante l'inferenza. È possibile eseguire il padding per riempire i valori mancanti. Il modello
|
|
sarà tracciato con una grande dimensione di input, tuttavia, anche le dimensioni della diverse matrici saranno grandi,
|
|
risultando in più calcoli.
|
|
|
|
Si raccomanda di prestare attenzione al numero totale di operazioni eseguite su ciascun input e di seguire da vicino le prestazioni
|
|
durante l'esportazione di modelli di sequenza-lunghezza variabili.
|
|
|
|
### Usare TorchSscript in Python
|
|
|
|
Di seguito è riportato un esempio, che mostra come salvare, caricare modelli e come utilizzare la traccia per l'inferenza.
|
|
|
|
#### Salvare un modello
|
|
|
|
Questo frammento di codice mostra come usare TorchScript per esportare un `BertModel`. Qui il `BertModel` è istanziato secondo
|
|
una classe `BertConfig` e quindi salvato su disco con il nome del file `traced_bert.pt`
|
|
|
|
```python
|
|
from transformers import BertModel, BertTokenizer, BertConfig
|
|
import torch
|
|
|
|
enc = BertTokenizer.from_pretrained("google-bert/bert-base-uncased")
|
|
|
|
# Tokenizing input text
|
|
text = "[CLS] Who was Jim Henson ? [SEP] Jim Henson was a puppeteer [SEP]"
|
|
tokenized_text = enc.tokenize(text)
|
|
|
|
# Masking one of the input tokens
|
|
masked_index = 8
|
|
tokenized_text[masked_index] = "[MASK]"
|
|
indexed_tokens = enc.convert_tokens_to_ids(tokenized_text)
|
|
segments_ids = [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
|
|
|
|
# Creating a dummy input
|
|
tokens_tensor = torch.tensor([indexed_tokens])
|
|
segments_tensors = torch.tensor([segments_ids])
|
|
dummy_input = [tokens_tensor, segments_tensors]
|
|
|
|
# Initializing the model with the torchscript flag
|
|
# Flag set to True even though it is not necessary as this model does not have an LM Head.
|
|
config = BertConfig(
|
|
vocab_size_or_config_json_file=32000,
|
|
hidden_size=768,
|
|
num_hidden_layers=12,
|
|
num_attention_heads=12,
|
|
intermediate_size=3072,
|
|
torchscript=True,
|
|
)
|
|
|
|
# Instantiating the model
|
|
model = BertModel(config)
|
|
|
|
# The model needs to be in evaluation mode
|
|
model.eval()
|
|
|
|
# If you are instantiating the model with *from_pretrained* you can also easily set the TorchScript flag
|
|
model = BertModel.from_pretrained("google-bert/bert-base-uncased", torchscript=True)
|
|
|
|
# Creating the trace
|
|
traced_model = torch.jit.trace(model, [tokens_tensor, segments_tensors])
|
|
torch.jit.save(traced_model, "traced_bert.pt")
|
|
```
|
|
|
|
#### Caricare un modello
|
|
|
|
Questo frammento di codice mostra come caricare il `BertModel` che era stato precedentemente salvato su disco con il nome `traced_bert.pt`.
|
|
Stiamo riutilizzando il `dummy_input` precedentemente inizializzato.
|
|
|
|
```python
|
|
loaded_model = torch.jit.load("traced_bert.pt")
|
|
loaded_model.eval()
|
|
|
|
all_encoder_layers, pooled_output = loaded_model(*dummy_input)
|
|
```
|
|
|
|
#### Utilizzare un modello tracciato per l'inferenza
|
|
|
|
Usare il modello tracciato per l'inferenza è semplice come usare il suo metodo dunder `__call__`:
|
|
|
|
```python
|
|
traced_model(tokens_tensor, segments_tensors)
|
|
```
|
|
|
|
### Implementare modelli HuggingFace TorchScript su AWS utilizzando Neuron SDK
|
|
|
|
AWS ha introdotto [Amazon EC2 Inf1](https://aws.amazon.com/ec2/instance-types/inf1/)
|
|
famiglia di istanze per l'inferenza di machine learning a basso costo e ad alte prestazioni nel cloud.
|
|
Le istanze Inf1 sono alimentate dal chip AWS Inferentia, un acceleratore hardware personalizzato,
|
|
specializzato in carichi di lavoro di inferenza di deep learning.
|
|
[AWS Neuron](https://awsdocs-neuron.readthedocs-hosted.com/en/latest/#)
|
|
è l'SDK per Inferentia che supporta il tracciamento e l'ottimizzazione dei modelli transformers per
|
|
distribuzione su Inf1. L'SDK Neuron fornisce:
|
|
|
|
|
|
1. API di facile utilizzo con una riga di modifica del codice per tracciare e ottimizzare un modello TorchScript per l'inferenza nel cloud.
|
|
2. Ottimizzazioni delle prestazioni pronte all'uso per [miglioramento dei costi-prestazioni](https://awsdocs-neuron.readthedocs-hosted.com/en/latest/neuron-guide/benchmark/>)
|
|
3. Supporto per i modelli di trasformatori HuggingFace costruiti con [PyTorch](https://awsdocs-neuron.readthedocs-hosted.com/en/latest/src/examples/pytorch/bert_tutorial/tutorial_pretrained_bert.html)
|
|
o [TensorFlow](https://awsdocs-neuron.readthedocs-hosted.com/en/latest/src/examples/tensorflow/huggingface_bert/huggingface_bert.html).
|
|
|
|
#### Implicazioni
|
|
|
|
Modelli Transformers basati su architettura [BERT (Bidirectional Encoder Representations from Transformers)](https://huggingface.co/docs/transformers/main/model_doc/bert),
|
|
o sue varianti come [distilBERT](https://huggingface.co/docs/transformers/main/model_doc/distilbert)
|
|
e [roBERTa](https://huggingface.co/docs/transformers/main/model_doc/roberta)
|
|
funzioneranno meglio su Inf1 per attività non generative come la question answering estrattive,
|
|
Classificazione della sequenza, Classificazione dei token. In alternativa, generazione di testo
|
|
le attività possono essere adattate per essere eseguite su Inf1, secondo questo [tutorial AWS Neuron MarianMT](https://awsdocs-neuron.readthedocs-hosted.com/en/latest/src/examples/pytorch/transformers-marianmt.html).
|
|
Ulteriori informazioni sui modelli che possono essere convertiti fuori dagli schemi su Inferentia possono essere
|
|
trovati nella [sezione Model Architecture Fit della documentazione Neuron](https://awsdocs-neuron.readthedocs-hosted.com/en/latest/neuron-guide/models/models-inferentia.html#models-inferentia).
|
|
|
|
#### Dipendenze
|
|
|
|
L'utilizzo di AWS Neuron per convertire i modelli richiede le seguenti dipendenze e l'ambiente:
|
|
|
|
* A [Neuron SDK environment](https://awsdocs-neuron.readthedocs-hosted.com/en/latest/neuron-guide/neuron-frameworks/pytorch-neuron/index.html#installation-guide),
|
|
which comes pre-configured on [AWS Deep Learning AMI](https://docs.aws.amazon.com/dlami/latest/devguide/tutorial-inferentia-launching.html).
|
|
|
|
#### Convertire un modello per AWS Neuron
|
|
|
|
Usando lo stesso script come in [Usando TorchScipt in Python](https://huggingface.co/docs/transformers/main/en/serialization#using-torchscript-in-python)
|
|
per tracciare un "BertModel", importi l'estensione del framework `torch.neuron` per accedere
|
|
i componenti di Neuron SDK tramite un'API Python.
|
|
|
|
```python
|
|
from transformers import BertModel, BertTokenizer, BertConfig
|
|
import torch
|
|
import torch.neuron
|
|
```
|
|
E modificare solo la riga di codice di traccia
|
|
|
|
Da:
|
|
|
|
```python
|
|
torch.jit.trace(model, [tokens_tensor, segments_tensors])
|
|
```
|
|
|
|
A:
|
|
|
|
```python
|
|
torch.neuron.trace(model, [token_tensor, segments_tensors])
|
|
```
|
|
|
|
Questa modifica consente a Neuron SDK di tracciare il modello e ottimizzarlo per l'esecuzione nelle istanze Inf1.
|
|
|
|
Per ulteriori informazioni sulle funzionalità, gli strumenti, i tutorial di esempi e gli ultimi aggiornamenti di AWS Neuron SDK,
|
|
consultare la [documentazione AWS NeuronSDK](https://awsdocs-neuron.readthedocs-hosted.com/en/latest/index.html). |