Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.
Ein PyTorch Trainingsskript ändern
In diesem Abschnitt erfahren Sie, wie Sie PyTorch Trainingsskripte ändern, um die SageMaker Modellparallelitätsbibliothek für automatische Partitionierung und manuelle Partitionierung zu konfigurieren.
Anmerkung
Informationen darüber, welche PyTorch Versionen von der Bibliothek unterstützt werden, finden Sie unter. Unterstützte Frameworks und AWS-Regionen
Tipp
end-to-endNotebook-Beispiele, die veranschaulichen, wie ein PyTorch Trainingsskript mit der SageMaker Modellparallelitätsbibliothek verwendet wird, finden Sie unter. Beispiele für die Amazon SageMaker AI-Modellparallelismusbibliothek v1
Beachten Sie, dass die automatische Partitionierung standardmäßig aktiviert ist. Sofern nicht anders angegeben, verwenden die folgenden Skripten automatische Partitionierung.
Themen
Automatisiertes Teilen mit PyTorch
Die folgenden Änderungen am Trainingsskript sind erforderlich, um ein PyTorch Trainingsskript mit SageMaker der Modellparallelismus-Bibliothek auszuführen:
-
Importieren und initialisieren Sie die Bibliothek mit
smdistributed.modelparallel.torch.init(). -
Schließen Sie das Modell mit
smdistributed.modelparallel.torch.DistributedModelum. Beachten Sie, dass alle Tensoren, die von der forwardMethode des zugrunde liegendennn.ModuleObjekts zurückgegeben werden, über modellparallele Geräte übertragen werden, was zu Kommunikationsaufwand führt. Daher sollten alle Tensoren, die außerhalb der Aufrufmethode nicht benötigt werden (z. B. Zwischenaktivierungen), nicht zurückgegeben werden.Anmerkung
Für das FP16 Training müssen Sie den Kontextmanager smdistributed.modelparallel.torch.model_creation
() verwenden, um das Modell zu umschließen. Weitere Informationen finden Sie unter FP16 Training mit Modellparallelität. -
Umschließen Sie den Optimierer mit
smdistributed.modelparallel.torch.DistributedOptimizer. Anmerkung
Für das Training müssen Sie eine statische oder dynamische Verlustskalierung einrichten FP16 . Weitere Informationen finden Sie unter FP16 Training mit Modellparallelität.
-
Verwenden Sie das zurückgegebene
DistributedModelObjekt anstelle eines Benutzermodells. -
Fügen Sie die Vorwärts- und Rückwärtslogik in eine Schrittfunktion ein und dekorieren Sie sie mit
smdistributed.modelparallel.torch.step. -
Beschränken Sie jeden Prozess auf sein eigenes Gerät durch
torch.cuda.set_device(smp.local_rank()). -
Verschieben Sie die Eingangstensoren mithilfe der
.to()API vor demsmp.stepAufruf auf die GPU (siehe Beispiel unten). -
Ersetzen Sie
torch.Tensor.backwardundtorch.autograd.backwardmitDistributedModel.backward. -
Führen Sie die Nachbearbeitung der Ausgaben für alle Mikrobatches mithilfe von
StepOutputMethoden wie reduce_meandurch. -
Wenn es einen Bewertungsschritt gibt, platzieren Sie die Vorwärtslogik auf ähnliche Weise in einer mit -
smp.stepdekorierten Funktionen und bearbeiten Sie die Ausgaben mithilfe derStepOutputAPInach. -
drop_last=TrueinDataLoadereinstellen. Alternativ können Sie einen Stapel in der Trainingsschleife manuell überspringen, wenn die Batchgröße nicht durch die Anzahl der Mikrobatches teilbar ist.
Weitere Informationen zur API SageMaker der Modellparallelismus-Bibliothek finden Sie in der API-Dokumentation
import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torchnet.dataset import SplitDataset from torchvision import datasets import smdistributed.modelparallel.torch as smp class GroupedNet(nn.Module): def __init__(self): super(GroupedNet, self).__init__() # define layers def forward(self, x): # define forward pass and return model outputs # smdistributed: Define smp.step. Return any tensors needed outside. @smp.step def train_step(model, data, target): output = model(data) loss = F.nll_loss(output, target, reduction="mean") model.backward(loss) return output, loss def train(model, device, train_loader, optimizer): model.train() for batch_idx, (data, target) in enumerate(train_loader): # smdistributed: Move input tensors to the GPU ID used by the current process, # based on the set_device call. data, target = data.to(device), target.to(device) optimizer.zero_grad() # Return value, loss_mb is a StepOutput object _, loss_mb = train_step(model, data, target) # smdistributed: Average the loss across microbatches. loss = loss_mb.reduce_mean() optimizer.step() # smdistributed: initialize the backend smp.init() # smdistributed: Set the device to the GPU ID used by the current process. # Input tensors should be transferred to this device. torch.cuda.set_device(smp.local_rank()) device = torch.device("cuda") # smdistributed: Download only on a single process per instance. # When this is not present, the file is corrupted by multiple processes trying # to download and extract at the same time dataset = datasets.MNIST("../data", train=True, download=False) # smdistributed: Shard the dataset based on data-parallel ranks if smp.dp_size() > 1: partitions_dict = {f"{i}": 1 / smp.dp_size() for i in range(smp.dp_size())} dataset = SplitDataset(dataset, partitions=partitions_dict) dataset.select(f"{smp.dp_rank()}") # smdistributed: Set drop_last=True to ensure that batch size is always divisible # by the number of microbatches train_loader = torch.utils.data.DataLoader(dataset, batch_size=64, drop_last=True) model = GroupedNet() optimizer = optim.Adadelta(model.parameters(), lr=4.0) # smdistributed: Use the DistributedModel container to provide the model # to be partitioned across different ranks. For the rest of the script, # the returned DistributedModel object should be used in place of # the model provided for DistributedModel class instantiation. model = smp.DistributedModel(model) optimizer = smp.DistributedOptimizer(optimizer) train(model, device, train_loader, optimizer)
Manuelles Teilen mit PyTorch
Verwenden Sie smp.partitionsmp.partition Kontext befindet, wird in den default_partition platziert. Das default_partition muss angegeben werden, wenn auto_partition auf False gesetzt ist. Die Module, die in einem bestimmten smp.partition Kontext erstellt werden, werden auf der entsprechenden Partition platziert.
import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torchnet.dataset import SplitDataset from torchvision import datasets import smdistributed.modelparallel.torch as smp class GroupedNet(nn.Module): def __init__(self): super(GroupedNet, self).__init__() with smp.partition(0): # define child modules on device 0 with smp.partition(1): # define child modules on device 1 def forward(self, x): # define forward pass and return model outputs # smdistributed: Define smp.step. Return any tensors needed outside. @smp.step def train_step(model, data, target): output = model(data) loss = F.nll_loss(output, target, reduction="mean") model.backward(loss) return output, loss def train(model, device, train_loader, optimizer): model.train() for batch_idx, (data, target) in enumerate(train_loader): # smdistributed: Move input tensors to the GPU ID used by the current process, # based on the set_device call. data, target = data.to(device), target.to(device) optimizer.zero_grad() # Return value, loss_mb is a StepOutput object _, loss_mb = train_step(model, data, target) # smdistributed: Average the loss across microbatches. loss = loss_mb.reduce_mean() optimizer.step() # smdistributed: initialize the backend smp.init() # smdistributed: Set the device to the GPU ID used by the current process. # Input tensors should be transferred to this device. torch.cuda.set_device(smp.local_rank()) device = torch.device("cuda") # smdistributed: Download only on a single process per instance. # When this is not present, the file is corrupted by multiple processes trying # to download and extract at the same time dataset = datasets.MNIST("../data", train=True, download=False) # smdistributed: Shard the dataset based on data-parallel ranks if smp.dp_size() > 1: partitions_dict = {f"{i}": 1 / smp.dp_size() for i in range(smp.dp_size())} dataset = SplitDataset(dataset, partitions=partitions_dict) dataset.select(f"{smp.dp_rank()}") # smdistributed: Set drop_last=True to ensure that batch size is always divisible # by the number of microbatches train_loader = torch.utils.data.DataLoader(dataset, batch_size=64, drop_last=True) model = GroupedNet() optimizer = optim.Adadelta(model.parameters(), lr=4.0) # smdistributed: Use the DistributedModel container to provide the model # to be partitioned across different ranks. For the rest of the script, # the returned DistributedModel object should be used in place of # the model provided for DistributedModel class instantiation. model = smp.DistributedModel(model) optimizer = smp.DistributedOptimizer(optimizer) train(model, device, train_loader, optimizer)
Überlegungen
Wenn Sie ein PyTorch Trainingsskript mithilfe SageMaker der Modellparallelismus-Bibliothek konfigurieren, sollten Sie Folgendes beachten:
-
Wenn Sie eine Optimierungstechnik verwenden, die auf globalen Gradientennormen basiert, z. B. der Gradientennorm aus dem gesamten Modell, wie z. B. einige Varianten des LAMB-Optimizers oder des globalen Gradientenclippings, müssen Sie alle Normen aus den Modellpartitionen zusammenstellen, um ihre Richtigkeit zu überprüfen. Zu diesem Zweck können Sie die grundlegenden Kommunikationsdatentypen der Bibliothek verwenden.
-
Alle
torch.TensorArgumente für die Vorwärtsmethoden vonnn.Modulesin Ihrem Modell müssen bei der Berechnung der Modulausgabe verwendet werden. Mit anderen Worten, die Bibliothek unterstützt nicht den Fall, dass es eintorch.TensorArgument für ein Modul gibt, von dem die Modulausgabe nicht abhängt. -
Das Argument für den
smp.DistributedModel.backward()Aufruf muss von allen Modellausgaben abhängen. Mit anderen Worten, es darf keine Ausgabe dessmp.DistributedModel.forwardAufrufs geben, die nicht bei der Berechnung des Tensors verwendet wird, der in densmp.DistributedModel.backwardAufruf eingespeist wird. -
Wenn Ihr Code
torch.cuda.synchronize()Aufrufe enthält, müssen Sie möglicherweisetorch.cuda.set_device(smp.local_rank())unmittelbar vor dem Synchronisierungsaufruf anrufen. Andernfalls könnten in Gerät 0 unnötige CUDA-Kontexte erstellt werden, die unnötig Speicherplatz verbrauchen. -
Da sich die Bibliothek
nn.Modulesauf unterschiedlichen Geräten befindet, dürfen die Module im Modell nicht von einem globalen Status abhängen, der im Inneren vonsmp.stepgeändert wird. Jeder Status, der während des gesamten Trainings unverändert bleibt oder außerhald vonsmp.stepso verändert wird, dass er für alle Prozesse sichtbar ist, ist zulässig. -
Sie müssen das Modell nicht auf die GPU verschieben (z. B. verwenden von
model.to(device)), wenn Sie die Bibliothek verwenden. Wenn Sie versuchen, das Modell auf die GPU zu verschieben, bevor das Modell partitioniert wurde (vor dem erstensmp.stepAufruf), wird der Move-Aufruf ignoriert. Die Bibliothek verschiebt den Teil des Modells, der einem Rang zugewiesen wurde, automatisch auf ihre GPU. Sobald das Training mit der Bibliothek begonnen hat, sollten Sie das Modell nicht auf die CPU verschieben und es verwenden, da es sonst keine korrekten Parameter für Module enthält, die nicht der vom Prozess gespeicherten Partition zugewiesen sind. Wenn Sie ein Modell neu trainieren oder es ohne die Bibliothek für Inferenz verwenden möchten, nachdem es mit der Modellparallelismus-Bibliothek trainiert wurde, empfiehlt es sich, das vollständige Modell mithilfe unserer Checkpoint-API zu speichern und es wieder in ein reguläres Modul zu laden. PyTorch -
Wenn Sie eine Liste von Modulen haben, bei denen die Ausgabe eines Moduls in ein anderes einfließen kann, kann das Ersetzen dieser Liste durch die Leistung erheblich verbessern.
nn.Sequential -
Das Gewichtsupdate (
optimizer.step()) muss außerhalb vonsmp.steperfolgen, da dann der gesamte Rückwärtsdurchlauf abgeschlossen ist und die Farbverläufe bereit sind. Wenn Sie ein Hybridmodell mit Modell- und Datenparallelität verwenden, ist zu diesem Zeitpunkt auch garantiert, dass die Gradienten beendet AllReduce sind. -
Wenn Sie die Bibliothek in Kombination mit Datenparallelität verwenden, stellen Sie sicher, dass die Anzahl der Batches auf allen datenparallelen Rängen gleich ist, damit Sie AllReduce nicht auf einen Rang warten, der nicht am Schritt teilnimmt.
-
Wenn Sie einen Trainingsjob mit einem ml.p4d-Instance-Typ (z. B. ml.p4d.24xlarge) starten, müssen Sie die Dataloader-Variable
num_workers=0festlegen. Sie könnenDataLoaderIhren beispielsweise wie folgt definieren:dataloader = torch.utils.data.DataLoader( data, batch_size=batch_size, num_workers=0, pin_memory=True, drop_last=True, shuffle=shuffle, ) -
Die Eingaben für
smp.stepmüssen die Modelleingaben sein, die vonDataLoadergeneriert wurden. Der Grund dafür ist, dasssmp.stepdie Eingabetensoren intern entlang der Stapeldimension aufteilt und sie in eine Pipeline einfügt. Dies bedeutet, dass es nicht funktioniert,DataLoadersich selbst an diesmp.stepFunktion zur Generierung der darin enthaltenen Modelleingaben zu übergeben.Wenn Sie beispielsweise a
DataLoaderwie folgt definieren:train_loader = torch.utils.data.DataLoader(dataset, batch_size=64, drop_last=True)Sie sollten auf die Modelleingaben zugreifen, die von generiert wurden,
train_loaderund diese an einesmp.stepdekorierte Funktion übergeben. Übergeben Sietrain_loadernicht direkt an diesmp.stepFunktion.def train(model, device, train_loader, optimizer): model.train() for batch_idx, (data, target) in enumerate(train_loader): ... _, loss_mb = train_step(model, data, target) ... @smp.step def train_step(model, data, target): ... return output, loss -
Die Eingangstensoren für
smp.stepmüssen mithilfe der.to()API auf das aktuelle Gerät verschoben werden, was nach demtorch.cuda.set_device(local_rank())Aufruf erfolgen muss.Sie können z. B. wie folgt die Funktion
traindefinieren. Diese Funktion fügt dem aktuellen Gerät mithilfe der.to()APIdataundtargethinzu, bevor diese Eingangstensoren zum Aufrufen vontrain_stepverwendet werden.def train(model, device, train_loader, optimizer): model.train() for batch_idx, (data, target) in enumerate(train_loader): # smdistributed: Move input tensors to the GPU ID used by the current process, # based on the set_device call. data, target = data.to(device), target.to(device) optimizer.zero_grad() # Return value, loss_mb is a StepOutput object _, loss_mb = train_step(model, data, target) # smdistributed: Average the loss across microbatches. loss = loss_mb.reduce_mean() optimizer.step()Die Eingangstensoren für diese
smp.setdekorierte Funktion wurden in der obigentrainFunktion auf das aktuelle Gerät verschoben. Das Modell muss nicht auf das aktuelle Gerät verschoben werden. Die Bibliothek verschiebt den Teil des Modells, der einem Rang zugewiesen ist, automatisch auf ihre GPU.@smp.step def train_step(model, data, target): output = model(data) loss = F.nll_loss(output, target, reduction="mean") model.backward(loss) return output, loss
Nicht unterstützte Framework-Funktionen
Die folgenden PyTorch Funktionen werden von der Modellparallelitätsbibliothek nicht unterstützt SageMaker:
-
Wenn Sie Datenparallelität mit dem nativen PyTorch DDP
verwenden, wird das torch.nn.parallel.DistributedDataParallelWrapper-Modul von der Bibliothek nicht unterstützt. Die Bibliothek verwaltet intern die Integration mit PyTorch DDP, einschließlich Parameterübertragung und Gradient. AllReduce Bei Verwendung der Bibliothek werden Modulpuffer zu Beginn des Trainings nur einmal übertragen. Wenn Ihr Modell über Modulpuffer verfügt, die bei jedem Schritt über datenparallele Gruppen hinweg synchronisiert werden müssen, können Sie dies über die torch.distributedAPI tun, indem Sie die Prozessgruppe verwenden, die übersmp.get_dp_process_group()abgerufen werden kann. -
Für gemischtes Präzisionstraining wird das
apex.ampModul nicht unterstützt. Es wird empfohlen, die Bibliothek mit automatischer Mixed-Precisiontorch.cuda.ampzu verwenden, mit der Ausnahme, dasssmp.amp.GradScaleranstelle der Implementierung in Torch verwendet wird. -
torch.jit.ScriptModulesundScriptFunctionswerden vonsmp.DistributedModelnicht unterstützt. -
apex:FusedLayerNorm,FusedAdamFusedLAMB, undFusedNovoGradvonapexwerden nicht unterstützt. Sie können stattdessen deren Bibliotheksimplementierungen übersmp.optimizersundsmp.nnAPIs verwenden.