Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/zh-cn/quantum/tutorials/quantum_data.ipynb
25118 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

量子数据

本教程以 MNIST 教程中所做比较为基础构建,将探讨 Huang 等人近期就不同数据集如何影响性能比较而撰写的论文。在该文章中,作者试图了解经典机器学习模型如何以及何时能够达到(或优于)量子模型的学习性能。该文章还通过精心编制的数据集展示了经典机器学习模型与量子机器学习模型之间的经验性能差距。您将:

  1. 准备一个降维的 Fashion-MNIST 数据集。

  2. 使用量子电路重新标记数据集并计算投影量子内核 (PQK) 特征。

  3. 在重新标记的数据集上训练经典神经网络,并将性能与可以访问 PQK 特征的模型进行比较。

安装

!pip install tensorflow==2.7.0 tensorflow-quantum==0.7.2
WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip. Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue. To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
# 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

筛选数据集,仅保留 T 恤/上衣和连衣裙,移除其他类。同时,将标签 y 转换为布尔值:0 为 True,3 为 False。

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>
Image in a Jupyter notebook

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 特征

现在,您将通过合并量子组件并重新标记您在上面创建的截断 Fashion-MNIST 数据集来准备一个“不自然”的量子数据集。为了在量子方法与经典方法之间形成最大差距,您将首先准备 PQK 特征,然后根据它们的值重新标记输出。

2.1 量子编码和 PQK 特征

您将基于 x_trainy_trainx_testy_test 创建一组新特征,这些特征被定义为以下所有量子比特的一阶约化密度矩阵:

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

其中 U1qbU_\text{1qb} 为一套单量子比特旋转和 V(θ^)=eiiθi^(XiXi+1+YiYi+1+ZiZi+1)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))))
Image in a Jupyter notebook

接下来,您可以借助 tfq.util.exponential 来准备 V(θ^)V(\hat{\theta}),它可以对任何交换 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]
Image in a Jupyter notebook

现在,您已拥有组合完整编码电路所需的所有构建块:

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 中,这是一个形状为 [n_points, n_qubits, 3]tf.Tensorrdm[i][j][k] 中的条目 = ψiOPjkψi\langle \psi_i | OP^k_j | \psi_i \rangle ,其中 i 在数据点上构建索引,j 在量子比特上构建索引,k{X^,Y^,Z^}\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_pqkx_test_pqk 中准备好这些量子生成的特征,是时候重新标记数据集了。为了在量子性能与经典性能之间形成最大差距,您可以基于在 x_train_pqkx_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)

现在,您已拥有重新标记数据集所需的一切!您可以查阅流程图,以更好地了解如何在重新标记数据集时最大化性能差距:

为了最大化量子模型与经典模型之间的差距,您将尝试使用 S_pqk, V_pqkS_original, V_original 来最大化原始数据集与 PQK 特征内核矩阵 g(K1K2)=K2K11K2g(K_1 || K_2) = \sqrt{ || \sqrt{K_2} K_1^{-1} \sqrt{K_2} || _\infty} 之间的几何差异。较大的 gg 值可以确保您最初在流程图中向右移动时能够在量子用例中获得预测优势。

注:计算 ssdd 的数量对于更好地理解性能差距也非常有用。在本例中,确保较大的 gg 值便足以拉开性能差距。

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. 比较模型

现在,您已准备好数据集,是时候比较模型性能了。您将创建两个小型前馈神经网络并对它们在访问 x_train_pqk 中的 PQK 特征时的性能进行比较。

3.1 创建 PQK 增强模型

使用标准 tf.keras 库特征,您现在可以在 x_train_pqky_train_new 数据点上创建和训练模型:

#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_trainy_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>
Image in a Jupyter notebook

恭喜,您已改编出可在公平(但经人为干预)环境中有意击败经典模型的“不自然”的量子数据集。请尝试比较使用其他类型经典模型的结果。下一步,请尝试看看您能否找到无需自行改编,便可击败经典模型的新的有趣数据集!

4. 重要结论

您可以从本文以及 MNIST 实验中得出以下几项重要结论:

  1. 当今的量子模型基本不大可能会击败经典模型在经典数据上的性能。尤其是在当今可以拥有超过一百万个数据点的经典数据集上。

  2. 不能仅凭数据来自经典方式难以模拟的量子电路,就推断经典模型难以学习该数据。

  3. 无论使用哪种模型架构或训练算法,都一定存在量子模型易于学习而经典模型难以学习的数据集(最终本质上为量子)。