augtab 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
augtab-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,2060 @@
1
+ Metadata-Version: 2.4
2
+ Name: augtab
3
+ Version: 0.1.0
4
+ Summary: AugTab: Learnable Feature Augmentation for Low-Dimensional Tabular Data
5
+ Author: Al Zadid Sultan Bin Habib, Md Younus Ahamed, Md Asif Bin Syed, Md Samiul Islam, Muntasir Tabasum, Tanpia Tasnim, Md. Ekramul Islam
6
+ Project-URL: Homepage, https://www.zadidhabib.com/augtab.html
7
+ Project-URL: Repository, https://github.com/zadid6pretam/AugTab
8
+ Project-URL: Paper, https://doi.org/10.1007/978-3-032-37670-1_24
9
+ Project-URL: Issues, https://github.com/zadid6pretam/AugTab/issues
10
+ Keywords: tabular-data,tabular-learning,feature-augmentation,deep-learning,machine-learning,low-dimensional-data,pytorch,feature-engineering,feature-interactions,input-adaptive-gating
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Operating System :: OS Independent
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: numpy>=1.23
24
+ Requires-Dist: torch>=2.0
25
+ Provides-Extra: experiments
26
+ Requires-Dist: pandas>=1.5; extra == "experiments"
27
+ Requires-Dist: scikit-learn>=1.2; extra == "experiments"
28
+ Requires-Dist: optuna>=3.0; extra == "experiments"
29
+ Requires-Dist: matplotlib>=3.6; extra == "experiments"
30
+ Requires-Dist: tqdm>=4.64; extra == "experiments"
31
+ Requires-Dist: scipy>=1.9; extra == "experiments"
32
+ Provides-Extra: notebooks
33
+ Requires-Dist: pandas>=1.5; extra == "notebooks"
34
+ Requires-Dist: scikit-learn>=1.2; extra == "notebooks"
35
+ Requires-Dist: optuna>=3.0; extra == "notebooks"
36
+ Requires-Dist: matplotlib>=3.6; extra == "notebooks"
37
+ Requires-Dist: jupyter>=1.0; extra == "notebooks"
38
+ Requires-Dist: notebook>=6.5; extra == "notebooks"
39
+ Requires-Dist: ipykernel>=6.0; extra == "notebooks"
40
+ Requires-Dist: tqdm>=4.64; extra == "notebooks"
41
+ Requires-Dist: scipy>=1.9; extra == "notebooks"
42
+ Provides-Extra: all
43
+ Requires-Dist: pandas>=1.5; extra == "all"
44
+ Requires-Dist: scikit-learn>=1.2; extra == "all"
45
+ Requires-Dist: optuna>=3.0; extra == "all"
46
+ Requires-Dist: matplotlib>=3.6; extra == "all"
47
+ Requires-Dist: jupyter>=1.0; extra == "all"
48
+ Requires-Dist: notebook>=6.5; extra == "all"
49
+ Requires-Dist: ipykernel>=6.0; extra == "all"
50
+ Requires-Dist: tqdm>=4.64; extra == "all"
51
+ Requires-Dist: scipy>=1.9; extra == "all"
52
+
53
+ # AugTab: Learnable Feature Augmentation for Low-Dimensional Tabular Data
54
+
55
+ ![Status](https://img.shields.io/badge/Status-Accepted-success)
56
+ ![ECML PKDD 2026](https://img.shields.io/badge/ECML%20PKDD-2026-purple)
57
+ ![Track](https://img.shields.io/badge/Track-Research%20Track-blue)
58
+ [![Paper](https://img.shields.io/badge/Paper-In%20Press-red)](https://doi.org/10.1007/978-3-032-37670-1_24)
59
+ ![Feature Augmentation](https://img.shields.io/badge/Focus-Feature%20Augmentation-blueviolet)
60
+ ![Low-Dimensional Tabular Data](https://img.shields.io/badge/Data-Low--Dimensional%20Tabular%20Data-teal)
61
+ ![Python](https://img.shields.io/badge/Python-3.10%2B-green)
62
+ ![PyTorch](https://img.shields.io/badge/PyTorch-2.x-red)
63
+ ![Optuna](https://img.shields.io/badge/Optuna-Hyperparameter%20Tuning-brightgreen)
64
+
65
+ <p align="center">
66
+ <img src="AugTab_Architecture.png" alt="AugTab Architecture" width="1000">
67
+ </p>
68
+
69
+ AugTab is a learnable feature augmentation framework for **low-dimensional tabular learning**. It introduces a **Feature Augmentation Layer (FAL)** that enriches the original feature space through complementary **nonlinear projection, explicit cross-interaction, and gated recombination** branches, allowing feature augmentation to be learned jointly with the downstream predictor rather than treated as fixed preprocessing or manual feature engineering. FAL combines a nonlinear MLP branch with a cross-interaction branch to construct candidate augmented features, followed by a learned projection and **input-adaptive gating** mechanism that selectively activates useful augmented dimensions for each sample. The resulting augmented representation retains the original features while appending task-aligned learned features, enabling the model to increase expressive capacity without discarding the original input information.
70
+
71
+ To keep this expansion controlled, AugTab incorporates regularization for **augmentation sparsity, feature diversity, orthogonality, computational budget, and robustness to distribution shift**. These objectives discourage redundant augmented features, encourage complementarity with the original feature space, and regulate the expected number of active augmented dimensions. The framework also supports perturbation-based drift consistency and missingness-aware learning. Although the Feature Augmentation Layer is designed to be **backbone-agnostic**, the ECML PKDD 2026 implementation instantiates AugTab with a lightweight **MLP backbone**. Theoretical analysis further connects learnable augmentation with increased expressivity, improved input conditioning, and generalization under a bounded gating budget. Across **15 low-dimensional classification and regression datasets**, AugTab achieves the best average rank across classification tasks and the top result on all evaluated regression tasks, with comparisons spanning **54 classification baselines and 16 regression baselines**.
72
+
73
+ ## Overview
74
+
75
+ **AugTab** is a tabular learning framework for problems where the original feature space is relatively low-dimensional and may provide limited expressive structure because of noise, heterogeneity, weak feature interactions, missingness, or distribution shift.
76
+
77
+ The key idea is to make **feature augmentation itself learnable**. Instead of applying a fixed polynomial expansion, manually constructing feature interactions, or relying solely on the downstream model to discover useful representations, AugTab inserts a trainable **Feature Augmentation Layer (FAL)** before the prediction backbone.
78
+
79
+ FAL constructs augmented features using:
80
+
81
+ - **Cross-interaction branch** - learns explicit second-order feature interactions through factorized transformations.
82
+ - **Nonlinear MLP branch** - learns nonlinear feature lifts from the original input.
83
+ - **Learned projection** - combines and re-mixes complementary augmented representations.
84
+ - **Input-adaptive gating** - determines which augmented dimensions should be activated for each sample.
85
+ - **Budget-aware regularization** - controls the effective augmentation width and computational cost.
86
+ - **Diversity and orthogonality regularization** - encourages augmented features to remain complementary rather than redundant.
87
+ - **Drift consistency** - promotes stable augmented representations under distribution shifts and feature perturbations.
88
+
89
+ For an input feature vector $x$, AugTab preserves the original features and concatenates them with the gated learned augmentation:
90
+
91
+ $$
92
+ \tilde{x} = x \oplus \big(g(x) \odot z(x)\big)
93
+ $$
94
+
95
+ where $z(x)$ represents the candidate augmented features and $g(x)$ provides input-dependent gates controlling their activation.
96
+
97
+ AugTab is **not tied to a specific low-dimensional dataset**. The Feature Augmentation Layer is designed as a modular front end that can, in principle, be combined with different downstream tabular architectures. In the experiments reported in the paper, AugTab is instantiated with an **MLP backbone**.
98
+
99
+ ## Citation
100
+
101
+ Al Zadid Sultan Bin Habib, Md Younus Ahamed, Md Asif Bin Syed, Md Samiul Islam, Muntasir Tabasum, Tanpia Tasnim, and Md. Ekramul Islam. **“AugTab: Learnable Feature Augmentation for Low-Dimensional Tabular Data.”** In *Machine Learning and Knowledge Discovery in Databases. Research Track (ECML PKDD 2026)*, Springer, 2027. https://doi.org/10.1007/978-3-032-37670-1_24
102
+
103
+ BibTeX:
104
+
105
+ ```bibtex
106
+ @inproceedings{habib2026augtab,
107
+ title = {AugTab: Learnable Feature Augmentation for Low-Dimensional Tabular Data},
108
+ author = {Habib, Al Zadid Sultan Bin and Ahamed, Md Younus and Syed, Md Asif Bin and Islam, Md Samiul and Tabasum, Muntasir and Tasnim, Tanpia and Islam, Md. Ekramul},
109
+ booktitle = {Machine Learning and Knowledge Discovery in Databases. Research Track},
110
+ year = {2027},
111
+ publisher = {Springer Nature Switzerland},
112
+ doi = {10.1007/978-3-032-37670-1_24}
113
+ }
114
+ ```
115
+
116
+ - **Paper:** https://doi.org/10.1007/978-3-032-37670-1_24 *(In Press)*
117
+ - **Project Page:** https://www.zadidhabib.com/augtab.html
118
+ - **GitHub:** https://github.com/zadid6pretam/AugTab
119
+
120
+ This repository contains the official implementation of **AugTab** together with example notebooks for reproducing the training workflow, hyperparameter tuning, evaluation, and package installation.
121
+
122
+ The repository currently includes:
123
+
124
+ - `augtab/augtab.py` - core implementation of the **AugTab** framework, including the Feature Augmentation Layer (FAL), MLP backbone, regularization components, and task-specific interfaces for binary classification, multiclass classification, and regression.
125
+ - `augtab/__init__.py` - package initialization file exposing the main AugTab classes and configuration objects through the `augtab` package.
126
+ - `AugTab Try.ipynb` - example notebook demonstrating how to use AugTab with **Optuna-based hyperparameter tuning** and **5-fold cross-validation**, reporting **mean accuracy ± standard deviation** on the **Water Potability** dataset.
127
+ - `PIP_Install_Check.ipynb` - lightweight notebook for verifying the **PyPI installation**, package imports, and basic AugTab functionality after installation with `pip install augtab`.
128
+ - `pyproject.toml` - modern Python project and build-system configuration used for packaging and PyPI distribution.
129
+ - `setup.cfg` - package metadata and configuration for installation and distribution.
130
+ - `requirements.txt` - Python dependencies required to run AugTab and the accompanying experiments.
131
+ - `AugTab_Architecture.png` - High-level architecture diagram of the AugTab framework.
132
+
133
+ The example notebook is written so that the dataset file and target column can be changed easily, allowing the same workflow to be reused for other low-dimensional tabular datasets.
134
+
135
+ In addition, `AugTab Try.ipynb` contains supplementary diagnostic and analysis code used to further inspect AugTab's behavior during training and evaluation.
136
+
137
+ ---
138
+
139
+ ## Repository Contents
140
+
141
+ ```text
142
+ .
143
+ ├── augtab/
144
+ │ ├── __init__.py
145
+ │ └── augtab.py
146
+
147
+ ├── AugTab Try.ipynb
148
+ ├── AugTab_Architecture.png
149
+ ├── PIP_Install_Check.ipynb
150
+ ├── pyproject.toml
151
+ ├── setup.cfg
152
+ ├── requirements.txt
153
+ ├── .gitignore
154
+ ├── LICENSE
155
+ └── README.md
156
+ ```
157
+
158
+ ### Package Structure
159
+
160
+ The installable Python package is contained in the `augtab/` directory:
161
+
162
+ ```text
163
+ augtab/
164
+ ├── __init__.py
165
+ └── augtab.py
166
+ ```
167
+
168
+ The public API can therefore be imported directly from the package:
169
+
170
+ ```python
171
+ from augtab import (
172
+ AugTabClassifier,
173
+ AugTabMulti,
174
+ AugTabRegressor,
175
+ )
176
+ ```
177
+
178
+ The three primary interfaces correspond to:
179
+
180
+ - `AugTabClassifier` - binary classification
181
+ - `AugTabMulti` - multiclass classification
182
+ - `AugTabRegressor` - regression
183
+
184
+ Lower-level configuration and model components are also exposed for users who want more control over the AugTab architecture and training configuration.
185
+
186
+ ### Main dependencies
187
+
188
+ The repository uses the following main dependencies:
189
+
190
+ ```
191
+ numpy>=1.23
192
+ pandas>=1.5
193
+ scikit-learn>=1.2
194
+ optuna>=3.0
195
+ torch>=2.0
196
+ matplotlib>=3.6
197
+ jupyter>=1.0
198
+ notebook>=6.5
199
+ ipykernel>=6.0
200
+ tqdm>=4.64
201
+ scipy>=1.9
202
+ ```
203
+ ## Installation
204
+
205
+ You can install **AugTab** in several ways depending on your workflow.
206
+
207
+ ---
208
+
209
+ ### Option 1: Clone the Repository (Recommended for Development)
210
+
211
+ ```bash
212
+ git clone https://github.com/zadid6pretam/AugTab.git
213
+ cd AugTab
214
+ pip install -r requirements.txt
215
+ pip install -e .
216
+ ```
217
+
218
+ - This is the recommended option if you want to modify the source code, run the provided experiment notebooks, reproduce the Optuna-based tuning workflow, or develop additional AugTab extensions.
219
+ - Editable installation (`-e`) allows changes made inside the local `augtab/` package to be reflected immediately without reinstalling the package.
220
+
221
+ ---
222
+
223
+ ### Option 2: Install Directly from GitHub (No Cloning Needed)
224
+
225
+ ```bash
226
+ pip install "git+https://github.com/zadid6pretam/AugTab.git"
227
+ ```
228
+
229
+ - This installs the latest version of **AugTab** directly from the GitHub repository.
230
+
231
+ After installation, the task-specific AugTab interfaces can be imported as:
232
+
233
+ ```python
234
+ from augtab import AugTabClassifier, AugTabMulti, AugTabRegressor
235
+ ```
236
+
237
+ ---
238
+
239
+ ### Option 3: Use a Virtual Environment
240
+
241
+ ```bash
242
+ python -m venv augtab-env
243
+
244
+ # macOS / Linux
245
+ source augtab-env/bin/activate
246
+
247
+ # Windows
248
+ # augtab-env\Scripts\activate
249
+
250
+ git clone https://github.com/zadid6pretam/AugTab.git
251
+ cd AugTab
252
+ pip install -r requirements.txt
253
+ pip install -e .
254
+ ```
255
+
256
+ - Using a virtual environment is recommended to keep **AugTab** and its dependencies isolated from other Python projects.
257
+ - Once activated, all AugTab dependencies and experiments will run within the isolated environment.
258
+
259
+ ---
260
+
261
+ ### Option 4: Local Install Without Editable Mode
262
+
263
+ ```bash
264
+ git clone https://github.com/zadid6pretam/AugTab.git
265
+ cd AugTab
266
+ pip install -r requirements.txt
267
+ pip install .
268
+ ```
269
+
270
+ - This performs a standard local installation of AugTab.
271
+ - Unlike editable mode, subsequent changes to the source code require reinstalling the package.
272
+
273
+ After installation:
274
+
275
+ ```python
276
+ from augtab import AugTabClassifier, AugTabMulti, AugTabRegressor
277
+ ```
278
+
279
+ ---
280
+
281
+ ### Option 5: Install from PyPI
282
+
283
+ ```bash
284
+ pip install augtab
285
+ ```
286
+
287
+ After installation, the main AugTab interfaces can be imported as:
288
+
289
+ ```python
290
+ from augtab import AugTabClassifier, AugTabMulti, AugTabRegressor
291
+ ```
292
+
293
+ The three high-level interfaces correspond to:
294
+
295
+ - `AugTabClassifier` - binary classification
296
+ - `AugTabMulti` - multiclass classification
297
+ - `AugTabRegressor` - regression
298
+
299
+ For example:
300
+
301
+ ```python
302
+ from augtab import AugTabClassifier
303
+
304
+ model = AugTabClassifier(
305
+ d_features=10,
306
+ k_aug=32,
307
+ device="cuda"
308
+ )
309
+ ```
310
+
311
+ Lower-level configuration and model components are also directly available:
312
+
313
+ ```python
314
+ from augtab import (
315
+ FALConfig,
316
+ BackboneConfig,
317
+ RegularizerConfig,
318
+ AugTabConfig,
319
+ AugTabCore,
320
+ )
321
+ ```
322
+
323
+ A lightweight installation check is provided in:
324
+
325
+ ```text
326
+ PIP_Install_Check.ipynb
327
+ ```
328
+
329
+ which can be used to verify the PyPI installation, package imports, and basic AugTab functionality.
330
+
331
+ ## Example Usage
332
+
333
+ AugTab supports **binary classification**, **multiclass classification**, and **regression** through three task-specific interfaces:
334
+
335
+ ```python
336
+ from augtab import AugTabClassifier, AugTabMulti, AugTabRegressor
337
+ ```
338
+
339
+ For a **new dataset**, we recommend using the **Optuna-tuned workflow** because the optimal augmentation width, FAL capacity, MLP backbone size, regularization strengths, learning rate, batch size, and number of training epochs may vary across datasets.
340
+
341
+ The examples below therefore provide six common configurations:
342
+
343
+ 1. Binary classification without hyperparameter tuning
344
+ 2. Binary classification with Optuna tuning
345
+ 3. Multiclass classification without hyperparameter tuning
346
+ 4. Multiclass classification with Optuna tuning
347
+ 5. Regression with Optuna tuning
348
+ 6. Regression without hyperparameter tuning
349
+
350
+ > **Recommended for new datasets:** Use Examples **2, 4, and 5**, which perform Optuna-based hyperparameter tuning with **5-fold cross-validation**. Keep the test set completely separate from Optuna and use it only for final evaluation.
351
+
352
+ > **Note:** The examples use synthetic low-dimensional datasets so that they can run without external data files. Replace the synthetic `X` and `y` arrays with your own tabular dataset.
353
+
354
+ ---
355
+
356
+ ### Example 1: Binary Classification Without Hyperparameter Tuning
357
+
358
+ This example trains `AugTabClassifier` using a fixed configuration.
359
+
360
+ ```python
361
+ import random
362
+ import numpy as np
363
+ import torch
364
+
365
+ from sklearn.datasets import make_classification
366
+ from sklearn.model_selection import train_test_split
367
+ from sklearn.preprocessing import StandardScaler
368
+ from sklearn.metrics import accuracy_score, f1_score, roc_auc_score
369
+
370
+ from augtab import AugTabClassifier, RegularizerConfig
371
+
372
+
373
+ # ============================================================
374
+ # Reproducibility
375
+ # ============================================================
376
+
377
+ SEED = 42
378
+
379
+ random.seed(SEED)
380
+ np.random.seed(SEED)
381
+ torch.manual_seed(SEED)
382
+
383
+ if torch.cuda.is_available():
384
+ torch.cuda.manual_seed_all(SEED)
385
+
386
+
387
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
388
+
389
+ print("Device:", DEVICE)
390
+
391
+
392
+ # ============================================================
393
+ # Example binary dataset
394
+ # Replace X and y with your own data
395
+ # ============================================================
396
+
397
+ X, y = make_classification(
398
+ n_samples=800,
399
+ n_features=12,
400
+ n_informative=8,
401
+ n_redundant=2,
402
+ n_classes=2,
403
+ random_state=SEED,
404
+ )
405
+
406
+ X = X.astype(np.float32)
407
+ y = y.astype(np.int64)
408
+
409
+
410
+ # ============================================================
411
+ # Train / test split
412
+ # ============================================================
413
+
414
+ X_train, X_test, y_train, y_test = train_test_split(
415
+ X,
416
+ y,
417
+ test_size=0.20,
418
+ random_state=SEED,
419
+ stratify=y,
420
+ )
421
+
422
+
423
+ # ============================================================
424
+ # Standardization
425
+ # Fit preprocessing on training data only
426
+ # ============================================================
427
+
428
+ scaler = StandardScaler()
429
+
430
+ X_train = scaler.fit_transform(X_train).astype(np.float32)
431
+ X_test = scaler.transform(X_test).astype(np.float32)
432
+
433
+
434
+ # ============================================================
435
+ # Regularization
436
+ # ============================================================
437
+
438
+ regs = RegularizerConfig(
439
+ lambda_sparse=1e-3,
440
+ lambda_div=1e-3,
441
+ lambda_orth=1e-3,
442
+ lambda_budget=1e-3,
443
+ lambda_drift=0.0,
444
+ )
445
+
446
+
447
+ # ============================================================
448
+ # Initialize AugTab
449
+ # ============================================================
450
+
451
+ model = AugTabClassifier(
452
+ d_features=X_train.shape[1],
453
+ k_aug=32,
454
+ kprime=64,
455
+ h_hidden=64,
456
+ widths=(128, 128),
457
+ activation="gelu",
458
+ append_mask=False,
459
+ gating="basic",
460
+ regs=regs,
461
+ device=DEVICE,
462
+ lr=2e-3,
463
+ weight_decay=1e-4,
464
+ )
465
+
466
+
467
+ # ============================================================
468
+ # Train
469
+ # ============================================================
470
+
471
+ model.fit(
472
+ X_train,
473
+ y_train,
474
+ epochs=80,
475
+ batch_size=64,
476
+ verbose=False,
477
+ )
478
+
479
+
480
+ # ============================================================
481
+ # Evaluate
482
+ # ============================================================
483
+
484
+ y_pred = model.predict(X_test).numpy()
485
+ y_prob = model.predict_proba(X_test).numpy().reshape(-1)
486
+
487
+ print("\nBinary Classification Results")
488
+ print("Accuracy :", accuracy_score(y_test, y_pred))
489
+ print("F1 :", f1_score(y_test, y_pred))
490
+ print("ROC-AUC :", roc_auc_score(y_test, y_prob))
491
+ ```
492
+
493
+ ---
494
+
495
+ ### Example 2: Binary Classification with Optuna Hyperparameter Tuning
496
+
497
+ For a **new binary classification dataset**, this is the recommended workflow.
498
+
499
+ Optuna searches the AugTab architecture, regularization, and optimization hyperparameters using **5-fold stratified cross-validation** on the training data. The test set remains completely untouched during tuning.
500
+
501
+ ```python
502
+ import random
503
+ import numpy as np
504
+ import optuna
505
+ import torch
506
+
507
+ from sklearn.datasets import make_classification
508
+ from sklearn.model_selection import train_test_split, StratifiedKFold
509
+ from sklearn.preprocessing import StandardScaler
510
+ from sklearn.metrics import accuracy_score, f1_score, roc_auc_score
511
+
512
+ from augtab import AugTabClassifier, RegularizerConfig
513
+
514
+
515
+ # ============================================================
516
+ # Reproducibility
517
+ # ============================================================
518
+
519
+ SEED = 42
520
+
521
+ random.seed(SEED)
522
+ np.random.seed(SEED)
523
+ torch.manual_seed(SEED)
524
+
525
+ if torch.cuda.is_available():
526
+ torch.cuda.manual_seed_all(SEED)
527
+
528
+
529
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
530
+
531
+ print("Device:", DEVICE)
532
+
533
+
534
+ # ============================================================
535
+ # Example binary dataset
536
+ # Replace X and y with your own data
537
+ # ============================================================
538
+
539
+ X, y = make_classification(
540
+ n_samples=800,
541
+ n_features=12,
542
+ n_informative=8,
543
+ n_redundant=2,
544
+ n_classes=2,
545
+ random_state=SEED,
546
+ )
547
+
548
+ X = X.astype(np.float32)
549
+ y = y.astype(np.int64)
550
+
551
+
552
+ # ============================================================
553
+ # Hold out the final test set BEFORE Optuna
554
+ # ============================================================
555
+
556
+ X_train, X_test, y_train, y_test = train_test_split(
557
+ X,
558
+ y,
559
+ test_size=0.20,
560
+ random_state=SEED,
561
+ stratify=y,
562
+ )
563
+
564
+
565
+ # ============================================================
566
+ # Optuna search space
567
+ # ============================================================
568
+
569
+ def sample_params(trial):
570
+
571
+ hidden_width = trial.suggest_categorical(
572
+ "hidden_width",
573
+ [64, 128, 256],
574
+ )
575
+
576
+ depth = trial.suggest_int(
577
+ "depth",
578
+ 1,
579
+ 3,
580
+ )
581
+
582
+ return {
583
+ "k_aug": trial.suggest_categorical(
584
+ "k_aug",
585
+ [8, 16, 24, 32, 48, 64],
586
+ ),
587
+
588
+ "kprime": trial.suggest_categorical(
589
+ "kprime",
590
+ [16, 32, 64, 128],
591
+ ),
592
+
593
+ "h_hidden": trial.suggest_categorical(
594
+ "h_hidden",
595
+ [32, 64, 128, 256],
596
+ ),
597
+
598
+ "activation": trial.suggest_categorical(
599
+ "activation",
600
+ ["gelu", "relu", "silu"],
601
+ ),
602
+
603
+ "widths": tuple(
604
+ [hidden_width] * depth
605
+ ),
606
+
607
+ "lambda_sparse": trial.suggest_float(
608
+ "lambda_sparse",
609
+ 1e-6,
610
+ 1e-2,
611
+ log=True,
612
+ ),
613
+
614
+ "lambda_div": trial.suggest_float(
615
+ "lambda_div",
616
+ 1e-6,
617
+ 1e-2,
618
+ log=True,
619
+ ),
620
+
621
+ "lambda_orth": trial.suggest_float(
622
+ "lambda_orth",
623
+ 1e-6,
624
+ 1e-2,
625
+ log=True,
626
+ ),
627
+
628
+ "lambda_budget": trial.suggest_float(
629
+ "lambda_budget",
630
+ 1e-6,
631
+ 1e-2,
632
+ log=True,
633
+ ),
634
+
635
+ "lambda_drift": trial.suggest_categorical(
636
+ "lambda_drift",
637
+ [0.0, 1e-5, 1e-4, 1e-3],
638
+ ),
639
+
640
+ "lr": trial.suggest_float(
641
+ "lr",
642
+ 1e-4,
643
+ 2e-2,
644
+ log=True,
645
+ ),
646
+
647
+ "weight_decay": trial.suggest_float(
648
+ "weight_decay",
649
+ 1e-6,
650
+ 3e-3,
651
+ log=True,
652
+ ),
653
+
654
+ "batch_size": trial.suggest_categorical(
655
+ "batch_size",
656
+ [32, 64, 128],
657
+ ),
658
+
659
+ "epochs": trial.suggest_categorical(
660
+ "epochs",
661
+ [50, 80, 120],
662
+ ),
663
+ }
664
+
665
+
666
+ # ============================================================
667
+ # Build AugTab
668
+ # ============================================================
669
+
670
+ def build_model(params, d_features):
671
+
672
+ regs = RegularizerConfig(
673
+ lambda_sparse=params["lambda_sparse"],
674
+ lambda_div=params["lambda_div"],
675
+ lambda_orth=params["lambda_orth"],
676
+ lambda_budget=params["lambda_budget"],
677
+ lambda_drift=params["lambda_drift"],
678
+ )
679
+
680
+ return AugTabClassifier(
681
+ d_features=d_features,
682
+ k_aug=params["k_aug"],
683
+ kprime=params["kprime"],
684
+ h_hidden=params["h_hidden"],
685
+ widths=params["widths"],
686
+ activation=params["activation"],
687
+ append_mask=False,
688
+ gating="basic",
689
+ regs=regs,
690
+ device=DEVICE,
691
+ lr=params["lr"],
692
+ weight_decay=params["weight_decay"],
693
+ )
694
+
695
+
696
+ # ============================================================
697
+ # 5-fold Optuna objective
698
+ # ============================================================
699
+
700
+ def objective(trial):
701
+
702
+ params = sample_params(trial)
703
+
704
+ cv = StratifiedKFold(
705
+ n_splits=5,
706
+ shuffle=True,
707
+ random_state=SEED,
708
+ )
709
+
710
+ scores = []
711
+
712
+ for fold, (train_idx, val_idx) in enumerate(
713
+ cv.split(X_train, y_train)
714
+ ):
715
+
716
+ # ----------------------------------------------------
717
+ # Fold-local preprocessing
718
+ # ----------------------------------------------------
719
+
720
+ scaler = StandardScaler()
721
+
722
+ X_tr = scaler.fit_transform(
723
+ X_train[train_idx]
724
+ ).astype(np.float32)
725
+
726
+ X_val = scaler.transform(
727
+ X_train[val_idx]
728
+ ).astype(np.float32)
729
+
730
+ y_tr = y_train[train_idx]
731
+ y_val = y_train[val_idx]
732
+
733
+
734
+ # ----------------------------------------------------
735
+ # Fresh model for every fold
736
+ # ----------------------------------------------------
737
+
738
+ torch.manual_seed(SEED + fold)
739
+
740
+ if torch.cuda.is_available():
741
+ torch.cuda.manual_seed_all(SEED + fold)
742
+
743
+ model = build_model(
744
+ params,
745
+ d_features=X_tr.shape[1],
746
+ )
747
+
748
+
749
+ # ----------------------------------------------------
750
+ # Train
751
+ # ----------------------------------------------------
752
+
753
+ model.fit(
754
+ X_tr,
755
+ y_tr,
756
+ epochs=params["epochs"],
757
+ batch_size=params["batch_size"],
758
+ verbose=False,
759
+ )
760
+
761
+
762
+ # ----------------------------------------------------
763
+ # Validation accuracy
764
+ # ----------------------------------------------------
765
+
766
+ score = model.score(
767
+ X_val,
768
+ y_val,
769
+ )
770
+
771
+ scores.append(score)
772
+
773
+
774
+ mean_score = float(np.mean(scores))
775
+ std_score = float(np.std(scores))
776
+
777
+ trial.set_user_attr(
778
+ "cv_mean",
779
+ mean_score,
780
+ )
781
+
782
+ trial.set_user_attr(
783
+ "cv_std",
784
+ std_score,
785
+ )
786
+
787
+ return mean_score
788
+
789
+
790
+ # ============================================================
791
+ # Run Optuna
792
+ # ============================================================
793
+
794
+ sampler = optuna.samplers.TPESampler(
795
+ seed=SEED
796
+ )
797
+
798
+ study = optuna.create_study(
799
+ direction="maximize",
800
+ sampler=sampler,
801
+ )
802
+
803
+
804
+ # Small value for demonstration.
805
+ # Increase for full experiments.
806
+ N_TRIALS = 20
807
+
808
+ study.optimize(
809
+ objective,
810
+ n_trials=N_TRIALS,
811
+ show_progress_bar=True,
812
+ )
813
+
814
+
815
+ print("\nBest 5-fold CV Accuracy:")
816
+ print(
817
+ f"{study.best_trial.user_attrs['cv_mean']:.4f} "
818
+ f"± {study.best_trial.user_attrs['cv_std']:.4f}"
819
+ )
820
+
821
+ print("\nBest Hyperparameters:")
822
+ print(study.best_params)
823
+
824
+
825
+ # ============================================================
826
+ # Reconstruct widths from Optuna parameters
827
+ # ============================================================
828
+
829
+ best = study.best_params.copy()
830
+
831
+ best["widths"] = tuple(
832
+ [best["hidden_width"]] * best["depth"]
833
+ )
834
+
835
+
836
+ # ============================================================
837
+ # Train final model using all training data
838
+ # ============================================================
839
+
840
+ final_scaler = StandardScaler()
841
+
842
+ X_train_final = final_scaler.fit_transform(
843
+ X_train
844
+ ).astype(np.float32)
845
+
846
+ X_test_final = final_scaler.transform(
847
+ X_test
848
+ ).astype(np.float32)
849
+
850
+
851
+ final_model = build_model(
852
+ best,
853
+ d_features=X_train_final.shape[1],
854
+ )
855
+
856
+
857
+ final_model.fit(
858
+ X_train_final,
859
+ y_train,
860
+ epochs=best["epochs"],
861
+ batch_size=best["batch_size"],
862
+ verbose=False,
863
+ )
864
+
865
+
866
+ # ============================================================
867
+ # Final held-out test evaluation
868
+ # ============================================================
869
+
870
+ y_pred = final_model.predict(
871
+ X_test_final
872
+ ).numpy()
873
+
874
+ y_prob = final_model.predict_proba(
875
+ X_test_final
876
+ ).numpy().reshape(-1)
877
+
878
+
879
+ print("\nFinal Test Results")
880
+ print(
881
+ "Accuracy :",
882
+ accuracy_score(y_test, y_pred),
883
+ )
884
+
885
+ print(
886
+ "F1 :",
887
+ f1_score(y_test, y_pred),
888
+ )
889
+
890
+ print(
891
+ "ROC-AUC :",
892
+ roc_auc_score(y_test, y_prob),
893
+ )
894
+ ```
895
+
896
+ > **Recommended:** Increase `N_TRIALS` for full experiments. The value above is intentionally kept small so that the example remains practical as a quick-start demonstration.
897
+
898
+ ---
899
+
900
+ ### Example 3: Multiclass Classification Without Hyperparameter Tuning
901
+
902
+ For multiclass problems, use `AugTabMulti` and provide the number of target classes through `n_classes`.
903
+
904
+ ```python
905
+ import random
906
+ import numpy as np
907
+ import torch
908
+
909
+ from sklearn.datasets import make_classification
910
+ from sklearn.model_selection import train_test_split
911
+ from sklearn.preprocessing import StandardScaler
912
+ from sklearn.metrics import accuracy_score, f1_score
913
+
914
+ from augtab import AugTabMulti, RegularizerConfig
915
+
916
+
917
+ # ============================================================
918
+ # Reproducibility
919
+ # ============================================================
920
+
921
+ SEED = 42
922
+
923
+ random.seed(SEED)
924
+ np.random.seed(SEED)
925
+ torch.manual_seed(SEED)
926
+
927
+ if torch.cuda.is_available():
928
+ torch.cuda.manual_seed_all(SEED)
929
+
930
+
931
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
932
+
933
+
934
+ # ============================================================
935
+ # Example multiclass dataset
936
+ # ============================================================
937
+
938
+ X, y = make_classification(
939
+ n_samples=900,
940
+ n_features=15,
941
+ n_informative=10,
942
+ n_redundant=2,
943
+ n_classes=3,
944
+ n_clusters_per_class=1,
945
+ random_state=SEED,
946
+ )
947
+
948
+ X = X.astype(np.float32)
949
+ y = y.astype(np.int64)
950
+
951
+ N_CLASSES = len(np.unique(y))
952
+
953
+
954
+ # ============================================================
955
+ # Train / test split
956
+ # ============================================================
957
+
958
+ X_train, X_test, y_train, y_test = train_test_split(
959
+ X,
960
+ y,
961
+ test_size=0.20,
962
+ random_state=SEED,
963
+ stratify=y,
964
+ )
965
+
966
+
967
+ # ============================================================
968
+ # Standardization
969
+ # ============================================================
970
+
971
+ scaler = StandardScaler()
972
+
973
+ X_train = scaler.fit_transform(
974
+ X_train
975
+ ).astype(np.float32)
976
+
977
+ X_test = scaler.transform(
978
+ X_test
979
+ ).astype(np.float32)
980
+
981
+
982
+ # ============================================================
983
+ # Regularization
984
+ # ============================================================
985
+
986
+ regs = RegularizerConfig(
987
+ lambda_sparse=1e-3,
988
+ lambda_div=1e-3,
989
+ lambda_orth=1e-3,
990
+ lambda_budget=1e-3,
991
+ lambda_drift=0.0,
992
+ )
993
+
994
+
995
+ # ============================================================
996
+ # Initialize AugTab
997
+ # ============================================================
998
+
999
+ model = AugTabMulti(
1000
+ d_features=X_train.shape[1],
1001
+ n_classes=N_CLASSES,
1002
+ k_aug=32,
1003
+ kprime=64,
1004
+ h_hidden=64,
1005
+ widths=(128, 128),
1006
+ activation="gelu",
1007
+ append_mask=False,
1008
+ gating="basic",
1009
+ regs=regs,
1010
+ device=DEVICE,
1011
+ lr=2e-3,
1012
+ weight_decay=1e-4,
1013
+ )
1014
+
1015
+
1016
+ # ============================================================
1017
+ # Train
1018
+ # ============================================================
1019
+
1020
+ model.fit(
1021
+ X_train,
1022
+ y_train,
1023
+ epochs=80,
1024
+ batch_size=64,
1025
+ verbose=False,
1026
+ )
1027
+
1028
+
1029
+ # ============================================================
1030
+ # Evaluate
1031
+ # ============================================================
1032
+
1033
+ y_pred = model.predict(
1034
+ X_test
1035
+ ).numpy()
1036
+
1037
+ y_prob = model.predict_proba(
1038
+ X_test
1039
+ ).numpy()
1040
+
1041
+
1042
+ print("\nMulticlass Classification Results")
1043
+
1044
+ print(
1045
+ "Accuracy :",
1046
+ accuracy_score(y_test, y_pred),
1047
+ )
1048
+
1049
+ print(
1050
+ "Macro F1 :",
1051
+ f1_score(
1052
+ y_test,
1053
+ y_pred,
1054
+ average="macro",
1055
+ ),
1056
+ )
1057
+
1058
+ print(
1059
+ "Probability matrix shape:",
1060
+ y_prob.shape,
1061
+ )
1062
+ ```
1063
+
1064
+ ---
1065
+
1066
+ ### Example 4: Multiclass Classification with Optuna Hyperparameter Tuning
1067
+
1068
+ For a **new multiclass dataset**, this is the recommended workflow.
1069
+
1070
+ ```python
1071
+ import random
1072
+ import numpy as np
1073
+ import optuna
1074
+ import torch
1075
+
1076
+ from sklearn.datasets import make_classification
1077
+ from sklearn.model_selection import train_test_split, StratifiedKFold
1078
+ from sklearn.preprocessing import StandardScaler
1079
+ from sklearn.metrics import accuracy_score, f1_score
1080
+
1081
+ from augtab import AugTabMulti, RegularizerConfig
1082
+
1083
+
1084
+ # ============================================================
1085
+ # Reproducibility
1086
+ # ============================================================
1087
+
1088
+ SEED = 42
1089
+
1090
+ random.seed(SEED)
1091
+ np.random.seed(SEED)
1092
+ torch.manual_seed(SEED)
1093
+
1094
+ if torch.cuda.is_available():
1095
+ torch.cuda.manual_seed_all(SEED)
1096
+
1097
+
1098
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
1099
+
1100
+
1101
+ # ============================================================
1102
+ # Example multiclass dataset
1103
+ # ============================================================
1104
+
1105
+ X, y = make_classification(
1106
+ n_samples=900,
1107
+ n_features=15,
1108
+ n_informative=10,
1109
+ n_redundant=2,
1110
+ n_classes=3,
1111
+ n_clusters_per_class=1,
1112
+ random_state=SEED,
1113
+ )
1114
+
1115
+ X = X.astype(np.float32)
1116
+ y = y.astype(np.int64)
1117
+
1118
+ N_CLASSES = len(np.unique(y))
1119
+
1120
+
1121
+ # ============================================================
1122
+ # Held-out test set
1123
+ # ============================================================
1124
+
1125
+ X_train, X_test, y_train, y_test = train_test_split(
1126
+ X,
1127
+ y,
1128
+ test_size=0.20,
1129
+ random_state=SEED,
1130
+ stratify=y,
1131
+ )
1132
+
1133
+
1134
+ # ============================================================
1135
+ # Optuna search space
1136
+ # ============================================================
1137
+
1138
+ def sample_params(trial):
1139
+
1140
+ hidden_width = trial.suggest_categorical(
1141
+ "hidden_width",
1142
+ [64, 128, 256],
1143
+ )
1144
+
1145
+ depth = trial.suggest_int(
1146
+ "depth",
1147
+ 1,
1148
+ 3,
1149
+ )
1150
+
1151
+ return {
1152
+ "k_aug": trial.suggest_categorical(
1153
+ "k_aug",
1154
+ [8, 16, 24, 32, 48, 64],
1155
+ ),
1156
+
1157
+ "kprime": trial.suggest_categorical(
1158
+ "kprime",
1159
+ [16, 32, 64, 128],
1160
+ ),
1161
+
1162
+ "h_hidden": trial.suggest_categorical(
1163
+ "h_hidden",
1164
+ [32, 64, 128, 256],
1165
+ ),
1166
+
1167
+ "activation": trial.suggest_categorical(
1168
+ "activation",
1169
+ ["gelu", "relu", "silu"],
1170
+ ),
1171
+
1172
+ "widths": tuple(
1173
+ [hidden_width] * depth
1174
+ ),
1175
+
1176
+ "lambda_sparse": trial.suggest_float(
1177
+ "lambda_sparse",
1178
+ 1e-6,
1179
+ 1e-2,
1180
+ log=True,
1181
+ ),
1182
+
1183
+ "lambda_div": trial.suggest_float(
1184
+ "lambda_div",
1185
+ 1e-6,
1186
+ 1e-2,
1187
+ log=True,
1188
+ ),
1189
+
1190
+ "lambda_orth": trial.suggest_float(
1191
+ "lambda_orth",
1192
+ 1e-6,
1193
+ 1e-2,
1194
+ log=True,
1195
+ ),
1196
+
1197
+ "lambda_budget": trial.suggest_float(
1198
+ "lambda_budget",
1199
+ 1e-6,
1200
+ 1e-2,
1201
+ log=True,
1202
+ ),
1203
+
1204
+ "lambda_drift": trial.suggest_categorical(
1205
+ "lambda_drift",
1206
+ [0.0, 1e-5, 1e-4, 1e-3],
1207
+ ),
1208
+
1209
+ "lr": trial.suggest_float(
1210
+ "lr",
1211
+ 1e-4,
1212
+ 2e-2,
1213
+ log=True,
1214
+ ),
1215
+
1216
+ "weight_decay": trial.suggest_float(
1217
+ "weight_decay",
1218
+ 1e-6,
1219
+ 3e-3,
1220
+ log=True,
1221
+ ),
1222
+
1223
+ "batch_size": trial.suggest_categorical(
1224
+ "batch_size",
1225
+ [32, 64, 128],
1226
+ ),
1227
+
1228
+ "epochs": trial.suggest_categorical(
1229
+ "epochs",
1230
+ [50, 80, 120],
1231
+ ),
1232
+ }
1233
+
1234
+
1235
+ # ============================================================
1236
+ # Build model
1237
+ # ============================================================
1238
+
1239
+ def build_model(params, d_features):
1240
+
1241
+ regs = RegularizerConfig(
1242
+ lambda_sparse=params["lambda_sparse"],
1243
+ lambda_div=params["lambda_div"],
1244
+ lambda_orth=params["lambda_orth"],
1245
+ lambda_budget=params["lambda_budget"],
1246
+ lambda_drift=params["lambda_drift"],
1247
+ )
1248
+
1249
+ return AugTabMulti(
1250
+ d_features=d_features,
1251
+ n_classes=N_CLASSES,
1252
+ k_aug=params["k_aug"],
1253
+ kprime=params["kprime"],
1254
+ h_hidden=params["h_hidden"],
1255
+ widths=params["widths"],
1256
+ activation=params["activation"],
1257
+ append_mask=False,
1258
+ gating="basic",
1259
+ regs=regs,
1260
+ device=DEVICE,
1261
+ lr=params["lr"],
1262
+ weight_decay=params["weight_decay"],
1263
+ )
1264
+
1265
+
1266
+ # ============================================================
1267
+ # 5-fold Optuna objective
1268
+ # ============================================================
1269
+
1270
+ def objective(trial):
1271
+
1272
+ params = sample_params(trial)
1273
+
1274
+ cv = StratifiedKFold(
1275
+ n_splits=5,
1276
+ shuffle=True,
1277
+ random_state=SEED,
1278
+ )
1279
+
1280
+ scores = []
1281
+
1282
+ for fold, (train_idx, val_idx) in enumerate(
1283
+ cv.split(X_train, y_train)
1284
+ ):
1285
+
1286
+ scaler = StandardScaler()
1287
+
1288
+ X_tr = scaler.fit_transform(
1289
+ X_train[train_idx]
1290
+ ).astype(np.float32)
1291
+
1292
+ X_val = scaler.transform(
1293
+ X_train[val_idx]
1294
+ ).astype(np.float32)
1295
+
1296
+ y_tr = y_train[train_idx]
1297
+ y_val = y_train[val_idx]
1298
+
1299
+
1300
+ torch.manual_seed(SEED + fold)
1301
+
1302
+ if torch.cuda.is_available():
1303
+ torch.cuda.manual_seed_all(SEED + fold)
1304
+
1305
+
1306
+ model = build_model(
1307
+ params,
1308
+ d_features=X_tr.shape[1],
1309
+ )
1310
+
1311
+
1312
+ model.fit(
1313
+ X_tr,
1314
+ y_tr,
1315
+ epochs=params["epochs"],
1316
+ batch_size=params["batch_size"],
1317
+ verbose=False,
1318
+ )
1319
+
1320
+
1321
+ score = model.score(
1322
+ X_val,
1323
+ y_val,
1324
+ )
1325
+
1326
+ scores.append(score)
1327
+
1328
+
1329
+ mean_score = float(np.mean(scores))
1330
+ std_score = float(np.std(scores))
1331
+
1332
+ trial.set_user_attr(
1333
+ "cv_mean",
1334
+ mean_score,
1335
+ )
1336
+
1337
+ trial.set_user_attr(
1338
+ "cv_std",
1339
+ std_score,
1340
+ )
1341
+
1342
+ return mean_score
1343
+
1344
+
1345
+ # ============================================================
1346
+ # Run Optuna
1347
+ # ============================================================
1348
+
1349
+ study = optuna.create_study(
1350
+ direction="maximize",
1351
+ sampler=optuna.samplers.TPESampler(
1352
+ seed=SEED
1353
+ ),
1354
+ )
1355
+
1356
+
1357
+ N_TRIALS = 20
1358
+
1359
+ study.optimize(
1360
+ objective,
1361
+ n_trials=N_TRIALS,
1362
+ show_progress_bar=True,
1363
+ )
1364
+
1365
+
1366
+ print("\nBest 5-fold CV Accuracy:")
1367
+
1368
+ print(
1369
+ f"{study.best_trial.user_attrs['cv_mean']:.4f} "
1370
+ f"± {study.best_trial.user_attrs['cv_std']:.4f}"
1371
+ )
1372
+
1373
+ print("\nBest Hyperparameters:")
1374
+ print(study.best_params)
1375
+
1376
+
1377
+ # ============================================================
1378
+ # Final training
1379
+ # ============================================================
1380
+
1381
+ best = study.best_params.copy()
1382
+
1383
+ best["widths"] = tuple(
1384
+ [best["hidden_width"]] * best["depth"]
1385
+ )
1386
+
1387
+
1388
+ scaler = StandardScaler()
1389
+
1390
+ X_train_final = scaler.fit_transform(
1391
+ X_train
1392
+ ).astype(np.float32)
1393
+
1394
+ X_test_final = scaler.transform(
1395
+ X_test
1396
+ ).astype(np.float32)
1397
+
1398
+
1399
+ final_model = build_model(
1400
+ best,
1401
+ d_features=X_train_final.shape[1],
1402
+ )
1403
+
1404
+
1405
+ final_model.fit(
1406
+ X_train_final,
1407
+ y_train,
1408
+ epochs=best["epochs"],
1409
+ batch_size=best["batch_size"],
1410
+ verbose=False,
1411
+ )
1412
+
1413
+
1414
+ # ============================================================
1415
+ # Final test evaluation
1416
+ # ============================================================
1417
+
1418
+ y_pred = final_model.predict(
1419
+ X_test_final
1420
+ ).numpy()
1421
+
1422
+
1423
+ print("\nFinal Test Results")
1424
+
1425
+ print(
1426
+ "Accuracy :",
1427
+ accuracy_score(y_test, y_pred),
1428
+ )
1429
+
1430
+ print(
1431
+ "Macro F1 :",
1432
+ f1_score(
1433
+ y_test,
1434
+ y_pred,
1435
+ average="macro",
1436
+ ),
1437
+ )
1438
+ ```
1439
+
1440
+ ---
1441
+
1442
+ ### Example 5: Regression with Optuna Hyperparameter Tuning
1443
+
1444
+ For a **new regression dataset**, we recommend tuning `AugTabRegressor` with Optuna and **5-fold cross-validation**.
1445
+
1446
+ The optimization objective below maximizes mean cross-validation $R^2$.
1447
+
1448
+ ```python
1449
+ import random
1450
+ import numpy as np
1451
+ import optuna
1452
+ import torch
1453
+
1454
+ from sklearn.datasets import make_regression
1455
+ from sklearn.model_selection import train_test_split, KFold
1456
+ from sklearn.preprocessing import StandardScaler
1457
+ from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
1458
+
1459
+ from augtab import AugTabRegressor, RegularizerConfig
1460
+
1461
+
1462
+ # ============================================================
1463
+ # Reproducibility
1464
+ # ============================================================
1465
+
1466
+ SEED = 42
1467
+
1468
+ random.seed(SEED)
1469
+ np.random.seed(SEED)
1470
+ torch.manual_seed(SEED)
1471
+
1472
+ if torch.cuda.is_available():
1473
+ torch.cuda.manual_seed_all(SEED)
1474
+
1475
+
1476
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
1477
+
1478
+
1479
+ # ============================================================
1480
+ # Example regression dataset
1481
+ # ============================================================
1482
+
1483
+ X, y = make_regression(
1484
+ n_samples=800,
1485
+ n_features=12,
1486
+ n_informative=8,
1487
+ noise=15.0,
1488
+ random_state=SEED,
1489
+ )
1490
+
1491
+ X = X.astype(np.float32)
1492
+ y = y.astype(np.float32)
1493
+
1494
+
1495
+ # ============================================================
1496
+ # Held-out test set
1497
+ # ============================================================
1498
+
1499
+ X_train, X_test, y_train, y_test = train_test_split(
1500
+ X,
1501
+ y,
1502
+ test_size=0.20,
1503
+ random_state=SEED,
1504
+ )
1505
+
1506
+
1507
+ # ============================================================
1508
+ # Optuna search space
1509
+ # ============================================================
1510
+
1511
+ def sample_params(trial):
1512
+
1513
+ hidden_width = trial.suggest_categorical(
1514
+ "hidden_width",
1515
+ [64, 128, 256],
1516
+ )
1517
+
1518
+ depth = trial.suggest_int(
1519
+ "depth",
1520
+ 1,
1521
+ 3,
1522
+ )
1523
+
1524
+ return {
1525
+ "k_aug": trial.suggest_categorical(
1526
+ "k_aug",
1527
+ [8, 16, 24, 32, 48, 64],
1528
+ ),
1529
+
1530
+ "kprime": trial.suggest_categorical(
1531
+ "kprime",
1532
+ [16, 32, 64, 128],
1533
+ ),
1534
+
1535
+ "h_hidden": trial.suggest_categorical(
1536
+ "h_hidden",
1537
+ [32, 64, 128, 256],
1538
+ ),
1539
+
1540
+ "activation": trial.suggest_categorical(
1541
+ "activation",
1542
+ ["gelu", "relu", "silu"],
1543
+ ),
1544
+
1545
+ "widths": tuple(
1546
+ [hidden_width] * depth
1547
+ ),
1548
+
1549
+ "lambda_sparse": trial.suggest_float(
1550
+ "lambda_sparse",
1551
+ 1e-6,
1552
+ 1e-2,
1553
+ log=True,
1554
+ ),
1555
+
1556
+ "lambda_div": trial.suggest_float(
1557
+ "lambda_div",
1558
+ 1e-6,
1559
+ 1e-2,
1560
+ log=True,
1561
+ ),
1562
+
1563
+ "lambda_orth": trial.suggest_float(
1564
+ "lambda_orth",
1565
+ 1e-6,
1566
+ 1e-2,
1567
+ log=True,
1568
+ ),
1569
+
1570
+ "lambda_budget": trial.suggest_float(
1571
+ "lambda_budget",
1572
+ 1e-6,
1573
+ 1e-2,
1574
+ log=True,
1575
+ ),
1576
+
1577
+ "lambda_drift": trial.suggest_categorical(
1578
+ "lambda_drift",
1579
+ [0.0, 1e-5, 1e-4, 1e-3],
1580
+ ),
1581
+
1582
+ "lr": trial.suggest_float(
1583
+ "lr",
1584
+ 1e-4,
1585
+ 2e-2,
1586
+ log=True,
1587
+ ),
1588
+
1589
+ "weight_decay": trial.suggest_float(
1590
+ "weight_decay",
1591
+ 1e-6,
1592
+ 3e-3,
1593
+ log=True,
1594
+ ),
1595
+
1596
+ "batch_size": trial.suggest_categorical(
1597
+ "batch_size",
1598
+ [32, 64, 128],
1599
+ ),
1600
+
1601
+ "epochs": trial.suggest_categorical(
1602
+ "epochs",
1603
+ [50, 80, 120],
1604
+ ),
1605
+ }
1606
+
1607
+
1608
+ # ============================================================
1609
+ # Build AugTab regressor
1610
+ # ============================================================
1611
+
1612
+ def build_model(params, d_features):
1613
+
1614
+ regs = RegularizerConfig(
1615
+ lambda_sparse=params["lambda_sparse"],
1616
+ lambda_div=params["lambda_div"],
1617
+ lambda_orth=params["lambda_orth"],
1618
+ lambda_budget=params["lambda_budget"],
1619
+ lambda_drift=params["lambda_drift"],
1620
+ )
1621
+
1622
+ return AugTabRegressor(
1623
+ d_features=d_features,
1624
+ k_aug=params["k_aug"],
1625
+ kprime=params["kprime"],
1626
+ h_hidden=params["h_hidden"],
1627
+ widths=params["widths"],
1628
+ activation=params["activation"],
1629
+ append_mask=False,
1630
+ gating="basic",
1631
+ regs=regs,
1632
+ device=DEVICE,
1633
+ lr=params["lr"],
1634
+ weight_decay=params["weight_decay"],
1635
+ )
1636
+
1637
+
1638
+ # ============================================================
1639
+ # 5-fold Optuna objective
1640
+ # ============================================================
1641
+
1642
+ def objective(trial):
1643
+
1644
+ params = sample_params(trial)
1645
+
1646
+ cv = KFold(
1647
+ n_splits=5,
1648
+ shuffle=True,
1649
+ random_state=SEED,
1650
+ )
1651
+
1652
+ scores = []
1653
+
1654
+ for fold, (train_idx, val_idx) in enumerate(
1655
+ cv.split(X_train)
1656
+ ):
1657
+
1658
+ scaler = StandardScaler()
1659
+
1660
+ X_tr = scaler.fit_transform(
1661
+ X_train[train_idx]
1662
+ ).astype(np.float32)
1663
+
1664
+ X_val = scaler.transform(
1665
+ X_train[val_idx]
1666
+ ).astype(np.float32)
1667
+
1668
+ y_tr = y_train[train_idx]
1669
+ y_val = y_train[val_idx]
1670
+
1671
+
1672
+ torch.manual_seed(SEED + fold)
1673
+
1674
+ if torch.cuda.is_available():
1675
+ torch.cuda.manual_seed_all(SEED + fold)
1676
+
1677
+
1678
+ model = build_model(
1679
+ params,
1680
+ d_features=X_tr.shape[1],
1681
+ )
1682
+
1683
+
1684
+ model.fit(
1685
+ X_tr,
1686
+ y_tr,
1687
+ epochs=params["epochs"],
1688
+ batch_size=params["batch_size"],
1689
+ verbose=False,
1690
+ )
1691
+
1692
+
1693
+ score = model.score(
1694
+ X_val,
1695
+ y_val,
1696
+ )
1697
+
1698
+ scores.append(score)
1699
+
1700
+
1701
+ mean_score = float(np.mean(scores))
1702
+ std_score = float(np.std(scores))
1703
+
1704
+ trial.set_user_attr(
1705
+ "cv_mean",
1706
+ mean_score,
1707
+ )
1708
+
1709
+ trial.set_user_attr(
1710
+ "cv_std",
1711
+ std_score,
1712
+ )
1713
+
1714
+ return mean_score
1715
+
1716
+
1717
+ # ============================================================
1718
+ # Run Optuna
1719
+ # ============================================================
1720
+
1721
+ study = optuna.create_study(
1722
+ direction="maximize",
1723
+ sampler=optuna.samplers.TPESampler(
1724
+ seed=SEED
1725
+ ),
1726
+ )
1727
+
1728
+
1729
+ N_TRIALS = 20
1730
+
1731
+ study.optimize(
1732
+ objective,
1733
+ n_trials=N_TRIALS,
1734
+ show_progress_bar=True,
1735
+ )
1736
+
1737
+
1738
+ print("\nBest 5-fold CV R2:")
1739
+
1740
+ print(
1741
+ f"{study.best_trial.user_attrs['cv_mean']:.4f} "
1742
+ f"± {study.best_trial.user_attrs['cv_std']:.4f}"
1743
+ )
1744
+
1745
+ print("\nBest Hyperparameters:")
1746
+ print(study.best_params)
1747
+
1748
+
1749
+ # ============================================================
1750
+ # Final training
1751
+ # ============================================================
1752
+
1753
+ best = study.best_params.copy()
1754
+
1755
+ best["widths"] = tuple(
1756
+ [best["hidden_width"]] * best["depth"]
1757
+ )
1758
+
1759
+
1760
+ scaler = StandardScaler()
1761
+
1762
+ X_train_final = scaler.fit_transform(
1763
+ X_train
1764
+ ).astype(np.float32)
1765
+
1766
+ X_test_final = scaler.transform(
1767
+ X_test
1768
+ ).astype(np.float32)
1769
+
1770
+
1771
+ final_model = build_model(
1772
+ best,
1773
+ d_features=X_train_final.shape[1],
1774
+ )
1775
+
1776
+
1777
+ final_model.fit(
1778
+ X_train_final,
1779
+ y_train,
1780
+ epochs=best["epochs"],
1781
+ batch_size=best["batch_size"],
1782
+ verbose=False,
1783
+ )
1784
+
1785
+
1786
+ # ============================================================
1787
+ # Final held-out test evaluation
1788
+ # ============================================================
1789
+
1790
+ y_pred = final_model.predict(
1791
+ X_test_final
1792
+ ).numpy().reshape(-1)
1793
+
1794
+
1795
+ rmse = np.sqrt(
1796
+ mean_squared_error(
1797
+ y_test,
1798
+ y_pred,
1799
+ )
1800
+ )
1801
+
1802
+ mae = mean_absolute_error(
1803
+ y_test,
1804
+ y_pred,
1805
+ )
1806
+
1807
+ r2 = r2_score(
1808
+ y_test,
1809
+ y_pred,
1810
+ )
1811
+
1812
+
1813
+ print("\nFinal Test Results")
1814
+ print("RMSE :", rmse)
1815
+ print("MAE :", mae)
1816
+ print("R2 :", r2)
1817
+ ```
1818
+
1819
+ ---
1820
+
1821
+ ### Example 6: Regression Without Hyperparameter Tuning
1822
+
1823
+ This example trains `AugTabRegressor` using a fixed configuration without Optuna.
1824
+
1825
+ ```python
1826
+ import random
1827
+ import numpy as np
1828
+ import torch
1829
+
1830
+ from sklearn.datasets import make_regression
1831
+ from sklearn.model_selection import train_test_split
1832
+ from sklearn.preprocessing import StandardScaler
1833
+ from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
1834
+
1835
+ from augtab import AugTabRegressor, RegularizerConfig
1836
+
1837
+
1838
+ # ============================================================
1839
+ # Reproducibility
1840
+ # ============================================================
1841
+
1842
+ SEED = 42
1843
+
1844
+ random.seed(SEED)
1845
+ np.random.seed(SEED)
1846
+ torch.manual_seed(SEED)
1847
+
1848
+ if torch.cuda.is_available():
1849
+ torch.cuda.manual_seed_all(SEED)
1850
+
1851
+
1852
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
1853
+
1854
+
1855
+ # ============================================================
1856
+ # Example regression dataset
1857
+ # ============================================================
1858
+
1859
+ X, y = make_regression(
1860
+ n_samples=800,
1861
+ n_features=12,
1862
+ n_informative=8,
1863
+ noise=15.0,
1864
+ random_state=SEED,
1865
+ )
1866
+
1867
+ X = X.astype(np.float32)
1868
+ y = y.astype(np.float32)
1869
+
1870
+
1871
+ # ============================================================
1872
+ # Train / test split
1873
+ # ============================================================
1874
+
1875
+ X_train, X_test, y_train, y_test = train_test_split(
1876
+ X,
1877
+ y,
1878
+ test_size=0.20,
1879
+ random_state=SEED,
1880
+ )
1881
+
1882
+
1883
+ # ============================================================
1884
+ # Standardization
1885
+ # ============================================================
1886
+
1887
+ scaler = StandardScaler()
1888
+
1889
+ X_train = scaler.fit_transform(
1890
+ X_train
1891
+ ).astype(np.float32)
1892
+
1893
+ X_test = scaler.transform(
1894
+ X_test
1895
+ ).astype(np.float32)
1896
+
1897
+
1898
+ # ============================================================
1899
+ # Regularization
1900
+ # ============================================================
1901
+
1902
+ regs = RegularizerConfig(
1903
+ lambda_sparse=1e-3,
1904
+ lambda_div=1e-3,
1905
+ lambda_orth=1e-3,
1906
+ lambda_budget=1e-3,
1907
+ lambda_drift=0.0,
1908
+ )
1909
+
1910
+
1911
+ # ============================================================
1912
+ # Initialize AugTab
1913
+ # ============================================================
1914
+
1915
+ model = AugTabRegressor(
1916
+ d_features=X_train.shape[1],
1917
+ k_aug=32,
1918
+ kprime=64,
1919
+ h_hidden=64,
1920
+ widths=(128, 128),
1921
+ activation="gelu",
1922
+ append_mask=False,
1923
+ gating="basic",
1924
+ regs=regs,
1925
+ device=DEVICE,
1926
+ lr=2e-3,
1927
+ weight_decay=1e-4,
1928
+ )
1929
+
1930
+
1931
+ # ============================================================
1932
+ # Train
1933
+ # ============================================================
1934
+
1935
+ model.fit(
1936
+ X_train,
1937
+ y_train,
1938
+ epochs=80,
1939
+ batch_size=64,
1940
+ verbose=False,
1941
+ )
1942
+
1943
+
1944
+ # ============================================================
1945
+ # Evaluate
1946
+ # ============================================================
1947
+
1948
+ y_pred = model.predict(
1949
+ X_test
1950
+ ).numpy().reshape(-1)
1951
+
1952
+
1953
+ rmse = np.sqrt(
1954
+ mean_squared_error(
1955
+ y_test,
1956
+ y_pred,
1957
+ )
1958
+ )
1959
+
1960
+ mae = mean_absolute_error(
1961
+ y_test,
1962
+ y_pred,
1963
+ )
1964
+
1965
+ r2 = r2_score(
1966
+ y_test,
1967
+ y_pred,
1968
+ )
1969
+
1970
+
1971
+ print("\nRegression Results")
1972
+ print("RMSE :", rmse)
1973
+ print("MAE :", mae)
1974
+ print("R2 :", r2)
1975
+ ```
1976
+
1977
+ ---
1978
+
1979
+ ### Adapting AugTab to Your Own Dataset
1980
+
1981
+ To apply AugTab to a new low-dimensional tabular dataset:
1982
+
1983
+ 1. Replace the synthetic `X` and `y` arrays with your own feature matrix and target.
1984
+ 2. Encode categorical variables before passing them to AugTab.
1985
+ 3. Encode classification labels as integer class IDs.
1986
+ 4. Standardize numerical features using statistics computed from the training split only.
1987
+ 5. Set `d_features=X.shape[1]` after preprocessing.
1988
+ 6. Use `AugTabClassifier` for binary classification.
1989
+ 7. Use `AugTabMulti` for multiclass classification and specify `n_classes`.
1990
+ 8. Use `AugTabRegressor` for regression.
1991
+ 9. Use `append_mask=False` when no explicit missingness mask is required.
1992
+ 10. For a new dataset, prefer the **Optuna workflow** to select AugTab and optimization hyperparameters.
1993
+ 11. During Optuna tuning, perform preprocessing independently inside each cross-validation fold.
1994
+ 12. Initialize a **fresh AugTab model for every cross-validation fold**.
1995
+ 13. Keep the test set completely separate from hyperparameter tuning and use it only for final evaluation.
1996
+
1997
+ > **Recommended for new datasets:** The fixed-configuration examples are useful for quick experiments and installation checks, while the **Optuna + 5-fold cross-validation workflows** are recommended when reporting dataset-specific AugTab results.
1998
+
1999
+ ## Related Work and Project Context
2000
+
2001
+ AugTab is part of my broader work on tabular deep learning, but it was developed as a **separate collaborative project outside my PhD dissertation research**. Like **ZAYAN**, it explores a complementary direction beyond my primary PhD research on feature ordering, sequencing, and high-dimensional tabular learning.
2002
+
2003
+ For broader context, some of our related tabular and multimodal learning projects are listed below.
2004
+
2005
+ ### GOTabPFN - ICML 2026
2006
+
2007
+ **GOTabPFN: From Feature Ordering to Compact Tokenization for Tabular Foundation Models on High-Dimensional Data**
2008
+
2009
+ - **Venue:** International Conference on Machine Learning (ICML 2026)
2010
+ - **GitHub:** https://github.com/zadid6pretam/GOTabPFN
2011
+
2012
+ ### iSyncTab - ECCV 2026
2013
+
2014
+ **iSyncTab: Learning Cross-Modal Feature Sequencing for Image-Tabular Data via Neural Synchrony**
2015
+
2016
+ - **Venue:** European Conference on Computer Vision (ECCV 2026)
2017
+ - **GitHub:** https://github.com/zadid6pretam/iSyncTab
2018
+
2019
+ ### BSTabDiff - ICLR 2026 DeLTa Workshop
2020
+
2021
+ **BSTabDiff: Block-Subunit Diffusion Priors for High-Dimensional Tabular Data Generation**
2022
+
2023
+ - **Venue:** ICLR 2026 Workshop on Deep Generative Models in Machine Learning: Theory, Principle and Efficacy (DeLTa)
2024
+ - **GitHub:** https://github.com/zadid6pretam/BSTabDiff
2025
+
2026
+ ### iStructTab - ICPR 2026
2027
+
2028
+ **iStructTab: Structured Feature Sequencing for Multimodal Learning of Image and Tabular Data**
2029
+
2030
+ - **Venue:** International Conference on Pattern Recognition (ICPR 2026)
2031
+ - **GitHub:** https://github.com/zadid6pretam/iStructTab
2032
+
2033
+ ### DynaTab - AAAI 2026 NeurAI Workshop
2034
+
2035
+ **DynaTab: Dynamic Feature Ordering as Neural Rewiring for High-Dimensional Tabular Data**
2036
+
2037
+ - **Venue:** AAAI 2026 Workshop on NeuroAI Multimodal Intelligence
2038
+ - **GitHub:** https://github.com/zadid6pretam/DynaTab
2039
+
2040
+ ### TabSeq - ICPR 2024
2041
+
2042
+ **TabSeq: A Framework for Deep Learning on Tabular Data via Sequential Ordering**
2043
+
2044
+ - **Venue:** International Conference on Pattern Recognition (ICPR 2024)
2045
+ - **GitHub:** https://github.com/zadid6pretam/TabSeq
2046
+
2047
+ ---
2048
+
2049
+ ### ZAYAN - ICPR 2026
2050
+
2051
+ **ZAYAN: Disentangled Contrastive Transformer for Tabular Remote Sensing Data**
2052
+
2053
+ - **Venue:** International Conference on Pattern Recognition (ICPR 2026)
2054
+ - **GitHub:** https://github.com/zadid6pretam/ZAYAN
2055
+
2056
+ > **Note:** AugTab and ZAYAN were developed as separate collaborative projects outside my PhD dissertation research.
2057
+
2058
+ ## Contact
2059
+
2060
+ For any questions, issues, or suggestions related to AugTab, please feel free to open an issue on GitHub.