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

Red neuronal convolucional cuántica

En este tutorial implementamos una red neuronal convolucional cuántica (QCNN), una analogía cuántica de la red neuronal convolucional clásica que también es traslacionalmente invariable.

Con este ejemplo se demuestra cómo detectar ciertas propiedades de una fuente de datos cuánticos, como un sensor cuántico o una simulación compleja de un dispositivo. La fuente de datos cuánticos es un estado de clúster que puede o no tener una excitación, que la QCNN aprenderá a detectar. (El conjunto de datos usado en la publicación fue el de clasificación de fase SPT).

Preparación

!pip install tensorflow==2.7.0

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

Ahora, hay que importar TensorFlow y las dependencias del 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. Creación de una QCNN

1.1 Ensamble de los circuitos en un grafo de TensorFlow

TensorFlow Quantum (TFQ) proporciona clases de capas diseñadas para la construcción de circuitos en grafos. Un ejemplo es la capa tfq.layers.AddCircuit que hereda de tf.keras.Layer. Esta capa puede anexar al principio o al final del lote de entrada de los circuitos, tal como se muestra en la siguiente figura.

En el siguiente fragmento se usa esta capa:

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)

Examinamos el tensor de entrada:

print(tfq.from_tensor(input_circuit_tensor))

Y el tensor de salida:

print(tfq.from_tensor(output_circuit_tensor))

Si bien es posible ejecutar los ejemplos siguientes sin usar tfq.layers.AddCircuit, es una buena oportunidad para entender de qué manera se puede incorporar una funcionalidad compleja a los grafos de cálculo de TensorFlow.

1.2 Panorama general del problema

Prepararemos un estado de clúster y entrenaremos un clasificador cuántico para detectar si está "excitado" o no. El estado de clúster está muy entrelazado, pero no necesariamente resulta difícil para usar en una computadora clásica. Para aclarar, cabe decir que es un conjunto de datos más simple que el usado en la publicación.

En esta tarea de clasificación implementaremos una QCNN profunda del estilo MERA, ya que:

  1. Al igual que la QCNN, el estado de clúster en un anillo es traslacionalmente invariable.

  2. El estado de clúster está muy entrelazado.

Esta arquitectura debería ser efectiva en la reducción del entrelazado y debería obtener la clasificación leyendo un solo bit cuántico.

Un estado de clúster "excitado" se define como un estado de clúster que ha tenido una puerta cirq.rx aplicada a cualquiera de sus bits cuánticos. Qconv y QPool se tratan más adelante en este mismo tutorial.

1.3 Las bases fundamentales para TensorFlow

Una manera de resolver este problema con TensorFlow Quantum es mediante la implementación de lo siguiente:

  1. La entrada al modelo es un tensor circuito, ya sea un circuito vacío o una puerta X en un bit cuántico particular, que indica una excitación.

  2. El resto de los componentes cuánticos del modelo se construyen con capas tfq.layers.AddCircuit.

  3. Para inferir, se usa una capa tfq.layers.PQC. La capa lee Z^\langle \hat{Z} \rangle y lo compara con una etiqueta de 1 para el estado excitado o -1 para un estado no excitado.

1.4 Los datos

Antes de crear un modelo, podemos generar los datos. En este caso serán excitaciones para el estado de clúster. (En la publicación original se usa un conjunto de datos más complicado). Las excitaciones se encuentran representadas por puertas cirq.rx. Una rotación lo suficientemente amplia es considerada una excitación y se la etiqueta con 1, mientras que una rotación que no lo es (suficientemente amplia) recibe la etiqueta de -1 (además de que no se la llama excitación).

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)

Se ve que, al igual que con cualquier aprendizaje automático común, podemos crear un conjunto de prueba y de entrenamiento para comparar con el modelo. Miremos rápidamente algunos puntos de datos con lo siguiente:

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 Definición de las capas

Ahora, definimos en TensorFlow las capas que se muestran en la figura anterior.

1.5.1 Estado de clúster

El primer paso consiste en definir el estado de clúster con Cirq, un marco de trabajo de Google que sirve para programar circuitos cuánticos. Ya que es una parte estática del modelo, lo incorporamos utilizando la funcionalidad 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

Se muestra un circuito de estado de clúster para un rectángulo de los cirq.GridQubit:

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

1.5.2 Capas de QCNN

Definamos las capas que componen al modelo con la publicación de Cong y Lukin sobre QCNN. Hay algunos requisitos previos con los que debemos contar:

  • Las matrices unitarias parametrizadas de uno y dos bits cuánticos de la publicación de Tucci.

  • Una operación de agrupamiento (pooling) de dos bits cuánticos parametrizada general.

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 lo que hemos creado, imprimamos un circuito unitario de un bit cuántico:

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

El circuito unitario de dos bits cuánticos:

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

Y el circuito de agrupamiento de dos bits cuánticos:

SVGCircuit(two_qubit_pool(*cirq.GridQubit.rect(1, 2), sympy.symbols('x0:6')))
1.5.2.1 Convolución cuántica

Tal como en la publicación de Cong y Lukin, definimos la convolución cuántica de 1 d como la aplicación de un unitario parametrizado de dos bits cuánticos para cada par de bits cuánticos adyacentes con un salto de uno.

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

Se muestra el circuito (muy horizontal):

SVGCircuit( quantum_conv_circuit(cirq.GridQubit.rect(1, 8), sympy.symbols('x0:15')))
1.5.2.2 Agrupamiento (pooling) cuántica

Una capa de agrupamiento cuántico agrupa NN bits cuánticos en N2\frac{N}{2}, utilizando el agrupamiento de dos bits cuánticos definido arriba.

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

Examinemos un circuito de componentes de agrupamiento (pooling):

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

1.6 Definición del modelo

Ahora, usamos las capas definidas para construir una CNN puramente cuántica. Empezamos con ocho bits cuánticos, los agrupamos en uno y después, medimos 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 Entrenamiento del modelo

Entrenamos el modelo con el lote completo para simplificar este ejemplo.

# 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

No hace falta pasar de ocho bits cuánticos a uno con la convolución cuántica. Podríamos haber hecho una o dos rondas de convolución cuántica y haber alimentado una red neuronal clásica con los resultados. En esta sección se analizan los modelos híbridos cuántico-clásicos.

2.1 Modelo híbrido con un filtro cuántico solo

Aplicamos una capa de convolución cuántica, que lee Z^n\langle \hat{Z}_n \rangle en todos los bits, seguida por una red neuronal densamente conectada.

2.1.1 Definición del 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 Entrenamiento del 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()

Tal como se puede ver, con cada instancia clásica modesta, el modelo híbrido, por lo general, converge más rápido que con la versión puramente cuántica.

2.2 Convolución híbrida con múltiples filtros cuánticos

Esta vez, probemos con una arquitectura que use varias convoluciones cuánticas y una red neuronal clásica para hacer una combinación.

2.2.1 Definición del 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 Entrenamiento del 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()