

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# 텐서 병렬 처리를 사용하여 SageMaker 분산 모델 병렬 훈련 작업 실행
<a name="model-parallel-extended-features-pytorch-tensor-parallelism-examples"></a>

이 섹션에서는 다음을 배웁니다.
+ 텐서 병렬 처리를 사용하도록 SageMaker PyTorch 예측기 및 SageMaker 모델 병렬 처리 옵션을 구성하는 방법
+ 텐서 병렬 처리에 확장 `smdistributed.modelparallel` 모듈을 사용하여 훈련 스크립트를 조정하는 방법

`smdistributed.modelparallel` 모듈에 대해 자세히 알아보려면 *SageMaker Python SDK 문서*에서 [SageMaker 모델 병렬 API](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smd_model_parallel.html)를 참조하세요.

**Topics**
+ [텐서 병렬 처리 단독](#model-parallel-extended-features-pytorch-tensor-parallelism-alone)
+ [텐서 병렬 처리와 파이프라인 병렬 처리의 결합](#model-parallel-extended-features-pytorch-tensor-and-pipeline-parallelism)

## 텐서 병렬 처리 단독
<a name="model-parallel-extended-features-pytorch-tensor-parallelism-alone"></a>

다음은 파이프라인 병렬 처리 없이 텐서 병렬 처리만 활성화하는 분산 훈련 옵션의 예입니다. `mpi_options` 및 `smp_options` 사전을 구성하여 SageMaker `PyTorch` 예측기에 분산 훈련 옵션을 지정합니다.

**참고**  
확장된 메모리 절약 기능은 SageMaker 모델 병렬 처리 라이브러리 v1.6.0 이상을 구현하는 PyTorch용 딥 러닝 컨테이너를 통해 사용할 수 있습니다.

**SageMaker PyTorch 예측기 구성**

```
mpi_options = {
    "enabled" : True,
    "processes_per_host" : 8,               # 8 processes
    "custom_mpi_options" : "--mca btl_vader_single_copy_mechanism none "
}
               
smp_options = {
    "enabled":True,
    "parameters": {
        "pipeline_parallel_degree": 1,    # alias for "partitions"
        "placement_strategy": "cluster",
        "tensor_parallel_degree": 4,      # tp over 4 devices
        "ddp": True
    }
}
              
smp_estimator = PyTorch(
    entry_point='your_training_script.py', # Specify
    role=role,
    instance_type='ml.p3.16xlarge',
    sagemaker_session=sagemaker_session,
    framework_version='1.13.1',
    py_version='py36',
    instance_count=1,
    distribution={
        "smdistributed": {"modelparallel": smp_options},
        "mpi": mpi_options
    },
    base_job_name="SMD-MP-demo",
)

smp_estimator.fit('s3://my_bucket/my_training_data/')
```

**작은 정보**  
`distribution` 파라미터의 전체 목록은 SageMaker Python SDK 문서의 [모델 병렬 처리를 위한 구성 파라미터](https://sagemaker.readthedocs.io/en/v2.199.0/api/training/smd_model_parallel_general.html)를 참조하세요.

**PyTorch 훈련 스크립트 조정**

다음 예제 훈련 스크립트는 SageMaker 모델 병렬 처리 라이브러리를 훈련 스크립트에 적용하는 방법을 보여줍니다. 이 예제에서는 스크립트 이름이 `your_training_script.py`라고 가정합니다.

```
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 Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.fc1 = nn.Linear(9216, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = F.max_pool2d(x, 2)
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        return F.log_softmax(x, 1)

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()
        output = model(data)
        loss = F.nll_loss(output, target, reduction="mean")
        loss.backward()
        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
if smp.local_rank() == 0:
    dataset = datasets.MNIST("../data", train=True, download=False)
smp.barrier()

# 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()}")

train_loader = torch.utils.data.DataLoader(dataset, batch_size=64)

# smdistributed: Enable tensor parallelism for all supported modules in the model
# i.e., nn.Linear in this case. Alternatively, we can use
# smp.set_tensor_parallelism(model.fc1, True)
# to enable it only for model.fc1
with smp.tensor_parallelism():
    model = Net()

# smdistributed: Use the DistributedModel wrapper to distribute the
# modules for which tensor parallelism is enabled
model = smp.DistributedModel(model)

optimizer = optim.AdaDelta(model.parameters(), lr=4.0)
optimizer = smp.DistributedOptimizer(optimizer)

train(model, device, train_loader, optimizer)
```

## 텐서 병렬 처리와 파이프라인 병렬 처리의 결합
<a name="model-parallel-extended-features-pytorch-tensor-and-pipeline-parallelism"></a>

다음은 텐서 병렬 처리와 파이프라인 병렬 처리를 결합할 수 있는 분산 훈련 옵션의 예입니다. SageMaker `PyTorch` 예측기를 구성할 때 `mpi_options` 및 `smp_options` 파라미터를 설정하여 텐서 병렬 처리로 모델 병렬 옵션을 지정합니다.

**참고**  
확장된 메모리 절약 기능은 SageMaker 모델 병렬 처리 라이브러리 v1.6.0 이상을 구현하는 PyTorch용 딥 러닝 컨테이너를 통해 사용할 수 있습니다.

**SageMaker PyTorch 예측기 구성**

```
mpi_options = {
    "enabled" : True,
    "processes_per_host" : 8,               # 8 processes
    "custom_mpi_options" : "--mca btl_vader_single_copy_mechanism none "
}
               
smp_options = {
    "enabled":True,
    "parameters": {
    "microbatches": 4,
        "pipeline_parallel_degree": 2,    # alias for "partitions"
        "placement_strategy": "cluster",
        "tensor_parallel_degree": 2,      # tp over 2 devices
        "ddp": True
    }
}
              
smp_estimator = PyTorch(
    entry_point='your_training_script.py', # Specify
    role=role,
    instance_type='ml.p3.16xlarge',
    sagemaker_session=sagemaker_session,
    framework_version='1.13.1',
    py_version='py36',
    instance_count=1,
    distribution={
        "smdistributed": {"modelparallel": smp_options},
        "mpi": mpi_options
    },
    base_job_name="SMD-MP-demo",
)

smp_estimator.fit('s3://my_bucket/my_training_data/')  
```

<a name="model-parallel-extended-features-pytorch-tensor-and-pipeline-parallelism-script"></a>**PyTorch 훈련 스크립트 조정**

다음 예제 훈련 스크립트는 SageMaker 모델 병렬 처리 라이브러리를 훈련 스크립트에 적용하는 방법을 보여줍니다. 현재 훈련 스크립트에는 `smp.step` 데코레이터가 포함되어 있습니다.

```
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 Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.fc1 = nn.Linear(9216, 128)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = F.max_pool2d(x, 2)
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        return F.log_softmax(x, 1)


# 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
if smp.local_rank() == 0:
    dataset = datasets.MNIST("../data", train=True, download=False)
smp.barrier()

# 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 = Net()

# smdistributed: enable tensor parallelism only for model.fc1
smp.set_tensor_parallelism(model.fc1, True)

# 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 = optim.AdaDelta(model.parameters(), lr=4.0)
optimizer = smp.DistributedOptimizer(optimizer)

train(model, device, train_loader, optimizer)
```