주 콘텐츠로 건너뛰기

양자 커널 훈련

사용 예상 시간: Heron r3 프로세서에서 1분 미만 (참고: 이는 추정치이며, 실제 실행 시간은 다를 수 있습니다.)

학습 목표

이 튜토리얼을 완료한 후 다음 정보를 이해할 수 있을 것입니다:

  • 커널 방법과 그 사용 사례
  • 양자 커널과 향상된 특성 공간을 제공하는 방법
  • 양자 커널 Circuit 구성
  • Qiskit 패턴을 사용한 양자 커널 훈련 방법: 매핑, 최적화, 실행 및 후처리

사전 요구 사항

양자 커널, 그 중요성 및 실제 활용 방법에 대해 미리 익혀두는 것을 권장합니다.

군론에 대한 기본적인 이해도 있으면 도움이 됩니다.

배경

커널 방법은 머신러닝 응용 분야에서 일반적으로 사용됩니다. 이 맥락에서 "커널"은 커널 행렬 또는 그 개별 항목을 의미합니다. 일반적으로 커널은 고차원 _특성 공간_에 인코딩된 데이터 간의 유사도 측정값으로, 예를 들어 서포트 벡터 머신을 사용한 분류 작업에 활용될 수 있습니다.

양자 커널 방법은 양자 컴퓨터를 사용하여 커널을 추정하는 방법입니다. 양자 컴퓨터는 데이터를 양자 강화 특성 공간에 인코딩하여 고전적인 유사 방법을 효과적으로 대체할 수 있습니다. xR\vec{x} \in \mathbb{R}Ψ(x)Rd\Psi(\vec{x}) \in \mathbb{R}^{d'}에 대해 (일반적으로 d>dd' >d), Ψ(x)\Psi(\vec{x})는 특성 맵 xΨ(x)\vec{x} \mapsto \Psi(\vec{x})입니다. Ψ(x)\Psi(\vec{x})의 목표는 데이터 범주를 초평면으로 구분하는 것입니다. 특성 매핑된 공간의 벡터를 인수로 받아, 커널 함수 K(x,y)=Ψ(x)Ψ(y)K(\vec{x}, \vec{y}) = \langle{\Psi(\vec{x}) | \Psi(\vec{y}) \rangle{}}는 내적을 반환합니다: K:RdK: \mathbb{R}^d \rightarrow Rd\mathbb{R}^d. 고전적으로는 커널 함수를 쉽게 평가할 수 있는 특성 맵, 즉 특성 매핑된 공간에서의 내적을 원래 데이터 벡터로 표현할 수 있고 Ψ(x)\Psi(\vec{x})Ψ(y)\Psi(\vec{y})를 직접 구성할 필요가 없는 경우가 관심 대상입니다. 양자 커널의 경우, 특성 매핑은 양자 Circuit을 통해 수행되며 커널은 Circuit에서 샘플링된 측정 확률을 사용하여 추정됩니다.

이 튜토리얼에서는 이진 분류에 사용되는 양자 커널 행렬의 항목을 평가하기 위한 Qiskit 패턴을 구축하는 방법을 설명합니다.

요구 사항

이 튜토리얼을 시작하기 전에 다음이 설치되어 있는지 확인하세요:

  • Qiskit SDK v2.3.1 이상 (시각화 지원 포함)
  • Qiskit Runtime v0.44.0 이상 (pip install qiskit-ibm-runtime)

설정

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy pandas qiskit qiskit-ibm-runtime
# General Imports and helper functions
import urllib.request

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit
from qiskit.circuit.library import unitary_overlap
from qiskit.primitives import StatevectorSampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, Sampler

# Download the dataset (portable across platforms)
urllib.request.urlretrieve(
"https://raw.githubusercontent.com/qiskit-community/prototype-quantum-kernel-training/main/data/dataset_graph7.csv",
"dataset_graph7.csv",
)

def visualize_counts(res_counts, num_qubits, num_shots):
"""Visualize the outputs from the Qiskit Sampler primitive."""
zero_prob = res_counts.get(0, 0.0)
top_10 = dict(
sorted(res_counts.items(), key=lambda item: item[1], reverse=True)[
:10
]
)
top_10.update({0: zero_prob})
by_key = dict(sorted(top_10.items(), key=lambda item: item[0]))
x_vals, y_vals = list(zip(*by_key.items()))
x_vals = [bin(x_val)[2:].zfill(num_qubits) for x_val in x_vals]
y_vals_prob = []
for t in range(len(y_vals)):
y_vals_prob.append(y_vals[t] / num_shots)
y_vals = y_vals_prob
plt.bar(x_vals, y_vals)
plt.xticks(rotation=75)
plt.title("Results of sampling")
plt.xlabel("Measured bitstring")
plt.ylabel("Probability")
plt.show()

def get_training_data():
"""Read the training data."""
df = pd.read_csv("dataset_graph7.csv", sep=",", header=None)
training_data = df.values[:20, :]
ind = np.argsort(training_data[:, -1])
X_train = training_data[ind][:, :-1]

return X_train

소규모 시뮬레이터 예제

이 섹션에서는 레이블링-오류-코셋 문제의 7 qubit 인스턴스에 대한 Qiskit 패턴의 네 단계를 살펴보고, Qiskit의 StatevectorSampler 프리미티브를 사용하여 단일 커널 행렬 항목을 평가합니다. 상태 벡터 시뮬레이터는 정확하며 (샷 노이즈까지) QPU 시간을 소비하지 않고 방법 전체를 보여줍니다. 이후 하드웨어 예제 섹션에서 실제 하드웨어로 동일한 인스턴스를 반복합니다.

1단계: 고전적 입력을 양자 문제로 변환

  • 입력: 훈련 데이터셋.
  • 출력: 커널 행렬 항목 계산을 위한 추상 Circuit.

여기서 해결하려는 이진 분류 문제는 "오류가 있는 코셋 레이블링"이라고 합니다. 입력 훈련 데이터셋에는 그룹과 부분군으로 형성된 두 코셋으로 구성된 그룹 구조가 포함되어 있습니다. 그룹은 Qubit에 대해 G=SU(2)nG = SU(2)^{\otimes n}으로 취하며, 이는 2×22 \times 2 행렬의 특수 유니타리 군으로 자연에서 광범위하게 적용됩니다. 예를 들어, 입자 물리학의 표준 모델이 있습니다. 모서리 E\mathcal{E}와 꼭짓점 V\mathcal{V}를 가진 그래프에 대해 Sgraph={Xik:(k,i)EZk}iV}S_\text{graph} = \langle \{ X_i \otimes _{k:(k,i) \in \mathcal{E}} Z_k\} _{i \in \mathcal{V}} \} \rangle인 (그래프 안정자) 부분군 Sgraph<GS_\text{graph} < G를 취합니다. 안정자는 Dsψ=ψ, sSgraphD_s | \psi \rangle = | \psi \rangle,~ \forall s \in S_\text{graph}이 되도록 안정자 상태를 고정한다는 점에 유의하세요. 마지막으로, 두 c±Gc_\pm \in G를 무작위로 추출하여 두 좌 코셋 C±=c±SgraphC_\pm = c_\pm S_\text{graph}을 정의합니다.

데이터셋과 생성 방법에 대한 자세한 내용은 Quantum Kernel Training Toolkit이 노트북을 참조하세요.

커널 행렬의 한 항목을 평가하는 데 사용되는 양자 Circuit을 생성합니다. 입력 데이터를 사용하여 Circuit의 매개변수화된 게이트에 대한 회전 각도를 결정합니다. 간단히 하기 위해 데이터 샘플 x1=14x2=19를 사용하겠습니다.

참고: 이 튜토리얼에 사용된 데이터셋은 여기에서 다운로드할 수 있습니다.

# Prepare training data
X_train = get_training_data()

# Empty kernel matrix
num_samples = np.shape(X_train)[0]
kernel_matrix = np.full((num_samples, num_samples), np.nan)

# Prepare feature map for computing overlap
num_features = np.shape(X_train)[1]
num_qubits = int(num_features / 2)
entangler_map = [[0, 2], [3, 4], [2, 5], [1, 4], [2, 3], [4, 6]]
fm = QuantumCircuit(num_qubits)
training_param = Parameter("θ")
feature_params = ParameterVector("x", num_qubits * 2)
fm.ry(training_param, fm.qubits)
for cz in entangler_map:
fm.cz(cz[0], cz[1])
for i in range(num_qubits):
fm.rz(-2 * feature_params[2 * i + 1], i)
fm.rx(-2 * feature_params[2 * i], i)

# Assign tunable parameter to known optimal value and set the data params for
# first two samples
x1 = 14
x2 = 19
unitary1 = fm.assign_parameters(list(X_train[x1]) + [np.pi / 2])
unitary2 = fm.assign_parameters(list(X_train[x2]) + [np.pi / 2])

# Create the overlap circuit
overlap_circ = unitary_overlap(unitary1, unitary2)
overlap_circ.measure_all()
overlap_circ.draw("mpl", scale=0.6, style="iqp")

이전 코드 셀의 출력

2단계: 양자 하드웨어 실행을 위한 문제 최적화

  • 입력: 특정 Backend에 최적화되지 않은 추상 Circuit.
  • 출력: 선택된 QPU에 최적화된 목표 Circuit.

이 섹션에서 사용하는 상태 벡터 시뮬레이터 경로의 경우, Backend별 최적화가 필요하지 않습니다: 추상 Circuit을 직접 샘플링할 수 있습니다. 이 단계는 아래의 하드웨어 예제에서 실행하며, 여기서는 optimization_level=3을 사용하는 generate_preset_pass_manager로 실제 QPU에 맞게 Circuit을 변환합니다.

3단계: Qiskit 프리미티브를 사용한 실행

  • 입력: 추상 Circuit.
  • 출력: 준확률 분포.

Qiskit의 StatevectorSampler 프리미티브를 사용하여 Circuit 샘플링으로부터 얻은 상태의 준확률 분포를 재구성합니다. 커널 행렬 생성 작업에서는 특히 |0> 상태를 측정할 확률에 관심이 있습니다.

sampler = StatevectorSampler()

# Execute and get counts
num_shots = 10_000
results = sampler.run([overlap_circ], shots=num_shots).result()
counts = results[0].data.meas.get_int_counts()

# Plot counts
visualize_counts(counts, num_qubits, num_shots)

이전 코드 셀의 출력

4단계: 후처리 및 원하는 고전적 형식으로 결과 반환

  • 입력: 확률 분포.
  • 출력: 단일 커널 행렬 요소.

오버랩 Circuit에서 0|0 \rangle을 측정할 확률을 계산하고, 이 특정 오버랩 Circuit이 나타내는 샘플에 해당하는 위치(행 15, 열 20)에 커널 행렬을 채웁니다.

kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots
print(f"Fidelity (simulator): {kernel_matrix[x1, x2]}")
Fidelity (simulator): 0.8261

하드웨어 예제

양자 커널 행렬은 NN개의 훈련 샘플에 대해 O(N2)\mathcal{O}(N^2)개의 항목을 가지며, 각 항목은 특성 맵의 크기에 따라 2 qubit 게이트 깊이가 증가하는 오버랩 Circuit을 실행해야 합니다. 따라서 이 튜토리얼을 더 큰 문제로 확장하면 두 가지 비용이 복합적으로 발생합니다: 커널 행렬당 QPU 시간이 NN에 따라 이차적으로 증가하고, unitary_overlap의 깊이(특성 맵과 그 수반 연산자를 합성함)가 현재 하드웨어의 시스템 크기 및 연결성에서 충실도를 저하시킵니다. 데모를 짧게 유지하고 깔끔한 비교를 위해, 소규모 예제에서와 동일한 7 qubit 인스턴스를 실제 QPU에서 실행하고 단일 커널 행렬 항목의 충실도를 위에서 계산한 시뮬레이터 값과 비교합니다.

# ------------------------------ Step 1 ------------------------------
# Prepare training data
X_train = get_training_data()

# Empty kernel matrix
num_samples = np.shape(X_train)[0]
kernel_matrix = np.full((num_samples, num_samples), np.nan)

# Prepare feature map for computing overlap
num_features = np.shape(X_train)[1]
num_qubits = int(num_features / 2)
entangler_map = [[0, 2], [3, 4], [2, 5], [1, 4], [2, 3], [4, 6]]
fm = QuantumCircuit(num_qubits)
training_param = Parameter("θ")
feature_params = ParameterVector("x", num_qubits * 2)
fm.ry(training_param, fm.qubits)
for cz in entangler_map:
fm.cz(cz[0], cz[1])
for i in range(num_qubits):
fm.rz(-2 * feature_params[2 * i + 1], i)
fm.rx(-2 * feature_params[2 * i], i)

# Assign tunable parameter to known optimal value and
# set the data params for first two samples
x1 = 14
x2 = 19
unitary1 = fm.assign_parameters(list(X_train[x1]) + [np.pi / 2])
unitary2 = fm.assign_parameters(list(X_train[x2]) + [np.pi / 2])

# Create the overlap circuit
overlap_circ = unitary_overlap(unitary1, unitary2)
overlap_circ.measure_all()

# ------------------------------ Step 2 ------------------------------
service = QiskitRuntimeService()
# backend = service.least_busy(
# operational=True, simulator=False, min_num_qubits=overlap_circ.num_qubits
# )
backend = service.backend("ibm_pittsburgh")
print(f"Using backend: {backend.name}")
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
overlap_ibm = pm.run(overlap_circ)

# ------------------------------ Step 3 ------------------------------
sampler = Sampler(mode=backend)
sampler.options.environment.job_tags = ["TUT_QKT"]

num_shots = 10_000
results = sampler.run([overlap_ibm], shots=num_shots).result()
counts = results[0].data.meas.get_int_counts()
visualize_counts(counts, num_qubits, num_shots)

# ------------------------------ Step 4 ------------------------------
kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots
print(f"Fidelity (hardware): {kernel_matrix[x1, x2]}")
Using backend: ibm_pittsburgh

이전 코드 셀의 출력

Fidelity (hardware): 0.7517

전체 커널 행렬을 채우려면 N(N+1)/2N(N+1)/2개의 고유 항목 각각에 대해 양자 실험을 실행해야 합니다. 아래 그림은 이 데이터셋에 대한 결과 행렬을 보여줍니다; 더 진한 빨간색은 1.0에 가까운 충실도를 나타냅니다.

kernel_matrix.png

다음 단계

권장 사항

이 작업이 흥미로우셨다면 다음 자료도 살펴보세요: