Training von Quantenkernen
Geschätzter Aufwand: unter einer Minute auf einem Heron-r3-Prozessor (HINWEIS: Dies ist nur eine Schätzung. Deine tatsächliche Laufzeit kann abweichen.)
Lernziele
Nach Abschluss dieses Tutorials kannst du folgende Informationen nachvollziehen:
- Kernel-Methoden und ihre Anwendungen
- Quantenkerne und wie sie erweiterte Feature-Räume bereitstellen können
- Aufbau von Quantenkern-Circuits
- Training eines Quantenkerns mithilfe eines Qiskit-Patterns: Abbilden, Optimieren, Ausführen und Nachverarbeiten
Voraussetzungen
Es wird empfohlen, dich mit Quantenkernen vertraut zu machen, warum sie wichtig sind und wie sie in der Praxis eingesetzt werden.
- Covariant quantum kernels for data with group structure (Fachartikel)
- Introduction to Quantum Kernels and Support Vector Machines (Video)
- Quantum Kernels in Practice (Video)
Außerdem ist ein grundlegendes Verständnis der Gruppentheorie hilfreich.
Hintergrund
Kernel-Methoden sind in maschinellen Lernanwendungen weit verbreitet. In diesem Zusammenhang bezeichnet „Kernel" die Kernmatrix oder einzelne Einträge darin. Im Allgemeinen ist ein Kernel ein Ähnlichkeitsmaß zwischen Daten, die in einem hochdimensionalen Feature-Raum kodiert sind, und kann beispielsweise bei Klassifikationsaufgaben mit Support-Vektor-Maschinen eingesetzt werden.
Quantenkern-Methoden sind solche, die Quantencomputer zur Schätzung eines Kernels verwenden. Es ist bekannt, dass Quantencomputer Daten in quantenverstärkten Feature-Räumen kodieren können und so klassische Pendants effektiv ersetzen. Für und , typischerweise mit , ist eine Feature-Map, . Das Ziel von ist es, Datenkategorien durch eine Hyperebene zu trennen. Die Kernfunktion nimmt die Vektoren im Feature-abgebildeten Raum als Argumente und gibt ihr inneres Produkt zurück: . Klassisch sind Feature-Maps von Interesse, bei denen die Kernfunktion leicht ausgewertet werden kann; das heißt, wenn das innere Produkt im Feature-abgebildeten Raum in Termen der ursprünglichen Datenvektoren geschrieben werden kann und und nicht explizit konstruiert werden müssen. Bei Quantenkernen wird die Feature-Abbildung durch einen Quantencircuit durchgeführt, und der Kernel wird anhand der Messprobabilitäten geschätzt, die aus dem Circuit gesampelt werden.
Dieses Tutorial zeigt, wie du ein Qiskit-Pattern erstellst, um Einträge einer Quantenkernmatrix für binäre Klassifikation zu berechnen.
Anforderungen
Stelle vor dem Start dieses Tutorials sicher, dass folgende Pakete installiert sind:
- Qiskit SDK v2.3.1 oder höher, mit Unterstützung für Visualisierung
- Qiskit Runtime v0.44.0 oder höher (
pip install qiskit-ibm-runtime)
Einrichtung
# 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
Kleines Simulatorbeispiel
In diesem Abschnitt gehen wir die vier Schritte des Qiskit-Patterns für eine Sieben-Qubit-Instanz des Problems „Labeling Cosets with Error" durch und werten einen einzelnen Kernmatrixeintrag mit dem StatevectorSampler-Primitiv aus Qiskit aus. Ein Zustandsvektorsimulator ist exakt (abgesehen von Shot-Rauschen) und zeigt uns die Methode von Anfang bis Ende, ohne QPU-Zeit zu verbrauchen. Wir wiederholen dieselbe Instanz dann auf echter Hardware im Abschnitt zum Hardware-Beispiel.
Schritt 1: Klassische Eingaben auf ein Quantenproblem abbilden
- Eingabe: Trainingsdatensatz.
- Ausgabe: Abstrakter Circuit zur Berechnung eines Kernmatrixeintrags.
Das binäre Klassifikationsproblem, das wir hier lösen möchten, wird als „Labeling Cosets with Error" bezeichnet. Der Eingabe-Trainingsdatensatz enthält eine Gruppenstruktur, bestehend aus zwei Nebenklassen, die durch eine Gruppe und eine Untergruppe gebildet werden. Die Gruppe ist für Qubits, d. h. die spezielle unitäre Gruppe der -Matrizen, die in der Natur weit verbreitet ist; z. B. im Standardmodell der Teilchenphysik. Wir wählen die (Graph-Stabilisator-)Untergruppe mit für einen Graphen mit Kanten und Knoten . Beachte, dass die Stabilisatoren einen Stabilisatorzustand fixieren, sodass . Schließlich definieren wir zwei linke Nebenklassen , indem wir zwei zufällig ziehen.
Weitere Details zum Datensatz und seiner Generierung findest du in diesem Notebook aus dem Quantum Kernel Training Toolkit.
Wir erstellen den Quantum Circuit, der zur Auswertung eines Eintrags in der Kernmatrix verwendet wird.
Die Eingabedaten werden genutzt, um die Rotationswinkel der parametrisierten Gates des Circuits zu bestimmen.
Der Einfachheit halber verwenden wir die Datenpunkte x1=14 und x2=19.
Hinweis: Der in diesem Tutorial verwendete Datensatz kann hier heruntergeladen werden.
# 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")
Schritt 2: Problem für die Ausführung auf Quantenhardware optimieren
- Eingabe: Abstrakter Circuit, nicht für ein bestimmtes Backend optimiert.
- Ausgabe: Ziel-Circuit, optimiert für den ausgewählten QPU.
Für den in diesem Abschnitt verwendeten Zustandsvektorsimulator ist keine Backend-spezifische Optimierung erforderlich: Der abstrakte Circuit kann direkt gesampelt werden. Wir führen diesen Schritt im Hardware-Beispiel unten aus, wo der Circuit mit generate_preset_pass_manager mit optimization_level=3 gegen einen echten QPU transpiliert wird.
Schritt 3: Ausführung mit Qiskit-Primitiven
- Eingabe: Abstrakter Circuit.
- Ausgabe: Quasi-Wahrscheinlichkeitsverteilung.
Verwende das StatevectorSampler-Primitiv aus Qiskit, um eine Quasi-Wahrscheinlichkeitsverteilung der Zustände zu rekonstruieren, die beim Sampling des Circuits entstehen. Für die Erstellung einer Kernmatrix interessiert uns besonders die Wahrscheinlichkeit, den Zustand |0> zu messen.
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)
Schritt 4: Nachverarbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format
- Eingabe: Wahrscheinlichkeitsverteilung.
- Ausgabe: Ein einzelnes Kernmatrixelement.
Berechne die Wahrscheinlichkeit, auf dem Overlap-Circuit zu messen, und befülle die Kernmatrix an der Position, die den durch diesen Circuit repräsentierten Datenpunkten entspricht (Zeile 15, Spalte 20).
kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots
print(f"Fidelity (simulator): {kernel_matrix[x1, x2]}")
Fidelity (simulator): 0.8261
Hardware-Beispiel
Eine Quantenkernmatrix hat Einträge für Trainingssamples, und jeder Eintrag erfordert die Ausführung eines Overlap-Circuits, dessen Tiefe der Zwei-Qubit-Gates mit der Größe der Feature-Map wächst. Das Skalieren dieses Tutorials auf ein größeres Problem hat daher zwei miteinander verknüpfte Kosten: Die QPU-Zeit pro Kernmatrix wächst quadratisch mit , und die Tiefe von unitary_overlap (das die Feature-Map mit ihrer Adjungierten verknüpft) verringert die Fidelität bei der Systemgröße und Konnektivität aktueller Hardware. Um die Demo kurz zu halten und einen sauberen Vergleich zu ermöglichen, führen wir daher dieselbe Sieben-Qubit-Instanz aus dem kleinen Beispiel auf einem echten QPU aus und vergleichen die Fidelität eines einzelnen Kernmatrixeintrags mit dem oben berechneten Simulatorwert.
# ------------------------------ 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
Um die gesamte Kernmatrix zu füllen, würde man ein Quantenexperiment für jeden der eindeutigen Einträge ausführen. Die folgende Abbildung zeigt die resultierende Matrix für diesen Datensatz; ein dunkleres Rot steht für Fidelitätswerte nahe 1,0.
Nächste Schritte
Wenn du diese Arbeit interessant fandest, könnte dich folgendes Material interessieren:
- Quantum Kernel Training Toolkit - das Prototyp-Repository, auf dem dieses Tutorial basiert
- Quantum Kernel Training for Machine Learning Applications - ein Qiskit Machine Learning-Tutorial zum Training des trainierbaren Parameters
- Introduction to Quantum Machine Learning - ein Kurs über maschinelles Quantenlernen
- Quantum Machine Learning from IBM Research - eine Übersicht zur QML-Forschung bei IBM
- Covariant quantum kernels for data with group structure - der Fachartikel, auf dem dieses Tutorial basiert