Sweeping method for TN training#

Classification of Breast Cancer dataset using Sweeping method

[1]:
import os
from pathlib import Path

os.environ["KMP_WARNINGS"] = "0"

import jax
import jax.numpy as jnp
import numpy as np
import optax
import pandas as pd
from jax.nn.initializers import *
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.utils.class_weight import compute_class_weight

from tn4ml.embeddings import *
from tn4ml.eval import *
from tn4ml.initializers import *
from tn4ml.metrics import *
from tn4ml.models.model import *
from tn4ml.models.mps import *
from tn4ml.util import *
[2]:
# Enable 64-bit precision and set matmul precision to highest
jax.config.update("jax_enable_x64", True)
jax.config.update("jax_default_matmul_precision", "highest")

Load dataset#

The dataset can be download from kaggle.com/breast-cancer/data.

[3]:
load_dir = "data"
save_dir = "results"
n_classes = 2
[4]:
# Load data
breast_cancer_path = Path(load_dir) / "breast-cancer.csv"
if breast_cancer_path.exists():
    data = pd.read_csv(breast_cancer_path)
else:
    breast_cancer = load_breast_cancer(as_frame=True)
    data = breast_cancer.frame.copy()
    data.insert(0, "id", np.arange(len(data)))
    data["diagnosis"] = data["target"].map({1: "B", 0: "M"})
    data = data.drop(columns=["target"])

data["diagnosis"] = data["diagnosis"].map({"M": 1, "B": 0})

X = data.drop(["id", "diagnosis"], axis=1)
y = data["diagnosis"].to_numpy()

feature_min = X.min()
feature_max = X.max()

X_normalized = (X - feature_min) / (feature_max - feature_min)
X_numpy = X_normalized.to_numpy()

if os.environ.get("CI"):
    X_numpy = X_numpy[:, :2]

X_train, X_test, y_train, y_test = train_test_split(
    X_numpy, y, test_size=0.2, random_state=42
)
X_train, X_valid, y_train, y_valid = train_test_split(
    X_train, y_train, test_size=0.2, random_state=42
)

if os.environ.get("CI"):
    X_train, y_train = X_train[:4], y_train[:4]
    X_valid, y_valid = X_valid[:4], y_valid[:4]
    X_test, y_test = X_test[:4], y_test[:4]

print("Train data shape: ", X_train.shape)
print("Validation data shape: ", X_valid.shape)
print("Test data shape: ", X_test.shape)

classes = np.unique(y_train)
print("Classes: ", classes)
class_weights = compute_class_weight(
    class_weight="balanced", classes=classes, y=y_train
)
print("Class weights: ", class_weights)

y_train = integer_to_one_hot(y_train, n_classes)
y_valid = integer_to_one_hot(y_valid, n_classes)
y_test = integer_to_one_hot(y_test, n_classes)
Train data shape:  (364, 30)
Validation data shape:  (91, 30)
Test data shape:  (114, 30)
Classes:  [0 1]
Class weights:  [0.78787879 1.36842105]

Training setup#

Define model parameters

[5]:
key = jax.random.key(42)
L = X_train.shape[1]
print("Number of tensors: ", L)
class_index = int(L // 2)
shape_method = "noteven"  # default method
compress = False  # connected with shape method
embedding = PolynomialEmbedding(
    degree=2, n=1, include_bias=True
)  # polynomial embedding of degree 2
phys_dim = 3
initializer = randn(0.9)
bond_dim = 1 if os.environ.get("CI") else 10
Number of tensors:  30

Initialize the MPS model

[6]:
model = MPS_initialize(
    L=L,
    initializer=initializer,
    key=key,
    shape_method=shape_method,
    compress=compress,
    cyclic=False,
    phys_dim=phys_dim,
    bond_dim=bond_dim,
    class_index=class_index,
    canonical_center=class_index,
    class_dim=n_classes,
    add_identity=True,
    boundary="obc",
)

Define training parameters

[7]:
def weighted_crossentropy_loss(*args, **kwargs):
    """Compute weighted cross-entropy loss."""
    return CrossEntropyWeighted(class_weights=class_weights)(*args, **kwargs).mean()


def crossentropy_loss(*args, **kwargs):
    """Compute softmax cross-entropy loss."""
    return OptaxWrapper(optax.softmax_cross_entropy)(*args, **kwargs).mean()
[8]:
learning_rate = 1e-4
optimizer = optax.adam
strategy = "global" if os.environ.get("CI") else "sweeps"
loss = weighted_crossentropy_loss
train_type = TrainingType.SUPERVISED
earlystop = EarlyStopping(min_delta=0, patience=5, monitor="loss", mode="min")
epochs = 1 if os.environ.get("CI") else 20
batch_size = 2 if os.environ.get("CI") else 128
val_batch_size = 2 if os.environ.get("CI") else 30
train_dtype = jnp.float32 if os.environ.get("CI") else jnp.float64

model.configure(
    optimizer=optimizer,
    strategy=strategy,
    loss=loss,
    train_type=train_type,
    learning_rate=learning_rate,
)
[9]:
history = model.train(
    X_train,
    targets=y_train,
    val_inputs=X_valid,
    val_targets=y_valid,
    epochs=epochs,
    batch_size=batch_size,
    embedding=embedding,
    normalize=True,
    dtype=train_dtype,
    earlystop=earlystop,
    canonize=(True, class_index),
    display_val_acc=not bool(os.environ.get("CI")),
    eval_metric=crossentropy_loss,
    val_batch_size=val_batch_size,
)
epoch: 100%|██████████ 20/20 , loss=0.8486, val_loss=0.3949, val_acc=0.8889

Plot loss

[10]:
plot_loss(history, validation=True, figsize=(8, 6))
../../_images/source_examples_mnist_ad_sweeps_16_0.png