Quantum convolutional neural network

Hello everyone, I want to construct a quantum convolutional neural network with a quantum convolutional layer and two classical fully connection layers. When using keras librarys to convert quantum convolutional layers into classical layers, an error accurred. It means the qnode function must return a single measurement, or a nonempty sequence of measurements, but In the code , I must return the data. Can you help me think of a way?


# -- coding: utf-8 --
import pennylane as qml
from pennylane import numpy as np
from pennylane.templates import RandomLayers
import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt

n_epochs = 30 # Number of optimization epochs
n_layers = 1 # Number of random layers
n_train = 50 # Size of the train dataset
n_test = 30 # Size of the test dataset

SAVE_PATH = “quanvolution/” # Data saving folder
PREPROCESS = True # If False, skip quantum processing and load data from SAVE_PATH
np.random.seed(0) # Seed for NumPy random number generator
tf.random.set_seed(0) # Seed for TensorFlow random number generator

mnist_dataset = keras.datasets.mnist
(train_images, train_labels), (test_images, test_labels) = mnist_dataset.load_data()

Reduce dataset size

train_images = train_images[:n_train]
train_labels = train_labels[:n_train]
test_images = test_images[:n_test]
test_labels = test_labels[:n_test]

Normalize pixel values within 0 and 1

train_images = train_images / 255
test_images = test_images / 255

Add extra dimension for convolution channels

train_images = np.array(train_images[…, tf.newaxis], requires_grad=False)

test_images = np.array(test_images[…, tf.newaxis], requires_grad=False)

train_images = tf.reshape(train_images,[-1,784])
test_images = tf.reshape(test_images,[-1,784])

train_images = tf.constant(train_images)
test_images = tf.constant(test_images)
train_labels = tf.one_hot(train_labels, depth=10)
test_labels = tf.one_hot(test_labels, depth=10)

dev = qml.device(“default.qubit”, wires=4)
dev1= qml.device(“default.qubit”, wires=4)

Random circuit parameters

rand_params = np.random.uniform(high=2 * np.pi, size=(n_layers, 4))

@qml.qnode(dev)
def circuit(phi,params):
# Encoding of 4 classical input values
for j in range(4):
qml.RY(np.pi * phi[j], wires=j)

# Random quantum circuit
RandomLayers(params, wires=list(range(4)))
# Measurement producing 4 classical output values
return [qml.expval(qml.PauliZ(j)) for j in range(4)]

@qml.qnode(dev1)
def quanv(inputs,params): #必须返回一个测量
“”“Convolves the input image with many applications of the same quantum circuit.”“”
if(inputs.shape[0] == 784):
inputs = tf.reshape(inputs,[28,28,1])
out = np.zeros((14, 14, 4))

# Loop over the coordinates of the top-left pixel of 2X2 squares
for j in range(0, 28, 2):
    for k in range(0, 28, 2):
        # Process a squared 2x2 region of the image with a quantum circuit
        q_results = circuit(
            [
                inputs[j, k, 0],
                inputs[j, k + 1, 0],
                inputs[j + 1, k, 0],
                inputs[j + 1, k + 1, 0]
            ],params
        )
        # Assign expectation values to different channels of the output pixel (j/2, k/2)
        for c in range(4):
            out[j // 2, k // 2, c] = q_results[c]
out = tf.reshape(out,[784,])    
return out   

weight_shapes = {“params”: (1,4)} #参数名必须和qnode中的参数名一致
qlayer = qml.qnn.KerasLayer(quanv,weight_shapes,output_dim = 10) # output_dim = 49
clayer1 = tf.keras.layers.Dense(10)
clayer2 = tf.keras.layers.Dense(10, activation = “softmax”)
q_model = tf.keras.models.Sequential([qlayer, clayer1, clayer2])

opt = tf.keras.optimizers.SGD(learning_rate=0.5)

q_model.compile(
optimizer=‘adam’,
loss=“sparse_categorical_crossentropy”,
metrics=[“accuracy”],
) #model.compile()方法用于在配置训练方法时,告知训练时用的优化器、损失函数和准确率评测标准

q_history = q_model.fit( #数据集数据类型必须是张量,将训练数据在模型中训练一定次数,返回loss和测量指标
train_images,
train_labels,
validation_data=(test_images,test_labels),
batch_size=4,
epochs=2,
verbose=2,
)

plt.style.use(“seaborn”)
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(6, 9))
ax1.plot(q_history.history[“val_accuracy”], “-ob”, label=“With quantum layer”)
#ax1.plot(c_history.history[“val_accuracy”], “-og”, label=“Without quantum layer”)
ax1.set_ylabel(“Accuracy”)
ax1.set_ylim([0, 1])
ax1.set_xlabel(“Epoch”)
ax1.legend()

Hi @shangshang_shi,

I think the problem is with your second qnode. You’re returning ‘out’ which looks like some tensor, but the return value needs to be a measurement, because your qnode should be a quantum circuit. From what I can see in that qnode you don’t have anything quantum in it so you may want to remove the @qml.qnode(dev1) decorator. I can see that you’re calling the quantum circuit but you don’t need the decorator again since you’re only getting the results.

I hope this helps you!

thank you,@CatalinaAlbornoz.
Your analysis is quite correct. I want to use the second qnode and return the convolutional output which is tensor type, but the qnode must return a single measurement or an nonempty sequence of measurements. The question is if I convert the quantum convolutional layer into a classical layer, it must be a qnode, but if it is a qnode, it cannot return the output that I want.
Can you give me some advice?
Thank you again

Hi @shangshang_shi, I’m not understanding your question. I think that removing the second qnode decorator should be enough. If everything is classical then there’s no need for a qnode.

than you again @CatalinaAlbornoz , Your words inspired me , the node is a classical qnode ,Why don’t I customize a Keras layer?

I’m glad this helped @shangshang_shi! Please let me know if you have any further questions.