Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/ja/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 et al. の最近の研究を説明します。この研究において、著者は古典的な機械学習モデルが量子モデルと同様に(またはそれ以上に)学習できる仕組みとそのタイミングについて理解しようとしています。この研究ではまた、慎重に作成されたデータセットによる、古典的機械学習モデルと量子機械学習モデルの経験的なパフォーマンスの分離についても示しています。ここでは、以下のことを行います。

  1. 次元を縮小した Fashion-MNIST データセットを準備します。

  2. 量子回路を使用して、データセットのラベルを付け直し、投影量子カーネル(PQK: Projected Quantum Kernel)の特徴量を計算します。

  3. ラベルの付け直しを行ったデータセットで古典ニューラルネットワークをトレーニングし、そのパフォーマンスを PQK 特徴量にアクセスできるモデルのパフォーマンスと比較します。

MNIST モデルをビルドする

!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 をブール型に変換し、True を 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>
Image in a Jupyter notebook

1.2 画像を縮小する

MNIST の例と同様に、現在の量子コンピューターの境界内に収まるように、これらの画像を縮小する必要があります。ただし、今回は、tf.image.resize 演算ではなく PCA 変換を使用して寸法を縮小します。

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 データセットのラベルを付け直して、STILT を適用した量子データセットを準備します。量子メソッドと古典メソッドの分離を最大限にするために、先に PQK 特徴量を準備してから、出力の値に基づいてラベルの付け直しを行います。

2.1 量子エンコーディングと PQK 特徴量

x_trainy_trainx_testy_test に基づいて、以下のすべての量子ビットで 1-RDM となるように定義された新しい特徴量セットを作成します。

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

次に、行き来するあらゆる cirq.PauliSum オブジェクトを指数関数化できる tfq.util.exponential を使用して、V(θ^)V(\hat{\theta}) を準備します。

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]
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)

次に、上記のデータセット回路の 1-RDM に基づいて PQK を計算し、その結果を、[n_points, n_qubits, 3] の形状を持つ tf.Tensor である rdm に格納します。エントリは rdm[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_pqk and x_test_pqk に量子で生成した特徴量を用意できたので、データセットのラベルを付け直すことにしましょう。量子パフォーマンスと古典パフォーマンスの最大限の分離を達成するには、x_train_pqkx_test_pqk にあるスペクトル情報に基づいて、データセットのラベルを付け直します。

注意: 古典モデルと量子モデルの間のパフォーマンスの分離を明示的に最大化するためのデータセットの準備は、ごまかしのように感じるかもしれませんが、古典的なコンピューターにとっては難しく、量子コンピューターにとってはモデル化が容易なデータセットに対して、非常に重要な存在証明を得られます。最初にこのようなものを作成してメリットを実証できなければ、QML で量子のメリットを探る意味がありません。

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(K1K2)=K2K11K2g(K_1 || K_2) = \sqrt{ || \sqrt{K_2} K_1^{-1} \sqrt{K_2} || _\infty} の幾何学的な違いを、S_pqk, V_pqkS_original, V_original を使って最大化するように試みます。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. モデルを比較する

データセットの準備ができたので、モデルのパフォーマンスを比較することにしましょう。2 つの小さなフィードフォワードニューラルネットワークを作成し、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 古典モデルを作成する

上記のコードと同様に、STILT データセットの 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 パフォーマンスを比較する

2 つのモデルをトレーニングしたので、次は 2 つのモデルの検証データのパフォーマンスのギャップを素早くプロットしましょう。通常、いずれのモデルもトレーニングデータでは > 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

成功: 公正な(ただしわざと行われた)設定で意図的に古典モデルを打ち負かす STILT 量子データセットを作成しました。他の種類の古典モデルを使用して結果を比較して見てください。次のステップでは、自分で作成しなくても、古典モデルを打ち負かせる新しい興味深いデータセットを見つけられるか試してみましょう!

4. 重要な結論

この実験と MNIST の実験から導き出せる重要な結論がいくつかあります。

  1. 古典的なデータにおいて、現在の量子モデルが古典モデルのパフォーマンスを超すことはあまりあり得ません。100 万件のデータポイントを持てる現在の古典データセットであれば、尚さら困難と言えます。

  2. 古典的なシミュレーションが困難な量子回路からデータを得たからと言って、必ずしも古典モデルにおいて、データの学習が困難になるというわけではありません。

  3. どんなモデルアーキテクチャやトレーニングアルゴリズムを使用したとしても、量子モデルにとって学習しやすく、古典モデルにとって学習しにくいデータセット(性質上最終的に量子)は実際に存在します。