Квантові ядра
Вступ до квантових ядер
«Квантовий метод ядра» — це будь-який метод, що використовує квантові комп'ютери для оцінки ядра. У цьому контексті під «ядром» розуміється матриця ядра або її окремі елементи. Нагадаємо: відображення ознак — це відображення з у де зазвичай , а мета цього відображення — зробити категорії даних лінійно відокремлюваними. Функція ядра приймає вектори у просторі відображених ознак як аргументи та повертає їх внутрішній добуток, тобто з . У класичному підході нас цікавлять відображення ознак, для яких функцію ядра легко обчислити. Це часто означає пошук такої функції ядра, для якої внутрішній добуток у просторі відображених ознак можна записати через вихідні вектори даних, не конструюючи та явно. У методі квантових ядер відображення ознак виконується квантовою схемою, а ядро оцінюється за результатами вимірювань цієї схеми та відносними ймовірностями вимірювань.
У цьому уроці ми вивчимо глибини попередньо закодованих кодувальних схем зі значним заплутуванням і порівняємо їх із глибинами схем, які ми пишемо вручну. Це не означає, що один підхід кращий за інший. Ти можеш виявити, що попередньо закодовані схеми надто глибокі, а заплутування у власноруч побудованій схемі недостатнє, щоб бути корисним. Ці приклади наведено лише для того, щоб ти міг досліджувати різні варіанти.
Перш ніж детально розбирати оцінку матриці ядра, окреслимо робочий процес у термінах патернів Qiskit.
Крок 1: Відображення класичних вхідних даних у квантову задачу
- Вхід: навчальний датасет
- Вихід: абстрактна схема для обчислення елемента матриці ядра
Маючи датасет, починаємо з кодування даних у квантову схему. Іншими словами, нам потрібно відобразити наші дані в гільбертовий простір станів квантового комп'ютера. Для цього ми будуємо схему, що залежить від даних. Є багато способів це зробити; попередній урок описував кілька варіантів. Ти можеш побудувати власну схему для кодування даних або скористатися готовим відображенням ознак, наприклад zz_feature_map. У цьому уроці ми зробимо обидва варіанти.
Зверни у вагу: щоб обчислити один елемент матриці ядра, нам потрібно закодувати дві різні точки, аби оцінити їх внутрішній добуток. Повний квантовий робочий процес для ядра, звичайно, включатиме багато таких внутрішніх добутків між відображеними векторами даних, а також класичні методи машинного навчання. Але основний крок, що повторюється, — це оцінка одного елемента матриці ядра. Для цього ми обираємо квантову схему, що залежить від даних, і відображаємо два вектори даних у простір ознак.

При побудові матриці ядра нас особливо цікавить імовірність виміряти стан , де всі кубітів перебувають у стані . Щоб зрозуміти чому, розглянемо: схема, що відповідає за кодування та відображення вектора даних , записується як , а схема для — як . Позначимо відображені стани:
Ці стани є відображенням даних у вищі виміри, тому потрібний нам елемент ядра — це внутрішній добуток:
Якщо ми діємо на початковий стан обома схемами та , то ймовірність подальшого вимірювання стану дорівнює:
Це саме те значення, яке нам потрібне (з точністю до ). Шар вимірювання нашої схеми поверне ймовірності вимірювань (або так звані «квазіймовірності», якщо застосовуються певні методи пом'якшення помилок). Нас цікавить ймовірність нульового стану .
Крок 2: Оптимізація задачі для квантового виконання
- Вхід: абстрактна схема, не оптимізована під конкретний бекенд
- Вихід: цільова схема та спостережувана, оптимізовані для обраного QPU
На цьому кроці ми використаємо функцію generate_preset_pass_manager з Qiskit, щоб задати процедуру оптимізації нашої схеми для реального квантового комп'ютера, на якому плануємо запускати експеримент. Ми встановимо optimization_level=3, що означає використання менеджера пропусків із найвищим рівнем оптимізації. У цьому контексті «оптимізація» стосується реалізації схеми на реальному квантовому комп'ютері. Це включає вибір фізичних кубітів, що відповідають кубітам у абстрактній квантовій схемі, з метою мінімізації глибини вентилів або вибору фізичних кубітів із найнижчими доступними рівнями помилок. Це не пов'язано безпосередньо з оптимізацією задачі машинного навчання (як у класичних оптимізаторах, таких як COBYLA).
Залежно від реалізації кроку 2, можливо, доведеться оптимізувати схему більше одного разу, оскільки кожна пара точок, що входить до елемента матриці, породжує окрему схему для вимірювання.
Крок 3: Виконання за допомогою Qiskit Runtime Primitives
- Вхід: цільова схема
- Вихід: розподіл імовірностей
Викорис товуй примітив Sampler з Qiskit Runtime для відновлення розподілу імовірностей станів, отриманих при семплюванні схеми. Зверни увагу: це може називатися «розподілом квазіймовірностей» — термін, що застосовується там, де шум є проблемою і де введено додаткові кроки, наприклад пом'якшення помилок. У таких випадках сума всіх імовірностей може бути не рівно 1; звідси «квазіймовірність».
Крок 4: Постобробка, повернення результату у класичному форматі
- Вхід: розподіл імовірностей
- Вихід: один елемент матриці ядра або вся матриця ядра при повторенні
Обчисли ймовірність виміряти на квантовій схемі та запиши значення в матрицю ядра на позиції, що відповідає двом використаним векторам даних. Щоб заповнити всю матрицю ядра, потрібно провести квантовий експеримент для кожного елемента. Коли матриця ядра готова, її можна використовувати в багатьох класичних алгоритмах машинного навчання, що приймають pre-calculated kernels. Наприклад: qml_svc = SVC(kernel="precomputed"). Після цього можна застосувати класичні робочі процеси до тестових даних і отримати оцінку точності. Залежно від того, чи задовольняє нас точність, може знадобитися переглянути певні аспекти обчислення, зокрема відображення ознак.
Огляд уроку
У цьому уроці ми виконаємо ці кроки кількома способами, щоб оптимально використати твій час на реальних квантових комп'ютерах. Ми застосуємо квантовий метод ядра до:
- одного елемента матриці ядра для даних із відносно невеликою кількістю ознак на реальному бекенді, щоб легко стежити за тим, що відбувається на кожному кроці;
- цілого датасету з відносно невеликою кількістю ознак на симульованому бекенді, щоб побачити, як квантовий робочий процес поєднується з класичними методами машинного навчання;
- одного елемента матриці ядра для даних із великою кількістю ознак на реальному квантовому комп'ютері. Ми не оцінюватимемо всю матрицю ядра для великого датасету, щоб не витрачати зайвий час на квантових комп'ютерах IBM®.
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy pandas qiskit qiskit-ibm-runtime scikit-learn
# If you have not already, install scikit learn
#!pip install scikit-learn
Один елемент матриці ядра
Крок 1: Відображення класичних вхідних даних у квантову задачу
Розглянемо спочатку датасет із невеликою кількістю ознак, скажімо 10. Датасет може бути довільного розміру, оскільки ми обчислюємо елементи матриці ядра по одному. Нам потрібні хоча б дві точки, тому почнемо з цього (у наступному прикладі ми імпортуємо повний датасет). Імпортуємо кілька необхідних пакетів:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Two mock data points, including category labels, as in training
small_data = [
[-0.194, 0.114, -0.006, 0.301, -0.359, -0.088, -0.156, 0.342, -0.016, 0.143, 1],
[-0.1, 0.002, 0.244, 0.127, -0.064, -0.086, 0.072, 0.043, -0.053, 0.02, -1],
]
# Data points with labels removed, for inner product
train_data = [small_data[0][:-1], small_data[1][:-1]]
Можемо спробувати скористатися z_feature_map.
# from qiskit.circuit.library import zz_feature_map
# fm = zz_feature_map(feature_dimension=np.shape(train_data)[1], entanglement='linear', reps=1)
from qiskit.circuit.library import z_feature_map
fm = z_feature_map(feature_dimension=np.shape(train_data)[1])
unitary1 = fm.assign_parameters(train_data[0])
unitary2 = fm.assign_parameters(train_data[1])
Два унітарні оператори вище точно відповідають та , описаним у вступі. Ми можемо об'єднати їх за допомогою unitary_overlap. Як завжди, варто стежити за глибиною схеми.
from qiskit.circuit.library import unitary_overlap
overlap_circ = unitary_overlap(unitary1, unitary2)
overlap_circ.measure_all()
print("circuit depth = ", overlap_circ.decompose().depth())
overlap_circ.decompose().draw("mpl", scale=0.6, style="iqp")
circuit depth = 9
Крок 2: Оптимізація задачі для квантового виконання
Починаємо з вибору найменш завантаженого бекенду, після чого оптимізуємо схему для запуску на ньому.
# Import needed packages
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
# Get the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=fm.num_qubits
)
print(backend)
<IBMBackend('ibm_brisbane')>
# Apply level 3 optimization to our overlap circuit
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
overlap_ibm = pm.run(overlap_circ)
Для складних схем цей крок суттєво збільшує глибину схеми, оскільки відбувається відображення на нативні вентилі реальних квантових комп'ютерів і може знадобитися переміщення інформації між кубітами. У цьому простому випадку глибина майже не змінюється.
print("circuit depth = ", overlap_ibm.decompose().depth())
overlap_ibm.decompose().depth(lambda instr: len(instr.qubits) > 1)
circuit depth = 10
1
Крок 3: Виконання за допомогою Qiskit Runtime Primitives
Синтаксис запуску на симуляторі закоментовано нижче. Для цього датасету з невеликою кількістю ознак запуск на симуляторі ще є варіантом. Для обчислень у масштабі корисності симуляція зазвичай нежиттєздатна. Симулятори слід використовувати лише для налагодження коду у зменшеному масштабі.
# Run this for a simulator
# from qiskit.primitives import StatevectorSampler
# from qiskit_ibm_runtime import Options, Session, Sampler
# num_shots = 10000
# Evaluate the problem using state vector-based primitives from Qiskit
# sampler = StatevectorSampler()
# results = sampler.run([overlap_circ], shots=num_shots).result()
# .get_counts() returns counts associated with a state labeled by bit results such as |001101...01>.
# counts_bit = results[0].data.meas.get_counts()
# .get_int_counts returns the same counts, but labeled by integer equivalent of the above bit string.
# counts = results[0].data.meas.get_int_counts()
# Benchmarked on an Eagle processor, 7-11-24, took 4 sec.
# Import our runtime primitive
from qiskit_ibm_runtime import Session, SamplerV2 as Sampler
num_shots = 10000
# Use sampler and get the counts
sampler = Sampler(mode=backend)
results = sampler.run([overlap_ibm], shots=num_shots).result()
# .get_counts() returns counts associated with a state labeled by bit results such as |001101...01>.
counts_bit = results[0].data.meas.get_counts()
# .get_int_counts returns the same counts, but labeled by integer equivalent of the above bit string.
counts = results[0].data.meas.get_int_counts()
Крок 4: Постобробка, повернення результату у класичному форматі
Як описано у вступі, найкорисніше вимірювання тут — це ймовірність виміряти нульовий стан .
counts.get(0, 0.0) / num_shots
0.6525
Це саме той результат, який нам потрібен: оцінка внутрішнього добутку (з точністю до модуля в квадраті) між векторами, що відповідають двом точкам даних. Якщо хочеш подивитися на повний розподіл ймовірностей вимірювань (або квазіймовірностей), скористайся функцією plot_distribution, як показано нижче. Для великої кількості кубітів такі графіки швидко стають непридатними для сприйняття.
from qiskit.visualization import plot_distribution
plot_distribution(counts_bit)

Як альтернативу, можна визначити візуалізацію, як наведено нижче, щоб дивитися лише на 10 найімовірніших вимірювань. Це може бути корисно для налагодження або спроби отримати більше інтуїції щодо даних. Але ймовірність вимірювання нульового стану — це і є наш елемент матриці ядра.
def visualize_counts(probs, num_qubits):
"""Visualize the outputs from the Qiskit Sampler primitive."""
zero_prob = probs.get(0, 0.0)
top_10 = dict(sorted(probs.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]))
xvals, yvals = list(zip(*by_key.items()))
xvals = [bin(xval)[2:].zfill(num_qubits) for xval in xvals]
plt.bar(xvals, yvals)
plt.xticks(rotation=75)
plt.title("Results of sampling")
plt.xlabel("Measured bitstring")
plt.ylabel("Counts")
plt.show()
visualize_counts(counts, overlap_circ.num_qubits)
З цієї інформації про лише один внутрішній добуток між двома точками даних у просторі ознак вищої розмірності ми можемо лише сказати, що їх перекриття є досить великим порівняно з максимальним перекриттям (яке дорівнювало б 1.0). Це може свідчити про те, що ці дві точки певним чином схожі за природою і будуть класифіковані в один клас. Або ж це може означати, що наше відображення ознак неефективно відображає у простір, де схожі дані мають велике перекриття, а несхожі — мале. Щоб дізнатися, що саме вірно, потрібно застосувати відображення ознак до всього набору даних і перевірити, чи можна маніпулювати отриманою матрицею ядра для ефективного розділення класів із високою точністю.
Варто зазначити, що ми використовували z_feature_map, який дав малу транспілювану глибину по двохкубітних вентилях (глибина 1, фактично). Якщо твої схеми стають надто глибокими, це неминуче призведе до великого рівня шуму, і це зробить ймовірність виміряти нульовий стан дуже низькою, навіть якщо твоє відображення ознак добре узгоджується з даними. Наприклад, повторення описаного процесу з використанням zz_feature_map та , entanglement='linear', reps=1 дало dist.get(0,0.0) = 0.0015 для тих самих точок даних. Це пов'язано зі значно більшими глибинами схем і двохкубітними глибинами у zz_feature_map. На малюнку нижче показано розподіл імовірностей для цього обчислення.

Варто поекспериментувати з кількома точками даних з одного класу, щоб з'ясувати, наскільки малою має бути глибина для отримання хороших результатів. Наведені нижче поради є грубими і матимуть винятки. Загалом, транспільована двохкубітна глибина до 10 — жодної проблеми. Транспільована двохкубітна глибина 50–60 — на межі можливостей, і потребуватиме передових методів пом'якшення помилок та інших інструментів. Між цими крайнощами результати можуть варіюватися залежно від схожості даних, виразності відображення ознак, ширини схеми та інших факторів. Зазвичай крок постобробки також включає класичні процеси машинного навчання. У наступному розділі ми розширимо цей процес на весь датасет і покажемо класичний робочий процес машинного навчання.
Перевір своє розуміння
Прочитай питання нижче, подумай над відповідями, а потім натисни трикутники, щоб розкрити рішення.
У 10-кубітній квантовій схемі скільки загалом різних станів може бути виміряно?
Відповідь:
або 1024.
Припустимо, що хтось, хто тільки починає вивчати квантові обчислення, намагається використати квантову схему з дуже великою двохкубітною глибиною і не застосовує пом'якшення помилок. Припустимо також, що це призводить до рівня помилок 10% на кожному кубіті. Якщо справжній (без помилок) елемент матриці ядра для цієї схеми дуже великий, скажімо 1.0, якою буде ймовірність виміряти всі 10 кубітів у стані |0>?
Відповідь:
Імовірність правильного виявлення кожного кубіта у стані |0> дорівнює 0.90. Імовірність того, що всі 10 кубітів виявляться у правильному стані, становить або близько 35%.
Поясни своїми словами, чому так важливо стежити за глибиною схем. Це справедливо загалом, але поясни це в контексті оцінки квантового ядра.
Відповідь:
У цьому робочому процесі оцінки квантового ядра (QKE) наші оцінки базуються на вимірюваннях нульового стану, тобто стану, в якому кожен кубіт перебуває у стані . Дуже глибокі схеми вводять високий рівень помилок. Коли цей рівень помилок накопичується по багатьох кубітах, це суттєво знижує ймовірність виміряти нульовий стан.
Повна матриця ядра
У цьому розділі ми розширимо описаний вище процес на бінарну класифікацію повного датасету. Це додасть дві важливі складові: (1) тепер ми можемо реалізувати класичне машинне навчання в постобробці і (2) отримати оц інки точності для навчання.
Крок 1: Відображення класичних вхідних даних у квантову задачу
Тепер ми імпортуємо наявний датасет для класифікації. Цей датасет містить 128 рядків (точок даних) і 14 ознак для кожної точки. Є 15-й елемент, що вказує на бінарну категорію кожної точки (). Датасет імпортується нижче, або ти можеш отримати доступ до датасету і переглянути його структуру тут.
Ми використаємо перші 90 точок даних для навчання та наступні 30 точок для тестування.
!wget https://raw.githubusercontent.com/qiskit-community/prototype-quantum-kernel-training/main/data/dataset_graph7.csv
df = pd.read_csv("dataset_graph7.csv", sep=",", header=None)
# Prepare training data
train_size = 90
X_train = df.values[0:train_size, :-1]
train_labels = df.values[0:train_size, -1]
# Prepare testing data
test_size = 30
X_test = df.values[train_size : train_size + test_size, :-1]
test_labels = df.values[train_size : train_size + test_size, -1]
--2024-07-11 23:05:22-- https://raw.githubusercontent.com/qiskit-community/prototype-quantum-kernel-training/main/data/dataset_graph7.csv
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.109.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 49405 (48K) [text/plain]
Saving to: ‘dataset_graph7.csv.15’
dataset_graph7.csv. 100%[===================>] 48.25K --.-KB/s in 0.02s
2024-07-11 23:05:23 (2.11 MB/s) - ‘dataset_graph7.csv.15’ saved [49405/49405]
Підготуємо заздалегідь збереження кількох виводів, побудувавши матрицю ядра та тестову матрицю відповідних розмірів.
# Empty kernel matrix
num_samples = np.shape(X_train)[0]
kernel_matrix = np.full((num_samples, num_samples), np.nan)
test_matrix = np.full((test_size, num_samples), np.nan)
Тепер ми створюємо відображення ознак для кодування та відображення наших класичних даних у квантовій схемі. Ми можемо побудувати власне відображення ознак або скористатися готовим. Можеш вільно змінювати відображення ознак нижче або повернутися до ZFeatureMap. Але завжди звертай увагу на глибину схеми. Згадаємо, що в попередньому прикладі на 6 кубітах транспільована глибина схеми виявилася непридатною при використанні zz_feature_map. Зі збільшенням масштабу і складності схеми глибина може стрімко зростати до точки, де шум захльоснє наші результати. Якщо ти знаєш щось про структуру своїх даних, що може підказати, яка структура відображення ознак буде найкориснішою, — варто створити власне відображення ознак, що використовує це знання.
from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit
# Prepare feature map for computing overlap
num_features = np.shape(X_train)[1]
num_qubits = int(num_features / 2)
# To use a custom feature map use the lines below.
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)
Кроки 2 та 3: Оптимізація задачі та виконання за допомогою примітивів
Ми побудуємо схему перекриття, і якби ми запускали її на реальному квантовому комп'ютері в цьому прикладі, ми б оптимізували її для виконання, як і раніше. Але в цьому випадку ми маємо намір перебрати всі точки даних і обчислити повну матрицю ядра. Для кожної пари векторів даних та ми створюємо іншу схему перекриття. Отже, для кожної пари точок даних потрібна окрема оптимізація. Таким чином, кроки 2 та 3 виконуватимуться разом у кількох ітераціях.
Комірка коду нижче виконує рівно той самий процес, що й раніше, для однієї пари точок даних. Цього разу він просто виконується всередині двох циклів for, і є додатковий рядок наприкінці kernel_matrix[x_1,x_2] = ... для зберігання результатів кожного обчислення. Зверни увагу: ми скористалися симетрією матриці ядра, щоб скоротити кількість обчислень удвічі. Ми також просто встановили діагональні елементи рівними 1, оскільки саме такими вони мають бути за відсутності шуму. Залежно від реалізації та потрібної точності, діагональні елементи можна також використовувати для оцінки шуму або його вивчення з метою пом'якшення помилок.
Після повного заповнення матриці ядра ми повторюємо процес для тестових даних і заповнюємо test_matrix. Це насправді теж матриця ядра; ми просто даємо їй іншу назву для розрізнення.
# To use a simulator
from qiskit.primitives import StatevectorSampler
# Remember to insert your token in the QiskitRuntimeService constructor to use real quantum computers
# service = QiskitRuntimeService()
# backend = service.least_busy(
# operational=True, simulator=False, min_num_qubits=fm.num_qubits
# )
num_shots = 10000
# Evaluate the problem using state vector-based primitives from Qiskit.
sampler = StatevectorSampler()
for x1 in range(0, train_size):
for x2 in range(x1 + 1, train_size):
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()
# These lines run the qiskit sampler primitive.
counts = (
sampler.run([overlap_circ], shots=num_shots)
.result()[0]
.data.meas.get_int_counts()
)
# Assign the probability of the 0 state to the kernel matrix, and the transposed element (since this is an inner product)
kernel_matrix[x1, x2] = counts.get(0, 0.0) / num_shots
kernel_matrix[x2, x1] = counts.get(0, 0.0) / num_shots
# Fill in on-diagonal elements with 1, again, since this is an inner-product corresponding to probability (or alter the code to check these entries and verify they yield 1)
kernel_matrix[x1, x1] = 1
print("training done")
# Similar process to above, but for testing data.
for x1 in range(0, test_size):
for x2 in range(0, train_size):
unitary1 = fm.assign_parameters(list(X_test[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()
counts = (
sampler.run([overlap_circ], shots=num_shots)
.result()[0]
.data.meas.get_int_counts()
)
test_matrix[x1, x2] = counts.get(0, 0.0) / num_shots
print("test matrix done")
training done
test matrix done
Крок 4: Постобробка, повернення результату в класичному форматі
Тепер, коли ми маємо матрицю ядра та відформатовану аналогічним чином тестову матрицю test_matrix, отриману методами квантового ядра, ми можемо застосувати класичні алгоритми машинного навчання, щоб зробити прогнози щодо тестових даних і перевірити їхню точність. Почнемо з імпорту sklearn.svc зі Scikit-Learn — класифікатора на основі метода опорних векторів (SVC). Треба вказати, що ми хочемо використовувати наше попередньо обчислене ядро: kernel = precomputed.
# import a support vector classifier from a classical ML package.
from sklearn.svm import SVC
# Specify that you want to use a pre-computed kernel matrix
qml_svc = SVC(kernel="precomputed")
За допомогою SVC.fit можна передати матрицю ядра та мітки навчальних даних, щоб отримати підгонку моделі. SVC.score потім оцінить наші тестові дані відносно цієї підгонки, використовуючи test_matrix, і поверне нашу точність.
# Feed in the pre-computed matrix and the labels of the training data. The classical algorithm gives you a fit.
qml_svc.fit(kernel_matrix, train_labels)
# Now use the .score to test your data, using the matrix of test data, and test labels as your inputs.
qml_score_precomputed_kernel = qml_svc.score(test_matrix, test_labels)
print(f"Precomputed kernel classification test score: {qml_score_precomputed_kernel}")
Precomputed kernel classification test score: 1.0
Бачимо, що точність нашої навченої моделі склала 100%. Це чудово і доводить, що QKE може працювати. Але це дуже далеко від квантової переваги. Класичні ядра, мабуть, теж могли б розв'язати цю задачу класифікації зі 100% точністю. Ще багато роботи залишається з характеристикою різних типів і зв'язків даних, щоб зрозуміти, де квантові ядра будуть найкориснішими в нинішню еру корисності. Залишаємо тобі можливість змінити частини цього робочого процесу і вивчити ефективність різних квантових карт ознак. Ось кілька речей, які варто розглянути:
- Наскільки стійка точність? Чи зберігається вона для широкого спектра даних чи лише для цих конкретних навчальних даних?
- Яка структура твоїх даних змушує тебе думати, що квантова карта ознак буде корисною?
- Як точність змінюється зі збільшенням/зменшенням обсягу навчальних даних?
- Які карти ознак можна використовувати і як результати залежать від вибору карти?
- Як точність і час виконання залежать від збільшення кількості ознак?
- Які тенденції, якщо такі є, ти очікуєш спостерігати на реальних квантових комп'ютерах?
Масштабування до більшої кількості ознак і кубітів
У цьому розділі ми повторимо обчислення одного елемента матриці, але для значно більшої кількості ознак, намітивши шлях до масштабування до рівня корисності. Обмеження одним елементом матриці зроблено для того, щоб показати процес без надмірного використання виділеного часу на квантових комп'ютерах.