Skip to content

Methods

shiftkit.methods provides domain adaptation training loops. All trainers record identical per-epoch history dicts so their results can be directly compared.

ShiftKit methods fall into two broad families:


Feature-based methods

Feature-based methods work by transforming the model's learned representations so that source and target features become indistinguishable. The encoder is trained jointly on a supervised task loss (cross-entropy on source labels) and a domain alignment loss that penalises differences between the source and target latent distributions. Because alignment happens in latent space, these methods require a model with a separate encode() step and work regardless of the input modality.

Trainer DA mechanism Key parameter Page
SourceOnlyTrainer No adaptation (baseline)
MMDTrainer Latent distribution matching via MMD mmd_weight λ
LMMDTrainer Per-class subdomain alignment via local MMD lmmd_weight λ
CORALTrainer Covariance alignment (second-order statistics) coral_weight λ
DANNTrainer Adversarial discriminator + GRL domain_weight λ
SIDDATrainer Sinkhorn optimal transport + learnable η weights warmup_epochs

Instance-based methods

Instance-based methods do not modify the feature space. Instead, they estimate how much more or less likely each source sample is under the target distribution and reweight the training loss accordingly. This approach is theoretically grounded under the covariate shift assumption — that the label conditionals are the same across domains (p_src(y|x) = p_tgt(y|x)) while only the input marginals differ. These methods require only a standard forward() interface and compute importance weights once before training begins.

Trainer DA mechanism Key parameter Page
KLIEPTrainer Importance weighting via density ratio estimation (gradient ascent) n_centers, weight_clip
KMMTrainer Importance weighting via kernel mean matching (QP) B, weight_clip

All trainers share the same interface:

trainer = AnyTrainer(model, source_loader, target_loader, ...)
history = trainer.fit(epochs=10)
result  = trainer.evaluate(test_loader, domain="target-test")

Method comparison

Source Only MMD LMMD CORAL DANN SIDDA KLIEP
DA family Feature-based Feature-based Feature-based Feature-based Feature-based Instance-based
Alignment target None Full distribution Per-class subdomains Covariance matrix Domain labels Optimal transport plan Sample weights (density ratio)
What is matched All moments (via kernel) Class-conditional moments 2nd-order statistics Domain membership Entire marginal distribution p_tgt(x) / p_src(x)
Kernel required No Yes — RBF, bandwidth σ Yes — RBF, bandwidth σ No No No (Sinkhorn entropic OT) Yes — RBF in input space
Needs source labels Yes Yes Yes Yes Yes Yes Yes
Needs target labels No No Pseudo-labels (soft) No No No No
Adversarial training No No No No Yes (GRL) No No
Learnable loss weights No No No No No Yes (η₁, η₂) No
Alignment cost Every batch Every batch Every batch Every batch Every batch Once at init
Model interface forward() encode() + classify() encode() + classify() encode() + classify() encode() + classify() full SIDDA interface forward() only
Computation per batch O(n·d) O(n²) kernel matrices O(n²) per class O(n·d²) covariance O(n·d) + discriminator O(n²) Sinkhorn iterations O(n·m) weight lookup
Key hyperparameter mmd_weight λ lmmd_weight λ coral_weight λ domain_weight λ warmup_epochs, blur schedule n_centers, weight_clip
Warmup supported Yes Yes Yes Yes Mandatory No
Covariate shift assumption No No No No No No Yes
Best suited for Reference baseline General distribution shift Class-level shift with label imbalance Shift in feature scale / correlation Strong covariate shift with large batches Unknown shift type; automatically reweights objectives Covariate shift on tabular / low-dim data

Choosing a method: Start with the Source-Only baseline to measure the domain gap. For most tasks, MMD or CORAL is a fast, strong first attempt. Use LMMD when class distributions differ across domains. Use DANN when the shift is severe and batch sizes are large enough to train the discriminator. Use SIDDA when you want automatic loss balancing without manual λ tuning. Use KLIEP when the covariate shift assumption holds and you prefer instance reweighting over feature alignment — especially effective on tabular data.


Shared history format

Every fit() call returns a list[dict] with one entry per epoch:

Key Type Description
epoch int Epoch index (1-based)
ce_loss float Cross-entropy loss
mmd_loss float MMD² loss (0.0 if not applicable)
domain_loss float Adversarial domain loss (0.0 if not applicable)
da_loss float Sinkhorn DA loss (0.0 if not applicable)
eta1 float Learned CE weight η₁ (SIDDA only)
eta2 float Learned DA weight η₂ (SIDDA only)
sigma float Sinkhorn blur used (SIDDA only)
mean_potential float Mean source Kantorovich potential (SIDDA with use_potentials=True only)
total_loss float Total combined loss
src_acc float Source domain accuracy
tgt_acc float Target domain accuracy (tracked, not directly optimised)