few-shot learning prototypical networks support-query framework

# Few-Shot Learning: Prototypical Networks & Support-Query Framework

## Introduction & Motivation

Few-shot learning learns from few labeled examples per class (k-shot, n-way). Prototypical networks: learn metric space; class prototype = mean support embeddings; classify by distance to prototypes. Meta-learning: learn to learn. Critical for rapid adaptation, data-limited domains.

Motivation: Deep learning requires large labeled sets. Few-shot learns from minimal data; generalizes to new tasks.

Applications: One-shot face recognition, medical diagnosis, rare disease detection, low-resource languages.

---

## Core Concepts & Theory

### Prototypical Networks

Per-class embedding prototype: c_k = mean_i φ(x_i) for x_i in class k.
Query classified to nearest prototype in embedding space.

### Support-Query Split

Support set: few labeled examples. Query set: test examples.

### Metric Learning

Learn distance metric (Euclidean, Cosine) to minimize classification error.

---

## Mathematical Formulation

Prototype:
$$c_k = \frac{1}{|S_k|} \sum_{(x,y) \in S_k} \phi_ heta(x)$$

Distance-based classification:
$$\hat{y}_q = \arg\min_k d(\phi_ heta(x_q), c_k)$$

Prototypical loss (cross-entropy in metric space):
$$\mathcal{L} = -\sum_q \log \frac{\exp(-d(f_q, c_{y_q}))}{\sum_k \exp(-d(f_q, c_k))}$$

---

## Advanced Theory & Extensions

### Matching Networks

Attention + embedding; learn task-conditioned classifier.

### Relation Networks

Learn similarity via siamese network; more flexible than fixed metric.

### Meta-Learning (MAML)

Learn initialization for rapid gradient-based adaptation.

---

## Computational Considerations

Per-task training: O(support_size × embedding_dim).

Prototype computation: O(k × n × d) for k classes, n shots.

Distance queries: O(k) per query sample.

---

## Practical Implementation Strategies

### Data Augmentation

Augment support set; improve prototype robustness.

### Episodic Training

Train on few-shot tasks sampled from dataset.

### Transductive Inference

Use unlabeled query set to refine prototypes.

---

## Benchmark Datasets & Evaluation

mini-ImageNet: 100 classes, 600 images/class; 5-way, 5-shot standard.

Omniglot: 1623 classes, 20 images/class; rapid learning.

Metrics: Accuracy on test tasks; convergence speed.

---

## Key Challenges & Limitations

### Domain Shift

Prototypes may not transfer to different domains.

### Limited Support

Prototype estimation noisy with few examples.

### Class Imbalance

Few-shot setup naturally imbalanced.

---

## Hyperparameter Tuning

Embedding dimension: 64-512.

Distance metric: Euclidean, Cosine.

Ways & Shots: 5-way, 5-shot standard; vary for challenge.

---

## Real-World Applications & Case Studies

Face Recognition: One-shot face identification.

Medical Imaging: Learn disease patterns from few examples.

NLP: Few-shot text classification via prototypes.

---

## Integration with Other Methods

Few-Shot + Uncertainty → Bayesian prototypes.

Few-Shot + Transfer → pretrain, finetune on target task.

---

## Summary & Key Takeaways

Few-shot learning leverages prototypical networks to classify via distance to class prototypes learned from minimal examples.

Principles:
1. Prototype = mean support embeddings.
2. Query classified to nearest prototype.
3. Episodic training matches test task structure.
4. Metric learning improves embedding quality.
5. Transductive inference leverages query set.

---

---

## Appendix: Practical Labs

### Lab 1: Prototypical Networks

import torch
import torch.nn as nn
import numpy as np

class PrototypicalNet(nn.Module):
 def __init__(self, input_dim=28*28, embedding_dim=64):
 super().__init__()
 self.embedding = nn.Sequential(
 nn.Linear(input_dim, 128),
 nn.ReLU(),
 nn.Linear(128, embedding_dim)
 )
 
 def forward(self, x):
 return self.embedding(x)

# Data
np.random.seed(42)
n_ways, n_shots, n_queries = 3, 5, 10
support_x = torch.randn(n_ways * n_shots, 28*28)
support_y = torch.repeat_interleave(torch.arange(n_ways), n_shots)
query_x = torch.randn(n_ways * n_queries, 28*28)
query_y = torch.repeat_interleave(torch.arange(n_ways), n_queries)

model = PrototypicalNet(input_dim=28*28, embedding_dim=64)

# Compute prototypes
embeddings_support = model(support_x)
prototypes = torch.stack([
 embeddings_support[support_y == k].mean(dim=0) for k in range(n_ways)
])

# Classify queries
embeddings_query = model(query_x)
distances = torch.cdist(embeddings_query, prototypes)
predictions = torch.argmin(distances, dim=1)

accuracy = (predictions == query_y).float().mean()
print(f"Prototypical Networks accuracy: {accuracy:.2%}")
assert 0 <= accuracy <= 1, "Accuracy should be in [0,1]"
print("✓ Prototypical networks working")

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

### Lab 2: Support-Query Episodic Training

import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np

class SimpleEmbedder(nn.Module):
 def __init__(self):
 super().__init__()
 self.net = nn.Sequential(nn.Linear(10, 32), nn.ReLU(), nn.Linear(32, 16))
 
 def forward(self, x):
 return self.net(x)

def episodic_training_step(model, support_x, support_y, query_x, query_y, optimizer, n_ways):
 optimizer.zero_grad()
 
 # Embeddings
 emb_support = model(support_x)
 emb_query = model(query_x)
 
 # Prototypes
 prototypes = torch.stack([emb_support[support_y == k].mean(dim=0) for k in range(n_ways)])
 
 # Distances
 distances = torch.cdist(emb_query, prototypes)
 logits = -distances
 
 loss = nn.functional.cross_entropy(logits, query_y)
 loss.backward()
 optimizer.step()
 
 return loss.item()

model = SimpleEmbedder()
optimizer = optim.Adam(model.parameters(), lr=0.01)

losses = []
for episode in range(20):
 support_x = torch.randn(15, 10)
 support_y = torch.tensor([0]*5 + [1]*5 + [2]*5)
 query_x = torch.randn(9, 10)
 query_y = torch.tensor([0]*3 + [1]*3 + [2]*3)
 
 loss = episodic_training_step(model, support_x, support_y, query_x, query_y, optimizer, n_ways=3)
 losses.append(loss)

print(f"Final episodic loss: {losses[-1]:.4f}")
assert len(losses) == 20, "Should have 20 episodes"
assert all(np.isfinite(l) for l in losses), "All losses should be finite"
print("✓ Episodic training working")

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

### Lab 3: Metric Learning

import torch
import torch.nn as nn
import numpy as np

class MetricLearner(nn.Module):
 def __init__(self, input_dim=10, embedding_dim=16):
 super().__init__()
 self.embedding = nn.Linear(input_dim, embedding_dim)
 
 def forward(self, x):
 return self.embedding(x)
 
 def compute_distances(self, x1, x2):
 emb1 = self.embedding(x1)
 emb2 = self.embedding(x2)
 return torch.cdist(emb1, emb2)

model = MetricLearner(input_dim=10, embedding_dim=16)

# Positive pair (same class)
x1 = torch.randn(1, 10)
x2 = x1 + 0.1 * torch.randn(1, 10)

# Negative pair (different class)
x3 = torch.randn(1, 10)

dist_pos = model.compute_distances(x1, x2)
dist_neg = model.compute_distances(x1, x3)

print(f"Positive distance: {dist_pos.item():.4f}")
print(f"Negative distance: {dist_neg.item():.4f}")
assert dist_pos.item() >= 0, "Distance should be non-negative"
assert dist_neg.item() >= 0, "Distance should be non-negative"
print("✓ Metric learning working")

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

### Lab 4: Few-Shot Classification Pipeline

import torch
import torch.nn as nn
import numpy as np

def few_shot_classify(support_x, support_y, query_x, embedding_fn, n_ways):
 """End-to-end few-shot classification"""
 emb_support = embedding_fn(support_x)
 emb_query = embedding_fn(query_x)
 
 # Compute class prototypes
 prototypes = []
 for k in range(n_ways):
 mask = support_y == k
 prototype = emb_support[mask].mean(dim=0)
 prototypes.append(prototype)
 prototypes = torch.stack(prototypes)
 
 # Classify queries
 distances = torch.cdist(emb_query, prototypes)
 predictions = torch.argmin(distances, dim=1)
 
 return predictions

class SimpleEmbedder(nn.Module):
 def __init__(self):
 super().__init__()
 self.net = nn.Sequential(nn.Linear(20, 32), nn.ReLU(), nn.Linear(32, 8))
 def forward(self, x):
 return self.net(x)

np.random.seed(42)
support_x = torch.randn(15, 20)
support_y = torch.tensor([0]*5 + [1]*5 + [2]*5)
query_x = torch.randn(9, 20)
query_y = torch.tensor([0]*3 + [1]*3 + [2]*3)

embedding_fn = SimpleEmbedder()
predictions = few_shot_classify(support_x, support_y, query_x, embedding_fn, n_ways=3)

accuracy = (predictions == query_y).float().mean()
print(f"Few-shot accuracy: {accuracy:.2%}")
assert 0 <= accuracy <= 1, "Accuracy in [0,1]"
print("✓ Few-shot pipeline working")

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

Go deeper with CFSGPT

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

Create Free Account