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

Rede Neural Convolucional Quântica

Este tutorial implementa uma Rede Neural Convolucional Quântica (QCNN), um análogo quântico proposto para uma rede neural convolucional clássica que também é translacionalmente invariante.

Este exemplo demonstra como detectar determinadas propriedades de uma fonte de dados quântica, como um sensor quântico ou uma simulação complexa de um dispositivo. A fonte de dados quântica é um estado de cluster que pode ou não ter excitação — o que a QCNN aprenderá a detectar (o dataset usado nesse artigo foi a classificação de fases SPT).

Configuração

!pip install tensorflow==2.7.0

Instale o TensorFlow Quantum:

!pip install tensorflow-quantum==0.7.2
# Update package resources to account for version changes. import importlib, pkg_resources importlib.reload(pkg_resources)

Agora importe o TensorFlow e as dependências de módulo:

import tensorflow as tf import tensorflow_quantum as tfq import cirq import sympy import numpy as np # visualization tools %matplotlib inline import matplotlib.pyplot as plt from cirq.contrib.svg import SVGCircuit

1. Crie uma QCNN

1.1 Reúna os circuitos em um grafo do TensorFlow

O TensorFlow Quantum (TFQ) fornece classes de camadas criadas para a construção de circuitos no grafo. Um exemplo é a camada tfq.layers.AddCircuit que herda de tf.keras.Layer. Essa camada pode preceder ou ser anexada ao lote de entrada de circuitos, conforme mostrado na figura abaixo.

O fragmento a seguir usa essa camada:

qubit = cirq.GridQubit(0, 0) # Define some circuits. circuit1 = cirq.Circuit(cirq.X(qubit)) circuit2 = cirq.Circuit(cirq.H(qubit)) # Convert to a tensor. input_circuit_tensor = tfq.convert_to_tensor([circuit1, circuit2]) # Define a circuit that we want to append y_circuit = cirq.Circuit(cirq.Y(qubit)) # Instantiate our layer y_appender = tfq.layers.AddCircuit() # Run our circuit tensor through the layer and save the output. output_circuit_tensor = y_appender(input_circuit_tensor, append=y_circuit)

Examine o tensor de entrada:

print(tfq.from_tensor(input_circuit_tensor))

E examine o tensor de saída:

print(tfq.from_tensor(output_circuit_tensor))

Embora seja possível executar os exemplos abaixo sem usar tfq.layers.AddCircuit, é recomendável entender como a funcionalidade complexa pode ser incorporada nos grafos computacionais do TensorFlow.

1.2 Visão geral do problema

Você preparará um estado de cluster e treinará um classificador quântico para detectar se está "excitado" ou não. O estado de cluster é altamente emaranhado, mas não necessariamente difícil para um computador clássico. Para maior clareza, esse é um dataset mais simples do que o usado no artigo.

Para essa tarefa de classificação, você implementará uma arquitetura de QCNN semelhante à MERA, porque:

  1. Como a QCNN, o estado de cluster em um anel é translacionalmente invariante.

  2. O estado de cluster é altamente emaranhado.

Essa arquitetura deve ser eficaz em reduzir o emaranhamento, obtendo a classificação ao ler um único qubit.

Um estado de cluster "excitado" é definido como um estado de cluster com uma porta cirq.rx aplicada a qualquer um dos qubits. Qconv e QPool são discutidos mais tarde neste tutorial.

1.3 Componentes básicos para o TensorFlow

Uma maneira de resolver esse problema com o TensorFlow Quantum é implementar o seguinte:

  1. A entrada do modelo é um tensor de circuito, seja um circuito vazio ou uma porta X em um qubit específico indicando uma excitação.

  2. O resto dos componentes quânticos do modelo são construídos com camadas tfq.layers.AddCircuit.

  3. Para inferência, é usada uma camada tfq.layers.PQC. Ela lê Z^\langle \hat{Z} \rangle e compara com um rótulo de 1 para um estado excitado ou -1 para um estado não excitado.

1.4 Dados

Antes de criar o modelo, você pode gerar seus dados. Nesse caso, será excitações no estado de cluster (o artigo original usa um dataset mais complicado). As excitações são representadas por portas cirq.rx. Uma rotação grande o suficiente é considerada uma excitação e rotulada como 1, enquanto uma rotação que não é grande o suficiente é rotulada como -1 e não é considerada uma excitação.

def generate_data(qubits): """Generate training and testing data.""" n_rounds = 20 # Produces n_rounds * n_qubits datapoints. excitations = [] labels = [] for n in range(n_rounds): for bit in qubits: rng = np.random.uniform(-np.pi, np.pi) excitations.append(cirq.Circuit(cirq.rx(rng)(bit))) labels.append(1 if (-np.pi / 2) <= rng <= (np.pi / 2) else -1) split_ind = int(len(excitations) * 0.7) train_excitations = excitations[:split_ind] test_excitations = excitations[split_ind:] train_labels = labels[:split_ind] test_labels = labels[split_ind:] return tfq.convert_to_tensor(train_excitations), np.array(train_labels), \ tfq.convert_to_tensor(test_excitations), np.array(test_labels)

Você pode ver que, assim como no aprendizado de máquina comum, é possível criar um dataset de treinamento e de teste para fazer o benchmarking do modelo. É possível analisar rapidamente alguns pontos de dados com:

sample_points, sample_labels, _, __ = generate_data(cirq.GridQubit.rect(1, 4)) print('Input:', tfq.from_tensor(sample_points)[0], 'Output:', sample_labels[0]) print('Input:', tfq.from_tensor(sample_points)[1], 'Output:', sample_labels[1])

1.5 Defina camadas

Agora defina as camadas mostradas na figura acima no TensorFlow.

1.5.1 Estado de cluster

O primeiro passo é definir o estado de cluster usando Cirq, um framework fornecido pelo Google para programar circuitos quânticos. Como essa é a parte estática do modelo, incorpore-a usando a funcionalidade tfq.layers.AddCircuit.

def cluster_state_circuit(bits): """Return a cluster state on the qubits in `bits`.""" circuit = cirq.Circuit() circuit.append(cirq.H.on_each(bits)) for this_bit, next_bit in zip(bits, bits[1:] + [bits[0]]): circuit.append(cirq.CZ(this_bit, next_bit)) return circuit

Exiba um circuito de estado de cluster para um retângulo de cirq.GridQubits:

SVGCircuit(cluster_state_circuit(cirq.GridQubit.rect(1, 4)))

1.5.2 Camadas de QCNN

Defina as camadas que compõem o modelo usando o artigo de Cong e Lukin sobre QCNN. Há alguns pré-requisitos:

  • As matrizes unitárias parametrizadas de um e dois qubits do artigo de Tucci.

  • Uma operação de pooling de dois qubits parametrizada geral.

def one_qubit_unitary(bit, symbols): """Make a Cirq circuit enacting a rotation of the bloch sphere about the X, Y and Z axis, that depends on the values in `symbols`. """ return cirq.Circuit( cirq.X(bit)**symbols[0], cirq.Y(bit)**symbols[1], cirq.Z(bit)**symbols[2]) def two_qubit_unitary(bits, symbols): """Make a Cirq circuit that creates an arbitrary two qubit unitary.""" circuit = cirq.Circuit() circuit += one_qubit_unitary(bits[0], symbols[0:3]) circuit += one_qubit_unitary(bits[1], symbols[3:6]) circuit += [cirq.ZZ(*bits)**symbols[6]] circuit += [cirq.YY(*bits)**symbols[7]] circuit += [cirq.XX(*bits)**symbols[8]] circuit += one_qubit_unitary(bits[0], symbols[9:12]) circuit += one_qubit_unitary(bits[1], symbols[12:]) return circuit def two_qubit_pool(source_qubit, sink_qubit, symbols): """Make a Cirq circuit to do a parameterized 'pooling' operation, which attempts to reduce entanglement down from two qubits to just one.""" pool_circuit = cirq.Circuit() sink_basis_selector = one_qubit_unitary(sink_qubit, symbols[0:3]) source_basis_selector = one_qubit_unitary(source_qubit, symbols[3:6]) pool_circuit.append(sink_basis_selector) pool_circuit.append(source_basis_selector) pool_circuit.append(cirq.CNOT(control=source_qubit, target=sink_qubit)) pool_circuit.append(sink_basis_selector**-1) return pool_circuit

Para ver o que você criou, imprima o circuito unitário de um qubit:

SVGCircuit(one_qubit_unitary(cirq.GridQubit(0, 0), sympy.symbols('x0:3')))

E o circuito unitário de dois qubits:

SVGCircuit(two_qubit_unitary(cirq.GridQubit.rect(1, 2), sympy.symbols('x0:15')))

E o circuito de pooling de dois qubits:

SVGCircuit(two_qubit_pool(*cirq.GridQubit.rect(1, 2), sympy.symbols('x0:6')))
1.5.2.1 Convolução quântica

Assim como no artigo de Cong e Lukin, defina a convolução quântica 1D como a aplicação de um unitário parametrizado de dois qubits a cada par de qubits adjacente com um stride de um.

def quantum_conv_circuit(bits, symbols): """Quantum Convolution Layer following the above diagram. Return a Cirq circuit with the cascade of `two_qubit_unitary` applied to all pairs of qubits in `bits` as in the diagram above. """ circuit = cirq.Circuit() for first, second in zip(bits[0::2], bits[1::2]): circuit += two_qubit_unitary([first, second], symbols) for first, second in zip(bits[1::2], bits[2::2] + [bits[0]]): circuit += two_qubit_unitary([first, second], symbols) return circuit

Exiba o circuito (bastante horizontal):

SVGCircuit( quantum_conv_circuit(cirq.GridQubit.rect(1, 8), sympy.symbols('x0:15')))
1.5.2.2 Pooling quântico

Uma camada de pooling quântico agrega os qubits NN para os qubits N2\frac{N}{2} usando o pool de dois qubits definido acima.

def quantum_pool_circuit(source_bits, sink_bits, symbols): """A layer that specifies a quantum pooling operation. A Quantum pool tries to learn to pool the relevant information from two qubits onto 1. """ circuit = cirq.Circuit() for source, sink in zip(source_bits, sink_bits): circuit += two_qubit_pool(source, sink, symbols) return circuit

Examine um circuito de componentes de pooling:

test_bits = cirq.GridQubit.rect(1, 8) SVGCircuit( quantum_pool_circuit(test_bits[:4], test_bits[4:], sympy.symbols('x0:6')))

1.6 Definição do modelo

Agora, use as camadas definidas para construir uma CNN estritamente quântica. Comece com oito qubits, reduza para um e meça Z^\langle \hat{Z} \rangle.

def create_model_circuit(qubits): """Create sequence of alternating convolution and pooling operators which gradually shrink over time.""" model_circuit = cirq.Circuit() symbols = sympy.symbols('qconv0:63') # Cirq uses sympy.Symbols to map learnable variables. TensorFlow Quantum # scans incoming circuits and replaces these with TensorFlow variables. model_circuit += quantum_conv_circuit(qubits, symbols[0:15]) model_circuit += quantum_pool_circuit(qubits[:4], qubits[4:], symbols[15:21]) model_circuit += quantum_conv_circuit(qubits[4:], symbols[21:36]) model_circuit += quantum_pool_circuit(qubits[4:6], qubits[6:], symbols[36:42]) model_circuit += quantum_conv_circuit(qubits[6:], symbols[42:57]) model_circuit += quantum_pool_circuit([qubits[6]], [qubits[7]], symbols[57:63]) return model_circuit # Create our qubits and readout operators in Cirq. cluster_state_bits = cirq.GridQubit.rect(1, 8) readout_operators = cirq.Z(cluster_state_bits[-1]) # Build a sequential model enacting the logic in 1.3 of this notebook. # Here you are making the static cluster state prep as a part of the AddCircuit and the # "quantum datapoints" are coming in the form of excitation excitation_input = tf.keras.Input(shape=(), dtype=tf.dtypes.string) cluster_state = tfq.layers.AddCircuit()( excitation_input, prepend=cluster_state_circuit(cluster_state_bits)) quantum_model = tfq.layers.PQC(create_model_circuit(cluster_state_bits), readout_operators)(cluster_state) qcnn_model = tf.keras.Model(inputs=[excitation_input], outputs=[quantum_model]) # Show the keras plot of the model tf.keras.utils.plot_model(qcnn_model, show_shapes=True, show_layer_names=False, dpi=70)

1.7 Treine o modelo

Treine o modelo com o lote completo para simplificar esse exemplo.

# Generate some training data. train_excitations, train_labels, test_excitations, test_labels = generate_data( cluster_state_bits) # Custom accuracy metric. @tf.function def custom_accuracy(y_true, y_pred): y_true = tf.squeeze(y_true) y_pred = tf.map_fn(lambda x: 1.0 if x >= 0 else -1.0, y_pred) return tf.keras.backend.mean(tf.keras.backend.equal(y_true, y_pred)) qcnn_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.02), loss=tf.losses.mse, metrics=[custom_accuracy]) history = qcnn_model.fit(x=train_excitations, y=train_labels, batch_size=16, epochs=25, verbose=1, validation_data=(test_excitations, test_labels))
plt.plot(history.history['loss'][1:], label='Training') plt.plot(history.history['val_loss'][1:], label='Validation') plt.title('Training a Quantum CNN to Detect Excited Cluster States') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show()

2. Modelos híbridos

Você não precisa reduzir oito qubits a um usando a convolução quântica — é possível fazer umas ou duas rodadas de convolução quântica e alimentar os resultados a uma rede neural clássica. Essa seção explora modelos quântico-clássico híbridos.

2.1 Modelo híbrido com um único filtro quântico

Aplique uma camada de convolução quântica, lendo Z^n\langle \hat{Z}_n \rangle em todos os bits, seguida por uma rede neural densamente conectada.

2.1.1 Definição do modelo

# 1-local operators to read out readouts = [cirq.Z(bit) for bit in cluster_state_bits[4:]] def multi_readout_model_circuit(qubits): """Make a model circuit with less quantum pool and conv operations.""" model_circuit = cirq.Circuit() symbols = sympy.symbols('qconv0:21') model_circuit += quantum_conv_circuit(qubits, symbols[0:15]) model_circuit += quantum_pool_circuit(qubits[:4], qubits[4:], symbols[15:21]) return model_circuit # Build a model enacting the logic in 2.1 of this notebook. excitation_input_dual = tf.keras.Input(shape=(), dtype=tf.dtypes.string) cluster_state_dual = tfq.layers.AddCircuit()( excitation_input_dual, prepend=cluster_state_circuit(cluster_state_bits)) quantum_model_dual = tfq.layers.PQC( multi_readout_model_circuit(cluster_state_bits), readouts)(cluster_state_dual) d1_dual = tf.keras.layers.Dense(8)(quantum_model_dual) d2_dual = tf.keras.layers.Dense(1)(d1_dual) hybrid_model = tf.keras.Model(inputs=[excitation_input_dual], outputs=[d2_dual]) # Display the model architecture tf.keras.utils.plot_model(hybrid_model, show_shapes=True, show_layer_names=False, dpi=70)

2.1.2 Treine o modelo

hybrid_model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.02), loss=tf.losses.mse, metrics=[custom_accuracy]) hybrid_history = hybrid_model.fit(x=train_excitations, y=train_labels, batch_size=16, epochs=25, verbose=1, validation_data=(test_excitations, test_labels))
plt.plot(history.history['val_custom_accuracy'], label='QCNN') plt.plot(hybrid_history.history['val_custom_accuracy'], label='Hybrid CNN') plt.title('Quantum vs Hybrid CNN performance') plt.xlabel('Epochs') plt.legend() plt.ylabel('Validation Accuracy') plt.show()

Como você pode ver, com uma assistência clássica bastante modesta, o modelo híbrido geralmente converge com mais rapidez do que a versão puramente quântica.

2.2 Convolução híbrida com vários filtros quânticos

Agora vamos testar uma arquitetura que usa várias convoluções quânticas e uma rede neural clássica para combiná-las.

2.2.1 Definição do modelo

excitation_input_multi = tf.keras.Input(shape=(), dtype=tf.dtypes.string) cluster_state_multi = tfq.layers.AddCircuit()( excitation_input_multi, prepend=cluster_state_circuit(cluster_state_bits)) # apply 3 different filters and measure expectation values quantum_model_multi1 = tfq.layers.PQC( multi_readout_model_circuit(cluster_state_bits), readouts)(cluster_state_multi) quantum_model_multi2 = tfq.layers.PQC( multi_readout_model_circuit(cluster_state_bits), readouts)(cluster_state_multi) quantum_model_multi3 = tfq.layers.PQC( multi_readout_model_circuit(cluster_state_bits), readouts)(cluster_state_multi) # concatenate outputs and feed into a small classical NN concat_out = tf.keras.layers.concatenate( [quantum_model_multi1, quantum_model_multi2, quantum_model_multi3]) dense_1 = tf.keras.layers.Dense(8)(concat_out) dense_2 = tf.keras.layers.Dense(1)(dense_1) multi_qconv_model = tf.keras.Model(inputs=[excitation_input_multi], outputs=[dense_2]) # Display the model architecture tf.keras.utils.plot_model(multi_qconv_model, show_shapes=True, show_layer_names=True, dpi=70)

2.2.2 Treine o modelo

multi_qconv_model.compile( optimizer=tf.keras.optimizers.Adam(learning_rate=0.02), loss=tf.losses.mse, metrics=[custom_accuracy]) multi_qconv_history = multi_qconv_model.fit(x=train_excitations, y=train_labels, batch_size=16, epochs=25, verbose=1, validation_data=(test_excitations, test_labels))
plt.plot(history.history['val_custom_accuracy'][:25], label='QCNN') plt.plot(hybrid_history.history['val_custom_accuracy'][:25], label='Hybrid CNN') plt.plot(multi_qconv_history.history['val_custom_accuracy'][:25], label='Hybrid CNN \n Multiple Quantum Filters') plt.title('Quantum vs Hybrid CNN performance') plt.xlabel('Epochs') plt.legend() plt.ylabel('Validation Accuracy') plt.show()