داده های کوانتومی

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

ساختمان را از مقایسه های انجام شده در MNIST آموزش، این آموزش به بررسی اثر اخیر هوانگ و همکاران که نشان می دهد چگونه مجموعه داده های مختلف بر مقایسه عملکرد تأثیر می گذارد. در این کار، نویسندگان به دنبال درک این هستند که چگونه و چه زمانی مدل‌های یادگیری ماشین کلاسیک می‌توانند به خوبی (یا بهتر از) مدل‌های کوانتومی یاد بگیرند. این کار همچنین یک جداسازی عملکرد تجربی بین مدل یادگیری ماشین کلاسیک و کوانتومی را از طریق یک مجموعه داده با دقت ساخته شده به نمایش می‌گذارد. شما:

  1. یک مجموعه داده با ابعاد کاهش یافته Fashion-MNIST آماده کنید.
  2. از مدارهای کوانتومی برای برچسب گذاری مجدد مجموعه داده و محاسبه ویژگی های هسته کوانتومی پیش بینی شده (PQK) استفاده کنید.
  3. یک شبکه عصبی کلاسیک را بر روی مجموعه داده های برچسب گذاری شده مجدد آموزش دهید و عملکرد را با مدلی مقایسه کنید که به ویژگی های PQK دسترسی دارد.

برپایی

pip install tensorflow==2.4.1 tensorflow-quantum
# Update package resources to account for version changes.
import importlib, pkg_resources
importlib.reload(pkg_resources)
import cirq
import sympy
import numpy as np
import tensorflow as tf
import tensorflow_quantum as tfq

# visualization tools
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit
np.random.seed(1234)

1. آماده سازی داده ها

شما با آماده کردن مجموعه داده های fashion-MNIST برای اجرا بر روی یک کامپیوتر کوانتومی شروع خواهید کرد.

1.1 دانلود fashion-MNIST

اولین قدم این است که مجموعه داده سنتی fashion-mnist را دریافت کنید. این را می توان با استفاده از انجام tf.keras.datasets ماژول.

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.fashion_mnist.load_data()

# Rescale the images from [0,255] to the [0.0,1.0] range.
x_train, x_test = x_train/255.0, x_test/255.0

print("Number of original training examples:", len(x_train))
print("Number of original test examples:", len(x_test))
Number of original training examples: 60000
Number of original test examples: 10000

مجموعه داده را فیلتر کنید تا فقط تی‌شرت‌ها/تاپ‌ها و لباس‌ها را نگه دارید، کلاس‌های دیگر را حذف کنید. در همان زمان تبدیل به برچسب، y ، به منطقی: واقعی برای 0 و غلط برای 3.

def filter_03(x, y):
    keep = (y == 0) | (y == 3)
    x, y = x[keep], y[keep]
    y = y == 0
    return x,y
x_train, y_train = filter_03(x_train, y_train)
x_test, y_test = filter_03(x_test, y_test)

print("Number of filtered training examples:", len(x_train))
print("Number of filtered test examples:", len(x_test))
Number of filtered training examples: 12000
Number of filtered test examples: 2000
print(y_train[0])

plt.imshow(x_train[0, :, :])
plt.colorbar()
True
<matplotlib.colorbar.Colorbar at 0x7f6db42c3460>

png

1.2 مقیاس تصاویر را کاهش دهید

درست مانند مثال MNIST، برای اینکه در محدوده کامپیوترهای کوانتومی فعلی قرار بگیرید، باید مقیاس این تصاویر را کاهش دهید. این بار با این حال شما یک تحول PCA به منظور کاهش ابعاد به جای یک استفاده tf.image.resize عملیات.

def truncate_x(x_train, x_test, n_components=10):
  """Perform PCA on image dataset keeping the top `n_components` components."""
  n_points_train = tf.gather(tf.shape(x_train), 0)
  n_points_test = tf.gather(tf.shape(x_test), 0)

  # Flatten to 1D
  x_train = tf.reshape(x_train, [n_points_train, -1])
  x_test = tf.reshape(x_test, [n_points_test, -1])

  # Normalize.
  feature_mean = tf.reduce_mean(x_train, axis=0)
  x_train_normalized = x_train - feature_mean
  x_test_normalized = x_test - feature_mean

  # Truncate.
  e_values, e_vectors = tf.linalg.eigh(
      tf.einsum('ji,jk->ik', x_train_normalized, x_train_normalized))
  return tf.einsum('ij,jk->ik', x_train_normalized, e_vectors[:,-n_components:]), \
    tf.einsum('ij,jk->ik', x_test_normalized, e_vectors[:, -n_components:])
DATASET_DIM = 10
x_train, x_test = truncate_x(x_train, x_test, n_components=DATASET_DIM)
print(f'New datapoint dimension:', len(x_train[0]))
New datapoint dimension: 10

آخرین مرحله کاهش اندازه مجموعه داده به تنها 1000 نقطه داده آموزشی و 200 نقطه داده آزمایشی است.

N_TRAIN = 1000
N_TEST = 200
x_train, x_test = x_train[:N_TRAIN], x_test[:N_TEST]
y_train, y_test = y_train[:N_TRAIN], y_test[:N_TEST]
print("New number of training examples:", len(x_train))
print("New number of test examples:", len(x_test))
New number of training examples: 1000
New number of test examples: 200

2. برچسب گذاری مجدد و محاسبه ویژگی های PQK

اکنون یک مجموعه داده کوانتومی "stilted" را با ترکیب اجزای کوانتومی و برچسب گذاری مجدد مجموعه داده های کوتاه شده fashion-MNIST که در بالا ایجاد کرده اید، آماده خواهید کرد. برای به دست آوردن بیشترین تفکیک بین روش‌های کوانتومی و کلاسیک، ابتدا ویژگی‌های PQK را آماده کرده و سپس خروجی‌ها را بر اساس مقادیر آنها برچسب‌گذاری مجدد می‌کنید.

2.1 رمزگذاری کوانتومی و ویژگی های PQK

شما یک مجموعه ای جدید از ویژگی های ایجاد، بر اساس x_train ، y_train ، x_test و y_test است که تعریف می شود 1-RDM در تمام کیوبیت از:

\(V(x_{\text{train} } / n_{\text{trotter} }) ^ {n_{\text{trotter} } } U_{\text{1qb} } | 0 \rangle\)

که در آن \(U_\text{1qb}\) یک دیوار از چرخش کیوبیت و \(V(\hat{\theta}) = e^{-i\sum_i \hat{\theta_i} (X_i X_{i+1} + Y_i Y_{i+1} + Z_i Z_{i+1})}\)

ابتدا، می توانید دیوار چرخش های تک کیوبیت را ایجاد کنید:

def single_qubit_wall(qubits, rotations):
  """Prepare a single qubit X,Y,Z rotation wall on `qubits`."""
  wall_circuit = cirq.Circuit()
  for i, qubit in enumerate(qubits):
    for j, gate in enumerate([cirq.X, cirq.Y, cirq.Z]):
      wall_circuit.append(gate(qubit) ** rotations[i][j])

  return wall_circuit

با نگاه کردن به مدار، می‌توانید به سرعت تأیید کنید که این کار می‌کند:

SVGCircuit(single_qubit_wall(
    cirq.GridQubit.rect(1,4), np.random.uniform(size=(4, 3))))

svg

بعد شما می توانید آماده \(V(\hat{\theta})\) با کمک tfq.util.exponential که می تواند exponentiate هر رفت و آمد cirq.PauliSum اشیاء:

def v_theta(qubits):
  """Prepares a circuit that generates V(\theta)."""
  ref_paulis = [
      cirq.X(q0) * cirq.X(q1) + \
      cirq.Y(q0) * cirq.Y(q1) + \
      cirq.Z(q0) * cirq.Z(q1) for q0, q1 in zip(qubits, qubits[1:])
  ]
  exp_symbols = list(sympy.symbols('ref_0:'+str(len(ref_paulis))))
  return tfq.util.exponential(ref_paulis, exp_symbols), exp_symbols

بررسی این مدار ممکن است با نگاه کردن به آن کمی سخت‌تر باشد، اما همچنان می‌توانید یک مورد دو کیوبیتی را بررسی کنید تا ببینید چه اتفاقی می‌افتد:

test_circuit, test_symbols = v_theta(cirq.GridQubit.rect(1, 2))
print(f'Symbols found in circuit:{test_symbols}')
SVGCircuit(test_circuit)
Symbols found in circuit:[ref_0]

svg

اکنون تمام بلوک‌های ساختمانی مورد نیاز برای قرار دادن مدارهای رمزگذاری کامل خود را در اختیار دارید:

def prepare_pqk_circuits(qubits, classical_source, n_trotter=10):
  """Prepare the pqk feature circuits around a dataset."""
  n_qubits = len(qubits)
  n_points = len(classical_source)

  # Prepare random single qubit rotation wall.
  random_rots = np.random.uniform(-2, 2, size=(n_qubits, 3))
  initial_U = single_qubit_wall(qubits, random_rots)

  # Prepare parametrized V
  V_circuit, symbols = v_theta(qubits)
  exp_circuit = cirq.Circuit(V_circuit for t in range(n_trotter))

  # Convert to `tf.Tensor`
  initial_U_tensor = tfq.convert_to_tensor([initial_U])
  initial_U_splat = tf.tile(initial_U_tensor, [n_points])

  full_circuits = tfq.layers.AddCircuit()(
      initial_U_splat, append=exp_circuit)
  # Replace placeholders in circuits with values from `classical_source`.
  return tfq.resolve_parameters(
      full_circuits, tf.convert_to_tensor([str(x) for x in symbols]),
      tf.convert_to_tensor(classical_source*(n_qubits/3)/n_trotter))

چند کیوبیت را انتخاب کنید و مدارهای رمزگذاری داده را آماده کنید:

qubits = cirq.GridQubit.rect(1, DATASET_DIM + 1)
q_x_train_circuits = prepare_pqk_circuits(qubits, x_train)
q_x_test_circuits = prepare_pqk_circuits(qubits, x_test)

بعد، محاسبه PQK ویژگی های در 1-RDM از مدارهای مجموعه داده فوق بر اساس و ذخیره نتایج در rdm ، یک tf.Tensor با شکل [n_points, n_qubits, 3] . این نوشته را در rdm[i][j][k] = \(\langle \psi_i | OP^k_j | \psi_i \rangle\) که در آن i شاخص بیش از datapoints، j شاخص بیش از کیوبیت و k شاخص بیش از \(\lbrace \hat{X}, \hat{Y}, \hat{Z} \rbrace\) .

def get_pqk_features(qubits, data_batch):
  """Get PQK features based on above construction."""
  ops = [[cirq.X(q), cirq.Y(q), cirq.Z(q)] for q in qubits]
  ops_tensor = tf.expand_dims(tf.reshape(tfq.convert_to_tensor(ops), -1), 0)
  batch_dim = tf.gather(tf.shape(data_batch), 0)
  ops_splat = tf.tile(ops_tensor, [batch_dim, 1])
  exp_vals = tfq.layers.Expectation()(data_batch, operators=ops_splat)
  rdm = tf.reshape(exp_vals, [batch_dim, len(qubits), -1])
  return rdm
x_train_pqk = get_pqk_features(qubits, q_x_train_circuits)
x_test_pqk = get_pqk_features(qubits, q_x_test_circuits)
print('New PQK training dataset has shape:', x_train_pqk.shape)
print('New PQK testing dataset has shape:', x_test_pqk.shape)
New PQK training dataset has shape: (1000, 11, 3)
New PQK testing dataset has shape: (200, 11, 3)

2.2 برچسب زدن مجدد بر اساس ویژگی های PQK

حالا که شما این ویژگی کوانتومی تولید شده در x_train_pqk و x_test_pqk ، آن هم به دوباره برچسب مجموعه داده است. برای رسیدن به حداکثر جدائی بین کوانتومی و عملکرد کلاسیک شما می توانید دوباره برچسب مجموعه داده را بر اساس اطلاعات طیف در یافت x_train_pqk و x_test_pqk .

def compute_kernel_matrix(vecs, gamma):
  """Computes d[i][j] = e^ -gamma * (vecs[i] - vecs[j]) ** 2 """
  scaled_gamma = gamma / (
      tf.cast(tf.gather(tf.shape(vecs), 1), tf.float32) * tf.math.reduce_std(vecs))
  return scaled_gamma * tf.einsum('ijk->ij',(vecs[:,None,:] - vecs) ** 2)

def get_spectrum(datapoints, gamma=1.0):
  """Compute the eigenvalues and eigenvectors of the kernel of datapoints."""
  KC_qs = compute_kernel_matrix(datapoints, gamma)
  S, V = tf.linalg.eigh(KC_qs)
  S = tf.math.abs(S)
  return S, V
S_pqk, V_pqk = get_spectrum(
    tf.reshape(tf.concat([x_train_pqk, x_test_pqk], 0), [-1, len(qubits) * 3]))

S_original, V_original = get_spectrum(
    tf.cast(tf.concat([x_train, x_test], 0), tf.float32), gamma=0.005)

print('Eigenvectors of pqk kernel matrix:', V_pqk)
print('Eigenvectors of original kernel matrix:', V_original)
Eigenvectors of pqk kernel matrix: tf.Tensor(
[[-2.09569391e-02  1.05973557e-02  2.16634180e-02 ...  2.80352887e-02
   1.55521873e-02  2.82677952e-02]
 [-2.29303762e-02  4.66355234e-02  7.91163836e-03 ... -6.14174758e-04
  -7.07804322e-01  2.85902526e-02]
 [-1.77853629e-02 -3.00758495e-03 -2.55225878e-02 ... -2.40783971e-02
   2.11018627e-03  2.69009806e-02]
 ...
 [ 6.05797209e-02  1.32483775e-02  2.69536003e-02 ... -1.38843581e-02
   3.05043962e-02  3.85345481e-02]
 [ 6.33309558e-02 -3.04112374e-03  9.77444276e-03 ...  7.48321265e-02
   3.42793856e-03  3.67484428e-02]
 [ 5.86028099e-02  5.84433973e-03  2.64811981e-03 ...  2.82612257e-02
  -3.80136147e-02  3.29943895e-02]], shape=(1200, 1200), dtype=float32)
Eigenvectors of original kernel matrix: tf.Tensor(
[[ 0.03835681  0.0283473  -0.01169789 ...  0.02343717  0.0211248
   0.03206972]
 [-0.04018159  0.00888097 -0.01388255 ...  0.00582427  0.717551
   0.02881948]
 [-0.0166719   0.01350376 -0.03663862 ...  0.02467175 -0.00415936
   0.02195409]
 ...
 [-0.03015648 -0.01671632 -0.01603392 ...  0.00100583 -0.00261221
   0.02365689]
 [ 0.0039777  -0.04998879 -0.00528336 ...  0.01560401 -0.04330755
   0.02782002]
 [-0.01665728 -0.00818616 -0.0432341  ...  0.00088256  0.00927396
   0.01875088]], shape=(1200, 1200), dtype=float32)

اکنون همه چیزهایی را که برای برچسب گذاری مجدد مجموعه داده نیاز دارید در اختیار دارید! اکنون می توانید برای درک بهتر نحوه به حداکثر رساندن جداسازی عملکرد هنگام برچسب گذاری مجدد مجموعه داده، با فلوچارت مشورت کنید:

به منظور به حداکثر رساندن جدائی بین کوانتومی و مدل های کلاسیک، شما تلاش خواهد کرد برای به حداکثر رساندن تفاوت هندسی بین مجموعه داده اصلی و PQK ویژگی های هسته ماتریس \(g(K_1 || K_2) = \sqrt{ || \sqrt{K_2} K_1^{-1} \sqrt{K_2} || _\infty}\) با استفاده از S_pqk, V_pqk و S_original, V_original . مقدار زیادی از \(g\) تضمین می کند که شما در ابتدا به سمت راست در پایین فلوچارت به سمت یک مزیت پیش بینی در مورد کوانتومی حرکت می کند.

def get_stilted_dataset(S, V, S_2, V_2, lambdav=1.1):
  """Prepare new labels that maximize geometric distance between kernels."""
  S_diag = tf.linalg.diag(S ** 0.5)
  S_2_diag = tf.linalg.diag(S_2 / (S_2 + lambdav) ** 2)
  scaling = S_diag @ tf.transpose(V) @ \
            V_2 @ S_2_diag @ tf.transpose(V_2) @ \
            V @ S_diag

  # Generate new lables using the largest eigenvector.
  _, vecs = tf.linalg.eig(scaling)
  new_labels = tf.math.real(
      tf.einsum('ij,j->i', tf.cast(V @ S_diag, tf.complex64), vecs[-1])).numpy()
  # Create new labels and add some small amount of noise.
  final_y = new_labels > np.median(new_labels)
  noisy_y = (final_y ^ (np.random.uniform(size=final_y.shape) > 0.95))
  return noisy_y
y_relabel = get_stilted_dataset(S_pqk, V_pqk, S_original, V_original)
y_train_new, y_test_new = y_relabel[:N_TRAIN], y_relabel[N_TRAIN:]

3. مقایسه مدل ها

اکنون که مجموعه داده خود را آماده کرده اید، زمان مقایسه عملکرد مدل فرا رسیده است. شما دو شبکه عصبی پیشخور کوچک ایجاد و مقایسه عملکرد در هنگام به آنها داده شده دسترسی به PQK ویژگی های در یافت x_train_pqk .

3.1 مدل پیشرفته PQK را ایجاد کنید

با استفاده از استاندارد tf.keras ویژگی های کتابخانه شما هم اکنون می توانید ایجاد و یک قطار یک مدل در x_train_pqk و y_train_new datapoints:

#docs_infra: no_execute
def create_pqk_model():
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Dense(32, activation='sigmoid', input_shape=[len(qubits) * 3,]))
    model.add(tf.keras.layers.Dense(16, activation='sigmoid'))
    model.add(tf.keras.layers.Dense(1))
    return model

pqk_model = create_pqk_model()
pqk_model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              optimizer=tf.keras.optimizers.Adam(learning_rate=0.003),
              metrics=['accuracy'])

pqk_model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 32)                1088      
_________________________________________________________________
dense_1 (Dense)              (None, 16)                528       
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 17        
=================================================================
Total params: 1,633
Trainable params: 1,633
Non-trainable params: 0
_________________________________________________________________
#docs_infra: no_execute
pqk_history = pqk_model.fit(tf.reshape(x_train_pqk, [N_TRAIN, -1]),
          y_train_new,
          batch_size=32,
          epochs=1000,
          verbose=0,
          validation_data=(tf.reshape(x_test_pqk, [N_TEST, -1]), y_test_new))

3.2 یک مدل کلاسیک ایجاد کنید

مشابه کد بالا، اکنون می‌توانید یک مدل کلاسیک نیز ایجاد کنید که به ویژگی‌های PQK در مجموعه داده‌های استیل دار شما دسترسی ندارد. این مدل می تواند با استفاده از آموزش دیده x_train و y_label_new .

#docs_infra: no_execute
def create_fair_classical_model():
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Dense(32, activation='sigmoid', input_shape=[DATASET_DIM,]))
    model.add(tf.keras.layers.Dense(16, activation='sigmoid'))
    model.add(tf.keras.layers.Dense(1))
    return model

model = create_fair_classical_model()
model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              optimizer=tf.keras.optimizers.Adam(learning_rate=0.03),
              metrics=['accuracy'])

model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_3 (Dense)              (None, 32)                352       
_________________________________________________________________
dense_4 (Dense)              (None, 16)                528       
_________________________________________________________________
dense_5 (Dense)              (None, 1)                 17        
=================================================================
Total params: 897
Trainable params: 897
Non-trainable params: 0
_________________________________________________________________
#docs_infra: no_execute
classical_history = model.fit(x_train,
          y_train_new,
          batch_size=32,
          epochs=1000,
          verbose=0,
          validation_data=(x_test, y_test_new))

3.3 مقایسه عملکرد

اکنون که دو مدل را آموزش داده‌اید، می‌توانید به سرعت شکاف‌های عملکردی در داده‌های اعتبارسنجی بین این دو را رسم کنید. به طور معمول هر دو مدل به دقت بیش از 0.9 در داده های آموزشی دست خواهند یافت. با این حال، در داده‌های اعتبارسنجی مشخص می‌شود که فقط اطلاعات موجود در ویژگی‌های PQK برای تعمیم مدل به نمونه‌های دیده نشده کافی است.

#docs_infra: no_execute
plt.figure(figsize=(10,5))
plt.plot(classical_history.history['accuracy'], label='accuracy_classical')
plt.plot(classical_history.history['val_accuracy'], label='val_accuracy_classical')
plt.plot(pqk_history.history['accuracy'], label='accuracy_quantum')
plt.plot(pqk_history.history['val_accuracy'], label='val_accuracy_quantum')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
<matplotlib.legend.Legend at 0x7f6d846ecee0>

png

4. نتیجه گیری مهم

چند نتیجه مهم شما می توانید از این و قرعه کشی وجود دارد MNIST آزمایش:

  1. خیلی بعید است که مدل های کوانتومی امروزی عملکرد مدل کلاسیک را بر روی داده های کلاسیک شکست دهند. به خصوص در مجموعه داده های کلاسیک امروزی که می توانند بیش از یک میلیون نقطه داده داشته باشند.

  2. فقط به این دلیل که داده ها ممکن است از یک مدار کوانتومی شبیه سازی کلاسیک سخت به دست بیایند، لزوما یادگیری داده ها را برای یک مدل کلاسیک سخت نمی کند.

  3. مجموعه‌های داده (در نهایت ماهیت کوانتومی) که یادگیری آن‌ها برای مدل‌های کوانتومی آسان و یادگیری مدل‌های کلاسیک دشوار است، بدون توجه به معماری مدل یا الگوریتم‌های آموزشی مورد استفاده، وجود دارند.