

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用 PennyLane 內嵌模擬器執行混合工作負載
<a name="pennylane-embedded-simulators"></a>

讓我們來看看如何在 Amazon Braket 混合任務上使用來自 PennyLane 的內嵌模擬器來執行混合工作負載。Pennylane 的 GPU 型內嵌模擬器 `lightning.gpu`使用 [Nvidia cuQuantum 程式庫](https://developer.nvidia.com/cuquantum-sdk)來加速電路模擬。嵌入式 GPU 模擬器已預先在所有 Braket [任務容器中](https://github.com/amazon-braket/amazon-braket-containers)設定，使用者可以立即使用。在此頁面上，我們會示範如何使用 `lightning.gpu` 來加速混合式工作負載。

## 將 `lightning.gpu`用於 QAOA 工作負載
<a name="lightning-gpu-qaoa"></a>

考慮此[筆記本](https://github.com/amazon-braket/amazon-braket-examples/tree/main/examples/hybrid_jobs/2_Using_PennyLane_with_Braket_Hybrid_Jobs)中的 Quantum 近似最佳化演算法 (QAOA) 範例。若要選取內嵌模擬器，請將引`device`數指定為格式的字串：`"local:<provider>/<simulator_name>"`。例如，您會`"local:pennylane/lightning.gpu"`為 設定 `lightning.gpu`。您在啟動時提供給混合任務的裝置字串會以環境變數 的形式傳遞給任務`"AMZN_BRAKET_DEVICE_ARN"`。

```
device_string = os.environ["AMZN_BRAKET_DEVICE_ARN"]
prefix, device_name = device_string.split("/")
device = qml.device(simulator_name, wires=n_wires)
```

在此頁面上，比較兩個內嵌的 PennyLane 狀態向量模擬器 `lightning.qubit`（以 CPU 為基礎） 和 `lightning.gpu`（以 GPU 為基礎）。為模擬器提供自訂閘道分解，以計算各種漸層。

現在您已準備好準備混合任務啟動指令碼。使用兩種執行個體類型執行 QAOA 演算法： `ml.m5.2xlarge`和 `ml.g4dn.xlarge`。`ml.m5.2xlarge` 執行個體類型與標準開發人員筆記型電腦相當。`ml.g4dn.xlarge` 是具有單一 NVIDIA T4 GPU 和 16GB 記憶體的加速運算執行個體。

若要執行 GPU，我們首先需要指定相容的映像和正確的執行個體 （預設為`ml.m5.2xlarge`執行個體）。

```
from braket.aws import AwsSession
from braket.jobs.image_uris import Framework, retrieve_image

image_uri = retrieve_image(Framework.PL_PYTORCH, AwsSession().region)
instance_config = InstanceConfig(instanceType="ml.g4dn.xlarge")
```

然後，我們需要將這些輸入混合任務裝飾項目，以及系統和混合任務引數中的更新裝置參數。

```
@hybrid_job(
        device="local:pennylane/lightning.gpu",
        input_data=input_file_path,
        image_uri=image_uri,
        instance_config=instance_config)
def run_qaoa_hybrid_job_gpu(p=1, steps=10):
    params = np.random.rand(2, p)

    braket_task_tracker = Tracker()

    graph = nx.read_adjlist(input_file_path, nodetype=int)
    wires = list(graph.nodes)
    cost_h, _mixer_h = qaoa.maxcut(graph)

    device_string = os.environ["AMZN_BRAKET_DEVICE_ARN"]
    prefix, device_name = device_string.split("/")
    dev= qml.device(simulator_name, wires=len(wires))
    ...
```

**注意**  
如果您將 指定`instance_config`為使用 GPU 型執行個體，但選擇 `device`做為內嵌的 CPU 型模擬器 (`lightning.qubit`)，則不會使用 GPU。如果您想要以 GPU 為目標，請務必使用嵌入式 GPU 型模擬器！

`m5.2xlarge` 執行個體的平均反覆運算時間約為 73 秒，而`ml.g4dn.xlarge`執行個體的反覆運算時間約為 0.6 秒。對於此 21 qubit 工作流程，GPU 執行個體可提供 100 倍的速度。如果您查看 Amazon Braket Hybrid Jobs [定價頁面](https://aws.amazon.com/braket/pricing/)，您可以看到`m5.2xlarge`執行個體的每分鐘成本為 0.00768 USD，而`ml.g4dn.xlarge`執行個體的每分鐘成本為 0.01227 USD。在此執行個體中，在 GPU 執行個體上執行會更快且更便宜。

## Quantum 機器學習和資料平行處理
<a name="quantumML-data-parallelism"></a>

如果您的工作負載類型是在資料集上訓練的量子機器學習 (QML)，您可以使用資料平行處理進一步加速工作負載。在 QML 中，模型包含一或多個量子電路。模型可能也可能不包含傳統神經網路。使用資料集訓練模型時，模型中的參數會更新，以將損失函數降至最低。損失函數通常針對單一資料點定義，以及整個資料集平均損失的總損失。在 QML 中，損失通常會先以序列方式計算，再平均至梯度運算的總損失。此程序耗時，特別是有數百個資料點時。

由於某個資料點的損失不取決於其他資料點，因此可以平行評估損失！您可以同時評估與不同資料點相關聯的損失和梯度。這稱為資料平行處理。使用 SageMaker 的分散式資料平行程式庫，Amazon Braket Hybrid Jobs 可讓您更輕鬆地使用資料平行處理來加速訓練。

請考慮下列資料平行處理的 QML 工作負載，其使用來自已知 UCI 儲存庫的 [Sonar 資料集](https://archive.ics.uci.edu/dataset/151/connectionist+bench+sonar+mines+vs+rocks)作為二進位分類的範例。聲納資料集具有 208 個資料點，每個資料點具有 60 個功能，這些功能是從聲納訊號從材料中彈射而來。每個資料點都標記為「M」表示礦區，或「R」表示石頭。我們的 QML 模型包含輸入層、做為隱藏層的量子電路，以及輸出層。輸入和輸出層是在 PyTorch 中實作的傳統神經網路。量子電路使用 PennyLane 的 qml.qnn 模組與 PyTorch 神經網路整合。如需工作負載的詳細資訊，請參閱我們的[範例筆記本](https://github.com/aws/amazon-braket-examples)。如同上述的 QAOA 範例，您可以使用 PennyLane 等嵌入式 GPU 型模擬器來利用 GPU 的強大功能`lightning.gpu`，以改善與嵌入式 CPU 型模擬器相比的效能。

若要建立混合任務，您可以透過其關鍵字引數呼叫`AwsQuantumJob.create`並指定演算法指令碼、裝置和其他組態。

```
instance_config = InstanceConfig(instanceType='ml.g4dn.xlarge')

hyperparameters={"nwires": "10",
                 "ndata": "32",
                 ...
}

job = AwsQuantumJob.create(
    device="local:pennylane/lightning.gpu",
    source_module="qml_source",
    entry_point="qml_source.train_single",
    hyperparameters=hyperparameters,
    instance_config=instance_config,
    ...
)
```

若要使用資料平行處理，您需要修改 SageMaker 分散式程式庫演算法指令碼中的幾行程式碼，以正確平行化訓練。首先，您會匯入 套件，該`smdistributed`套件會處理將工作負載分散到多個 GPUs 和多個執行個體的大部分繁重工作。此套件已在 Braket PyTorch 和 TensorFlow 容器中預先設定。`dist` 模組會告知我們的演算法指令碼，訓練的 GPUs 總數 (`world_size`) 以及 GPU 核心`local_rank`的 `rank`和 。 `rank` 是 GPU 在所有執行個體的絕對索引，而 `local_rank`是執行個體內 GPU 的索引。例如，如果有四個執行個體，每個執行個體都有八個 GPUs 配置給訓練，`rank`範圍從 0 到 31，`local_rank`範圍從 0 到 7。

```
import smdistributed.dataparallel.torch.distributed as dist

dp_info = {
    "world_size": dist.get_world_size(),
    "rank": dist.get_rank(),
    "local_rank": dist.get_local_rank(),
}
batch_size //= dp_info["world_size"] // 8
batch_size = max(batch_size, 1)
```

接著，您可以`DistributedSampler`根據 定義 `world_size``rank`，然後將它傳遞至資料載入器。此取樣器會避免 GPUs 存取資料集的相同配量。

```
train_sampler = torch.utils.data.distributed.DistributedSampler(
    train_dataset,
    num_replicas=dp_info["world_size"],
    rank=dp_info["rank"]
)
train_loader = torch.utils.data.DataLoader(
    train_dataset,
    batch_size=batch_size,
    shuffle=False,
    num_workers=0,
    pin_memory=True,
    sampler=train_sampler,
)
```

接著，您可以使用 `DistributedDataParallel`類別來啟用資料平行處理。

```
from smdistributed.dataparallel.torch.parallel.distributed import DistributedDataParallel as DDP

model = DressedQNN(qc_dev).to(device)
model = DDP(model)
torch.cuda.set_device(dp_info["local_rank"])
model.cuda(dp_info["local_rank"])
```

以上是使用資料平行處理所需的變更。在 QML 中，您通常想要儲存結果並列印訓練進度。如果每個 GPU 執行儲存和列印命令，日誌會充滿重複的資訊，結果會互相覆寫。若要避免這種情況，您只能從具有 0 `rank` 的 GPU 儲存和列印。

```
if dp_info["rank"]==0:
    print('elapsed time: ', elapsed)
    torch.save(model.state_dict(), f"{output_dir}/test_local.pt")
    save_job_result({"last loss": loss_before})
```

 Amazon Braket Hybrid Jobs 支援 SageMaker 分散式資料平行程式庫的`ml.g4dn.12xlarge`執行個體類型。您可以透過混合任務中的 `InstanceConfig` 引數來設定執行個體類型。若要讓 SageMaker 分散式資料平行程式庫知道已啟用資料平行處理，您需要新增兩個額外的超參數，將 `"sagemaker_distributed_dataparallel_enabled"`設定為 `"true"`，並將 `"sagemaker_instance_type"`設定為您正在使用的執行個體類型。`smdistributed` 套件會使用這兩個超參數。您的演算法指令碼不需要明確使用它們。在 Amazon Braket SDK 中，它提供方便的關鍵字引數 `distribution`。在建立混合任務`distribution="data_parallel"`時，Amazon Braket SDK 會自動為您插入兩個超參數。如果您使用 Amazon Braket API，則需要包含這兩個超參數。

設定執行個體和資料平行處理後，您現在可以提交混合任務。`ml.g4dn.12xlarge` 執行個體中有 4 個 GPUs。當您設定 `instanceCount=1` 時，工作負載會分散到執行個體中的 8 個 GPUs。當您設定`instanceCount`大於一個時，工作負載會分散到所有執行個體中可用的 GPUs。使用多個執行個體時，每個執行個體會根據您使用的時間而產生費用。例如，當您使用四個執行個體時，計費時間為每個執行個體執行時間的四倍，因為有四個執行個體同時執行您的工作負載。

```
instance_config = InstanceConfig(instanceType='ml.g4dn.12xlarge',
                                 instanceCount=1,
)

hyperparameters={"nwires": "10",
                 "ndata": "32",
                 ...,
}

job = AwsQuantumJob.create(
    device="local:pennylane/lightning.gpu",
    source_module="qml_source",
    entry_point="qml_source.train_dp",
    hyperparameters=hyperparameters,
    instance_config=instance_config,
    distribution="data_parallel",
    ...
)
```

**注意**  
在上述混合任務建立中， `train_dp.py`是使用資料平行處理的修改演算法指令碼。請注意，只有在您根據上述章節修改演算法指令碼時，資料平行處理才能正常運作。如果在未正確修改演算法指令碼的情況下啟用資料平行處理選項，混合任務可能會擲回錯誤，或者每個 GPU 可能會重複處理相同的資料配量，這很低效率。

如果正確使用，使用多個執行個體可能會導致時間和成本大幅減少。如需[詳細資訊，請參閱範例筆記本](https://github.com/amazon-braket/amazon-braket-examples/blob/main/examples/hybrid_jobs/5_Parallelize_training_for_QML/Parallelize_training_for_QML.ipynb)。