Transfer Learning Domain Adaptation Knowledge Transfer

# Transfer Learning: Domain Adaptation & Knowledge Transfer

## Introduction & Motivation

Transfer learning enables leveraging knowledge from source tasks to improve performance on target tasks. The core insight: rather than learning from scratch on limited target data, initialize models from pre-trained weights learned on large-scale source data. This dramatically reduces data requirements, training time, and computational cost while improving generalization.

Motivation: Many practical problems lack large labeled datasets. Medical imaging, rare disease diagnosis, and specialized NLP tasks have few examples. Transfer learning bridges this gap via inductive bias from related, well-resourced domains.

Applications: ImageNet pre-trained vision models on domain-specific medical images; BERT on biomedical text; GPT-based fine-tuning for specific tasks. Transfer learning has become standard practice—few practitioners train deep models from scratch.

---

## Core Concepts & Theory

### Domains and Tasks

A domain is a distribution P(\mathbf{x}) over features. A task is a conditional distribution P(y | \mathbf{x}). Transfer learning addresses the gap when source domain/task differs from target.

### Transfer Paradigms

Domain Adaptation: Same task, different domain distribution. Learn mappings between domains.

Multitask Learning: Multiple related tasks in same domain. Shared representations benefit all tasks.

Fine-Tuning: Pretrain on source, minimize target loss with small learning rate.

Feature Extraction: Fix pretrained features, train only output layer on target.

---

## Mathematical Formulation

Let source task be (X_s, Y_s, P_s(y|\mathbf{x})) and target be (X_t, Y_t, P_t(y|\mathbf{x})).

Fine-tuning objective:
$$\min_{ heta} \mathcal{L}_t( heta; D_t) + \lambda \| heta - heta_s\|^2$$

where heta_s is pretrained weights, \lambda controls regularization.

Domain adaptation via maximum mean discrepancy (MMD):
$$ ext{MMD}^2 = \left\| \frac{1}{n_s} \sum_{i=1}^{n_s} \phi(\mathbf{x}_s^i) - \frac{1}{n_t} \sum_{j=1}^{n_t} \phi(\mathbf{x}_t^j) ight\|^2$$

Minimize MMD to align feature distributions.

---

## Advanced Theory & Extensions

### Domain-Adversarial Training

Learn features indistinguishable between source/target via adversarial objective:
$$\min_{ heta_f} \max_{ heta_d} \mathcal{L}_s( heta_f, heta_y) - \lambda \mathcal{L}_d( heta_f, heta_d)$$

where heta_d is domain discriminator.

### Self-Supervised Pretraining

Learn representations without labels (SimCLR, MoCo, MAE) on unlabeled source data, then fine-tune.

### Few-Shot Learning

Learn from very few target examples via meta-learning (MAML) or prototype networks.

### Continual Transfer

Sequentially learn multiple tasks without catastrophic forgetting via rehearsal, regularization, or architecture adaptation.

---

## Computational Considerations

Pretraining cost: Large-scale source data (~millions images, trillions tokens). Done once, amortized over many tasks.

Fine-tuning cost: O( ext{parameters} imes ext{target samples}). Typically fast (hours on GPU vs. days/weeks from scratch).

Memory: Can fine-tune large models via LoRA (rank-r factorization) reducing parameters by 100x.

---

## Practical Implementation Strategies

### Choosing Pretrain Data

ImageNet for vision; Wikipedia/books for NLP; domain-specific corpora for specialized tasks.

### Freezing vs. Fine-Tuning

Freeze early layers: Keep task-agnostic features, train only task-specific layers.

Fine-tune all layers: Start with small learning rate to avoid catastrophic forgetting.

Layer-wise learning rates: Lower rates for early layers, higher for later layers.

### Handling Domain Shift

Small domain gap: simple fine-tuning sufficient. Large gap: data augmentation, domain adversarial training, or self-supervised adaptation.

### Hyperparameter Tuning

Learning rate: Critical. Start lower than training from scratch (e.g., 1e-5 to 1e-4).

Batch size: May differ from pretraining. Smaller batches often work well.

Epochs: Early stopping on validation essential to avoid overfitting.

---

## Benchmark Datasets & Evaluation

Vision: ImageNet pretrain → CIFAR-10, Stanford Cars, Caltech-256 fine-tune.

NLP: BERT pretrain → GLUE, SQuAD, MRPC fine-tune.

Metrics: Target task performance (accuracy, F1, BLEU). Compare to training from scratch.

---

## Key Challenges & Limitations

### Negative Transfer

Source knowledge conflicts with target task, hurting performance. Mitigate via careful domain selection or adversarial adaptation.

### Catastrophic Forgetting

Fine-tuning overwrites source knowledge. Solutions: regularization (EWC), rehearsal (replay), architecture adaptation (adapters).

### Domain Shift

Large distribution mismatch reduces transfer benefit. Address via domain adaptation, augmentation, or selecting related source domains.

---

## Hyperparameter Tuning

Grid search: learning rate \{1e-5, 1e-4, 1e-3\}, freeze depth, warmup epochs.

---

## Real-World Applications & Case Studies

Medical Imaging: ImageNet pretrain → CT/MRI diagnosis. Reduces training data from millions to hundreds.

NLP: BERT → Domain-specific NER, sentiment, QA. Standardized pipeline.

Speech: Wav2Vec pretrain → low-resource language ASR.

---

## Integration with Other Methods

Transfer + Data Augmentation → Improved generalization.

Transfer + Ensemble → Multiple pretrained models combined.

Transfer + Active Learning → Efficiently label small target set.

---

## Future Research Directions

Continual transfer; multimodal pretraining; efficient adaptation; theoretical understanding of when transfer succeeds.

---

## Summary & Key Takeaways

Transfer learning is a cornerstone of modern ML. Pretrain on large-scale data, fine-tune on target tasks. Default approach for most practitioners.

Principles:
1. Large-scale pretraining captures general knowledge.
2. Fine-tuning on target task specializes knowledge.
3. Learning rate and regularization prevent catastrophic forgetting.
4. Domain adaptation techniques address distribution shift.
5. Few-shot and continual transfer extend applicability.

---

---

## Appendix: Practical Labs

### Lab 1: Fine-Tuning on Target Task

import torch
import torch.nn as nn
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# Pretrained network (simulated)
class PretrainedNet(nn.Module):
 def __init__(self):
 super().__init__()
 self.feature_extractor = nn.Sequential(
 nn.Linear(4, 64),
 nn.ReLU(),
 nn.Linear(64, 32)
 )
 self.source_head = nn.Linear(32, 10)
 
 def forward(self, x):
 features = self.feature_extractor(x)
 return features

# Target model: freeze features, train new head
class TargetNet(nn.Module):
 def __init__(self, pretrained):
 super().__init__()
 self.feature_extractor = pretrained.feature_extractor
 self.target_head = nn.Linear(32, 3)
 
 def forward(self, x):
 features = self.feature_extractor(x)
 return self.target_head(features)

# Test on Iris
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
X_train = torch.FloatTensor(StandardScaler().fit_transform(X_train))
y_train = torch.LongTensor(y_train)

pretrained = PretrainedNet()
target_model = TargetNet(pretrained)

# Fine-tune
opt = torch.optim.Adam(target_model.parameters(), lr=1e-3)
loss_fn = nn.CrossEntropyLoss()

for epoch in range(20):
 opt.zero_grad()
 logits = target_model(X_train)
 loss = loss_fn(logits, y_train)
 loss.backward()
 opt.step()

print(f"Final loss: {loss.item():.4f}")
assert loss.item() < 1.0, "Training failed"
print("✓ Fine-tuning successful")

if __name__ == "__main__":
 print("Lab 1: Fine-Tuning - PASSED")

### Lab 2: Feature Extraction

import torch
import torch.nn as nn

# Pretrained model
pretrained = nn.Sequential(
 nn.Linear(10, 64),
 nn.ReLU(),
 nn.Linear(64, 32)
)

# Freeze pretrained features
for param in pretrained.parameters():
 param.requires_grad = False

# New classification head
classifier = nn.Linear(32, 5)

# Forward pass
X = torch.randn(100, 10)
features = pretrained(X)
logits = classifier(features)

print(f"Features shape: {features.shape}")
assert features.shape == (100, 32), "Shape mismatch"
print("✓ Feature extraction working")

if __name__ == "__main__":
 print("Lab 2: Feature Extraction - PASSED")

### Lab 3: Domain Adversarial Training

import torch
import torch.nn as nn

class DomainAdversarial(nn.Module):
 def __init__(self):
 super().__init__()
 self.feature_extractor = nn.Linear(20, 16)
 self.classifier = nn.Linear(16, 2)
 self.domain_discriminator = nn.Linear(16, 1)
 
 def forward(self, x, return_features=False):
 features = self.feature_extractor(x)
 class_logits = self.classifier(features)
 domain_logits = self.domain_discriminator(features)
 if return_features:
 return class_logits, domain_logits, features
 return class_logits, domain_logits

model = DomainAdversarial()

# Source and target data
X_source = torch.randn(50, 20)
y_source = torch.randint(0, 2, (50,))
X_target = torch.randn(40, 20) + 0.5 # Shifted distribution

class_loss_fn = nn.CrossEntropyLoss()
domain_loss_fn = nn.BCEWithLogitsLoss()

opt = torch.optim.Adam(model.parameters(), lr=0.01)

for epoch in range(10):
 # Train on source
 y_class, y_domain, _ = model(X_source, return_features=True)
 loss_class = class_loss_fn(y_class, y_source)
 loss_domain_s = domain_loss_fn(y_domain, torch.zeros(50, 1))
 
 # Train domain discriminator on target
 _, y_domain_t, _ = model(X_target, return_features=True)
 loss_domain_t = domain_loss_fn(y_domain_t, torch.ones(40, 1))
 
 loss = loss_class + 0.1 * (loss_domain_s + loss_domain_t)
 
 opt.zero_grad()
 loss.backward()
 opt.step()

print(f"Final loss: {loss.item():.4f}")
print("✓ Domain adversarial training working")

if __name__ == "__main__":
 print("Lab 3: Domain Adversarial - PASSED")

### Lab 4: Early Stopping

import torch
import torch.nn as nn

class SimpleNet(nn.Module):
 def __init__(self):
 super().__init__()
 self.layers = nn.Sequential(nn.Linear(10, 32), nn.ReLU(), nn.Linear(32, 2))
 
 def forward(self, x):
 return self.layers(x)

model = SimpleNet()
opt = torch.optim.Adam(model.parameters(), lr=0.01)
loss_fn = nn.CrossEntropyLoss()

# Synthetic train/val
X_train = torch.randn(100, 10)
y_train = torch.randint(0, 2, (100,))
X_val = torch.randn(30, 10)
y_val = torch.randint(0, 2, (30,))

best_val_loss = float('inf')
patience = 3
patience_counter = 0

for epoch in range(20):
 # Train
 opt.zero_grad()
 logits = model(X_train)
 train_loss = loss_fn(logits, y_train)
 train_loss.backward()
 opt.step()
 
 # Validate
 with torch.no_grad():
 val_logits = model(X_val)
 val_loss = loss_fn(val_logits, y_val)
 
 if val_loss < best_val_loss:
 best_val_loss = val_loss
 patience_counter = 0
 else:
 patience_counter += 1
 
 if patience_counter >= patience:
 print(f"Early stopping at epoch {epoch}")
 break

print(f"Best val loss: {best_val_loss:.4f}")
assert best_val_loss < 1.0, "Validation loss too high"
print("✓ Early stopping working")

if __name__ == "__main__":
 print("Lab 4: Early Stopping - PASSED")

Go deeper with CFSGPT

Get AI-powered deep-dives, save terms, and run advanced simulations — free account.

Create Free Account