Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.
Suggerimenti e insidie per la configurazione della SageMaker Distributed Model Parallelism Library
Consulta i seguenti suggerimenti e insidie prima di utilizzare la libreria di parallelismo SageMaker dei modelli di Amazon. Questo elenco include suggerimenti applicabili a tutti i framework. Per TensorFlow suggerimenti PyTorch specifici, vedi Modificare uno script di addestramento TensorFlow eModificare uno script PyTorch di addestramento, rispettivamente.
Dimensione del batch e numero di microbatch
-
La libreria è più efficiente quando la dimensione del batch viene aumentata. Nei casi d'uso in cui il modello si inserisce in un singolo dispositivo, ma può essere addestrato solo con batch di piccole dimensioni, la dimensione del batch può e deve essere aumentata dopo l'integrazione della libreria. Il parallelismo dei modelli consente di risparmiare memoria per i modelli di grandi dimensioni, consentendovi di addestrare utilizzando batch di dimensioni che in precedenza non rientravano nella memoria.
-
La scelta di un numero di microbatch troppo piccolo o troppo grande può ridurre le prestazioni. La libreria esegue ogni microbatch in sequenza in ogni dispositivo, pertanto la dimensione del microbatch (dimensione del batch divisa per il numero di microbatch) deve essere sufficientemente grande da consentire l'utilizzo completo di ciascuna GPU. Allo stesso tempo, l'efficienza della pipeline aumenta con il numero di microbatch, quindi è importante trovare il giusto equilibrio. In genere, un buon punto di inizio è provare 2 o 4 microbatch, aumentando la dimensione del batch fino al limite di memoria, e quindi sperimentare con batch di dimensioni e numeri di microbatch più grandi. Con l'aumento del numero di microbatch, potrebbero diventare possibili lotti di dimensioni maggiori se si utilizza una pipeline interlacciata.
-
La dimensione del batch deve essere sempre divisibile per il numero di microbatch. Tieni presente che, a seconda delle dimensioni del set di dati, a volte l'ultimo batch di ogni epoca può avere dimensioni inferiori rispetto al resto e questo batch più piccolo deve essere divisibile anche per il numero di microbatch.
drop_remainder=True
In caso contrario, è possibile impostare latf.Dataset.batch()
chiamata (in TensorFlow) o impostareDataLoader
(drop_last=True
in PyTorch), in modo che quest'ultimo, piccolo batch non venga utilizzato. Se si utilizza un'API diversa per la pipeline di dati, potrebbe essere necessario saltare manualmente l'ultimo batch ogni volta che non è divisibile per il numero di microbatch.
Partizionamento manuale
-
Se utilizzi il partizionamento manuale, tieni presente i parametri utilizzati da più operazioni e moduli del modello, come la tabella di incorporamento nelle architetture dei trasformatori. I moduli che condividono lo stesso parametro devono essere collocati nello stesso dispositivo per motivi di correttezza. Quando viene utilizzato il partizionamento automatico, la libreria applica automaticamente questo vincolo.
Preparazione dei dati
-
Se il modello richiede più input, assicurati di impostare le operazioni casuali nella data pipeline (ad esempio, lo shuffling) con
smp.dp_rank()
. Se il set di dati viene suddiviso in modo deterministico su dispositivi di parallelismo dei dati, assicurati che lo shard sia indicizzato dasmp.dp_rank()
. Questo serve a garantire che l'ordine dei dati visualizzati su tutte le classificazioni che formano una partizione del modello sia coerente.
Restituzione di tensori da smp.DistributedModel
-
Qualsiasi tensore restituito dalla funzione
smp.DistributedModel.call
(for TensorFlow) osmp.DistributedModel.forward
(for PyTorch) viene trasmesso a tutti gli altri ranghi, dal rango che ha calcolato quel particolare tensore. Di conseguenza, qualsiasi tensore non necessario al di fuori dei metodi call e forward (attivazioni intermedie, ad esempio) non dovrebbe essere restituito, poiché ciò causa inutili comunicazioni e sovraccarico di memoria e danneggia le prestazioni.
Il decoratore @smp.step
-
Se una funzione
smp.step
decorata ha un argomento di tensore che non ha una dimensione batch, il nome dell'argomento deve essere fornito nell'elenconon_split_inputs
durante la chiamatasmp.step
. Ciò impedisce alla libreria di tentare di dividere il tensore in microbatch. Per ulteriori informazioni, consultasmp.step
nella documentazione API.
Ritardo dell'inizializzazione dei parametri
Per modelli molto grandi con oltre 100 miliardi di parametri, l'inizializzazione del peso tramite la memoria della CPU potrebbe causare un errore. out-of-memory Per ovviare a questo problema, la libreria offre un gestore di contesto smp.delay_param_initialization
. Ciò ritarda l'allocazione fisica dei parametri fino a quando non passano alla GPU durante la prima esecuzione di una funzione smp.step
decorata. Ciò evita un utilizzo non necessario della memoria della CPU durante l'inizializzazione dell'addestramento. Utilizza il gestore di contesto quando crei un oggetto modello, come mostrato nel codice seguente.
with smp.delay_param_initialization(enabled=True): model = MyModel()
Tensor Parallelism per PyTorch
-
Se stai usando un seed per risultati deterministici, imposta il seed in base a
smp.dp_rank()
(ad esempio,torch.manual_seed(42 + smp.dp_rank())
). Se non effettui questa operazione, diverse partizioni di unnn.Parameter
vengono inizializzate nello stesso modo, con un impatto sulla convergenza. -
SageMakerla libreria di parallelismo dei modelli utilizza NCCL per implementare i collettivi necessari per la distribuzione dei moduli. Soprattutto per i modelli più piccoli, se sulla GPU sono programmate troppe chiamate NCCL contemporaneamente, l'utilizzo della memoria potrebbe aumentare a causa dello spazio aggiuntivo utilizzato da NCCL. Per ovviare a questo problema,
smp
limita le chiamate NCCL in modo che il numero di operazioni NCCL in corso in un dato momento sia inferiore o uguale a un determinato limite. Il limite predefinito è 8, ma può essere regolato utilizzando la variabile di ambienteSMP_NCCL_THROTTLE_LIMIT
. Se osservi un utilizzo della memoria maggiore del previsto durante l'utilizzo del parallelismo tensoriale, puoi provare a ridurre questo limite. Tuttavia, la scelta di un limite troppo piccolo potrebbe causare una perdita di throughput. Per disabilitare completamente la limitazione, puoi impostareSMP_NCCL_THROTTLE_LIMIT=-1
. -
La seguente identità, che vale quando il grado di parallelismo tensoriale è 1, non vale quando il grado di parallelismo tensoriale è maggiore di 1:
smp.mp_size() * smp.dp_size() == smp.size()
. Questo perché il gruppo parallelo tensoriale fa parte sia del gruppo di parallelismo del modello che del gruppo di parallelismo dei dati. Se il codice contiene riferimenti esistenti amp_rank
,mp_size
,MP_GROUP
e così via e se desideri lavorare solo con il gruppo pipeline parallel, potrebbe essere necessario sostituire i riferimenti consmp.pp_size()
. Le seguenti identità sono sempre vere:-
smp.mp_size() * smp.rdp_size() == smp.size()
-
smp.pp_size() * smp.dp_size() == smp.size()
-
smp.pp_size() * smp.tp_size() * smp.rdp_size() == smp.size()
-
-
Poiché il wrapper
smp.DistributedModel
modifica i parametri del modello quando il parallelismo tensoriale è abilitato, l'ottimizzatore deve essere creato dopo la chiamata asmp.DistributedModel
, con i parametri distribuiti. Ad esempio, quanto segue non funziona:## WRONG model = MyModel() optimizer = SomeOptimizer(model.parameters()) model = smp.DistributedModel(model) # optimizer now has outdated parameters!
Invece, l'ottimizzatore dovrebbe essere creato con i seguenti parametri del
smp.DistributedModel
:## CORRECT model = smp.DistributedModel(MyModel()) optimizer = SomeOptimizer(model.optimizers())
-
Quando un modulo viene sostituito con la sua controparte distribuita tramite il parallelismo tensoriale, il modulo distribuito non eredita i suoi pesi dal modulo originale e inizializza nuovi pesi. Ciò significa che, ad esempio, se i pesi devono essere inizializzati in una particolare chiamata (ad esempio, tramite una chiamata
load_state_dict
), ciò deve avvenire dopo la chiamatasmp.DistributedModel
, una volta avvenuta la distribuzione del modulo. -
Quando accedi direttamente ai parametri dei moduli distribuiti, tieni presente che il peso non ha la stessa forma del modulo originale. Ad esempio,
with smp.tensor_parallelism(): linear = nn.Linear(60, 60) # will pass assert tuple(linear.weight.shape) == (60, 60) distributed_linear = smp.DistributedModel(linear) # will fail. the number of input channels will have been divided by smp.tp_size() assert tuple(distributed_linear.module.weight.shape) == (60, 60)
-
L'uso di
torch.utils.data.distributed.DistributedSampler
è fortemente consigliato per il parallelismo tensoriale. Ciò garantisce che ogni classificazione di parallelismo dei dati riceva lo stesso numero di campioni di dati, il che impedisce i blocchi che potrebbero derivare dall'esecuzione di diversi numeri di fasi da parte di diversidp_rank
. -
Se si utilizza la
DistributedDataParallel
classejoin
API of PyTorch per gestire i casi in cui ranghi paralleli di dati diversi hanno un numero diverso di batch, è comunque necessario assicurarsi che i ranghi che si trovano nello stessoTP_GROUP
numero di batch; altrimenti i collettivi di comunicazione utilizzati nell'esecuzione distribuita dei moduli potrebbero bloccarsi. Le classificazioni che si trovano inTP_GROUP
diversi possono avere un numero diverso di batch, purché venga utilizzata l'APIjoin
. -
Se desideri controllare il tuo modello e utilizzare il parallelismo tensoriale, considera quanto segue:
-
Per evitare situazioni di stallo e condizioni di competizione durante il salvataggio e il caricamento dei modelli quando utilizzi il parallelismo tensoriale, assicurati di richiamare le funzioni appropriate dai seguenti stati del modello e dell'ottimizzatore all'interno di una classificazione di parallelismo dei dati ridotta.
-
Se stai effettuando la transizione di uno script parallelo di pipeline esistente e abiliti il parallelismo tensoriale per lo script, assicurati di modificare qualsiasi blocco
if smp.dp_rank() == 0
utilizzato per il salvataggio e il caricamento con blocchiif smp.rdp_rank() == 0
. In caso contrario, il processo di addestramento potrebbe bloccarsi.
Per ulteriori informazioni sul checkpoint di un modello con parallelismo tensoriale, consulta Checkpoint di un modello distribuito.
-