Quantum Machine Learning for Medical AI
A friendly, hands-on walkthrough — from quantum concepts to a working Alzheimer's classifier.
Hi everyone! 👋 I'm Dr. Javeria Amin, and this post shares the slides and code from my seminar on Quantum Machine Learning (QML) for Medical AI. I've written it for students and beginners — so even if you've never touched a qubit before, you should be able to follow along. By the end you'll understand how a quantum computer can help classify brain MRI scans, and you'll have the full code to try it yourself.
Everything from the seminar is free to download and use for learning:
Tip: upload the files to Google Drive, click Share → Anyone with the link, and paste those links above.
So… what is Quantum Machine Learning?
Quantum Machine Learning brings together two big ideas: quantum computing and machine learning. The goal is to solve complex problems that are hard for ordinary computers.
The key difference comes down to how information is stored:
Can only be 0 or 1 — like a light switch that's either off or on.
Can be a blend of 0 and 1 at the same time — called superposition. This lets it explore many possibilities at once.
The 4 core ideas (in plain words)
How the pipeline works, step by step
Here's the journey our data takes — from a raw brain scan to a diagnosis. Think of it like an assembly line:
| 01 | MRI Dataset — labeled brain scans (ADNI dataset). |
| 02 | Preprocessing — resize, grayscale, normalize the images. |
| 03 | PCA — squeeze 4,096 pixels down to just 6 key features (the most important variation). |
| 04 | Quantum Encoding — turn those 6 features into qubit rotations (Rx/Ry/Rz angles). |
| 05 | Variational Quantum Circuit — a trainable quantum circuit (the "quantum neural network"). |
| 06 | Prediction — output the class: AD, MCI, or CN. |
💡 The three classes: AD = Alzheimer's Disease, MCI = Mild Cognitive Impairment, CN = Cognitively Normal.
A quantum circuit is like a neural network
If you already know neural networks, this analogy makes everything click. A Variational Quantum Circuit (VQC) is the quantum cousin of a neural network — both are trainable, just built differently:
The circuit learns by repeatedly adjusting its gate angles to reduce prediction error — just like backpropagation, but happening in quantum space. (That's also why we call it "variational": the angles vary during training.)
Tools used
The whole project is built around PennyLane — think of it as the PyTorch of Quantum Machine Learning. Here's the stack:
- PennyLane — designs the quantum circuits and computes gradients.
- PennyLane Lightning — a fast C++ simulator (10–100× speed-up).
- Scikit-Learn — PCA and train/test splitting.
- NumPy — numerical computing.
- PIL — loading and resizing MRI images.
- Matplotlib & Seaborn — training curves and confusion matrices.
What's inside the code notebook
The notebook is organized as a clear, reproducible 11-step journey. Each step is short and commented so you can run it cell by cell:
- Install libraries (PennyLane, Lightning, Scikit-Learn)
- Import dependencies
- Set the dataset path
- Load MRI images from class folders
- Preprocessing → PCA → angle encoding
- Train / test split
- Build the Quantum Circuit (VQC)
- Training loop
- Training progress plots
- Evaluation & confusion matrix
- Full dashboard & final summary
Grab the slides and the full code below — free for learning and teaching.
📊 Download Slides 💻 Download CodeIf you have questions or want to collaborate on quantum machine learning for medical imaging, feel free to leave a comment below — I'd love to hear from you. Thanks for reading, and enjoy exploring the quantum world! ⚛️
— Dr. Javeria Amin · QML for Medical AI Seminar · Republic of Korea, 2026
QML Medical AI — Alzheimer's MRI Classification
The complete, runnable code from the seminar — 11 clear steps, with a short note before each.
Below is the full pipeline you can copy and run cell by cell in Jupyter or Google Colab. Before running, install the libraries (Step 0) and set your dataset path (Step 1). Each code box scrolls sideways if a line is long.
⚙️ Step 0 — Install Libraries
First, install the quantum libraries. Run this once.
!pip install pennylane pennylane-lightning -q
📦 Import Libraries
Bring in all the tools we'll use — PennyLane (quantum), NumPy, scikit-learn, PIL, and plotting.
import os, time, warnings
warnings.filterwarnings('ignore')
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import seaborn as sns
from pathlib import Path
from PIL import Image
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.metrics import (confusion_matrix, classification_report,
accuracy_score, balanced_accuracy_score,
f1_score, roc_auc_score, matthews_corrcoef)
import pennylane as qml
print('✅ All libraries imported successfully!')
📁 STEP 1 — Dataset Path Yahan Likho
Tell the code where your MRI image folders are. Each class (AD, MCI, CN) should be its own folder.
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
DATA_ROOT = '/kaggle/input/datasets/javeria/adnisample/ADNI'
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
IMG_SIZE = 64 # Image resize size
N_QUBITS = 6 # Number of qubits
N_LAYERS = 3 # VQC layers
EPOCHS = 10 # Training epochs
LR = 0.05 # Learning rate
TEST_SIZE = 0.20 # 20% test data
SEED = 42
GRAYSCALE = True
np.random.seed(SEED)
# ── Folders check ──
data_path = Path(DATA_ROOT)
print(f'📂 Dataset path : {data_path}')
print(f'📂 Exists : {data_path.exists()}')
if data_path.exists():
print('\n📁 Contents:')
for item in sorted(data_path.iterdir()):
if item.is_dir():
imgs = list(item.glob('*.*'))
print(f' 📂 {item.name}/ → {len(imgs)} files')
else:
print(f' 📄 {item.name}')
🖼️ STEP 2 — Load Images from Class Folders
Load the images from each class folder and turn them into arrays the model can read.
EXTS = {'.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff'}
# Auto-detect class folders
CLASS_NAMES = sorted([
d.name for d in data_path.iterdir()
if d.is_dir() and not d.name.startswith('.')
])
N_CLASSES = len(CLASS_NAMES)
print(f'🏷️ Classes found : {CLASS_NAMES}')
print(f'🔢 Total classes : {N_CLASSES}\n')
X_list, y_list = [], []
for ci, cls in enumerate(CLASS_NAMES):
cls_dir = data_path / cls
img_paths = [p for p in cls_dir.iterdir() if p.suffix.lower() in EXTS]
loaded = 0
for p in img_paths:
try:
img = Image.open(p)
img = img.convert('L') if GRAYSCALE else img.convert('RGB')
img = img.resize((IMG_SIZE, IMG_SIZE), Image.LANCZOS)
arr = np.array(img, dtype=np.float32) / 255.0
X_list.append(arr.flatten())
y_list.append(ci)
loaded += 1
except Exception as e:
print(f' ⚠️ Skip {p.name}: {e}')
print(f' ✅ {cls:<8} : {loaded} images loaded')
X_raw = np.array(X_list, dtype=np.float32)
y_all = np.array(y_list, dtype=np.int32)
print(f'\n📊 Total samples : {len(y_all)}')
print(f'📐 Feature shape : {X_raw.shape}')
# Sample images dikhao
fig, axes = plt.subplots(1, min(N_CLASSES*2, 6), figsize=(14, 3))
fig.suptitle('Sample MRI Images per Class', fontsize=13, fontweight='bold')
shown = {c: 0 for c in range(N_CLASSES)}
idx_shown = 0
for i, (xi, yi) in enumerate(zip(X_list, y_list)):
if shown[yi] < 2 and idx_shown < len(axes):
axes[idx_shown].imshow(xi.reshape(IMG_SIZE, IMG_SIZE), cmap='gray')
axes[idx_shown].set_title(CLASS_NAMES[yi], fontsize=10)
axes[idx_shown].axis('off')
shown[yi] += 1
idx_shown += 1
if idx_shown == len(axes):
break
plt.tight_layout()
plt.show()
🔬 STEP 3 — Preprocessing (Normalize → PCA → Angle Encode)
Preprocess: normalize the images, use PCA to shrink them to 6 features, and scale those into rotation angles.
print('🔄 Preprocessing pipeline started...\n')
# Standardize
print(' 1️⃣ Standardizing features (zero mean, unit variance)...')
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_raw)
# PCA
print(f' 2️⃣ PCA: {X_raw.shape[1]} → {N_QUBITS} principal components...')
pca = PCA(n_components=N_QUBITS, random_state=SEED)
X_pca = pca.fit_transform(X_scaled)
var_exp = pca.explained_variance_ratio_.sum() * 100
print(f' Explained variance: {var_exp:.2f}%')
# Angle encoding: scale to [-π, π]
print(f' 3️⃣ Angle encoding to [-π, +π] for qubit rotations...')
X_enc = np.pi * (X_pca - X_pca.min(0)) / ((X_pca.max(0) - X_pca.min(0)) + 1e-8) * 2 - np.pi
print(f'\n✅ Preprocessing complete!')
print(f' Final shape: {X_enc.shape} (samples × qubits)')
# PCA variance plot
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
ax1.bar(range(1, N_QUBITS+1), pca.explained_variance_ratio_*100, color='#4fc3f7', alpha=0.85)
ax1.set_title('PCA — Variance per Component', fontweight='bold')
ax1.set_xlabel('Component'); ax1.set_ylabel('Variance (%)')
ax1.grid(alpha=0.3)
ax2.scatter(X_enc[:, 0], X_enc[:, 1],
c=y_all, cmap='Set1', alpha=0.7, s=30)
ax2.set_title('PCA — First 2 Components (Class Separation)', fontweight='bold')
ax2.set_xlabel('PC1 (angle encoded)'); ax2.set_ylabel('PC2 (angle encoded)')
for ci, cls in enumerate(CLASS_NAMES):
ax2.scatter([], [], label=cls, c=[plt.cm.Set1(ci/N_CLASSES)])
ax2.legend(); ax2.grid(alpha=0.3)
plt.tight_layout(); plt.show()
✂️ STEP 4 — Train / Test Split
Split the data into training and testing sets so we can check the model honestly.
X_train, X_test, y_train, y_test = train_test_split(
X_enc, y_all,
test_size = TEST_SIZE,
stratify = y_all,
random_state = SEED
)
print(f'📊 Split Summary')
print(f' Total : {len(y_all)} samples')
print(f' Train : {len(y_train)} samples ({100*(1-TEST_SIZE):.0f}%)')
print(f' Test : {len(y_test)} samples ({100*TEST_SIZE:.0f}%)\n')
print(' Class-wise distribution:')
for ci, cls in enumerate(CLASS_NAMES):
tr = (y_train == ci).sum()
te = (y_test == ci).sum()
print(f' {cls:<8} Train: {tr:>4} Test: {te:>4}')
# Distribution bar chart
fig, ax = plt.subplots(figsize=(7, 4))
x = np.arange(N_CLASSES)
ax.bar(x-0.2, [(y_train==i).sum() for i in range(N_CLASSES)], 0.35, label='Train', color='#4fc3f7', alpha=0.85)
ax.bar(x+0.2, [(y_test ==i).sum() for i in range(N_CLASSES)], 0.35, label='Test', color='#ff8a65', alpha=0.85)
ax.set_xticks(x); ax.set_xticklabels(CLASS_NAMES)
ax.set_ylabel('Samples'); ax.set_title('Class Distribution — Train vs Test', fontweight='bold')
ax.legend(); ax.grid(alpha=0.3)
plt.tight_layout(); plt.show()
⚛️ STEP 5 — Quantum Circuit (VQC)
The heart of the project — the Variational Quantum Circuit (VQC) that encodes data and learns.
dev = qml.device('default.qubit', wires=N_QUBITS)
@qml.qnode(dev, diff_method='best')
def quantum_circuit(params, x):
"""
VQC Architecture:
[Encoding] AngleEmbedding — PCA feature → Ry rotation on each qubit
[Ansatz] BasicEntanglerLayers — Rx rotations + CNOT ring entanglement
[Readout] PauliZ expectation on all qubits
"""
qml.AngleEmbedding(x, wires=range(N_QUBITS), rotation='Y')
qml.BasicEntanglerLayers(params, wires=range(N_QUBITS))
return [qml.expval(qml.PauliZ(i)) for i in range(N_QUBITS)]
param_shape = qml.BasicEntanglerLayers.shape(n_layers=N_LAYERS, n_wires=N_QUBITS)
total_params = int(np.prod(param_shape)) + N_QUBITS * N_CLASSES + N_CLASSES
print('⚛️ VQC Circuit Details')
print(f' Qubits : {N_QUBITS}')
print(f' VQC Layers : {N_LAYERS}')
print(f' Circuit params : {int(np.prod(param_shape))}')
print(f' Linear head params : {N_QUBITS * N_CLASSES + N_CLASSES}')
print(f' Total params : {total_params}')
print(f' Encoding : AngleEmbedding (Y-rotation)')
print(f' Ansatz : BasicEntanglerLayers')
print(f' Measurement : ⟨Z⟩ on all {N_QUBITS} qubits')
# Draw circuit
print('\n📐 Circuit Diagram:')
sample_params = np.zeros(param_shape)
sample_x = np.zeros(N_QUBITS)
print(qml.draw(quantum_circuit)(sample_params, sample_x))
🏋️ STEP 6 — Training Loop
Train the circuit: it adjusts its rotation angles step by step to reduce the error.
# ── Helper functions ─────────────────────────────────────
def softmax(z):
e = np.exp(z - z.max())
return e / e.sum()
def cross_entropy(p, y_true, n_cls):
oh = np.zeros(n_cls); oh[y_true] = 1.0
return -np.sum(oh * np.log(p + 1e-9))
def forward(params, W, b, Xs, ys):
loss, preds = 0.0, []
for xi, yi in zip(Xs, ys):
feats = np.array(quantum_circuit(params, xi))
logits = feats @ W + b
probs = softmax(logits)
loss += cross_entropy(probs, yi, N_CLASSES)
preds.append(np.argmax(probs))
return loss / len(ys), np.array(preds)
def num_grad(params, W, b, Xs, ys, eps=1e-3):
gP = np.zeros_like(params)
for idx in np.ndindex(params.shape):
p1 = params.copy(); p1[idx] += eps
p2 = params.copy(); p2[idx] -= eps
gP[idx] = (forward(p1,W,b,Xs,ys)[0] - forward(p2,W,b,Xs,ys)[0]) / (2*eps)
gW = np.zeros_like(W)
for i in range(W.shape[0]):
for j in range(W.shape[1]):
W1 = W.copy(); W1[i,j] += eps
W2 = W.copy(); W2[i,j] -= eps
gW[i,j] = (forward(params,W1,b,Xs,ys)[0] - forward(params,W2,b,Xs,ys)[0]) / (2*eps)
gb = np.zeros_like(b)
for j in range(b.shape[0]):
b1 = b.copy(); b1[j] += eps
b2 = b.copy(); b2[j] -= eps
gb[j] = (forward(params,W,b1,Xs,ys)[0] - forward(params,W,b2,Xs,ys)[0]) / (2*eps)
return gP, gW, gb
# ── Initialise parameters ────────────────────────────────
np.random.seed(SEED)
params = np.random.uniform(-np.pi, np.pi, param_shape)
W_head = np.random.randn(N_QUBITS, N_CLASSES) * 0.1
b_head = np.zeros(N_CLASSES)
BATCH = min(16, len(X_train))
train_losses, test_losses = [], []
train_accs, test_accs = [], []
print(f'🚀 Training Started — Epochs: {EPOCHS} | LR: {LR} | Batch: {BATCH}')
print('─' * 65)
print(f' {"Epoch":>5} {"Tr Loss":>8} {"Tr Acc":>7} {"Te Loss":>8} {"Te Acc":>7} {"Time":>6}')
print('─' * 65)
t0 = time.time()
for epoch in range(1, EPOCHS + 1):
idx = np.random.permutation(len(X_train))[:BATCH]
gP, gW, gb = num_grad(params, W_head, b_head, X_train[idx], y_train[idx])
params -= LR * gP
W_head -= LR * gW
b_head -= LR * gb
tr_loss, tr_pred = forward(params, W_head, b_head, X_train, y_train)
te_loss, te_pred = forward(params, W_head, b_head, X_test, y_test)
tr_acc = accuracy_score(y_train, tr_pred)
te_acc = accuracy_score(y_test, te_pred)
train_losses.append(tr_loss); test_losses.append(te_loss)
train_accs.append(tr_acc); test_accs.append(te_acc)
if epoch % 10 == 0 or epoch == 1:
elapsed = time.time() - t0
bar = '█' * int(te_acc * 20) + '░' * (20 - int(te_acc * 20))
print(f' {epoch:>5} {tr_loss:>8.4f} {tr_acc*100:>6.1f}% '
f'{te_loss:>8.4f} {te_acc*100:>6.1f}% {elapsed:>5.1f}s')
print(f' Test Progress [{bar}] {te_acc*100:.1f}%')
print('─' * 65)
print(f'✅ Training complete in {time.time()-t0:.1f}s')
📈 STEP 7 — Training Progress Plots
Plot the training curves so you can see accuracy go up and loss go down.
epochs_range = range(1, EPOCHS + 1)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
fig.suptitle('Training & Testing Progress', fontsize=14, fontweight='bold')
# Loss curve
ax1.plot(epochs_range, train_losses, color='#4fc3f7', lw=2, label='Train Loss')
ax1.plot(epochs_range, test_losses, color='#ff8a65', lw=2, ls='--', label='Test Loss')
ax1.set_title('Loss over Epochs', fontweight='bold')
ax1.set_xlabel('Epoch'); ax1.set_ylabel('Cross-Entropy Loss')
ax1.legend(); ax1.grid(alpha=0.3)
# Accuracy curve
ax2.plot(epochs_range, [a*100 for a in train_accs], color='#81c784', lw=2, label='Train Acc')
ax2.plot(epochs_range, [a*100 for a in test_accs], color='#e57373', lw=2, ls='--', label='Test Acc')
ax2.fill_between(epochs_range, [a*100 for a in test_accs], alpha=0.15, color='#e57373')
ax2.set_ylim(0, 105)
ax2.set_title('Accuracy over Epochs', fontweight='bold')
ax2.set_xlabel('Epoch'); ax2.set_ylabel('Accuracy (%)')
ax2.legend(); ax2.grid(alpha=0.3)
plt.tight_layout(); plt.show()
🎯 STEP 8 — Final Evaluation & Confusion Matrix
Evaluate on the test set — final accuracy and a full classification report.
_, final_preds = forward(params, W_head, b_head, X_test, y_test)
acc = accuracy_score(y_test, final_preds)
bal_acc = balanced_accuracy_score(y_test, final_preds)
f1_mac = f1_score(y_test, final_preds, average='macro')
mcc = matthews_corrcoef(y_test, final_preds)
# Probabilities for AUC
y_prob = []
for xi in X_test:
feats = np.array(quantum_circuit(params, xi))
logits = feats @ W_head + b_head
y_prob.append(softmax(logits))
y_prob = np.array(y_prob)
auc = (roc_auc_score(y_test, y_prob[:, 1]) if N_CLASSES == 2
else roc_auc_score(y_test, y_prob, multi_class='ovr', average='macro'))
print('━' * 50)
print(' 📊 FINAL RESULTS ON TEST SET')
print('━' * 50)
print(f' Accuracy : {acc*100:.2f}%')
print(f' Balanced Accuracy : {bal_acc*100:.2f}%')
print(f' F1-Score (Macro) : {f1_mac*100:.2f}%')
print(f' AUC-ROC (OvR) : {auc*100:.2f}%')
print(f' MCC Score : {mcc:.3f}')
print('━' * 50)
print()
print('📋 Per-Class Report:')
print(classification_report(y_test, final_preds, target_names=CLASS_NAMES))
🔥 STEP 9 — Confusion Matrix
Draw the confusion matrix to see which classes were predicted correctly.
cm = confusion_matrix(y_test, final_preds)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# Raw counts
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=CLASS_NAMES, yticklabels=CLASS_NAMES,
ax=ax1, linewidths=0.5)
ax1.set_title('Confusion Matrix (Counts)', fontweight='bold')
ax1.set_xlabel('Predicted Label'); ax1.set_ylabel('True Label')
# Normalized
cm_norm = cm.astype(float) / cm.sum(axis=1, keepdims=True)
sns.heatmap(cm_norm, annot=True, fmt='.2f', cmap='YlOrRd',
xticklabels=CLASS_NAMES, yticklabels=CLASS_NAMES,
ax=ax2, linewidths=0.5)
ax2.set_title('Confusion Matrix (Normalized)', fontweight='bold')
ax2.set_xlabel('Predicted Label'); ax2.set_ylabel('True Label')
plt.tight_layout(); plt.show()
📊 STEP 10 — Full Dashboard
Tell the code where your MRI image folders are. Each class (AD, MCI, CN) should be its own folder.
fig = plt.figure(figsize=(20, 12))
fig.patch.set_facecolor('#0d1117')
gs = gridspec.GridSpec(2, 3, figure=fig, hspace=0.45, wspace=0.35)
BLUE='#4fc3f7'; ORANGE='#ff8a65'; GREEN='#81c784'; RED='#e57373'
GRID_C='#1e2a38'; TITLE_C='#e0e0e0'; TEXT_C='#b0bec5'
def style_ax(ax, title):
ax.set_facecolor('#111827')
ax.set_title(title, color=TITLE_C, fontsize=11, fontweight='bold', pad=8)
ax.tick_params(colors=TEXT_C)
ax.xaxis.label.set_color(TEXT_C); ax.yaxis.label.set_color(TEXT_C)
for sp in ax.spines.values(): sp.set_edgecolor(GRID_C)
ax.grid(alpha=0.2, color=GRID_C)
ep = range(1, EPOCHS+1)
ax1 = fig.add_subplot(gs[0,0])
ax1.plot(ep, train_losses, color=BLUE, lw=2, label='Train Loss')
ax1.plot(ep, test_losses, color=ORANGE, lw=2, ls='--', label='Test Loss')
ax1.set_xlabel('Epoch'); ax1.set_ylabel('Loss')
ax1.legend(facecolor='#1e2a38', edgecolor='none', labelcolor=TEXT_C)
style_ax(ax1, 'Loss Curve')
ax2 = fig.add_subplot(gs[0,1])
ax2.plot(ep, [a*100 for a in train_accs], color=GREEN, lw=2, label='Train Acc')
ax2.plot(ep, [a*100 for a in test_accs], color=RED, lw=2, ls='--', label='Test Acc')
ax2.fill_between(ep, [a*100 for a in test_accs], alpha=0.15, color=RED)
ax2.set_ylim(0,105); ax2.set_xlabel('Epoch'); ax2.set_ylabel('Accuracy (%)')
ax2.legend(facecolor='#1e2a38', edgecolor='none', labelcolor=TEXT_C)
style_ax(ax2, 'Accuracy Progress')
ax3 = fig.add_subplot(gs[0,2])
sns.heatmap(cm, annot=True, fmt='d', cmap='YlOrRd',
xticklabels=CLASS_NAMES, yticklabels=CLASS_NAMES,
ax=ax3, linewidths=0.5, cbar_kws={'shrink':0.8})
ax3.set_facecolor('#111827'); ax3.set_xlabel('Predicted', color=TEXT_C)
ax3.set_ylabel('True', color=TEXT_C); ax3.tick_params(colors=TEXT_C)
ax3.set_title('Confusion Matrix', color=TITLE_C, fontsize=11, fontweight='bold', pad=8)
ax4 = fig.add_subplot(gs[1,0])
x = np.arange(N_CLASSES)
ax4.bar(x-0.2,[(y_train==i).sum() for i in range(N_CLASSES)],0.35,label='Train',color=BLUE,alpha=0.85)
ax4.bar(x+0.2,[(y_test ==i).sum() for i in range(N_CLASSES)],0.35,label='Test', color=ORANGE,alpha=0.85)
ax4.set_xticks(x); ax4.set_xticklabels(CLASS_NAMES)
ax4.set_ylabel('Samples')
ax4.legend(facecolor='#1e2a38', edgecolor='none', labelcolor=TEXT_C)
style_ax(ax4, 'Class Distribution')
ax5 = fig.add_subplot(gs[1,1])
f1_pc = f1_score(y_test, final_preds, average=None)
cols = [BLUE, GREEN, RED, ORANGE][:N_CLASSES]
bars = ax5.bar(CLASS_NAMES, f1_pc*100, color=cols, alpha=0.85)
for bar,val in zip(bars,f1_pc):
ax5.text(bar.get_x()+bar.get_width()/2, bar.get_height()+1,
f'{val*100:.1f}%', ha='center', color=TEXT_C, fontsize=10)
ax5.set_ylim(0,115); ax5.set_ylabel('F1-Score (%)')
style_ax(ax5, 'Per-Class F1-Score')
ax6 = fig.add_subplot(gs[1,2])
ax6.set_facecolor('#111827'); ax6.axis('off')
metrics = [('Accuracy', f'{acc*100:.2f}%'),('Balanced Acc', f'{bal_acc*100:.2f}%'),
('F1-Macro', f'{f1_mac*100:.2f}%'),('AUC-ROC', f'{auc*100:.2f}%'),
('MCC Score', f'{mcc:.3f}'),('Train Samples', str(len(y_train))),
('Test Samples', str(len(y_test))),('Qubits', str(N_QUBITS)),('VQC Layers', str(N_LAYERS))]
ax6.text(0.5, 0.97, '📊 Performance Summary', ha='center', va='top',
transform=ax6.transAxes, color=TITLE_C, fontsize=12, fontweight='bold')
for i,(k,v) in enumerate(metrics):
yp = 0.85 - i*0.09
ax6.text(0.05,yp,k,transform=ax6.transAxes,color=TEXT_C,fontsize=10)
ax6.text(0.95,yp,v,transform=ax6.transAxes,color=BLUE,fontsize=10,fontweight='bold',ha='right')
ax6.axhline(y=yp-0.02,xmin=0.03,xmax=0.97,color=GRID_C,lw=0.5,transform=ax6.transAxes)
for sp in ax6.spines.values(): sp.set_edgecolor(GRID_C)
fig.suptitle('QML Medical AI — Alzheimer MRI Classification Dashboard',
color='#ffffff', fontsize=14, fontweight='bold', y=0.98)
plt.savefig('qml_dashboard.png', dpi=150, bbox_inches='tight', facecolor=fig.get_facecolor())
plt.show()
print('✅ Dashboard saved as qml_dashboard.png')
✅ STEP 11 — Final Summary
Tell the code where your MRI image folders are. Each class (AD, MCI, CN) should be its own folder.
print('━' * 55)
print(' ✅ QML MEDICAL AI — PIPELINE COMPLETE')
print('━' * 55)
print(f' Dataset : {len(y_all)} MRI images')
print(f' Classes : {CLASS_NAMES}')
print(f' Image size : {IMG_SIZE}×{IMG_SIZE} px (grayscale)')
print(f' PCA features : {N_QUBITS} ({var_exp:.1f}% variance)')
print(f' Qubits : {N_QUBITS}')
print(f' VQC layers : {N_LAYERS}')
print(f' Epochs : {EPOCHS} | LR: {LR}')
print(f' Train/Test : {len(y_train)} / {len(y_test)}')
print()
print(f' Accuracy : {acc*100:.2f}%')
print(f' Balanced Accuracy : {bal_acc*100:.2f}%')
print(f' F1-Macro : {f1_mac*100:.2f}%')
print(f' AUC-ROC (OvR) : {auc*100:.2f}%')
print(f' MCC Score : {mcc:.3f}')
print('━' * 55)
💡 To run it: open the notebook in Google Colab or Jupyter, install the libraries from Step 0, put your MRI images in three folders (AD, MCI, CN), set the path in Step 1, then run each step in order.
— Dr. Javeria Amin · QML for Medical AI Seminar · Republic of Korea, 2026
0 Comments