Trénování kvantového kernelu
Odhadovaná doba použití: méně než jedna minuta na procesoru Heron r3 (POZNÁMKA: Jedná se pouze o odhad. Tvá skutečná doba běhu se může lišit.)
Výsledky učení
Po dokončení tohoto tutoriálu by sis měl/a umět vysvětlit následující informace:
- Metody kernelu a jejich použití
- Kvantové kernely a jak mohou poskytovat rozšířené prostory příznaků
- Konstrukce obvodu kvantového kernelu
- Jak trénovat kvantový kernel pomocí Qiskit pattern: mapovat, optimalizovat, spustit a post-processovat
Předpoklady
Doporučujeme, abys se seznámil/a s kvantovými kernely, proč jsou důležité a jak se v praxi používají.
- Covariant quantum kernels for data with group structure (paper)
- Introduction to Quantum Kernels and Support Vector Machines (video)
- Quantum Kernels in Practice (video)
Užitečné je také základní pochopení teorie grup.
Pozadí
Metody kernelu jsou v aplikacích strojového učení běžné. V tomto kontextu pojem „kernel" označuje matici kernelu nebo jednotlivé její položky. Obecně je kernel mírou podobnosti mezi daty zakódovanými ve vysokodimenzionálním prostoru příznaků a lze jej využít například v klasifikačních úlohách se support vector machines.
Kvantové metody kernelu jsou ty, které k odhadu kernelu používají kvantové počítače. Je známo, že kvantové počítače mohou kódovat data v kvantově rozšířených prostorech příznaků a efektivně nahrazovat klasické analogy. Pro a , typicky s , je feature map, . Cílem je oddělení kategorií dat nadrovinou. Bere-li se jako argument vektory v prostoru feature map, vrací funkce kernelu jejich skalární součin: . Klasicky jsou zajímavé ty feature mapy, u nichž lze funkci kernelu snadno vyhodnotit; tedy tehdy, kdy skalární součin v prostoru feature mapy lze vyjádřit ve smyslu původních datových vektorů a a není třeba konstruovat. V případě kvantových kernelů provádí feature mapping kvantový obvod a kernel se odhaduje pomocí pravděpodobností měření vzorkovaných z obvodu.
Tento tutoriál ukazuje, jak sestavit Qiskit pattern pro vyhodnocování položek matice kvantového kernelu používané pro binární klasifikaci.
Požadavky
Před zahájením tohoto tutoriálu se ujisti, že máš nainstalováno následující:
- Qiskit SDK v2.3.1 nebo novější, s podporou vizualizace
- Qiskit Runtime v0.44.0 nebo novější (
pip install qiskit-ibm-runtime)
Nastavení
# 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
Příklad s malým simulátorem
V této části projdeme čtyři kroky Qiskit pattern na sedmiqubitové instanci problému označování cosetů s chybou a vyhodnotíme jedinou položku matice kernelu pomocí primitiva StatevectorSampler z Qisku. Stavový vektorový simulátor je přesný (až na shot noise) a umožňuje nám ukázat metodu od začátku do konce bez spotřeby času na QPU. Stejnou instanci poté opakujeme na reálném hardwaru v části s hardwarovým příkladem.
Krok 1: Mapování klasických vstupů na kvantový problém
- Vstup: Trénovací datová sada.
- Výstup: Abstraktní Circuit pro výpočet položky matice kernelu.
Problém binární klasifikace, který zde chceme řešit, se označuje jako „označování cosetů s chybou." Vstupní trénovací datová sada obsahuje grupovou strukturu, tvořenou dvěma cosety vytvořenými skupinou a podskupinou. Skupina je pro qubity volena jako , což je speciální unitární grupa matic s širokým uplatněním v přírodě; například ve Standardním modelu fyziky částic. Jako (grafový stabilizátor) podgrupu volíme s pro graf s hranami a vrcholy . Stabilizátory fixují stabilizátorový stav tak, že . Nakonec definujeme dva levé cosety výběrem dvou náhodně.
Více podrobností o datové sadě a způsobu jejího generování najdeš v tomto notebooku z Quantum Kernel Training Toolkit.
Vytvoříme kvantový Circuit používaný k vyhodnocení jedné položky v matici kernelu.
Vstupní data jsou použita k určení úhlů rotace pro parametrizované Gate obvodu.
Pro jednoduchost použijeme datové vzorky x1=14 a x2=19.
Poznámka: Datovou sadu použitou v tomto tutoriálu si můžeš stáhnout zde.
# 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")
Krok 2: Optimalizace problému pro spuštění na kvantovém hardwaru
- Vstup: Abstraktní Circuit, neoptimalizovaný pro konkrétní Backend.
- Výstup: Cílový Circuit, optimalizovaný pro vybraný QPU.
V cestě se stavovým vektorem použité v této části není vyžadována žádná hardwarově specifická optimalizace: abstraktní Circuit lze vzorkovat přímo. Tento krok si vyzkoušíme v hardwarovém příkladu níže, kde je Circuit transpilován proti reálnému QPU pomocí generate_preset_pass_manager s optimization_level=3.
Krok 3: Spuštění pomocí Qiskit primitiv
- Vstup: Abstraktní Circuit.
- Výstup: Kvazi-pravděpodobnostní rozdělení.
Použij primitivum StatevectorSampler z Qisku k rekonstrukci kvazi-pravděpodobnostního rozdělení stavů získaných vzorkováním obvodu. Pro úlohu generování matice kernelu nás zajímá zejména pravděpodobnost naměření stavu |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)
Krok 4: Post-processing a vrácení výsledku v požadovaném klasickém formátu
- Vstup: Pravděpodobnostní rozdělení.
- Výstup: Jeden prvek matice kernelu.
Vypočítej pravděpodobnost naměření na overlap obvodu a naplň matici kernelu na pozici odpovídající vzorkům reprezentovaným tímto konkrétním overlap obvodem (řádek 15, sloupec 20).
kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots
print(f"Fidelity (simulator): {kernel_matrix[x1, x2]}")
Fidelity (simulator): 0.8261
Hardwarový příklad
Matice kvantového kernelu má položek pro trénovacích vzorků a každá položka vyžaduje spuštění overlap obvodu, jehož hloubka dvouqubitových gate roste s velikostí feature mapy. Škálování tohoto tutoriálu na větší problém proto přináší dvě kumulující se náklady: čas QPU na matici kernelu roste kvadraticky s a hloubka unitary_overlap (která skládá feature mapu s jejím adjunktem) snižuje fidelitu při velikosti systému a konektivitě současného hardwaru. Aby byla ukázka krátká a srovnání čisté, spouštíme proto stejnou sedmiqubitovou instanci z příkladu s malým simulátorem na reálném QPU a porovnáváme fidelitu jedné položky matice kernelu s hodnotou simulátoru vypočítanou výše.
# ------------------------------ 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
Pro naplnění celé matice kernelu bychom spustili kvantový experiment pro každou z jejích unikátních položek. Níže uvedený obrázek zobrazuje výslednou matici pro tuto datovou sadu; tmavší červená označuje fidelity blíže k 1,0.
Další kroky
Pokud tě tato práce zaujala, možná tě budou zajímat tyto materiály:
- Quantum Kernel Training Toolkit - prototypové úložiště, na němž je tento tutoriál založen
- Quantum Kernel Training for Machine Learning Applications - tutoriál Qiskit Machine Learning ukazující, jak trénovat trénovatelný parametr
- Introduction to Quantum Machine Learning - kurz o kvantovém strojovém učení
- Quantum Machine Learning from IBM Research - přehled výzkumu QML v IBM
- Covariant quantum kernels for data with group structure - paper, na němž je tento tutoriál založen