QML on MNIST Classification¶
Overview¶
The aim of this tutorial is not about the machine learning perspective on better design of QML method for MNIST classification. Instead, we use a simple parameterized circuit and demonstrate the QML-related technical ingredients of tensorcircuit. Nevertheless, this note is by no means a good practice on QML.
[WIP note]
Setup¶
[1]:
from functools import partial
import numpy as np
import tensorflow as tf
import jax
from jax import config
config.update("jax_enable_x64", True)
from jax import numpy as jnp
import optax
import tensorcircuit as tc
[2]:
tc.set_backend("tensorflow")
tc.set_dtype("complex128")
[2]:
('complex128', 'float64')
Data Processing¶
We utilize MNIST data and resize them to 3*3 to fit into a 9-qubit circuit. The testbed we use is a binary classification task, digit 1 vs. 5. And since this tutorial is not about good practice on QML, we leave the validation set away. And we only collect 100 data points for a small demo.
[3]:
# numpy data
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
x_train = x_train[..., np.newaxis] / 255.0
def filter_pair(x, y, a, b):
keep = (y == a) | (y == b)
x, y = x[keep], y[keep]
y = y == a
return x, y
x_train, y_train = filter_pair(x_train, y_train, 1, 5)
x_train_small = tf.image.resize(x_train, (3, 3)).numpy()
x_train_bin = np.array(x_train_small > 0.5, dtype=np.float32)
x_train_bin = np.squeeze(x_train_bin)[:100]
[4]:
# tensorflow data
x_train_tf = tf.reshape(tf.constant(x_train_bin, dtype=tf.float64), [-1, 9])
y_train_tf = tf.constant(y_train[:100], dtype=tf.float64)
# jax data
x_train_jax = jnp.array(x_train_bin, dtype=np.float64).reshape([100, -1])
y_train_jax = jnp.array(y_train[:100], dtype=np.float64).reshape([100])
Using vectorized_value_and_grad API¶
[5]:
nlayers = 3
def qml_loss(x, y, weights, nlayers):
n = 9
weights = tc.backend.cast(weights, "complex128")
x = tc.backend.cast(x, "complex128")
c = tc.Circuit(n)
for i in range(n):
c.rx(i, theta=x[i])
for j in range(nlayers):
for i in range(n - 1):
c.cnot(i, i + 1)
for i in range(n):
c.rx(i, theta=weights[2 * j, i])
c.ry(i, theta=weights[2 * j + 1, i])
ypred = c.expectation([tc.gates.z(), (4,)])
ypred = tc.backend.real(ypred)
ypred = (tc.backend.real(ypred) + 1) / 2.0
return -y * tc.backend.log(ypred) - (1 - y) * tc.backend.log(1 - ypred), ypred
[6]:
def get_qml_vvag():
qml_vvag = tc.backend.vectorized_value_and_grad(
qml_loss, argnums=(2,), vectorized_argnums=(0, 1), has_aux=True
)
qml_vvag = tc.backend.jit(qml_vvag, static_argnums=(3,))
return qml_vvag
qml_vvag = get_qml_vvag()
qml_vvag(x_train_tf, y_train_tf, tf.ones([nlayers * 2, 9], dtype=tf.float64), nlayers)
[6]:
((<tf.Tensor: shape=(100,), dtype=float64, numpy=
array([0.8433698 , 0.56257199, 0.54653163, 0.56257199, 0.82036163,
0.56257199, 0.56257199, 0.58030506, 0.82036163, 0.56257199,
0.82036163, 0.56257199, 0.82036163, 0.56257199, 0.54653163,
0.54653163, 0.56257199, 0.56257199, 0.58030506, 0.82036163,
0.54653163, 0.56257199, 0.56257199, 0.56257199, 0.56257199,
0.56257199, 0.56257199, 0.85182866, 0.56257199, 0.82036163,
0.82036163, 0.56257199, 0.8433698 , 0.56257199, 0.8433698 ,
0.56257199, 0.85182866, 0.56257199, 0.82036163, 0.54653163,
0.56257199, 0.56257199, 0.56257199, 0.56257199, 0.8433698 ,
0.58030506, 0.56257199, 0.82036163, 0.8433698 , 0.8433698 ,
0.54653163, 0.56257199, 0.82036163, 0.86501404, 0.56257199,
0.56257199, 0.8433698 , 0.56257199, 0.85182866, 0.82036163,
0.82036163, 0.56257199, 0.82036163, 0.56257199, 0.56257199,
0.56257199, 0.82036163, 0.8433698 , 0.8433698 , 0.82036163,
0.56257199, 0.56257199, 0.56257199, 0.56257199, 0.56257199,
0.54653163, 0.86501404, 0.54653163, 0.54653163, 0.82036163,
0.56257199, 0.54653163, 0.8433698 , 0.54653163, 0.8433698 ,
0.56257199, 0.56257199, 0.8433698 , 0.82036163, 0.8433698 ,
0.56257199, 0.56257199, 0.56257199, 0.56257199, 0.56257199,
0.82036163, 0.56257199, 0.58030506, 0.8433698 , 0.56257199])>,
<tf.Tensor: shape=(100,), dtype=float64, numpy=
array([0.56974181, 0.56974181, 0.57895436, 0.56974181, 0.55972759,
0.56974181, 0.56974181, 0.55972759, 0.55972759, 0.56974181,
0.55972759, 0.56974181, 0.55972759, 0.56974181, 0.57895436,
0.57895436, 0.56974181, 0.56974181, 0.55972759, 0.55972759,
0.57895436, 0.56974181, 0.56974181, 0.56974181, 0.56974181,
0.56974181, 0.56974181, 0.57336595, 0.56974181, 0.55972759,
0.55972759, 0.56974181, 0.56974181, 0.56974181, 0.56974181,
0.56974181, 0.57336595, 0.56974181, 0.55972759, 0.57895436,
0.56974181, 0.56974181, 0.56974181, 0.56974181, 0.56974181,
0.55972759, 0.56974181, 0.55972759, 0.56974181, 0.56974181,
0.57895436, 0.56974181, 0.55972759, 0.57895436, 0.56974181,
0.56974181, 0.56974181, 0.56974181, 0.57336595, 0.55972759,
0.55972759, 0.56974181, 0.55972759, 0.56974181, 0.56974181,
0.56974181, 0.55972759, 0.56974181, 0.56974181, 0.55972759,
0.56974181, 0.56974181, 0.56974181, 0.56974181, 0.56974181,
0.57895436, 0.57895436, 0.57895436, 0.57895436, 0.55972759,
0.56974181, 0.57895436, 0.56974181, 0.57895436, 0.56974181,
0.56974181, 0.56974181, 0.56974181, 0.55972759, 0.56974181,
0.56974181, 0.56974181, 0.56974181, 0.56974181, 0.56974181,
0.55972759, 0.56974181, 0.55972759, 0.56974181, 0.56974181])>),
[<tf.Tensor: shape=(6, 9), dtype=float64, numpy=
array([[ 5.79464357e-02, 1.12182823e-01, 8.13605755e-02,
1.52611620e-01, 1.13641690e+00, -1.41695736e+00,
7.62883290e-01, 1.11022302e-15, 2.22044605e-16],
[ 3.57155554e-02, 1.61488509e-01, 7.62331819e-02,
1.50335863e-01, -1.10363460e-01, -3.23606686e-01,
-3.14523756e-01, 4.44089210e-16, 1.22124533e-15],
[-3.04959149e-02, 6.03271869e-02, 2.47760477e-02,
-7.61859417e-02, 1.72064441e+00, 1.66891120e+00,
-1.22124533e-15, 0.00000000e+00, -6.66133815e-16],
[-8.26503466e-03, -6.90338030e-02, -1.07589110e-01,
1.88650816e-01, -2.68228700e+00, -2.41159987e+00,
4.44089210e-16, -2.22044605e-16, -2.22044605e-16],
[-8.88178420e-16, 6.66133815e-16, -1.11022302e-16,
-2.22044605e-16, 4.02608813e-01, 4.44089210e-16,
2.22044605e-16, -7.77156117e-16, -8.88178420e-16],
[ 0.00000000e+00, 3.33066907e-16, 6.66133815e-16,
4.44089210e-16, -1.11152817e+00, -1.11022302e-16,
-7.77156117e-16, -7.77156117e-16, 5.55111512e-16]])>])
[7]:
# %timeit qml_vvag(x_train_tf, y_train_tf, tf.ones([nlayers*2, 9], dtype=tf.float64), nlayers)
Jax Backend Compatibility¶
[8]:
tc.set_backend("jax")
[8]:
jax_backend
[9]:
qml_vvag = get_qml_vvag()
qml_vvag(
x_train_jax, y_train_jax, jnp.ones([nlayers * 2, 9], dtype=np.float64), nlayers
)
[9]:
((Array([0.8433698 , 0.56257199, 0.54653163, 0.56257199, 0.82036163,
0.56257199, 0.56257199, 0.58030506, 0.82036163, 0.56257199,
0.82036163, 0.56257199, 0.82036163, 0.56257199, 0.54653163,
0.54653163, 0.56257199, 0.56257199, 0.58030506, 0.82036163,
0.54653163, 0.56257199, 0.56257199, 0.56257199, 0.56257199,
0.56257199, 0.56257199, 0.85182866, 0.56257199, 0.82036163,
0.82036163, 0.56257199, 0.8433698 , 0.56257199, 0.8433698 ,
0.56257199, 0.85182866, 0.56257199, 0.82036163, 0.54653163,
0.56257199, 0.56257199, 0.56257199, 0.56257199, 0.8433698 ,
0.58030506, 0.56257199, 0.82036163, 0.8433698 , 0.8433698 ,
0.54653163, 0.56257199, 0.82036163, 0.86501404, 0.56257199,
0.56257199, 0.8433698 , 0.56257199, 0.85182866, 0.82036163,
0.82036163, 0.56257199, 0.82036163, 0.56257199, 0.56257199,
0.56257199, 0.82036163, 0.8433698 , 0.8433698 , 0.82036163,
0.56257199, 0.56257199, 0.56257199, 0.56257199, 0.56257199,
0.54653163, 0.86501404, 0.54653163, 0.54653163, 0.82036163,
0.56257199, 0.54653163, 0.8433698 , 0.54653163, 0.8433698 ,
0.56257199, 0.56257199, 0.8433698 , 0.82036163, 0.8433698 ,
0.56257199, 0.56257199, 0.56257199, 0.56257199, 0.56257199,
0.82036163, 0.56257199, 0.58030506, 0.8433698 , 0.56257199], dtype=float64),
Array([0.56974181, 0.56974181, 0.57895436, 0.56974181, 0.55972759,
0.56974181, 0.56974181, 0.55972759, 0.55972759, 0.56974181,
0.55972759, 0.56974181, 0.55972759, 0.56974181, 0.57895436,
0.57895436, 0.56974181, 0.56974181, 0.55972759, 0.55972759,
0.57895436, 0.56974181, 0.56974181, 0.56974181, 0.56974181,
0.56974181, 0.56974181, 0.57336595, 0.56974181, 0.55972759,
0.55972759, 0.56974181, 0.56974181, 0.56974181, 0.56974181,
0.56974181, 0.57336595, 0.56974181, 0.55972759, 0.57895436,
0.56974181, 0.56974181, 0.56974181, 0.56974181, 0.56974181,
0.55972759, 0.56974181, 0.55972759, 0.56974181, 0.56974181,
0.57895436, 0.56974181, 0.55972759, 0.57895436, 0.56974181,
0.56974181, 0.56974181, 0.56974181, 0.57336595, 0.55972759,
0.55972759, 0.56974181, 0.55972759, 0.56974181, 0.56974181,
0.56974181, 0.55972759, 0.56974181, 0.56974181, 0.55972759,
0.56974181, 0.56974181, 0.56974181, 0.56974181, 0.56974181,
0.57895436, 0.57895436, 0.57895436, 0.57895436, 0.55972759,
0.56974181, 0.57895436, 0.56974181, 0.57895436, 0.56974181,
0.56974181, 0.56974181, 0.56974181, 0.55972759, 0.56974181,
0.56974181, 0.56974181, 0.56974181, 0.56974181, 0.56974181,
0.55972759, 0.56974181, 0.55972759, 0.56974181, 0.56974181], dtype=float64)),
(Array([[ 5.79464357e-02, 1.12182823e-01, 8.13605755e-02,
1.52611620e-01, 1.13641690e+00, -1.41695736e+00,
7.62883290e-01, 1.30451205e-15, -6.24500451e-16],
[ 3.57155554e-02, 1.61488509e-01, 7.62331819e-02,
1.50335863e-01, -1.10363460e-01, -3.23606686e-01,
-3.14523756e-01, 9.08995101e-16, 9.15933995e-16],
[-3.04959149e-02, 6.03271869e-02, 2.47760477e-02,
-7.61859417e-02, 1.72064441e+00, 1.66891120e+00,
-2.02615702e-15, -8.46545056e-16, -2.19269047e-15],
[-8.26503466e-03, -6.90338030e-02, -1.07589110e-01,
1.88650816e-01, -2.68228700e+00, -2.41159987e+00,
1.14491749e-15, 5.68989300e-16, -1.31145095e-15],
[-9.36750677e-16, -9.71445147e-17, -7.49400542e-16,
4.57966998e-16, 4.02608813e-01, -4.02455846e-16,
-1.11716192e-15, 1.59594560e-16, -2.09554596e-15],
[-6.45317133e-16, 1.38777878e-17, 3.33066907e-16,
7.49400542e-16, -1.11152817e+00, -9.29811783e-16,
-5.48172618e-16, -1.21430643e-15, -2.22044605e-16]], dtype=float64),))
[10]:
# %timeit qml_vvag(x_train_jax, y_train_jax, jnp.ones([nlayers * 2, 9], dtype=np.float64), nlayers)
Training Using tf.data¶
[11]:
# switch back to tensorflow
tc.set_backend("tensorflow")
qml_vvag = get_qml_vvag()
qml_vvag = tc.backend.jit(qml_vvag, static_argnums=(3,))
[12]:
mnist_data = (
tf.data.Dataset.from_tensor_slices((x_train_tf, y_train_tf))
.repeat(200)
.shuffle(100)
.batch(32)
)
[13]:
opt = tf.keras.optimizers.Adam(1e-2)
w = tf.Variable(
initial_value=tf.random.normal(shape=(2 * nlayers, 9), stddev=0.5, dtype=tf.float64)
)
for i, (xs, ys) in zip(range(2000), mnist_data):
(losses, ypreds), grad = qml_vvag(xs, ys, w, nlayers)
if i % 20 == 0:
print(tf.reduce_mean(losses))
opt.apply_gradients([(grad[0], w)])
tf.Tensor(0.6882181339902962, shape=(), dtype=float64)
tf.Tensor(0.6814181009432645, shape=(), dtype=float64)
tf.Tensor(0.6139398088609226, shape=(), dtype=float64)
tf.Tensor(0.6484759331523565, shape=(), dtype=float64)
tf.Tensor(0.643387450151498, shape=(), dtype=float64)
tf.Tensor(0.6160787679905442, shape=(), dtype=float64)
tf.Tensor(0.7146481929542476, shape=(), dtype=float64)
tf.Tensor(0.6389381815122707, shape=(), dtype=float64)
tf.Tensor(0.6194927902308465, shape=(), dtype=float64)
tf.Tensor(0.6077618359706897, shape=(), dtype=float64)
tf.Tensor(0.6066195153501247, shape=(), dtype=float64)
tf.Tensor(0.6006516042151808, shape=(), dtype=float64)
tf.Tensor(0.6349453151067586, shape=(), dtype=float64)
tf.Tensor(0.5885498297394218, shape=(), dtype=float64)
tf.Tensor(0.6184492817518275, shape=(), dtype=float64)
tf.Tensor(0.6042946833003386, shape=(), dtype=float64)
tf.Tensor(0.5923694402745249, shape=(), dtype=float64)
tf.Tensor(0.6607884794325751, shape=(), dtype=float64)
tf.Tensor(0.5707917477047824, shape=(), dtype=float64)
tf.Tensor(0.6095123615012861, shape=(), dtype=float64)
tf.Tensor(0.6012157368170894, shape=(), dtype=float64)
tf.Tensor(0.6589463272333929, shape=(), dtype=float64)
tf.Tensor(0.5419974398095152, shape=(), dtype=float64)
tf.Tensor(0.5586035636860434, shape=(), dtype=float64)
tf.Tensor(0.6286397021937381, shape=(), dtype=float64)
tf.Tensor(0.6440015627994593, shape=(), dtype=float64)
tf.Tensor(0.6039320678146346, shape=(), dtype=float64)
tf.Tensor(0.5713502248153103, shape=(), dtype=float64)
tf.Tensor(0.565715083139002, shape=(), dtype=float64)
tf.Tensor(0.5949027348523617, shape=(), dtype=float64)
tf.Tensor(0.6012267756034322, shape=(), dtype=float64)
tf.Tensor(0.6109874317155778, shape=(), dtype=float64)
Using tf.keras API¶
[14]:
from tensorcircuit import keras
def qml_y(x, weights, nlayers):
n = 9
weights = tc.backend.cast(weights, "complex128")
x = tc.backend.cast(x, "complex128")
c = tc.Circuit(n)
for i in range(n):
c.rx(i, theta=x[i])
for j in range(nlayers):
for i in range(n - 1):
c.cnot(i, i + 1)
for i in range(n):
c.rx(i, theta=weights[2 * j, i])
c.ry(i, theta=weights[2 * j + 1, i])
ypred = c.expectation([tc.gates.z(), (4,)])
ypred = tc.backend.real(ypred)
ypred = (tc.backend.real(ypred) + 1) / 2.0
return ypred
ql = keras.QuantumLayer(partial(qml_y, nlayers=nlayers), [(2 * nlayers, 9)])
[15]:
# keras interface with value and grad paradigm
@tf.function
def my_vvag(xs, ys):
with tf.GradientTape() as tape:
ypred = ql(xs)
loss = tf.keras.losses.BinaryCrossentropy()(ys, ypred)
return loss, tape.gradient(loss, ql.variables)
my_vvag(x_train_tf, y_train_tf)
[15]:
(<tf.Tensor: shape=(), dtype=float32, numpy=0.6987088918685913>,
[<tf.Tensor: shape=(6, 9), dtype=float64, numpy=
array([[-1.10936817e-02, -1.33291538e-03, -5.87745373e-03,
-6.35860588e-03, -2.93374022e-05, -5.94974417e-03,
-2.07285971e-04, -1.51788304e-17, 3.46944695e-18],
[ 1.12992936e-02, 2.18895149e-03, 3.53805373e-03,
5.40332844e-03, 7.12623702e-03, 1.38455270e-03,
-1.62189613e-02, 1.99493200e-17, -8.67361738e-19],
[ 5.90694766e-03, -8.79280433e-03, 8.48293460e-04,
-7.19331663e-03, 3.53495779e-06, -2.28551831e-04,
8.89045781e-18, 2.16840434e-18, -2.81892565e-17],
[-5.24045302e-03, -4.10061147e-03, -2.07908310e-03,
-8.17067217e-03, 1.80390058e-02, 1.12861689e-04,
4.77048956e-18, 3.03576608e-18, 1.73472348e-18],
[-4.22838847e-18, -1.64798730e-17, 5.63785130e-18,
-2.60208521e-18, -2.72541524e-03, 1.73472348e-18,
6.81692116e-18, 9.97465999e-18, -1.95156391e-17],
[-1.56125113e-17, -2.42861287e-17, -1.51788304e-17,
1.34441069e-17, -8.56806553e-03, -1.12757026e-17,
-2.08166817e-17, 8.45677695e-18, -1.57209315e-17]])>])
[16]:
# %timeit my_vvag(x_train_tf, y_train_tf)
[17]:
# keras interface with keras training paradigm
model = tf.keras.Sequential([ql])
model.compile(
loss=tf.keras.losses.BinaryCrossentropy(),
optimizer=tf.keras.optimizers.Adam(0.01),
metrics=[tf.keras.metrics.BinaryAccuracy()],
)
model.fit(x_train_tf, y_train_tf, batch_size=32, epochs=100)
Epoch 1/100
4/4 19s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6982
Epoch 2/100
4/4 0s 13ms/step - binary_accuracy: 0.6300 - loss: 0.6974
Epoch 3/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6947
Epoch 4/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6929
Epoch 5/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6916
Epoch 6/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6896
Epoch 7/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6871
Epoch 8/100
4/4 0s 16ms/step - binary_accuracy: 0.6300 - loss: 0.6835
Epoch 9/100
4/4 0s 13ms/step - binary_accuracy: 0.6300 - loss: 0.6810
Epoch 10/100
4/4 0s 13ms/step - binary_accuracy: 0.6300 - loss: 0.6782
Epoch 11/100
4/4 0s 13ms/step - binary_accuracy: 0.6300 - loss: 0.6761
Epoch 12/100
4/4 0s 13ms/step - binary_accuracy: 0.6300 - loss: 0.6739
Epoch 13/100
4/4 0s 13ms/step - binary_accuracy: 0.6300 - loss: 0.6706
Epoch 14/100
4/4 0s 13ms/step - binary_accuracy: 0.6300 - loss: 0.6677
Epoch 15/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6652
Epoch 16/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6615
Epoch 17/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6590
Epoch 18/100
4/4 0s 15ms/step - binary_accuracy: 0.6300 - loss: 0.6563
Epoch 19/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6541
Epoch 20/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6517
Epoch 21/100
4/4 0s 15ms/step - binary_accuracy: 0.6300 - loss: 0.6497
Epoch 22/100
4/4 0s 15ms/step - binary_accuracy: 0.6300 - loss: 0.6496
Epoch 23/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6481
Epoch 24/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6456
Epoch 25/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6436
Epoch 26/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6410
Epoch 27/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6404
Epoch 28/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6387
Epoch 29/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6380
Epoch 30/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6369
Epoch 31/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6369
Epoch 32/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6343
Epoch 33/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6333
Epoch 34/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6326
Epoch 35/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6319
Epoch 36/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6321
Epoch 37/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6313
Epoch 38/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6302
Epoch 39/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6283
Epoch 40/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6288
Epoch 41/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6294
Epoch 42/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6281
Epoch 43/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6270
Epoch 44/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6267
Epoch 45/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6262
Epoch 46/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6258
Epoch 47/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6252
Epoch 48/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6243
Epoch 49/100
4/4 0s 15ms/step - binary_accuracy: 0.6300 - loss: 0.6241
Epoch 50/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6236
Epoch 51/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6234
Epoch 52/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6231
Epoch 53/100
4/4 0s 13ms/step - binary_accuracy: 0.6300 - loss: 0.6231
Epoch 54/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6232
Epoch 55/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6223
Epoch 56/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6215
Epoch 57/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6227
Epoch 58/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6232
Epoch 59/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6210
Epoch 60/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6220
Epoch 61/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6219
Epoch 62/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6218
Epoch 63/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6205
Epoch 64/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6194
Epoch 65/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6200
Epoch 66/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6202
Epoch 67/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6203
Epoch 68/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6201
Epoch 69/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6194
Epoch 70/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6189
Epoch 71/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6195
Epoch 72/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6185
Epoch 73/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6184
Epoch 74/100
4/4 0s 13ms/step - binary_accuracy: 0.6300 - loss: 0.6183
Epoch 75/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6185
Epoch 76/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6182
Epoch 77/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6179
Epoch 78/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6183
Epoch 79/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6179
Epoch 80/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6181
Epoch 81/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6185
Epoch 82/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6195
Epoch 83/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6184
Epoch 84/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6168
Epoch 85/100
4/4 0s 14ms/step - binary_accuracy: 0.6300 - loss: 0.6167
Epoch 86/100
4/4 0s 14ms/step - binary_accuracy: 0.7500 - loss: 0.6169
Epoch 87/100
4/4 0s 13ms/step - binary_accuracy: 0.7700 - loss: 0.6190
Epoch 88/100
4/4 0s 14ms/step - binary_accuracy: 0.7700 - loss: 0.6211
Epoch 89/100
4/4 0s 14ms/step - binary_accuracy: 0.7700 - loss: 0.6218
Epoch 90/100
4/4 0s 14ms/step - binary_accuracy: 0.7700 - loss: 0.6212
Epoch 91/100
4/4 0s 15ms/step - binary_accuracy: 0.7700 - loss: 0.6185
Epoch 92/100
4/4 0s 15ms/step - binary_accuracy: 0.7100 - loss: 0.6189
Epoch 93/100
4/4 0s 15ms/step - binary_accuracy: 0.6300 - loss: 0.6161
Epoch 94/100
4/4 0s 15ms/step - binary_accuracy: 0.6300 - loss: 0.6159
Epoch 95/100
4/4 0s 15ms/step - binary_accuracy: 0.6300 - loss: 0.6160
Epoch 96/100
4/4 0s 15ms/step - binary_accuracy: 0.6300 - loss: 0.6165
Epoch 97/100
4/4 0s 15ms/step - binary_accuracy: 0.6300 - loss: 0.6167
Epoch 98/100
4/4 0s 16ms/step - binary_accuracy: 0.6300 - loss: 0.6181
Epoch 99/100
4/4 0s 16ms/step - binary_accuracy: 0.6300 - loss: 0.6192
Epoch 100/100
4/4 0s 15ms/step - binary_accuracy: 0.6300 - loss: 0.6188
[17]:
<keras.src.callbacks.history.History at 0x7732302573e0>
Quantum-Classical Hybrid Model in Keras¶
[18]:
def qml_ys(x, weights, nlayers):
n = 9
weights = tc.backend.cast(weights, "complex128")
x = tc.backend.cast(x, "complex128")
c = tc.Circuit(n)
for i in range(n):
c.rx(i, theta=x[i])
for j in range(nlayers):
for i in range(n - 1):
c.cnot(i, i + 1)
for i in range(n):
c.rx(i, theta=weights[2 * j, i])
c.ry(i, theta=weights[2 * j + 1, i])
ypreds = []
for i in range(n):
ypred = c.expectation([tc.gates.z(), (i,)])
ypred = tc.backend.real(ypred)
ypred = (tc.backend.real(ypred) + 1) / 2.0
ypreds.append(ypred)
return tc.backend.stack(ypreds)
[19]:
ql = tc.keras.QuantumLayer(partial(qml_ys, nlayers=nlayers), [(2 * nlayers, 9)])
model = tf.keras.Sequential([ql, tf.keras.layers.Dense(1, activation="sigmoid")])
[20]:
model.compile(
loss=tf.keras.losses.BinaryCrossentropy(),
optimizer=tf.keras.optimizers.Adam(0.01),
metrics=[tf.keras.metrics.BinaryAccuracy()],
)
model.fit(x_train_tf, y_train_tf, batch_size=32, epochs=100)
Epoch 1/100
4/4 24s 21ms/step - binary_accuracy: 0.3700 - loss: 0.9667
Epoch 2/100
4/4 0s 21ms/step - binary_accuracy: 0.3700 - loss: 0.8628
Epoch 3/100
4/4 0s 21ms/step - binary_accuracy: 0.3700 - loss: 0.7793
Epoch 4/100
4/4 0s 19ms/step - binary_accuracy: 0.3700 - loss: 0.7279
Epoch 5/100
4/4 0s 21ms/step - binary_accuracy: 0.4400 - loss: 0.6928
Epoch 6/100
4/4 0s 19ms/step - binary_accuracy: 0.6300 - loss: 0.6705
Epoch 7/100
4/4 0s 19ms/step - binary_accuracy: 0.6300 - loss: 0.6634
Epoch 8/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6582
Epoch 9/100
4/4 0s 19ms/step - binary_accuracy: 0.6300 - loss: 0.6574
Epoch 10/100
4/4 0s 19ms/step - binary_accuracy: 0.6300 - loss: 0.6602
Epoch 11/100
4/4 0s 19ms/step - binary_accuracy: 0.6300 - loss: 0.6603
Epoch 12/100
4/4 0s 19ms/step - binary_accuracy: 0.6300 - loss: 0.6582
Epoch 13/100
4/4 0s 22ms/step - binary_accuracy: 0.6300 - loss: 0.6569
Epoch 14/100
4/4 0s 21ms/step - binary_accuracy: 0.6300 - loss: 0.6558
Epoch 15/100
4/4 0s 25ms/step - binary_accuracy: 0.6300 - loss: 0.6558
Epoch 16/100
4/4 0s 21ms/step - binary_accuracy: 0.6300 - loss: 0.6551
Epoch 17/100
4/4 0s 21ms/step - binary_accuracy: 0.6300 - loss: 0.6547
Epoch 18/100
4/4 0s 21ms/step - binary_accuracy: 0.6300 - loss: 0.6533
Epoch 19/100
4/4 0s 21ms/step - binary_accuracy: 0.6300 - loss: 0.6528
Epoch 20/100
4/4 0s 21ms/step - binary_accuracy: 0.6300 - loss: 0.6521
Epoch 21/100
4/4 0s 21ms/step - binary_accuracy: 0.6300 - loss: 0.6515
Epoch 22/100
4/4 0s 22ms/step - binary_accuracy: 0.6300 - loss: 0.6508
Epoch 23/100
4/4 0s 21ms/step - binary_accuracy: 0.6300 - loss: 0.6500
Epoch 24/100
4/4 0s 21ms/step - binary_accuracy: 0.6300 - loss: 0.6490
Epoch 25/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6480
Epoch 26/100
4/4 0s 21ms/step - binary_accuracy: 0.6300 - loss: 0.6467
Epoch 27/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6454
Epoch 28/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6443
Epoch 29/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6429
Epoch 30/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6410
Epoch 31/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6400
Epoch 32/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6383
Epoch 33/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6366
Epoch 34/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6365
Epoch 35/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6345
Epoch 36/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6334
Epoch 37/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6324
Epoch 38/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6296
Epoch 39/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6277
Epoch 40/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6267
Epoch 41/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6276
Epoch 42/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6282
Epoch 43/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6264
Epoch 44/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6226
Epoch 45/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6223
Epoch 46/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6214
Epoch 47/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6197
Epoch 48/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6183
Epoch 49/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6173
Epoch 50/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6162
Epoch 51/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6166
Epoch 52/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6150
Epoch 53/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6130
Epoch 54/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6125
Epoch 55/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6119
Epoch 56/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6099
Epoch 57/100
4/4 0s 19ms/step - binary_accuracy: 0.6300 - loss: 0.6088
Epoch 58/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6084
Epoch 59/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6071
Epoch 60/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.6060
Epoch 61/100
4/4 0s 20ms/step - binary_accuracy: 0.6700 - loss: 0.6062
Epoch 62/100
4/4 0s 20ms/step - binary_accuracy: 0.7200 - loss: 0.6062
Epoch 63/100
4/4 0s 20ms/step - binary_accuracy: 0.7200 - loss: 0.6055
Epoch 64/100
4/4 0s 20ms/step - binary_accuracy: 0.7200 - loss: 0.6044
Epoch 65/100
4/4 0s 20ms/step - binary_accuracy: 0.7200 - loss: 0.6032
Epoch 66/100
4/4 0s 19ms/step - binary_accuracy: 0.7200 - loss: 0.6011
Epoch 67/100
4/4 0s 20ms/step - binary_accuracy: 0.7000 - loss: 0.5999
Epoch 68/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.5993
Epoch 69/100
4/4 0s 22ms/step - binary_accuracy: 0.7200 - loss: 0.5989
Epoch 70/100
4/4 0s 20ms/step - binary_accuracy: 0.7200 - loss: 0.5989
Epoch 71/100
4/4 0s 20ms/step - binary_accuracy: 0.7700 - loss: 0.6000
Epoch 72/100
4/4 0s 20ms/step - binary_accuracy: 0.7700 - loss: 0.6010
Epoch 73/100
4/4 0s 20ms/step - binary_accuracy: 0.7700 - loss: 0.5984
Epoch 74/100
4/4 0s 20ms/step - binary_accuracy: 0.7200 - loss: 0.5945
Epoch 75/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.5954
Epoch 76/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.5953
Epoch 77/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.5937
Epoch 78/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.5926
Epoch 79/100
4/4 0s 20ms/step - binary_accuracy: 0.7200 - loss: 0.5918
Epoch 80/100
4/4 0s 22ms/step - binary_accuracy: 0.7200 - loss: 0.5910
Epoch 81/100
4/4 0s 20ms/step - binary_accuracy: 0.7200 - loss: 0.5902
Epoch 82/100
4/4 0s 19ms/step - binary_accuracy: 0.7200 - loss: 0.5893
Epoch 83/100
4/4 0s 26ms/step - binary_accuracy: 0.7000 - loss: 0.5890
Epoch 84/100
4/4 0s 20ms/step - binary_accuracy: 0.6300 - loss: 0.5890
Epoch 85/100
4/4 0s 21ms/step - binary_accuracy: 0.6600 - loss: 0.5881
Epoch 86/100
4/4 0s 21ms/step - binary_accuracy: 0.7200 - loss: 0.5867
Epoch 87/100
4/4 0s 20ms/step - binary_accuracy: 0.7500 - loss: 0.5858
Epoch 88/100
4/4 0s 20ms/step - binary_accuracy: 0.7700 - loss: 0.5858
Epoch 89/100
4/4 0s 20ms/step - binary_accuracy: 0.7700 - loss: 0.5853
Epoch 90/100
4/4 0s 20ms/step - binary_accuracy: 0.7700 - loss: 0.5865
Epoch 91/100
4/4 0s 19ms/step - binary_accuracy: 0.7700 - loss: 0.5836
Epoch 92/100
4/4 0s 19ms/step - binary_accuracy: 0.7600 - loss: 0.5844
Epoch 93/100
4/4 0s 20ms/step - binary_accuracy: 0.7200 - loss: 0.5829
Epoch 94/100
4/4 0s 20ms/step - binary_accuracy: 0.7200 - loss: 0.5824
Epoch 95/100
4/4 0s 20ms/step - binary_accuracy: 0.7300 - loss: 0.5818
Epoch 96/100
4/4 0s 20ms/step - binary_accuracy: 0.7700 - loss: 0.5829
Epoch 97/100
4/4 0s 20ms/step - binary_accuracy: 0.7700 - loss: 0.5811
Epoch 98/100
4/4 0s 21ms/step - binary_accuracy: 0.7700 - loss: 0.5807
Epoch 99/100
4/4 0s 20ms/step - binary_accuracy: 0.7700 - loss: 0.5807
Epoch 100/100
4/4 0s 21ms/step - binary_accuracy: 0.7700 - loss: 0.5787
[20]:
<keras.src.callbacks.history.History at 0x7731903da180>
Hybrid Model in Jax¶
[21]:
tc.set_backend("jax")
[21]:
jax_backend
[22]:
key = jax.random.PRNGKey(42)
key, *subkeys = jax.random.split(key, num=4)
params = {
"qweights": jax.random.normal(subkeys[0], shape=[nlayers * 2, 9]),
"cweights:w": jax.random.normal(subkeys[1], shape=[9]),
"cweights:b": jax.random.normal(subkeys[2], shape=[1]),
}
[23]:
def qml_hybrid_loss(x, y, params, nlayers):
weights = params["qweights"]
w = params["cweights:w"]
b = params["cweights:b"]
ypred = qml_ys(x, weights, nlayers)
ypred = tc.backend.reshape(ypred, [-1, 1])
ypred = w @ ypred + b
ypred = jax.nn.sigmoid(ypred)
ypred = ypred[0]
loss = -y * tc.backend.log(ypred) - (1 - y) * tc.backend.log(1 - ypred)
return loss
[24]:
qml_hybrid_loss_vag = tc.backend.jit(
tc.backend.vvag(qml_hybrid_loss, vectorized_argnums=(0, 1), argnums=2),
static_argnums=3,
)
[25]:
qml_hybrid_loss_vag(x_train_jax, y_train_jax, params, nlayers)
[25]:
(Array([0.42296214, 1.06451071, 1.06809695, 1.06451071, 0.42617817,
0.99293054, 1.06451071, 1.05843045, 0.42617817, 0.99293054,
0.42617817, 1.06451071, 0.42617817, 1.06451071, 1.06809695,
1.00047892, 1.06451071, 1.06451071, 1.05843045, 0.42617817,
1.00047892, 1.06451071, 1.06451071, 0.99293054, 1.06451071,
1.06451071, 0.99293054, 0.41962206, 1.06451071, 0.47539413,
0.42617817, 0.99293054, 0.42296214, 0.99293054, 0.42296214,
1.06451071, 0.46151499, 1.06451071, 0.42617817, 1.06809695,
1.06451071, 1.06451071, 0.99293054, 0.99293054, 0.42296214,
0.97190916, 0.99293054, 0.47539413, 0.42296214, 0.46281253,
1.06809695, 0.99293054, 0.42617817, 0.45839653, 1.06451071,
1.06451071, 0.42296214, 1.06451071, 0.41962206, 0.47539413,
0.42617817, 0.99293054, 0.42617817, 1.06451071, 0.99293054,
1.06451071, 0.47539413, 0.46281253, 0.46281253, 0.42617817,
1.06451071, 1.06451071, 1.06451071, 1.06451071, 0.99293054,
1.06809695, 0.45839653, 1.00047892, 1.06809695, 0.47539413,
1.06451071, 1.00047892, 0.42296214, 1.06809695, 0.42296214,
0.99293054, 1.06451071, 0.42296214, 0.47539413, 0.42296214,
1.06451071, 1.06451071, 0.99293054, 0.99293054, 1.06451071,
0.42617817, 1.06451071, 1.05843045, 0.42296214, 1.06451071], dtype=float64),
{'cweights:b': Array([-27.60969466], dtype=float64),
'cweights:w': Array([-10.78040346, -10.87314726, -15.07110661, -12.72698165,
-15.33980945, -12.70154898, -17.276793 , -14.65462215,
-15.10169607], dtype=float64),
'qweights': Array([[ 1.07837222, 1.14862857, 4.08188093, -0.73899154, 2.24145584,
1.08221089, -0.63208409, -0.37780274, -0.00984224],
[-2.50629362, 4.14620331, -0.10406577, -4.20878939, 0.01668428,
-0.39961727, -3.29669781, 0.13660433, 3.28594573],
[-0.84835589, -0.72194368, 2.2384005 , 1.97123603, 1.7586361 ,
3.06693874, 0.21521147, 0.0979662 , -0.04180935],
[ 0.04185801, -3.67384719, -1.69659406, 4.00656887, -1.82225239,
-0.03083275, -1.31920138, -1.62435761, 2.42158261],
[ 0.34029456, -0.29444358, -0.79588072, 0.11709385, -0.1857751 ,
-0.16023102, -0.13206503, 0.47098048, 0.05892652],
[ 2.15180685, 3.76408443, -0.77598454, -0.09997724, -0.52802781,
-0.21509553, 6.63832787, 0.05986613, 0.37488656]], dtype=float64)})
[26]:
optimizer = optax.adam(5e-3)
opt_state = optimizer.init(params)
for i, (xs, ys) in zip(range(2000), mnist_data): # using tf data loader here
xs = xs.numpy()
ys = ys.numpy()
v, grads = qml_hybrid_loss_vag(xs, ys, params, nlayers)
updates, opt_state = optimizer.update(grads, opt_state)
params = optax.apply_updates(params, updates)
if i % 30 == 0:
print(jnp.mean(v))
0.7595997774524819
0.6994468456477553
0.6051339455522041
0.6907751908593655
0.6504548087589277
0.6298531482984697
0.6905749656686843
0.5683908219255306
0.5860599600754657
0.53816559172625
0.6077727533609933
0.49719410218086174
0.49481526574527157
0.595603699577792
0.5821019592745469
0.6153932214816075
0.4175060645911137
0.5555623797141207
0.49696723224154776
0.49861575232585675
0.6292010184536571