Weights not updating during optimization

I am new to QML. I was practicing with the Breast Cancer Dataset for practicing QML. It is a binary classification task. Originally, I want to work on a different dataset.
The issue in my code is: the weights are not updating after each optimization step. Weights should change, so that my cost can reduce.

# Binnary classification for Breast Cancer Dataset. Issue: Weights are not updating during optimization
# importing libraries
import pennylane as qml
from pennylane import numpy as np
from sklearn.datasets import load_breast_cancer

# loading data
data, target = load_breast_cancer(return_X_y=True)     # data shape -> (569, 30)

# making target: {0, 1} ---> {-1, 1}
for i in range(len(target)):
    if target[i] == 0:
        target[i] = -1

# device initialization
dev = qml.device('default.qubit', shots = 100)

# declaring weights
weights = np.random.random(size=(5), requires_grad=True)

# circuit
@qml.qnode(dev)
def circuit(weights, X):
    # amplitude encoding
    qml.AmplitudeEmbedding(X, wires=[0,1,2,3,4], normalize=True, pad_with=0)

    # variational layers
    for i in range(5):
        qml.RX(weights[i], wires=i)
        qml.RX(weights[i], wires=i)
        qml.RY(weights[i], wires=i)

    for ii in ([0,1], [1,2], [2,3], [3,4], [4,0]):
        qml.CNOT(wires=ii)

    return qml.expval(qml.PauliZ(0))

def predictor(weights):        # function to find the y_pred
    y_pred = []
    for row in data:
        pred = np.sign(circuit(weights, row))
        if pred == 0:
            pred = 1
        y_pred.append(pred)
    return y_pred

def cost(weights):            # function to find cost
    y_pred = predictor(weights)
    return np.mean((target-y_pred)**2)

def accuracy(target, y_pred):               # function to find accuracy in classification
    acc = sum(abs(l - p) < 1e-5 for l, p in zip(target, y_pred))
    acc = acc / len(target)
    return acc

opt = qml.GradientDescentOptimizer(0.001)       # declaration of optimizer

steps = 100        # number of steps

for ii in range(steps):
    weights = opt.step(cost, weights)           
    if ii%10 == 0:
        print(f'cost at step {ii+1} is: {cost(weights)}')
        print(f'weights at step {ii+1} is: {weights}')
        acc = accuracy(target, predictor(weights))
        print(f'accuracy at step {ii+1} is: {acc}')
        print('')

If you want help with diagnosing an error, please put the full error message below:

# there are no errors, but the code behavior is not desired. 

qml.about() output:

Name: PennyLane
Version: 0.35.1
Summary: PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
Home-page: GitHub - PennyLaneAI/pennylane: PennyLane is a cross-platform Python library for differentiable programming of quantum computers. Train a quantum computer the same way as a neural network.
Author:
Author-email:
License: Apache License 2.0
Location: /usr/local/lib/python3.10/dist-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions
Required-by: PennyLane_Lightning

Platform info: Linux-6.1.58±x86_64-with-glibc2.35
Python version: 3.10.12
Numpy version: 1.25.2
Scipy version: 1.11.4
Installed devices:

  • default.clifford (PennyLane-0.35.1)
  • default.gaussian (PennyLane-0.35.1)
  • default.mixed (PennyLane-0.35.1)
  • default.qubit (PennyLane-0.35.1)
  • default.qubit.autograd (PennyLane-0.35.1)
  • default.qubit.jax (PennyLane-0.35.1)
  • default.qubit.legacy (PennyLane-0.35.1)
  • default.qubit.tf (PennyLane-0.35.1)
  • default.qubit.torch (PennyLane-0.35.1)
  • default.qutrit (PennyLane-0.35.1)
  • null.qubit (PennyLane-0.35.1)
  • lightning.qubit (PennyLane_Lightning-0.35.1)

Hi @sakhujasaiyam,

I think the issue here is in how you’re handling your different data shapes.

The code below uses a smaller version of your dataset to show this. The latter part of the code is based on the code at the end of the PennyLane blog post How to start learning quantum machine learning. In this example I’m using a loss function as well as a cost function, to handle the data in a way that’s trainable.

You may find that the training isn’t very good, after all you’re just using 10 datapoints, and you may benefit from using a different optimizer such as qml.AdamOptimizer, but at least you won’t see any warnings that “Output seems independent of input”. Note that this warning only appears the first time you use a certain runtime so I encourage you to use Google Colab and click on Runtime → Restart session every time until you’re confident that this warning won’t show up anymore.

# Binary classification for Breast Cancer Dataset. Issue: Weights are not updating during optimization
# importing libraries
import pennylane as qml
from pennylane import numpy as np
from sklearn.datasets import load_breast_cancer

# loading data
data, target = load_breast_cancer(return_X_y=True)     # data shape -> (569, 30)

# making target: {0, 1} ---> {-1, 1}
for i in range(len(target)):
    if target[i] == 0:
        target[i] = -1

# device initialization
dev = qml.device('default.qubit', shots = 100)

# declaring weights
weights = np.random.random(size=(5), requires_grad=True)

# Used a small subset of the dataset for faster computation
data = data[:10]
target = target[:10]
print(data.shape)
print(target.shape)

# circuit
@qml.qnode(dev)
def circuit(weights, X):
    # amplitude encoding
    qml.AmplitudeEmbedding(X, wires=[0,1,2,3,4], normalize=True, pad_with=0)

    # variational layers
    for i in range(5):
        qml.RX(weights[i], wires=i)
        qml.RX(weights[i], wires=i)
        qml.RY(weights[i], wires=i)

    for ii in ([0,1], [1,2], [2,3], [3,4], [4,0]):
        qml.CNOT(wires=ii)

    return qml.expval(qml.PauliZ(0))


opt = qml.GradientDescentOptimizer(0.001)       # declaration of optimizer

# The code below is taken from the example in this PennyLane blog post 
# https://pennylane.ai/blog/2021/10/how-to-start-learning-quantum-machine-learning/

# Step 3 - Classical Pre/Postprocessing
def loss_func(predictions):
    # This is a postprocessing step. Here we use a least squares metric
    # based on the predictions of the quantum circuit and the outputs
    # of the training data points.

    total_losses = 0
    for i in range(len(target)):
        output = target[i]
        prediction = predictions[i]
        loss = (prediction - output)**2
        total_losses += loss
    return total_losses

# Step 4 - Define your cost function, including any classical pre/postprocessing
def cost_fn(params):
    # We get the predictions of the quantum circuit for a specific
    # set of parameters along the entire input dataset
    predictions = [circuit(params, x)  for x in data]
    # We calculate the cost including any classical postprocessing
    cost = loss_func(predictions)
    return cost

# Step 5.4 - We iterate over a number of defined steps (100)
for i in range (100):
    # Over each step the parameters change to give a better cost
    weights = opt.step(cost_fn,weights)
    if i%10 == 0:
         # We print the result after every 10 steps
        print(f'Step = {i} Cost = {cost_fn(weights)}')

I hope this helps you!

Thankyou so much @CatalinaAlbornoz. It working now.

Hello, @CatalinaAlbornoz can you please explain a little more about the difference in our approaches? I was using another function to predict the values and calculating the error in cost function only, but you used slightly different approach. On paper both approaches about same. I m a little confused here.

Hi @sakhujasaiyam,

While both approaches seem very similar the way of handling the data is different and sometimes these nuances can be enough to make the program succeed or fail. My recommendation is that you use the working code as the base and slowly incorporate the elements of the non-working code until you find the exact line that’s causing the issue.

I hope this helps!

1 Like