بيانات الكم

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

بناء الخروج من مقارنات في 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

تتمثل الخطوة الأولى في الحصول على مجموعة بيانات الموضة التقليدية. ويمكن القيام بذلك باستخدام 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 و False لمدة 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>

بي إن جي

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

ستقوم الآن بإعداد مجموعة بيانات كمومية "متقلبة" من خلال دمج المكونات الكمية وإعادة تسمية مجموعة بيانات MNIST الموضة المقتطعة التي قمت بإنشائها أعلاه. من أجل الحصول على أكبر قدر من الفصل بين الطرق الكمومية والكلاسيكية ، سوف تقوم أولاً بإعداد ميزات PQK ثم إعادة تسمية المخرجات بناءً على قيمها.

2.1 الترميز الكمي وميزات PQK

سوف تقوم بإنشاء مجموعة جديدة من الميزات، على أساس x_train ، y_train ، x_test و y_test التي تم تعريفها ليكون-RDM 1 على كافة المكدسة من:

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

حيث \(U_\text{1qb}\) هو جدار من التناوب و qubit واحدة و \(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

قد يكون التحقق من هذه الدائرة أصعب قليلاً من خلال النظر إليها ، ولكن لا يزال بإمكانك فحص حالة مكونة من 2 كيوبت لمعرفة ما يحدث:

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 على أساس-RDM 1 من الدوائر بيانات أعلاه وتخزين النتائج في 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. مقارنة النماذج

الآن بعد أن أعددت مجموعة البيانات الخاصة بك ، حان الوقت لمقارنة أداء النموذج. سوف تقوم بإنشاء شبكتي ال feedforward الصغيرة ومقارنة الأداء عندما تعطى الوصول إلى 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>

بي إن جي

4. استنتاجات مهمة

هناك العديد من النتائج الهامة التي يمكن استخلاصها من هذا و MNIST التجارب:

  1. من غير المحتمل جدًا أن تتفوق النماذج الكمومية اليوم على أداء النموذج الكلاسيكي في البيانات الكلاسيكية. خاصة في مجموعات البيانات الكلاسيكية الحالية التي يمكن أن تحتوي على ما يزيد عن مليون نقطة بيانات.

  2. فقط لأن البيانات قد تأتي من دائرة كمومية يصعب محاكاتها بشكل كلاسيكي ، لا يجعل بالضرورة من الصعب تعلم البيانات لنموذج كلاسيكي.

  3. توجد مجموعات البيانات (في النهاية الكمومية في الطبيعة) التي يسهل على النماذج الكمومية تعلمها ويصعب على النماذج الكلاسيكية تعلمها ، بغض النظر عن بنية النموذج أو خوارزميات التدريب المستخدمة.