Addestramento del kernel quantistico
Stima di utilizzo: meno di un minuto su un processore Heron r3 (NOTA: Questa è solo una stima. Il tempo di esecuzione effettivo potrebbe variare.)
Risultati dell'apprendimento
Al termine di questo tutorial, potrai comprendere le seguenti informazioni:
- I metodi kernel e i loro utilizzi
- I kernel quantistici e come possono fornire spazi delle caratteristiche migliorati
- La costruzione di circuiti per kernel quantistici
- Come addestrare un kernel quantistico usando un pattern Qiskit: mappare, ottimizzare, eseguire e post-elaborare
Prerequisiti
Si raccomanda di familiarizzare con i kernel quantistici, perché sono importanti e come vengono utilizzati in pratica.
- Covariant quantum kernels for data with group structure (articolo)
- Introduction to Quantum Kernels and Support Vector Machines (video)
- Quantum Kernels in Practice (video)
È utile anche avere una comprensione di base della teoria dei gruppi.
Contesto
I metodi kernel sono comuni nelle applicazioni di machine learning. In questo contesto, "kernel" si riferisce alla matrice kernel o alle singole voci al suo interno. In generale, un kernel è una misura di similarità tra dati codificati in uno spazio delle caratteristiche (feature space) ad alta dimensionalità e può essere utilizzato, ad esempio, in compiti di classificazione con macchine a vettori di supporto.
I metodi kernel quantistici sono quelli che usano i computer quantistici per stimare un kernel. È noto che i computer quantistici possono codificare i dati in spazi delle caratteristiche potenziati quantisticamente, sostituendo efficacemente gli analoghi classici. Per e , tipicamente con , è una mappa delle caratteristiche, . L'obiettivo di è fare in modo che le categorie dei dati siano separate da un iperpiano. Prendendo i vettori nello spazio mappato alle caratteristiche come argomenti, la funzione kernel restituisce il loro prodotto interno: . Classicamente, le mappe delle caratteristiche di interesse sono quelle in cui la funzione kernel può essere facilmente valutata; ovvero, quando il prodotto interno nello spazio mappato alle caratteristiche può essere scritto in termini dei vettori di dati originali e e non devono essere costruiti. Nel caso dei kernel quantistici, la mappatura delle caratteristiche viene eseguita da un circuito quantistico, e il kernel viene stimato usando le probabilità di misura campionate dal circuito.
Questo tutorial mostra come costruire un pattern Qiskit per valutare le voci in una matrice kernel quantistica utilizzata per la classificazione binaria.
Requisiti
Prima di iniziare questo tutorial, assicurati di avere installato quanto segue:
- Qiskit SDK v2.3.1 o successiva, con supporto per la visualizzazione
- Qiskit Runtime v0.44.0 o successiva (
pip install qiskit-ibm-runtime)
Configurazione
# 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
Esempio con simulatore su piccola scala
In questa sezione, percorriamo i quattro passaggi del pattern Qiskit su un'istanza a sette qubit del problema di etichettatura dei coset con errore e valutiamo una singola voce della matrice kernel usando la primitiva StatevectorSampler di Qiskit. Un simulatore statevector è esatto (fino al rumore dei shot) e ci mostra il metodo dall'inizio alla fine senza consumare tempo QPU. Poi ripetiamo la stessa istanza su hardware reale nella sezione dell'esempio hardware.
Passaggio 1: Mappare gli input classici a un problema quantistico
- Input: Dataset di addestramento.
- Output: Circuito astratto per calcolare una voce della matrice kernel.
Il problema di classificazione binaria che vogliamo risolvere qui è detto "etichettatura dei coset con errore". Il dataset di addestramento in input contiene una struttura di gruppo, composta da due coset formati da un gruppo e un sottogruppo. Il gruppo è preso come per i qubit, che è il gruppo unitario speciale di matrici e ha ampia applicabilità in natura; ad esempio, nel Modello Standard della fisica delle particelle. Prendiamo il sottogruppo (stabilizzatore di grafo) con per un grafo con archi e vertici . Si noti che gli stabilizzatori fissano uno stato stabilizzatore tale che . Infine, definiamo due coset sinistri estraendo due casualmente.
Per maggiori dettagli sul dataset e su come viene generato, consulta questo notebook dal Quantum Kernel Training Toolkit.
Creiamo il circuito quantistico utilizzato per valutare una voce nella matrice kernel.
I dati di input vengono utilizzati per determinare gli angoli di rotazione per i gate parametrizzati del circuito.
Per semplicità, useremo i campioni di dati x1=14 e x2=19.
Nota: Il dataset utilizzato in questo tutorial può essere scaricato qui.
# 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")
Passaggio 2: Ottimizzare il problema per l'esecuzione su hardware quantistico
- Input: Circuito astratto, non ottimizzato per un particolare backend.
- Output: Circuito target, ottimizzato per la QPU selezionata.
Per il percorso con simulatore statevector utilizzato in questa sezione, non è necessaria alcuna ottimizzazione specifica per il backend: il circuito astratto può essere campionato direttamente. Eseguiamo questo passaggio nell'esempio hardware qui sotto, dove il circuito viene transpilato su una QPU reale usando generate_preset_pass_manager con optimization_level=3.
Passaggio 3: Eseguire utilizzando le primitive Qiskit
- Input: Circuito astratto.
- Output: Distribuzione di quasi-probabilità.
Utilizza la primitiva StatevectorSampler di Qiskit per ricostruire una distribuzione di quasi-probabilità degli stati ottenuti dal campionamento del circuito. Per il compito di generare una matrice kernel, siamo particolarmente interessati alla probabilità di misurare lo stato |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)
Passaggio 4: Post-elaborare e restituire il risultato nel formato classico desiderato
- Input: Distribuzione di probabilità.
- Output: Un singolo elemento della matrice kernel.
Calcola la probabilità di misurare sul circuito di sovrapposizione e popola la matrice kernel nella posizione corrispondente ai campioni rappresentati da questo particolare circuito di sovrapposizione (riga 15, colonna 20).
kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots
print(f"Fidelity (simulator): {kernel_matrix[x1, x2]}")
Fidelity (simulator): 0.8261
Esempio hardware
Una matrice kernel quantistica ha voci per campioni di addestramento, e ogni voce richiede l'esecuzione di un circuito di sovrapposizione la cui profondità di gate a due qubit cresce con le dimensioni della mappa delle caratteristiche. Di conseguenza, scalare questo tutorial a un problema più grande ha due costi cumulativi: il tempo QPU per matrice kernel cresce quadraticamente con , e la profondità di unitary_overlap (che compone la mappa delle caratteristiche con il suo aggiunto) erode la fedeltà alla dimensione del sistema e alla connettività dell'hardware attuale. Per mantenere la demo breve e fare un confronto pulito, eseguiamo quindi la stessa istanza a sette qubit dell'esempio su piccola scala su una QPU reale e confrontiamo la fedeltà di una singola voce della matrice kernel con il valore del simulatore calcolato sopra.
# ------------------------------ 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
Per completare l'intera matrice kernel, eseguiremmo un esperimento quantistico per ciascuna delle sue voci uniche. La figura sottostante mostra la matrice risultante per questo dataset; il rosso più scuro indica fedeltà più vicine a 1.0.
Passi successivi
Se hai trovato questo lavoro interessante, potresti essere interessato al seguente materiale:
- Quantum Kernel Training Toolkit - il repository prototipo su cui si basa questo tutorial
- Quantum Kernel Training for Machine Learning Applications - un tutorial di Qiskit Machine Learning che mostra come addestrare il parametro addestrabile
- Introduction to Quantum Machine Learning - un corso sul machine learning quantistico
- Quantum Machine Learning from IBM Research - una panoramica della ricerca QML di IBM
- Covariant quantum kernels for data with group structure - il paper su cui si basa questo tutorial