Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/pt-br/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.

Dados quânticos

Com base nas comparações feitas no tutorial MNIST, este tutorial explora o artigo recente de Huang et al. que mostra como diferentes datasets afetam as comparações de desempenho. No trabalho, os autores buscam entender como e quando os modelos de aprendizado de máquina clássicos podem aprender tão bem quanto (ou melhor que) os modelos quânticos. O trabalho também apresenta uma separação de desempenho empírica entre o modelo de aprendizado de máquina clássico e quântico através de um dataset cuidadosamente elaborado. Você fará o seguinte:

  1. Preparará um dataset Fashion-MNIST de dimensões reduzidas.

  2. Usará circuitos quânticos para rotular novamente o dataset e computar características de Kernels Quânticos Projetados (PQK).

  3. Treinará uma rede neural clássica com o dataset rotulado novamente e comparará o desempenho com um modelo que tem acesso a características de PQK.

Configuração

!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. Preparação de dados

Você começará preparando o dataset Fashion-MNIST para a execução em um computador quântico.

1.1 Baixe o Fashion-MNIST

O primeiro passo é obter o dataset fashion-mnist tradicional. Isso pode ser feito usando o módulo 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

Filtre o dataset para manter apenas "t-shirts/tops" (camisetas/blusas) e "dresses" (vestidos), removendo as outras classes. Ao mesmo tempo, converta o rótulo, y, para booleano: True para 0 e False para 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 Reduza a escala das imagens

Como no exemplo MNIST, você precisará reduzir a escala dessas imagens para que fiquem dentro dos limites para computadores quânticos atuais. Desta vez, porém, você usará uma transformação de PCA para diminuir as dimensões, em vez de uma operação 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

O último passo é reduzir o tamanho do dataset para apenas 1000 pontos de dados de treinamento e 200 pontos de dados de teste.

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. Rotule novamente e compute as características de PQK

Agora, você preparará um dataset quântico "artificial" ao incorporar componentes quânticos e rotular novamente o dataset Fashion-MNIST truncado que foi criado acima. Para obter a maior separação entre os métodos quântico e clássico, primeiro você preparará as características de PQK e depois rotulará novamente as saídas com base nos valores delas.

2.1 Encoding quântico e características de PQK

Você criará um novo conjunto de características, com base no x_train, y_train, x_test e y_test que é definido como a 1-RDM em todos os qubits de:

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

Em que U1qbU_\text{1qb} é uma parede de rotações de um único qubit e 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})}

Primeiro, você pode gerar a parede de rotações de um único qubit:

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

É possível verificar rapidamente se isso funciona ao analisar o circuito:

SVGCircuit(single_qubit_wall( cirq.GridQubit.rect(1,4), np.random.uniform(size=(4, 3))))
Image in a Jupyter notebook

Em seguida, você pode preparar V(θ^)V(\hat{\theta}) com a ajuda de tfq.util.exponential, que consegue exponenciar qualquer objeto cirq.PauliSum comutativo:

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

Pode ser um pouco mais difícil de verificar esse circuito apenas olhando para ele, mas você ainda pode examinar um caso de dois qubits para ver o que está acontecendo:

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

Agora, você tem todos os elementos básicos necessários para colocar seus circuitos de encoding juntos:

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

Escolha alguns qubits e prepare os circuitos de encoding de dados:

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)

Em seguida, compute as características de PQK com base na 1-RDM dos circuitos do dataset acima e armazene os resultados em rdm, um tf.Tensor com formato [n_points, n_qubits, 3]. As entradas em rdm[i][j][k] = ψiOPjkψi\langle \psi_i | OP^k_j | \psi_i \rangle, em que i indexa sobre pontos de dados, j indexa sobre qubits e k indexa sobre {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 Rotule novamente com base nas características de PQK

Agora que você tem essas características geradas com quântica em x_train_pqk e x_test_pqk, é hora de rotular novamente o dataset. Para obter a separação máxima entre o desempenho quântico e clássico, você pode rotular novamente o dataset com base nas informações de espectro encontradas em x_train_pqk e x_test_pqk.

Observação: essa preparação do dataset para maximizar explicitamente a separação no desempenho entre os modelos clássico e quântico pode parecer trapaça, mas oferece uma prova de existência bastante importante para datasets de modelagem difícil em computadores clássicos e fácil em computadores quânticos. Não haveria razão em buscar a vantagem quântica em QML se não posse possível criar primeiro algo assim para demonstrar a vantagem.

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)

Você já tem tudo o que precisa para rotular novamente o dataset! Agora pode consultar o fluxograma para entender melhor como maximizar a separação de desempenho com essa nova rotulagem:

Para maximizar a separação entre os modelos quântico e clássico, tente maximizar a diferença geométrica entre o dataset original e as matrizes de kernels para características de PQK g(K1K2)=K2K11K2g(K_1 || K_2) = \sqrt{ || \sqrt{K_2} K_1^{-1} \sqrt{K_2} || _\infty} usando S_pqk, V_pqk e S_original, V_original. Um valor gg grande garante que você se mova inicialmente para a direita no fluxograma descendo em direção a uma vantagem de precisão no caso quântico.

Observação: a computação de quantidades para ss e dd também é bastante útil ao tentar entender melhor as separações de desempenho. Nesse caso, garantir um valor gg grande é o suficiente para ver a separação de desempenho.

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. Compare os modelos

Depois de preparar o dataset, é hora de comparar o desempenho dos modelos. Crie duas pequenas redes feedforward e compare o desempenho quando elas recebem acesso às características de PQK encontradas em x_train_pqk.

3.1 Crie o modelo otimizado de PQK

Usando os recursos da biblioteca tf.keras padrão, você pode criar e treinar um modelo com os pontos de dados x_train_pqk e y_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 Crie um modelo clássico

De maneira semelhante ao código acima, agora também é possível criar um modelo clássico que não tem acesso às características de PQK no seu dataset artificial. Esse modelo pode ser treinado usando x_train e 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 Compare o desempenho

Agora que você treinou os dois modelos, é possível plotar rapidamente as lacunas de desempenho nos dados de validação entre os dois. Geralmente, os dois modelos alcançam uma exatidão > 0,9 nos dados de treinamento. No entanto, nos dados de validação, fica claro que somente as informações encontradas nas características de PQK são suficientes para fazer o modelo generalizar bem para instâncias não vistas.

#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

Sucesso: você criou um dataset quântico artificial que consegue superar modelos clássicos deliberadamente em uma configuração imparcial (mas inventada). Tente comparar os resultados usando outros tipos de modelos clássicos. O próximo passo é ver se você consegue encontrar novos datasets interessantes para superar os modelos clássicos sem precisar criá-los por conta própria.

4. Conclusões importantes

Há várias conclusões importantes que você pode retirar disso e dos experimentos do MNIST:

  1. É bastante improvável que os modelos quânticos atuais superem o desempenho dos modelos clássicos com dados clássicos, especialmente nos datasets clássicos atuais que podem ter mais de um milhão de pontos de dados.

  2. Só porque os dados podem vir de um circuito quântico difícil de simular de forma clássica, não significa necessariamente que os dados se tornam difíceis de aprender para um modelo clássico.

  3. Existem datasets (em última instância, de natureza quântica) que são fáceis para os modelos quânticos aprenderem e difíceis para os modelos clássicos aprenderem, independentemente da arquitetura do modelo ou dos algoritmos de treinamento usados.