2
$\begingroup$

I am trying to bulid a quantum convolutional neural network for image classification with Pennylane and Keras but the model isn't training and I keep getting the warning:

WARNING:tensorflow:Gradients do not exist for variables ['params:0', 'params:0'] when minimizing the loss. If you're using model.compile(), did you forget to provide a loss argument?

How do I fix this issue?

I have used code from Quantum Convolutional Neural Network using Keras and Quanvolutional Neural Network.

Here are the relavent snippents of my code:

import pennylane as qml
from pennylane import numpy as np
import tensorflow as tf
from tensorflow import keras

This code creates the quantum circuit to act as a filter:

q = 4

layers=2

dev = qml.device("lightning.qubit", wires=q)

#params has dimension 2
def unit2(params, wires=[0, 1]):
    for j in range(2):
        qml.RY(params[j], wires=wires[j])
    qml.CNOT(wires=wires)

##### POOLING UNITS

#ZX pooling, params has dimensions 2
def pool1(params, wires=[0, 1]):
    qml.CRZ(params[0], wires=wires)
    qml.CRX(params[1], wires=wires)

@qml.qnode(dev, diff_method='parameter-shift')
def circuit(inputs, params):
    height = int(q/2)
    site = range(int(q))
    
    ##### ENCODING
    for i in range(q):
        qml.RX(inputs[i]*2*np.pi, wires=i)
    
    count = 0
    pdim = 2 #param dimension of unit
    for t in range(1, layers + 1):
        l = int(len(site)/2)
        
        ### CONVOLUTION
        for i in range(l):
            ## Change unit here
            unit2(params[count:count + pdim], wires=[site[2*i], site[2*i + 1]])
            count += pdim
        for i in range(l-1):
            ## Change unit here
            unit2(params[count:count + pdim], wires=[site[2*i + 1], site[2*i + 2]])
            count += pdim
        
        ### POOLING
        for i in range(l):
            ## Change unit here
            pool1(params[count:count + 2], wires=[site[2*i], site[2*i + 1]])
            count += 2
            
        trial = []
        for u in range(int(len(site)/2)):
            trial = trial + [site[2*u+1]]
        site = trial
    #print(count)
        
    return [qml.expval(qml.PauliZ(q-1))] #### Only measures off final qubit

which looks like this where I only read off the final qubit (numbers on gates are placeholders for the filters weights): graph of quantum circuit defined above

I create the model here:

class QuantumClassifier(tf.keras.Model):

    def __init__(self, filter_channels, kernal_params):
        super(QuantumClassifier,self).__init__()
        self.filters = filter_channels
        self.qfilters = []
        weight_shapes = {"params": (kernal_params)}
        for i in range(filter_channels):
            self.qfilters.append(qml.qnn.KerasLayer(circuit, weight_shapes,output_dim=1, name='quantum_filter')) 
        self.hidden = tf.keras.layers.Dense(128, activation = 'relu')
        self.flatten = tf.keras.layers.Flatten()
        self.dense = tf.keras.layers.Dense(10, activation='softmax')
        print('INITILIZED')


    def call(self, inputs):
 
        width, length = inputs.shape[1], inputs.shape[2]
        batch_size = inputs.shape[0]

        total_out = tf.TensorArray(tf.float32, size=batch_size)
        count = 0
        #perform convolution with no padding and stride of 2
        for a in range(batch_size):
            
            out = np.zeros((width//2, length//2, self.filters))
            print(f"Image: {a}")
            
            
            for i in range(0, width, 2):
                
                for j in range(0, length, 2):
                    
                    for f in range(self.filters):
                        
                        # convolution windows, now only applying to one channel image
                        sub_input =  inputs[a,i:i+q, j:j+q, :]
                        # flatten into 1-D
                        sub_input = tf.reshape(sub_input, [-1])
                        quantum_filter = self.qfilters[f]
                    
                        
                        out[i//2][j//2][f] = quantum_filter(sub_input)
                                

            total_out = total_out.write(count, out)
            count += 1
            
        total_out = total_out.stack()
        print("All input data for one batch have been convolved!")

        x = self.flatten(total_out)

        x = self.hidden(x)

        x = self.dense(x)
        return x

I created the model for 2 quantum filters where each one has 14 parameters:

model = QuantumClassifier(2, 14)
model.compile(loss='sparse_categorical_crossentropy',
             optimizer='adam',
             metrics=['accuracy'])
r = model.fit(x_train_small, y_train, epochs=10, batch_size=16, validation_data=(x_test_small, y_test))

Here is the dataset I am using:

mnist_dataset = keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist_dataset.load_data()
# Reduce dataset size
x_train = x_train[:n_train]
y_train = y_train[:n_train]
x_test = x_test[:n_test]
y_test = y_test[:n_test]
# Normalize pixel values within 0 and 1
x_train = x_train / 255
x_test = x_test / 255
# # Add extra dimension for "color" channels
x_train = np.array(x_train[..., tf.newaxis])
x_test = np.array(x_test[..., tf.newaxis])
print(f"train_images_shape: {x_train.shape}")
# use Bilinear Interpolation for downscaling
x_train_small = tf.image.resize(x_train, (10,10)).numpy()
x_test_small = tf.image.resize(x_test, (10,10)).numpy()
print(f"x_train_reshape: {x_train_small.shape}")

This is all in: python==3.11.5 tensorflow==2.15.0 PennyLane==0.34.0

Any solutions (or ways to speed up my code) greatly appreciated!

$\endgroup$
1

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.