Nota
Esta página fue generada a partir de docs/tutorials/02_neural_network_classifier_and_regressor.ipynb.
Clasificador y Regresor de Redes Neuronales¶
En este tutorial mostramos cómo se utiliza el NeuralNetworkClassifier
y el NeuralNetworkRegressor
. Ambos toman como entrada una NeuralNetwork
(Cuántica) y la aprovechan en un contexto específico. En ambos casos también ofrecemos una variante preconfigurada para mayor comodidad, el Clasificador Cuántico Variacional (Variational Quantum Classifier, VQC
) y el Regresor Cuántico Variacional (Variational Quantum Regressor, VQR
). El tutorial está estructurado de la siguiente manera:
-
Clasificación con un
OpflowQNN
Clasificación con un
CircuitQNN
Clasificador Cuántico Variacional (Variational Quantum Classifier,
VQC
)
-
Regresión con un
OpflowQNN
Regresor Cuántico Variacional (Variational Quantum Regressor,
VQR
)
[1]:
import numpy as np
import matplotlib.pyplot as plt
from qiskit import Aer, QuantumCircuit
from qiskit.opflow import Z, I, StateFn
from qiskit.utils import QuantumInstance, algorithm_globals
from qiskit.circuit import Parameter
from qiskit.circuit.library import RealAmplitudes, ZZFeatureMap
from qiskit.algorithms.optimizers import COBYLA, L_BFGS_B
from qiskit_machine_learning.neural_networks import TwoLayerQNN, CircuitQNN
from qiskit_machine_learning.algorithms.classifiers import NeuralNetworkClassifier, VQC
from qiskit_machine_learning.algorithms.regressors import NeuralNetworkRegressor, VQR
from typing import Union
from qiskit_machine_learning.exceptions import QiskitMachineLearningError
from IPython.display import clear_output
algorithm_globals.random_seed = 42
[2]:
quantum_instance = QuantumInstance(Aer.get_backend("aer_simulator"), shots=1024)
Clasificación¶
Preparamos un conjunto de datos de clasificación simple para ilustrar los siguientes algoritmos.
[3]:
num_inputs = 2
num_samples = 20
X = 2 * algorithm_globals.random.random([num_samples, num_inputs]) - 1
y01 = 1 * (np.sum(X, axis=1) >= 0) # in { 0, 1}
y = 2 * y01 - 1 # in {-1, +1}
y_one_hot = np.zeros((num_samples, 2))
for i in range(num_samples):
y_one_hot[i, y01[i]] = 1
for x, y_target in zip(X, y):
if y_target == 1:
plt.plot(x[0], x[1], "bo")
else:
plt.plot(x[0], x[1], "go")
plt.plot([-1, 1], [1, -1], "--", color="black")
plt.show()

Clasificación con un OpflowQNN
¶
Primero mostramos cómo se puede usar un OpflowQNN
para la clasificación dentro de un NeuralNetworkClassifier
. En este contexto, se espera que el OpflowQNN
devuelva una salida unidimensional en \([-1, +1]\). Esto solo funciona para la clasificación binaria y asignamos las dos clases a \(\{-1, +1\}\). Para mayor comodidad, utilizamos el TwoLayerQNN
, que es un tipo especial de OpflowQNN
definido a través de un mapa de características y un ansatz.
[4]:
# construct QNN
opflow_qnn = TwoLayerQNN(num_inputs, quantum_instance=quantum_instance)
[5]:
# QNN maps inputs to [-1, +1]
opflow_qnn.forward(X[0, :], algorithm_globals.random.random(opflow_qnn.num_weights))
[5]:
array([[0.25]])
Agregaremos una función de devolución de llamada nombrada callback_graph
. Esto se llamará para cada iteración del optimizador y se le pasarán dos parámetros: los pesos actuales y el valor de la función objetivo en esos pesos. Para nuestra función, agregamos el valor de la función objetivo a una matriz para poder trazar la iteración frente al valor de la función objetivo y actualizar la gráfica con cada iteración. Sin embargo, puedes hacer lo que desees con una función de devolución de llamada siempre y cuando se pasen los dos parámetros mencionados.
[6]:
# callback function that draws a live plot when the .fit() method is called
def callback_graph(weights, obj_func_eval):
clear_output(wait=True)
objective_func_vals.append(obj_func_eval)
plt.title("Objective function value against iteration")
plt.xlabel("Iteration")
plt.ylabel("Objective function value")
plt.plot(range(len(objective_func_vals)), objective_func_vals)
plt.show()
[7]:
# construct neural network classifier
opflow_classifier = NeuralNetworkClassifier(opflow_qnn, optimizer=COBYLA(), callback=callback_graph)
[8]:
# create empty array for callback to store evaluations of the objective function
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)
# fit classifier to data
opflow_classifier.fit(X, y)
# return to default figsize
plt.rcParams["figure.figsize"] = (6, 4)
# score classifier
opflow_classifier.score(X, y)

[8]:
0.8
[9]:
# evaluate data points
y_predict = opflow_classifier.predict(X)
# plot results
# red == wrongly classified
for x, y_target, y_p in zip(X, y, y_predict):
if y_target == 1:
plt.plot(x[0], x[1], "bo")
else:
plt.plot(x[0], x[1], "go")
if y_target != y_p:
plt.scatter(x[0], x[1], s=200, facecolors="none", edgecolors="r", linewidths=2)
plt.plot([-1, 1], [1, -1], "--", color="black")
plt.show()

Clasificación con un CircuitQNN
¶
A continuación, mostramos cómo se puede utilizar un CircuitQNN
para la clasificación dentro de un NeuralNetworkClassifier
. En este contexto, se espera que el CircuitQNN
devuelva un vector de probabilidad \(d\)-dimensional como salida, donde \(d\) denota el número de clases. El muestreo de un QuantumCircuit
da como resultado automáticamente una distribución de probabilidad y solo necesitamos definir un mapeo de las cadenas de bits medidas a las diferentes clases. Para la clasificación binaria usamos el mapeo de paridad.
[10]:
# construct feature map
feature_map = ZZFeatureMap(num_inputs)
# construct ansatz
ansatz = RealAmplitudes(num_inputs, reps=1)
# construct quantum circuit
qc = QuantumCircuit(num_inputs)
qc.append(feature_map, range(num_inputs))
qc.append(ansatz, range(num_inputs))
qc.decompose().draw(output="mpl")
[10]:

[11]:
# parity maps bitstrings to 0 or 1
def parity(x):
return "{:b}".format(x).count("1") % 2
output_shape = 2 # corresponds to the number of classes, possible outcomes of the (parity) mapping.
[12]:
# construct QNN
circuit_qnn = CircuitQNN(
circuit=qc,
input_params=feature_map.parameters,
weight_params=ansatz.parameters,
interpret=parity,
output_shape=output_shape,
quantum_instance=quantum_instance,
)
[13]:
# construct classifier
circuit_classifier = NeuralNetworkClassifier(
neural_network=circuit_qnn, optimizer=COBYLA(), callback=callback_graph
)
[14]:
# create empty array for callback to store evaluations of the objective function
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)
# fit classifier to data
circuit_classifier.fit(X, y01)
# return to default figsize
plt.rcParams["figure.figsize"] = (6, 4)
# score classifier
circuit_classifier.score(X, y01)

[14]:
0.75
[15]:
# evaluate data points
y_predict = circuit_classifier.predict(X)
# plot results
# red == wrongly classified
for x, y_target, y_p in zip(X, y01, y_predict):
if y_target == 1:
plt.plot(x[0], x[1], "bo")
else:
plt.plot(x[0], x[1], "go")
if y_target != y_p:
plt.scatter(x[0], x[1], s=200, facecolors="none", edgecolors="r", linewidths=2)
plt.plot([-1, 1], [1, -1], "--", color="black")
plt.show()

Clasificador Cuántico Variacional (Variational Quantum Classifier, VQC
)¶
El VQC
es una variante especial del NeuralNetworkClassifier
con un CircuitQNN
. Aplica un mapeo de paridad (o extensiones a múltiples clases) para mapear desde la cadena de bits hasta la clasificación, lo que da como resultado un vector de probabilidad, que se interpreta como un resultado codificado one-hot. De forma predeterminada, aplica esta función CrossEntropyLoss
que espera que las etiquetas se proporcionen en un formato codificado one-hot y también devolverá predicciones en ese formato.
[16]:
# construct feature map, ansatz, and optimizer
feature_map = ZZFeatureMap(num_inputs)
ansatz = RealAmplitudes(num_inputs, reps=1)
# construct variational quantum classifier
vqc = VQC(
feature_map=feature_map,
ansatz=ansatz,
loss="cross_entropy",
optimizer=COBYLA(),
quantum_instance=quantum_instance,
callback=callback_graph,
)
[17]:
# create empty array for callback to store evaluations of the objective function
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)
# fit classifier to data
vqc.fit(X, y_one_hot)
# return to default figsize
plt.rcParams["figure.figsize"] = (6, 4)
# score classifier
vqc.score(X, y_one_hot)

[17]:
0.5
[18]:
# evaluate data points
y_predict = vqc.predict(X)
# plot results
# red == wrongly classified
for x, y_target, y_p in zip(X, y_one_hot, y_predict):
if y_target[0] == 1:
plt.plot(x[0], x[1], "bo")
else:
plt.plot(x[0], x[1], "go")
if not np.all(y_target == y_p):
plt.scatter(x[0], x[1], s=200, facecolors="none", edgecolors="r", linewidths=2)
plt.plot([-1, 1], [1, -1], "--", color="black")
plt.show()

Regresión¶
Preparamos un conjunto de datos de regresión simple para ilustrar los siguientes algoritmos.
[19]:
num_samples = 20
eps = 0.2
lb, ub = -np.pi, np.pi
X_ = np.linspace(lb, ub, num=50).reshape(50, 1)
f = lambda x: np.sin(x)
X = (ub - lb) * algorithm_globals.random.random([num_samples, 1]) + lb
y = f(X[:, 0]) + eps * (2 * algorithm_globals.random.random(num_samples) - 1)
plt.plot(X_, f(X_), "r--")
plt.plot(X, y, "bo")
plt.show()

Regresión con un OpflowQNN
¶
Aquí restringimos la regresión con un OpflowQNN
que devuelve valores entre \([-1, +1]\). Se podrían construir modelos más complejos y también multidimensionales, también basados en CircuitQNN
pero eso excede el alcance de este tutorial.
[20]:
# construct simple feature map
param_x = Parameter("x")
feature_map = QuantumCircuit(1, name="fm")
feature_map.ry(param_x, 0)
# construct simple ansatz
param_y = Parameter("y")
ansatz = QuantumCircuit(1, name="vf")
ansatz.ry(param_y, 0)
# construct QNN
regression_opflow_qnn = TwoLayerQNN(1, feature_map, ansatz, quantum_instance=quantum_instance)
[21]:
# construct the regressor from the neural network
regressor = NeuralNetworkRegressor(
neural_network=regression_opflow_qnn,
loss="squared_error",
optimizer=L_BFGS_B(),
callback=callback_graph,
)
[22]:
# create empty array for callback to store evaluations of the objective function
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)
# fit to data
regressor.fit(X, y)
# return to default figsize
plt.rcParams["figure.figsize"] = (6, 4)
# score the result
regressor.score(X, y)

[22]:
0.9763610762801076
[23]:
# plot target function
plt.plot(X_, f(X_), "r--")
# plot data
plt.plot(X, y, "bo")
# plot fitted line
y_ = regressor.predict(X_)
plt.plot(X_, y_, "g-")
plt.show()

Regresión con el Regresor Cuántico Variacional (Variational Quantum Regressor, VQR
)¶
Similar al VQC
para la clasificación, el VQR
es una variante especial del NeuralNetworkRegressor
con un OpflowQNN
. De forma predeterminada, considera la función L2Loss
para minimizar el error cuadrático medio entre las predicciones y los objetivos.
[24]:
vqr = VQR(
feature_map=feature_map,
ansatz=ansatz,
optimizer=L_BFGS_B(),
quantum_instance=quantum_instance,
callback=callback_graph,
)
[25]:
# create empty array for callback to store evaluations of the objective function
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)
# fit regressor
vqr.fit(X, y)
# return to default figsize
plt.rcParams["figure.figsize"] = (6, 4)
# score result
vqr.score(X, y)

[25]:
0.9748043399155905
[26]:
# plot target function
plt.plot(X_, f(X_), "r--")
# plot data
plt.plot(X, y, "bo")
# plot fitted line
y_ = vqr.predict(X_)
plt.plot(X_, y_, "g-")
plt.show()

[28]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.19.0 |
qiskit-aer | 0.9.0 |
qiskit-ignis | 0.7.0 |
qiskit-ibmq-provider | 0.17.0 |
qiskit-aqua | 0.10.0 |
qiskit-machine-learning | 0.3.0 |
System information | |
Python | 3.8.10 (default, Jun 2 2021, 10:49:15) [GCC 9.4.0] |
OS | Linux |
CPUs | 4 |
Memory (Gb) | 7.6849517822265625 |
Mon Aug 30 16:41:59 2021 IST |
This code is a part of Qiskit
© Copyright IBM 2017, 2021.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.