TabNet vs XGBoost: Which Should You Use?

TabNet is a deep learning architecture designed specifically for tabular data. It uses sequential attention to select which features to focus on at each decision step — essentially learning feature selection and transformation jointly rather than relying on hand-crafted engineering. XGBoost is gradient boosted trees, the dominant tabular baseline for the past decade. The comparison matters because TabNet is the most credible deep learning challenger to XGBoost on structured tabular data, and understanding when one beats the other saves a lot of wasted experimentation.

What TabNet Actually Does

Standard neural networks process all input features simultaneously at every layer. TabNet processes features sequentially, using a learned attention mask at each step to select a sparse subset of features. This has two benefits: the model is interpretable (you can see which features each step focused on) and it handles the high-dimensionality and feature redundancy common in tabular datasets better than fully-connected networks. TabNet also uses ghost batch normalisation, which stabilises training on small batches. The architecture was introduced by Google in 2019 and remains the most widely benchmarked deep learning model for tabular data.

Quick TabNet Setup

pip install pytorch-tabnet
from pytorch_tabnet.tab_model import TabNetClassifier
import numpy as np

clf = TabNetClassifier(
    n_d=64,              # dimension of the prediction layer per step
    n_a=64,              # dimension of the attention embedding per step
    n_steps=5,           # number of sequential attention steps
    gamma=1.5,           # coefficient for feature reusage in attention
    n_independent=2,     # number of independent GLU layers per step
    n_shared=2,          # number of shared GLU layers per step
    optimizer_fn=torch.optim.Adam,
    optimizer_params=dict(lr=2e-3),
    scheduler_params={'step_size': 10, 'gamma': 0.9},
    scheduler_fn=torch.optim.lr_scheduler.StepLR,
    mask_type='sparsemax',
    verbose=10,
    seed=42
)

# TabNet expects numpy arrays
clf.fit(
    X_train.values, y_train.values,
    eval_set=[(X_val.values, y_val.values)],
    eval_metric=['auc'],
    max_epochs=200,
    patience=20,
    batch_size=1024,
    virtual_batch_size=128
)

preds = clf.predict(X_val.values)
probas = clf.predict_proba(X_val.values)[:,1]

Quick XGBoost Setup (for comparison)

import xgboost as xgb
from sklearn.metrics import roc_auc_score

model = xgb.XGBClassifier(
    n_estimators=1000,
    learning_rate=0.05,
    max_depth=6,
    subsample=0.8,
    colsample_bytree=0.8,
    early_stopping_rounds=50,
    eval_metric='auc',
    random_state=42,
    verbosity=0
)
model.fit(X_train, y_train, eval_set=[(X_val, y_val)], verbose=False)
xgb_auc = roc_auc_score(y_val, model.predict_proba(X_val)[:,1])
tab_auc = roc_auc_score(y_val, probas)
print(f"XGBoost AUC: {xgb_auc:.4f}")
print(f"TabNet AUC:  {tab_auc:.4f}")

When XGBoost Wins

On most tabular benchmarks, XGBoost (or LightGBM/CatBoost) beats TabNet. The original TabNet paper showed competitive performance on specific datasets, but broader benchmarks — particularly the Grinsztajn et al. (2022) “Why tree-based models still outperform deep learning on tabular data” paper — consistently find gradient boosted trees ahead on datasets with fewer than ~100k rows. XGBoost wins in these situations: small to medium datasets (under 50k rows), datasets with many categorical features (especially with CatBoost), when training time matters (XGBoost trains 10–100x faster than TabNet on CPU), when you need interpretability via SHAP (XGBoost’s TreeExplainer is exact and fast), and when you have limited compute (TabNet needs a GPU to be competitive on speed).

When TabNet Wins

TabNet is genuinely competitive — and sometimes clearly better — in specific conditions. Large datasets with 100k+ rows where deep learning’s capacity advantage emerges. Datasets with continuous numerical features and few categoricals, where neural networks’ smooth interpolation outperforms step-wise tree splits. Applications requiring built-in feature attribution without a separate SHAP step — TabNet’s attention masks give per-step feature importance as part of inference. Transfer learning scenarios where you can pre-train on a large related dataset and fine-tune on a small target dataset — TabNet supports this natively; XGBoost does not. And when you need to integrate tabular predictions into a larger neural pipeline (embedding tables, multi-modal inputs) where having a differentiable model matters.

Tuning TabNet

TabNet has more hyperparameters than XGBoost but the key ones are n_d, n_a, and n_steps. A practical starting point: set n_d = n_a (recommended by the original paper), start with 32-64, and use 3-5 steps. The gamma parameter controls feature reuse across steps — higher values encourage the model to use different features at each step; lower values allow reuse. Batch size matters significantly for TabNet: very small batches (under 256) destabilise training; 1024-4096 is the practical sweet spot.

import optuna
from pytorch_tabnet.tab_model import TabNetClassifier
from sklearn.metrics import roc_auc_score

def objective(trial):
    clf = TabNetClassifier(
        n_d=trial.suggest_categorical('n_d', [16, 32, 64, 128]),
        n_a=trial.suggest_categorical('n_a', [16, 32, 64, 128]),
        n_steps=trial.suggest_int('n_steps', 3, 8),
        gamma=trial.suggest_float('gamma', 1.0, 2.0),
        optimizer_params=dict(lr=trial.suggest_float('lr', 1e-4, 1e-2, log=True)),
        mask_type='sparsemax',
        verbose=0, seed=42
    )
    clf.fit(X_train.values, y_train.values,
            eval_set=[(X_val.values, y_val.values)],
            eval_metric=['auc'], max_epochs=100, patience=15,
            batch_size=trial.suggest_categorical('batch_size', [512, 1024, 2048]),
            virtual_batch_size=128)
    return roc_auc_score(y_val, clf.predict_proba(X_val.values)[:,1])

study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=30)
print(study.best_params)

Feature Importance from TabNet

import pandas as pd, matplotlib.pyplot as plt

# TabNet provides global feature importance from attention masks
feat_importance = clf.feature_importances_
fi = pd.Series(feat_importance, index=X_train.columns).sort_values(ascending=False)
fi.head(15).plot(kind='barh', figsize=(8,6))
plt.title('TabNet Feature Importance (attention-based)')
plt.tight_layout(); plt.show()

# Per-step masks for a specific prediction (local explanation)
masks = clf.explain(X_val.values[:1])
# masks: list of arrays, one per step, shape (1, n_features)

Figure 1 — TabNet vs XGBoost: decision guide by dataset characteristics

Characteristic Use XGBoost Consider TabNet Dataset size Under 50k rows 100k+ rows Feature types Mixed / many categoricals Continuous numerical Training speed Fast — CPU fine Slow — GPU needed Interpretability SHAP (exact, fast) Attention masks (built-in) Transfer learning Not supported Native support Tuning effort Lower — fewer params Higher — batch size matters

The Benchmark Reality

The 2022 Grinsztajn et al. paper (“Why tree-based models still outperform deep learning on tabular data”) is the most comprehensive head-to-head to date. Testing 45 datasets, they found that tree-based models (XGBoost, Random Forest, gradient boosting) outperform neural networks on medium-sized tabular datasets (15k rows) in the majority of cases, with the gap largest on datasets with many categorical features or irregular numerical distributions that tree splits handle naturally. TabNet specifically was competitive but not dominant. The honest conclusion: XGBoost is the better default starting point. Test TabNet when you have a large numerical dataset, you need transfer learning, or XGBoost has clearly plateaued and you have GPU budget to explore further.

Running Both and Ensembling

import numpy as np
from sklearn.metrics import roc_auc_score

xgb_probas = model.predict_proba(X_val)[:,1]
tab_probas  = clf.predict_proba(X_val.values)[:,1]

# Simple average ensemble
ensemble = (xgb_probas + tab_probas) / 2
print(f"XGBoost: {roc_auc_score(y_val, xgb_probas):.4f}")
print(f"TabNet:  {roc_auc_score(y_val, tab_probas):.4f}")
print(f"Ensemble:{roc_auc_score(y_val, ensemble):.4f}")

# Weighted ensemble — weight by individual val AUC
w_xgb = roc_auc_score(y_val, xgb_probas)
w_tab = roc_auc_score(y_val, tab_probas)
total = w_xgb + w_tab
weighted = (xgb_probas * w_xgb + tab_probas * w_tab) / total
print(f"Weighted:{roc_auc_score(y_val, weighted):.4f}")

Ensembling XGBoost and TabNet is often the best of both worlds — the diversity between tree-based and neural predictions provides decorrelated errors that averaging exploits well. If XGBoost scores 0.88 and TabNet scores 0.87, a simple average often reaches 0.89–0.90. This is a practical strategy for Kaggle competitions and production systems where the marginal accuracy gain justifies the added serving complexity.

Start with XGBoost. It trains faster, requires less tuning, handles categorical features better out of the box, and beats TabNet on the majority of tabular benchmarks below 100k rows. Add TabNet to your evaluation when you have a large numerical dataset, a GPU for training, or the baseline XGBoost score is not meeting your target. Use them together as an ensemble when squeezing out the last fraction of a percent matters. That sequencing gets you most of the potential accuracy of both approaches with minimal wasted compute. The two models are complementary, not competing — XGBoost for speed and reliability across the broadest range of tabular problems, TabNet for the specific scenarios where its neural architecture provides a genuine edge — large continuous datasets, transfer learning, and differentiable integration into broader neural pipelines.

Figure 1 — TabNet vs XGBoost: decision guide by dataset characteristics

Characteristic Use XGBoost Consider TabNet Dataset size Under 50k rows 100k+ rows Training speed Fast — CPU fine Slow — GPU needed Feature types Mixed / many categoricals Continuous numerical Transfer learning Not supported Native support Tuning effort Lower Higher — batch size critical Benchmark winner Majority of datasets Large numerical datasets

The Benchmark Reality

The 2022 Grinsztajn et al. paper (“Why tree-based models still outperform deep learning on tabular data”) is the most comprehensive head-to-head to date. Testing 45 datasets, they found tree-based models — XGBoost, Random Forest, gradient boosting — outperform neural networks on medium-sized tabular datasets in the majority of cases, with the gap largest on datasets with many categorical features or irregular numerical distributions. TabNet specifically was competitive but not dominant. The honest conclusion: XGBoost is the better default. Test TabNet when you have a large numerical dataset, you need transfer learning, or XGBoost has clearly plateaued and you have GPU budget to explore further.

Ensembling XGBoost and TabNet

Ensembling the two is often the best of both worlds — the diversity between tree-based and neural predictions provides decorrelated errors that averaging exploits well. If XGBoost scores 0.88 and TabNet scores 0.87, a simple average typically reaches 0.89–0.90. This is a practical strategy for Kaggle competitions and production systems where a marginal accuracy gain justifies the added serving complexity of running two models.

import numpy as np
from sklearn.metrics import roc_auc_score

xgb_p = model.predict_proba(X_val)[:,1]
tab_p = clf.predict_proba(X_val.values)[:,1]

# Simple average
ensemble = (xgb_p + tab_p) / 2
print(f"XGBoost:  {roc_auc_score(y_val, xgb_p):.4f}")
print(f"TabNet:   {roc_auc_score(y_val, tab_p):.4f}")
print(f"Ensemble: {roc_auc_score(y_val, ensemble):.4f}")

# Weighted by individual AUC
w1 = roc_auc_score(y_val, xgb_p)
w2 = roc_auc_score(y_val, tab_p)
weighted = (xgb_p * w1 + tab_p * w2) / (w1 + w2)
print(f"Weighted: {roc_auc_score(y_val, weighted):.4f}")

Training Stability Tips for TabNet

TabNet is more sensitive to training instability than XGBoost. A few practices that help. Use virtual_batch_size smaller than batch_size — ghost batch normalisation divides each batch into virtual sub-batches; a virtual_batch_size of 128 works well regardless of the main batch size. Use a learning rate scheduler — TabNet’s attention mechanism benefits from learning rate decay; the StepLR scheduler in the setup code above is a solid default. Monitor training loss closely for the first 20 epochs — if it does not decrease, the learning rate is too high or the batch size is too small. Pre-process numerical features with RobustScaler or StandardScaler; TabNet is more sensitive to feature scale than XGBoost, which is scale-invariant.

A Note on TabNet Reproducibility

TabNet training is less deterministic than XGBoost — the same hyperparameters on the same data can produce noticeably different validation scores across runs due to the stochastic attention mechanism and mini-batch sampling. Always run TabNet at least 3 times and report the mean and standard deviation rather than a single run score. This variability is one more reason to benchmark against XGBoost before committing to TabNet in production: a TabNet that scores 0.882 on one run and 0.871 on another may be worse than an XGBoost that reliably scores 0.878 on every run. Reproducibility has value beyond the average score, especially in regulated applications where consistent outputs matter. For most practitioners, running TabNet once to establish its score on your specific dataset is worth the experiment; after that, the ensemble or the better single model is the production decision, not a repeated debate.

Start with XGBoost. It trains faster, requires less tuning, handles categorical features better, and beats TabNet on most tabular benchmarks below 100k rows. Add TabNet when you have a large numerical dataset, GPU compute, or XGBoost has clearly plateaued. Ensemble both when the marginal accuracy gain justifies the serving complexity. That sequencing gets you most of the potential accuracy of both approaches with minimal wasted compute.

Leave a Comment