<!--Copyright 2022 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. | |
--> | |
# Istanziare un big model | |
Quando vuoi utilizzare un modello preaddestrato (pretrained) molto grande, una sfida è minimizzare l'uso della RAM. Il workflow classico | |
in PyTorch è: | |
1. Crea il tuo modello con pesi casuali (random weights). | |
2. Carica i tuoi pesi preaddestrati. | |
3. Inserisci i pesi preaddestrati nel tuo modello casuale. | |
I passi 1 e 2 una versione completa del modello in memoria, in molti casi non è un problema, ma se il modello inizia a pesare diversi GigaBytes, queste due copie possono sturare la nostra RAM. Ancora peggio, se stai usando `torch.distributed` per seguire l'addestramento (training) in distribuito, ogni processo caricherà il modello preaddestrato e memorizzerà queste due copie nella RAM. | |
<Tip> | |
Nota che il modello creato casualmente è inizializzato con tensori "vuoti", che occupano spazio in memoria ma senza riempirlo (quindi i valori casuali sono quelli che si trovavano in questa porzione di memoria in un determinato momento). L'inizializzazione casuale che segue la distribuzione appropriata per il tipo di modello/parametri istanziato (come la distribuzione normale per le istanze) è eseguito solo dopo il passaggio 3 sui pesi non inizializzati, per essere più rapido possibile! | |
</Tip> | |
In questa guida, esploreremo le soluzioni che Transformers offre per affrontare questo problema. C'è da tenere in conto che questa è un'area in cui si sta attualmente sviluppando, quindi le API spiegate qui possono variare velocemente in futuro. | |
## Checkpoints condivisi | |
Dalla versione 4.18.0, i checkpoints dei modelli che occupano più di 10GB di spazio vengono automaticamente frammentati in più parti. Per quanto riguarda la possibilità di avere un unico checkpoint quando si utilizza `model.save_pretrained(save_dir)`, si hanno diversi checkpoint parziali (ognuno con dimensione < 10GB) e un indice che mappa i nomi dei parametri ai file in cui sono memorizzati. | |
Puoi controllare la dimensione massima dopo la frammentazione con il parametro `max_shard_size`, nel prossimo esempio, useremo modelli di dimensioni normali con frammenti di piccoli dimensioni: prendiamo un modello BERT classico. | |
```py | |
from transformers import AutoModel | |
model = AutoModel.from_pretrained("bert-base-cased") | |
``` | |
Se tu salvi usando [`~PreTrainedModel.save_pretrained`], avrai una nuova cartella con due file: il config del modello e i suoi pesi: | |
```py | |
>>> import os | |
>>> import tempfile | |
>>> with tempfile.TemporaryDirectory() as tmp_dir: | |
... model.save_pretrained(tmp_dir) | |
... print(sorted(os.listdir(tmp_dir))) | |
['config.json', 'pytorch_model.bin'] | |
``` | |
Adesso usiamo una dimensione massima di frammentazione di 200MB: | |
```py | |
>>> with tempfile.TemporaryDirectory() as tmp_dir: | |
... model.save_pretrained(tmp_dir, max_shard_size="200MB") | |
... print(sorted(os.listdir(tmp_dir))) | |
['config.json', 'pytorch_model-00001-of-00003.bin', 'pytorch_model-00002-of-00003.bin', 'pytorch_model-00003-of-00003.bin', 'pytorch_model.bin.index.json'] | |
``` | |
In aggiunta alla configurazione del modello, vediamo tre differenti file dei pesi, e un file `index.json` che è il nostro indice. Un checkpoint può essere ricaricato totalmente usando il metodo [`~PreTrainedModel.from_pretrained`]: | |
```py | |
>>> with tempfile.TemporaryDirectory() as tmp_dir: | |
... model.save_pretrained(tmp_dir, max_shard_size="200MB") | |
... new_model = AutoModel.from_pretrained(tmp_dir) | |
``` | |
Il vantaggio principale di applicare questo metodo per modelli grandi è che durante il passo 2 del workflow illustrato in precedenza, ogni frammento del checkpoint viene caricato dopo il precedente, limitando l'utilizzo della RAM alla dimensione del modello più la dimensione del frammento più grande. | |
Dietro le quinte, il file indice è utilizzato per determinare quali chiavi sono nel checkpoint, e dove i corrispondenti pesi sono memorizzati. Possiamo caricare l'indice come un qualsiasi json e ottenere un dizionario: | |
```py | |
>>> import json | |
>>> with tempfile.TemporaryDirectory() as tmp_dir: | |
... model.save_pretrained(tmp_dir, max_shard_size="200MB") | |
... with open(os.path.join(tmp_dir, "pytorch_model.bin.index.json"), "r") as f: | |
... index = json.load(f) | |
>>> print(index.keys()) | |
dict_keys(['metadata', 'weight_map']) | |
``` | |
I metadati consistono solo nella dimensione totale del modello per ora. Abbiamo in programma di aggiungere altre informazioni in futuro: | |
```py | |
>>> index["metadata"] | |
{'total_size': 433245184} | |
``` | |
La mappa dei pesi è la parte principale di questo indice, che mappa ogni nome dei parametri (si trova solitamente nei modelli PyTorch come `state_dict`) al file in cui è memorizzato: | |
```py | |
>>> index["weight_map"] | |
{'embeddings.LayerNorm.bias': 'pytorch_model-00001-of-00003.bin', | |
'embeddings.LayerNorm.weight': 'pytorch_model-00001-of-00003.bin', | |
... | |
``` | |
Se vuoi caricare direttamente un checkpoint frammentato in un modello senza usare [`~PreTrainedModel.from_pretrained`] (come si farebbe con `model.load_state_dict()` per un checkpoint completo) devi usare [`~modeling_utils.load_sharded_checkpoint`]: | |
```py | |
>>> from transformers.modeling_utils import load_sharded_checkpoint | |
>>> with tempfile.TemporaryDirectory() as tmp_dir: | |
... model.save_pretrained(tmp_dir, max_shard_size="200MB") | |
... load_sharded_checkpoint(model, tmp_dir) | |
``` | |
## Caricamento low memory | |
Frammentare i checkpoint l'utilizzo di memoria al passo 2 del workflow citato in precedenza, ma per utilizzare questo modello in un ambiente con poca memoria, consigliamo di utilizzare i nostri strumenti basati sulla libreria Accelerate. | |
Per ulteriori informazioni, leggere la seguente guida: [Large model loading using Accelerate](./main_classes/model#large-model-loading) |