I'm doing a small program that is supposed to classify the data of the Wisconsin Breast Cancer database contained in sklearn.datasets using Quantum Neural Networks (specifically EstimatorQNN).
I think the code does the job just fine, but I have 2 major problems that I would like to fix:
- I'm using SPSA as an optimizer, which seems to work, but if I try to use COBYLA, it doesn't work. Why is that?
- While the code works, it takes a long time to do the job, and the accuracy that I seem to get is not that high (around 40%). Can you guys help me out to optimize this and make it a bit better?
The code is this one:
from qiskit import QuantumCircuit
from qiskit.circuit.library import ZZFeatureMap, RealAmplitudes
from qiskit_machine_learning.neural_networks import EstimatorQNN
from qiskit_algorithms.optimizers import COBYLA
from qiskit_algorithms.optimizers import SPSA
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.feature_selection import SelectKBest, f_classif
import numpy as np
import matplotlib.pyplot as plt
def print_circuit(circuit):
print("*****************************************************")
print(f"Circuit depth: {circuit.depth()}")
print(f"Number of qubits: {circuit.num_qubits}")
print(f"Number of classical bits: {circuit.num_clbits}")
print(f"Total operations: {len(circuit)}")
print("\nCircuit composition:")
print(circuit)
print("*****************************************************")
print("Decomposed Circuit:")
circuit_decomposed = circuit.decompose().decompose() # Multiple decompositions for better view
print(circuit_decomposed)
with open("Quantum_Circuit_Result.txt", "w", encoding="utf-8") as ResultFile:
ResultFile.write("COMPLETE QUANTUM CIRCUIT ANALYSIS\n")
ResultFile.write("*****************************************************\n\n")
# Individual components
ResultFile.write("INDIVIDUAL COMPONENTS:\n")
ResultFile.write("*****************************************************\n\n")
ResultFile.write("ZZFeatureMap:\n")
ResultFile.write("-"*15 + "\n")
ResultFile.write(str(feature_map) + "\n\n")
ResultFile.write("RealAmplitudes Ansatz:\n")
ResultFile.write("-"*20 + "\n")
ResultFile.write(str(ansatz) + "\n\n")
# Combined circuit
ResultFile.write("COMBINED CIRCUIT:\n")
ResultFile.write("-"*15 + "\n")
ResultFile.write(str(circuit) + "\n\n")
# Decomposed versions
ResultFile.write("DECOMPOSED CIRCUITS:\n")
ResultFile.write("*****************************************************\n\n")
ResultFile.write("Level 1 Decomposition:\n")
ResultFile.write("-"*20 + "\n")
decomposed1 = circuit.decompose()
ResultFile.write(str(decomposed1) + "\n\n")
ResultFile.write("Level 2 Decomposition:\n")
ResultFile.write("-"*20 + "\n")
decomposed2 = circuit.decompose().decompose()
ResultFile.write(str(decomposed2) + "\n\n")
# Gate analysis
ResultFile.write("GATE COUNT ANALYSIS:\n")
ResultFile.write("*****************************************************\n\n")
gate_counts = {}
for instruction in circuit.data:
gate_name = instruction.operation.name
gate_counts[gate_name] = gate_counts.get(gate_name, 0) + 1
for gate, count in sorted(gate_counts.items()):
ResultFile.write(f"{gate}: {count}\n")
ResultFile.write(f"\nTotal gates: {len(circuit)}\n")
print("Detailed analysis saved to: Quantum_Circuit_Detailed.txt")
print("*****************************************************")
print("")
# per stampare tutti i dettagli del QNN
def print_qnn_details(qnn, feature_map, ansatz):
print("*****************************************************")
print("ESTIMATOR QNN CONSTRUCTION DETAILS")
print("*****************************************************")
print(f"\nFeature Map (ZZFeatureMap):")
print(f" - Number of features: {len(feature_map.parameters)}")
print(f" - Repetitions: {feature_map.reps}")
print(f" - Number of parameters: {len(feature_map.parameters)}")
print(f" - Parameters: {list(feature_map.parameters)}")
print(f"\nAnsatz (RealAmplitudes):")
print(f" - Number of qubits: {ansatz.num_qubits}")
print(f" - Repetitions: {ansatz.reps}")
print(f" - Number of parameters: {ansatz.num_parameters}")
print(f" - Entanglement type: {ansatz.entanglement}")
print(f" - Parameters: {list(ansatz.parameters)}")
print(f"\nQuantum Circuit Composition:")
print(f" - Total qubits: {qnn.circuit.num_qubits}")
print(f" - Total gates: {len(qnn.circuit)}")
print(f" - Depth: {qnn.circuit.depth()}")
print(f" - Number of parameters: {len(qnn.circuit.parameters)}")
print(f"\nEstimatorQNN Configuration:")
print(f" - Input parameters: {len(qnn.input_params)}")
print(f" - Weight parameters: {len(qnn.weight_params)}")
print(f" - Input parameter names: {[str(p) for p in qnn.input_params]}")
print(f" - Weight parameter names: {[str(p) for p in qnn.weight_params]}")
print(f"How the EstimatorQNN is made:\n")
print(qnn)
print("*****************************************************")
print("")
# una funzione per plottare i valori predetti e metterli a confronto con quelli veri
def plot_predictions(y_true, y_pred, title="Predicted vs Actual Values"):
plt.figure(figsize=(12, 6))
# Plot actual vs predicted
indices = np.arange(len(y_true))
plt.scatter(indices, y_true, alpha=0.7, label='Actual Values', color='blue')
plt.scatter(indices, y_pred, alpha=0.7, label='Predicted Values', color='red', marker='x')
plt.xlabel('Sample Index')
plt.ylabel('Value')
plt.title(title)
plt.legend()
plt.grid(True, alpha=0.3)
# Add some statistics
mse = np.mean((y_true - y_pred) ** 2)
plt.figtext(0.15, 0.02, f'MSE: {mse:.4f}', fontsize=10)
plt.tight_layout()
plt.show()
# una funzione per plottare i progressi di training
def plot_training_progress(callback):
plt.figure(figsize=(10, 5))
plt.plot(callback.loss_history)
plt.xlabel('Iteration')
plt.ylabel('Loss')
plt.title('Training Progress')
plt.grid(True)
plt.show()
# callback for SPSA
class SPSACallback:
def __init__(self):
self.loss_history = []
self.iteration = 0
def __call__(self, nfev, weights, loss_value, *args):
self.loss_history.append(loss_value)
self.iteration += 1
if self.iteration % 10 == 0:
print(f"Iteration {self.iteration}, Loss: {loss_value:.4f}")
# Callback for COBYLA
class COBYLACallback:
def __init__(self):
self.loss_history = []
self.iteration = 0
def __call__(self, loss_value):
self.loss_history.append(loss_value)
self.iteration += 1
if self.iteration % 10 == 0:
print(f"Iteration {self.iteration}, Loss: {loss_value:.4f}")
# Carica il dataset contente il numero di cancri al seno nel Winsconsin
data = load_breast_cancer()
# recupero sia la matrice dei dati, sia il classification target
X, y = data.data, data.target
print("Dataset shape:", X.shape) # shape del dataset
print("Target distribution:", np.bincount(y))
# Normalizzo le feature dei dati recuperati tra [0, 1] (cruciale per la feature map quantistica)
X_scaled = MinMaxScaler().fit_transform(X)
# Feature selection con PCA per preservare varianza
#pca = PCA(n_components=4)
#X_processed = pca.fit_transform(X_scaled)
# Feature selection
selector = SelectKBest(f_classif, k=4)
X_processed = selector.fit_transform(X_scaled, y)
# Split training-test
# la maggior parte dei dati sono usati per train (80% circa), mentre la restante parte è per la validazione del training
X_train, X_test, y_train, y_test = train_test_split(X_processed, y, test_size=0.2, random_state=42,stratify=y)
# cercare un altro set diverso su cui fare il test (?)
print(f"Training set: {X_train.shape[0]} samples")
print(f"Test set: {X_test.shape[0]} samples")
# Circuito quantistico: Feature Map + Ansatz
num_features = X_train.shape[1] # Usiamo solo 4 features per limitare i qubit (puoi aumentare se il PC lo permette)
feature_map = ZZFeatureMap(feature_dimension=num_features, reps=2)
# definisco qui il mio "ansatz" (ovvero la mia ipotesi/funzione di prova)
# utilizzo RealAmplitudes come entanglement perfetto. Questo pattern viene fortemente usato per lavori di classificazione.
# It is called RealAmplitudes since the prepared quantum states will only have real amplitudes, the complex part is always 0.
ansatz = RealAmplitudes(num_qubits=num_features, reps=3, entanglement='full')
# creazione del circuito di quantum
qc = QuantumCircuit(num_features)
qc.compose(feature_map, inplace=True)
qc.compose(ansatz, inplace=True)
# Creo qui il mio EstimatorQNN, passandogli il circuito e i parametri di input e di weight
qnn = EstimatorQNN(
circuit=qc,
input_params=feature_map.parameters,
weight_params=ansatz.parameters
)
#stampo tutti i dettagli del QNN appena creato
print_qnn_details(qnn, feature_map, ansatz)
# Funzione di loss (Mean Squared Error)
def loss_fnOLD(weights, X, y):
predictions = qnn.forward(X, weights)
return np.mean((predictions - y) ** 2)
# funzione di loss
def loss_fn(weights, X, y):
predictions = qnn.forward(X, weights)
epsilon = 1e-8
predictions = np.clip(predictions, epsilon, 1-epsilon)
return -np.mean(y * np.log(predictions) + (1-y) * np.log(1-predictions))
# Ottimizzazione
# COBYLA svolge un lavoro di grid search (studio più approfondito a riguardo)
# cercare cosa altro usare magari come funzione di loss o altro
# ho usato alla fine SPSA
callback = SPSACallback()
optimizer = SPSA(maxiter=120, callback=callback)
initial_weights = 0.1 * np.random.rand(ansatz.num_parameters)
result = optimizer.minimize(
fun=lambda w: loss_fn(w, X_train, y_train),
x0=initial_weights)
print(f"\nOptimization completed:")
print(f" - Final loss: {result.fun:.4f}")
print(f" - Function evaluations: {result.nfev}")
print(f" - Optimal weights shape: {result.x.shape}")
# Valutazione sul test set
test_predictions = qnn.forward(X_test, result.x)
test_accuracy = np.mean((test_predictions > 0.5) == y_test)
print(f"Accuracy sul test set: {test_accuracy * 100:.2f}%")
# Plot training progress
plot_training_progress(callback)
#Plot predicted vs actual values
plot_predictions(y_test, test_predictions, "Test Set: Predicted vs Actual Values")
# Additional analysis
print(f"\nDetailed Performance Analysis:")
print(f"Mean Squared Error: {np.mean((test_predictions - y_test) ** 2):.4f}")
print(f"Mean Absolute Error: {np.mean(np.abs(test_predictions - y_test)):.4f}")
# mostriamo qui il circuito Quantum creato ed EstimatorQNN
#mostro qui il circuito
print_circuit(qc)
desertnaut
60.7k32 gold badges155 silver badges183 bronze badges
-
there are similar sites for ML: DataScience, CrossValidated. And there is site Kagglefuras– furas2025年09月17日 17:26:01 +00:00Commented Sep 17 at 17:26
lang-py