

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

# 로컬 코드를 하이브리드 작업으로 실행
<a name="braket-hybrid-job-decorator"></a>

Amazon Braket Hybrid Jobs는 Amazon EC2 컴퓨팅 리소스와 Amazon Braket Quantum Processing Unit(QPU) 액세스를 결합하여 하이브리드 양자-고전 알고리즘의 완전관리형 오케스트레이션을 제공합니다. 하이브리드 작업에서 생성된 양자 작업은 개별 양자 작업보다 대기열에서 우선순위를 가지므로, 양자 작업 대기열의 변동으로 인해 알고리즘이 중단되지 않습니다. 각 QPU는 별도의 하이브리드 작업 대기열을 유지하여 한 번에 하나의 하이브리드 작업만 실행되도록 보장합니다.

**Topics**
+ [로컬 Python 코드에서 하이브리드 작업 생성](#create-hybrid-job-from-local-python-code)
+ [추가 Python 패키지 및 소스 코드 설치](#install-python-packages-and-code)
+ [하이브리드 작업 인스턴스에 데이터 저장 및 로드](#save-load-data-into-instance)
+ [하이브리드 작업 데코레이터 모범 사례](#best-practices)

## 로컬 Python 코드에서 하이브리드 작업 생성
<a name="create-hybrid-job-from-local-python-code"></a>

로컬 Python 코드를 Amazon Braket Hybrid Jobs로 실행할 수 있습니다. 다음 코드 예제와 같이 `@hybrid_job` 데코레이터로 코드에 주석을 달아 이 작업을 수행할 수 있습니다. 사용자 지정 환경의 경우 Amazon Elastic Container Registry(ECR)의 [사용자 지정 컨테이너를 사용](braket-jobs-byoc.md)하도록 선택할 수 있습니다.

**참고**  
Python 3.12만 기본적으로 지원됩니다.

 `@hybrid_job` 데코레이터를 사용하여 함수에 주석을 달 수 있습니다. Braket은 데코레이터 내부의 코드를 Braket 하이브리드 작업 [알고리즘 스크립트](braket-jobs-first.md)로 변환합니다. 그런 다음 하이브리드 작업은 Amazon EC2 인스턴스의 데코레이터 내에서 함수를 간적접으로 호출합니다. `job.state()` 또는 Braket 콘솔을 통해 작업 진행 상황을 모니터링할 수 있습니다. 다음 코드 예제는 State Vector Simulator (SV1) device에서 다섯 개의 상태 시퀀스를 실행하는 방법을 보여줍니다.

```
from braket.aws import AwsDevice
from braket.circuits import Circuit, FreeParameter, Observable
from braket.devices import Devices
from braket.jobs.hybrid_job import hybrid_job
from braket.jobs.metrics import log_metric

device_arn = Devices.Amazon.SV1


@hybrid_job(device=device_arn)  # Choose priority device
def run_hybrid_job(num_tasks=1):
    device = AwsDevice(device_arn)  # Declare AwsDevice within the hybrid job

    # Create a parametric circuit
    circ = Circuit()
    circ.rx(0, FreeParameter("theta"))
    circ.cnot(0, 1)
    circ.expectation(observable=Observable.X(), target=0)

    theta = 0.0  # Initial parameter

    for i in range(num_tasks):
        task = device.run(circ, shots=100, inputs={"theta": theta})  # Input parameters
        exp_val = task.result().values[0]

        theta += exp_val  # Modify the parameter (possibly gradient descent)

        log_metric(metric_name="exp_val", value=exp_val, iteration_number=i)

    return {"final_theta": theta, "final_exp_val": exp_val}
```

일반적인 Python 함수와 마찬가지로 함수를 간접적으로 호출하여 하이브리드 작업을 생성합니다. 그러나 데코레이터 함수는 함수의 결과가 아닌 하이브리드 작업 핸들을 반환합니다. 완료 후 결과를 검색하려면 `job.result()`를 사용합니다.

```
job = run_hybrid_job(num_tasks=1)
result = job.result()
```

`@hybrid_job` 데코레이터의 디바이스 인수는 하이브리드 작업이 우선적으로 액세스할 수 있는 디바이스를 지정합니다. 이 경우 SV1 시뮬레이터입니다. QPU 우선순위를 얻으려면 함수 내에서 사용되는 디바이스 ARN이 데코레이터에 지정된 것과 일치하는지 확인해야 합니다. 편의를 위해 헬퍼 함수 `get_job_device_arn()`을 사용하여 `@hybrid_job`에 선언된 디바이스 ARN을 캡처할 수 있습니다.

**참고**  
각 하이브리드 작업은 Amazon EC2에서 컨테이너화된 환경을 생성하기 때문에 시작 시간이 최소 1분입니다. 따라서 단일 회로 또는 여러 회로의 배치와 같이 매우 짧은 워크로드의 경우 양자 작업을 사용하기에 충분할 수 있습니다.

**하이퍼파라미터** 

`run_hybrid_job()` 함수는 인수 `num_tasks`를 사용하여 생성된 양자 작업 수를 제어합니다. 하이브리드 작업은 이를 [하이퍼파라미터](braket-jobs-hyperparameters.md)로 자동으로 캡처합니다.

**참고**  
하이퍼파라미터는 Braket 콘솔에 문자열로 표시되며, 문자열은 2,500자로 제한됩니다.

**지표 및 로깅** 

`run_hybrid_job()` 함수 내에서 반복 알고리즘의 지표는 `log_metrics`를 사용하여 기록됩니다. 지표는 하이브리드 작업 탭 아래의 Braket 콘솔 페이지에 자동으로 플롯됩니다. [Braket 비용 추적기](braket-pricing.md)를 사용하면 하이브리드 작업 실행 중에 지표를 사용하여 거의 실시간으로 양자 작업 비용을 추적할 수 있습니다. 위 예제에서는 [결과 유형](braket-result-types.md)의 첫 번째 확률을 기록하는 지표 이름 “probability”를 사용합니다.

**결과 검색** 

하이브리드 작업이 완료되면 `job.result()`를 사용하여 하이브리드 작업 결과를 검색합니다. 반환 문의 모든 객체는 Braket에 의해 자동으로 캡처됩니다. 함수에서 반환하는 객체는 각 요소가 직렬화 가능한 튜플이어야 합니다. 예를 들어, 다음 코드는 작동하는 예제와 실패하는 예제를 보여줍니다.

```
import numpy as np


# Working example
@hybrid_job(device=Devices.Amazon.SV1)
def passing():
    np_array = np.random.rand(5)
    return np_array  # Serializable

# # Failing example
# @hybrid_job(device=Devices.Amazon.SV1)
# def failing():
#     return MyObject() # Not serializable
```

**작업 이름** 

기본적으로 이 하이브리드 작업의 이름은 함수 이름에서 추론됩니다. 최대 50자까지 사용자 지정 이름을 지정할 수도 있습니다. 예를 들어, 다음 코드에서 작업 이름은 "my-job-name"입니다.

```
@hybrid_job(device=Devices.Amazon.SV1, job_name="my-job-name")
def function():
    pass
```

**로컬 모드** 

[로컬 작업](braket-jobs-local-mode.md)은 데코레이터에 인수 `local=True`를 추가하여 생성됩니다. 그러면 랩톱과 같은 로컬 컴퓨팅 환경의 컨테이너화된 환경에서 하이브리드 작업이 실행됩니다. 로컬 작업에는 양자 작업에 대한 우선순위 대기열이 **없습니다**. 다중 노드 또는 MPI와 같은 고급 사례의 경우 로컬 작업이 필요한 Braket 환경 변수에 액세스할 수 있습니다. 다음 코드는 디바이스를 SV1 시뮬레이터로 사용하여 로컬 하이브리드 작업을 생성합니다.

```
@hybrid_job(device=Devices.Amazon.SV1, local=True)
def run_hybrid_job(num_tasks=1):
    return ...
```

다른 모든 하이브리드 작업 옵션도 지원됩니다. 옵션 목록은 [braket.jobs.quantum\$1job\$1creation 모듈](https://amazon-braket-sdk-python.readthedocs.io/en/stable/_apidoc/braket.jobs.quantum_job_creation.html)을 참조하세요.

## 추가 Python 패키지 및 소스 코드 설치
<a name="install-python-packages-and-code"></a>

원하는 Python 패키지를 사용하도록 런타임 환경을 사용자 지정할 수 있습니다. `requirements.txt` 파일, 패키지 이름 목록을 사용하거나 [자체 컨테이너를 사용할 수 있습니다(BYOC)](braket-jobs-byoc.md). 예를 들어 `requirements.txt` 파일에는 설치할 다른 패키지가 포함될 수 있습니다.

```
qiskit 
pennylane >= 0.31
mitiq == 0.29
```

`requirements.txt` 파일을 사용하여 런타임 환경을 사용자 지정하려면 다음 코드 예제를 참조하세요.

```
@hybrid_job(device=Devices.Amazon.SV1, dependencies="requirements.txt")
def run_hybrid_job(num_tasks=1):
    return ...
```

또는 다음과 같이 패키지 이름을 Python 목록으로 제공할 수 있습니다.

```
@hybrid_job(device=Devices.Amazon.SV1, dependencies=["qiskit", "pennylane>=0.31", "mitiq==0.29"])
def run_hybrid_job(num_tasks=1):
    return ...
```

추가 소스 코드는 다음 코드 예제와 같이 모듈 목록 또는 단일 모듈로 지정할 수 있습니다.

```
@hybrid_job(device=Devices.Amazon.SV1, include_modules=["my_module1", "my_module2"])
def run_hybrid_job(num_tasks=1):
    return ...
```

## 하이브리드 작업 인스턴스에 데이터 저장 및 로드
<a name="save-load-data-into-instance"></a>

**입력 훈련 데이터 지정**

하이브리드 작업을 생성할 때 Amazon Simple Storage Service(Amazon S3) 버킷을 지정하여 입력 훈련 데이터세트를 제공할 수 있습니다. 로컬 경로를 지정할 수도 있으며, 그러면 Braket이 자동으로 데이터를 `s3://<default_bucket_name>/jobs/<job_name>/<timestamp>/data/<channel_name>`의 Amazon S3에 업로드합니다. 로컬 경로를 지정하는 경우 채널 이름은 기본적으로 “input”으로 설정됩니다. 다음 코드는 로컬 경로 `data/file.npy`의 넘파이 파일을 보여줍니다.

```
import numpy as np


@hybrid_job(device=Devices.Amazon.SV1, input_data="data/file.npy")
def run_hybrid_job(num_tasks=1):
    data = np.load("data/file.npy")
    return ...
```

S3의 경우 `get_input_data_dir()` 헬퍼 함수를 사용해야 합니다.

```
import numpy as np
from braket.jobs import get_input_data_dir

s3_path = "s3://amazon-braket-us-east-1-123456789012/job-data/file.npy"


@hybrid_job(device=None, input_data=s3_path)
def job_s3_input():
    np.load(get_input_data_dir() + "/file.npy")


@hybrid_job(device=None, input_data={"channel": s3_path})
def job_s3_input_channel():
    np.load(get_input_data_dir("channel") + "/file.npy")
```

채널 값 및 S3 URI 또는 로컬 경로의 딕셔너리를 제공하여 여러 입력 데이터 소스를 지정할 수 있습니다.

```
import numpy as np
from braket.jobs import get_input_data_dir

input_data = {
    "input": "data/file.npy",
    "input_2": "s3://amzn-s3-demo-bucket/data.json"
}


@hybrid_job(device=None, input_data=input_data)
def multiple_input_job():
    np.load(get_input_data_dir("input") + "/file.npy")
    np.load(get_input_data_dir("input_2") + "/data.json")
```

**참고**  
입력 데이터가 크면(>1GB) 작업이 생성되기까지 대기 시간이 길어집니다. 이는 로컬 입력 데이터가 S3 버킷에 처음 업로드된 후 S3 경로가 작업 요청에 추가되기 때문입니다. 마지막으로, 작업 요청이 Braket 서비스에 제출됩니다.

**S3에 결과 저장**

데코레이션된 함수의 반환 문에 포함되지 않은 결과를 저장하려면 모든 파일 쓰기 작업에 올바른 디렉터리를 추가해야 합니다. 다음 예제에서는 넘파이 배열과 matplotlib 그림을 저장하는 방법을 보여줍니다.

```
import matplotlib.pyplot as plt
import numpy as np


@hybrid_job(device=Devices.Amazon.SV1)
def run_hybrid_job(num_tasks=1):
    result = np.random.rand(5)

    # Save a numpy array
    np.save("result.npy", result)

    # Save a matplotlib figure
    plt.plot(result)
    plt.savefig("fig.png")
    return ...
```

모든 결과는 `model.tar.gz`라는 파일로 압축됩니다. Python 함수 `job.result()`를 사용하거나 Braket 관리 콘솔의 하이브리드 작업 페이지에서 결과 폴더로 이동하여 결과를 다운로드할 수 있습니다.

**체크포인트에서 저장 및 재개**

장기 실행 하이브리드 작업의 경우 알고리즘의 중간 상태를 주기적으로 저장하는 것이 좋습니다. 기본 제공 `save_job_checkpoint()` 헬퍼 함수를 사용하거나 파일을 `AMZN_BRAKET_JOB_RESULTS_DIR` 경로에 저장할 수 있습니다. 후자의 경우 헬퍼 함수 `get_job_results_dir()`과 함께 사용할 수 있습니다.

다음은 하이브리드 작업 데코레이터를 사용하여 체크포인트를 저장하고 로드하는 최소 작업 예제입니다.

```
from braket.jobs import save_job_checkpoint, load_job_checkpoint, hybrid_job


@hybrid_job(device=None, wait_until_complete=True)
def function():
    save_job_checkpoint({"a": 1})


job = function()
job_name = job.name
job_arn = job.arn


@hybrid_job(device=None, wait_until_complete=True, copy_checkpoints_from_job=job_arn)
def continued_function():
    load_job_checkpoint(job_name)


continued_job = continued_function()
```

첫 번째 하이브리드 작업에서는 `save_job_checkpoint()`가 저장하려는 데이터가 포함된 딕셔너리와 함께 직접적으로 호출됩니다. 기본적으로 모든 값은 텍스트로 직렬화 가능해야 합니다. 넘파이 배열과 같은 더 복잡한 Python 객체를 체크포인트하려면 `data_format = PersistedJobDataFormat.PICKLED_V4`를 설정할 수 있습니다. 이 코드는 "checkpoints"라는 하위 폴더 아래의 하이브리드 작업 아티팩트에서 기본 이름 `<jobname>.json`으로 체크포인트 파일을 생성하고 덮어씁니다.

체크포인트에서 계속 진행하기 위해 새로운 하이브리드 작업을 생성하려면 `copy_checkpoints_from_job=job_arn`을 전달해야 합니다. 여기서 `job_arn`은 이전 작업의 하이브리드 작업 ARN입니다. 그런 다음 `load_job_checkpoint(job_name)`을 사용하여 체크포인트에서를 로드합니다.

## 하이브리드 작업 데코레이터 모범 사례
<a name="best-practices"></a>

**비동기성 수용**

데코레이터 주석으로 생성된 하이브리드 작업은 비동기적입니다. 즉, 고전 리소스와 양자 리소스를 사용할 수 있게 되면 실행됩니다. Braket Management Console 또는 Amazon CloudWatch를 사용하여 알고리즘의 진행 상황을 모니터링합니다. 실행할 알고리즘을 제출하면 Braket은 확장 가능한 컨테이너화된 환경에서 해당 알고리즘을 실행하고 알고리즘이 완료되면 결과가 검색됩니다.

**반복 변분 알고리즘 실행**

하이브리드 작업은 반복적인 양자-고전 알고리즘을 실행할 수 있는 도구를 제공합니다. 순수하게 양자 문제인 경우 [양자 작업](braket-submit-tasks.md) 또는 [양자 작업 배치](braket-batching-tasks.md)를 사용합니다. 특정 QPU에 대한 우선 액세스는 중간에 고전적 처리를 수행하면서 QPU에 대한 여러 번의 반복 직접 호출을 필요로 하는 장기 실행 변분 알고리즘에 가장 유용합니다.

**로컬 모드를 사용한 디버깅**

QPU에서 하이브리드 작업을 실행하기 전에 먼저 시뮬레이터 SV1에서를 실행하여 예상대로 실행되는지 확인하는 것이 좋습니다. 소규모 테스트의 경우 빠른 반복 및 디버깅을 위해 로컬 모드로 실행할 수 있습니다.

**[BYOC(Bring Your Own Container)](braket-jobs-byoc.md)를 통한 재현성 향상**

컨테이너화된 환경 내에서 소프트웨어와 해당 종속성을 캡슐화하여 재현 가능한 실험을 생성합니다. 컨테이너에 모든 코드, 종속성 및 설정을 패키징하면 잠재적인 충돌 및 버전 관리 문제를 방지할 수 있습니다.

**다중 인스턴스 분산 시뮬레이터**

많은 수의 회로를 실행하려면 기본 제공 MPI 지원을 사용하여 단일 하이브리드 작업 내의 여러 인스턴스에서 로컬 시뮬레이터를 실행하는 것이 좋습니다. 자세한 내용은 [임베디드 시뮬레이터](pennylane-embedded-simulators.md)를 참조하세요.

**파라메트릭 회로 사용**

하이브리드 작업에서 제출하는 파라메트릭 회로는 [파라메트릭 컴파일](braket-jobs-parametric-compilation.md)을 사용하여 특정 QPU에서 자동으로 컴파일되어 알고리즘의 런타임을 개선합니다.

**주기적인 체크포인트**

장기 실행 하이브리드 작업의 경우 알고리즘의 중간 상태를 주기적으로 저장하는 것이 좋습니다.

**추가 예제, 사용 사례 및 모범 사례는 [Amazon Braket 예제 GitHub](https://github.com/amazon-braket/amazon-braket-examples)를 참조하세요.**