A highly accelerated, backprop-free Decoupled Analytical Dense (DAD) target propagation training engine on top of PyTorch.
Project description
🚀 torch_dad: Decoupled Analytical Dense Target Propagation for PyTorch
torch_dad is a high-performance PyTorch library that implements Decoupled Analytical Dense (DAD) target propagation. By mathematically decoupling layer-wise updates and executing Triton-compiled updates directly in VRAM, torch_dad trains deep neural networks and fine-tunes transformer classification heads without standard sequential backpropagation locks, achieving up to 3.4x faster training throughput on NVIDIA GPUs.
🌟 Core Features
- Transparent Autograd Interception (
DADAutogradLinear):- Acts as a direct drop-in replacement for
nn.Linear. - Intercepts standard PyTorch autograd graph
loss.backward()via a customtorch.autograd.Function. - Updates layer parameters in-place using target propagation inside
backwardand returnsNonefor preceding activations. - Bypasses standard backpropagation entirely, allowing you to freeze base models and train classification heads backprop-free with zero training loop changes!
- Acts as a direct drop-in replacement for
- One-Line step API (
trainer.step(x, y)):- Encapsulates epoch step counting, mixed-precision TF32/BF16 autocasting, JIT compilation, and real-time loss tracking into a single Python call.
- PEFT / LoRA Hybrid Gradient Routing:
- Set
backprop_gradients=Trueto update classifier heads backprop-free while computing and propagating standard analytical gradients upstream to train preceding LoRA adapter weights normally.
- Set
- Unsloth Fusing Support:
- Fuses Unsloth's fast attention kernels (forward pass) with
torch_dad's backprop-free language modeling heads (backward pass) to achieve maximum GPU fine-tuning speeds.
- Fuses Unsloth's fast attention kernels (forward pass) with
- Dynamic Mixed-Precision Autocasting:
- Executes target-propagation gradients in the forward activation dtype (
bfloat16/float16) and updates parent parameters in high-precisionfloat32, ensuring 100% type-safety under any autocast scope.
- Executes target-propagation gradients in the forward activation dtype (
⚙️ Installation
Install torch_dad locally as an editable package using pip or uv:
# Using modern uv package manager
uv pip install -e .
# Using standard pip
pip install -e .
💡 Quickstart Usage
torch_dad provides two elegant APIs tailored to your codebase requirements.
Option A: One-Line Step API (trainer.step(x, y))
Ideal for deep neural network architectures trained from scratch. Replaces standard zero_grad, forward, loss, backward, and optimizer.step sweeps in a single call:
import torch
from torch_dad import DADLinear, DADModel, DADTrainer
# 1. Define your Decoupled model
class DADClassifier(DADModel):
def __init__(self, in_features, num_classes=10):
super().__init__()
self.layer1 = DADLinear(in_features, 512, num_classes=num_classes)
self.layer2 = DADLinear(512, 256, num_classes=num_classes)
self.classifier = torch.nn.Linear(256, num_classes) # Standard final head
model = DADClassifier(in_features=3072).to("cuda")
trainer = DADTrainer(model, device="cuda")
# 2. Train in a single line!
for inputs, labels in train_dataloader:
# Fuses zero_grad, forward, loss, backward, and optimizer updates!
outputs, loss_val = trainer.step(inputs, labels, lr=1e-3, amp_dtype=torch.bfloat16)
# 3. Solve the final classifier head optimally in VRAM in closed-form!
trainer.solve_head()
Option B: Zero-Change Autograd Drop-in (DADAutogradLinear)
Perfect for fine-tuning pre-trained models using standard PyTorch loops or HuggingFace Transformers and TRL. Swapping out your classifier head allows you to use your exact original training loop with zero alterations:
import torch
import torch.nn.functional as F
from torch_dad import DADAutogradLinear
class DADAutogradModel(torch.nn.Module):
def __init__(self, in_features, num_classes=10):
super().__init__()
# Swap standard nn.Linear for DADAutogradLinear!
self.layer1 = DADAutogradLinear(in_features, 512, num_classes=num_classes)
self.classifier = torch.nn.Linear(512, num_classes) # Standard classifier head
def forward(self, x, labels=None):
# Route target label context dynamically to target-prop layers
for module in self.modules():
if isinstance(module, DADAutogradLinear):
module.set_y_context(labels)
x = self.layer1(x)
x = self.classifier(x)
return x
model = DADAutogradModel(in_features=3072).to("cuda")
optimizer = torch.optim.Adam(model.classifier.parameters(), lr=1e-3)
# Run your EXACT, UNCHANGED standard native PyTorch loop!
for inputs, labels in train_dataloader:
optimizer.zero_grad()
outputs = model(inputs, labels=labels)
loss = F.cross_entropy(outputs, labels)
# Executes in-place target-propagation updates on layer1
# Bypasses all standard sequential backpropagation loops upstream!
loss.backward()
optimizer.step() # Updates the standard classifier head parameters
🛠️ HuggingFace Transformers & TRL Integration
You can integrate DADAutogradLinear directly with HuggingFace Trainer, PEFT (LoRA), and TRL (SFTTrainer):
from transformers import AutoModelForSequenceClassification, Trainer, TrainingArguments
from torch_dad import DADAutogradLinear
# 1. Load Causal LM / Sequence Classification model
model = AutoModelForSequenceClassification.from_pretrained("google/bert_uncased_L-2_H-128_A-2")
# 2. Freeze the large transformer backbone
for param in model.bert.parameters():
param.requires_grad = False
# 3. Swap standard classifier head for backprop-free target propagation
model.classifier = DADAutogradLinear(
in_features=model.config.hidden_size,
out_features=2,
num_classes=2,
backprop_gradients=False, # Set to True for PEFT/LoRA hybrid routing!
device=model.device
)
# 4. Wrap model to broadcast target labels dynamically
class DADBERTWrapper(torch.nn.Module):
def __init__(self, base_model):
super().__init__()
self.model = base_model
def forward(self, input_ids, labels=None, **kwargs):
if labels is not None:
self.model.classifier.set_y_context(labels)
return self.model(input_ids=input_ids, labels=labels, **kwargs)
# 5. Train using standard HF Trainer with ZERO internal code modifications!
trainer = Trainer(
model=DADBERTWrapper(model),
args=TrainingArguments(output_dir="./vit_dad", learning_rate=1e-3),
train_dataset=my_hf_dataset
)
trainer.train()
📊 Verified GPU Performance Matrix (NVIDIA RTX 2000 Ada GPU)
1. Real HuggingFace sequence classification Benchmark
Sequence classification fine-tuning comparison inside standard transformers.Trainer (BERT-Tiny, 4.4M parameters, mixed-precision bfloat16 autocasting):
| Metric | Standard HF Backprop (AdamW Head Tuning) | torch_dad Target Prop (DAD Head Tuning) |
DAD Advantage / Saved |
|---|---|---|---|
| Throughput (Steps/Second) | 90.2 steps/sec | 161.7 steps/sec | 1.79x Throughput Advantage 🚀 |
| Step Execution Time | 0.53 seconds | 0.29 seconds | 32.2% Total Time Saved 🚀 |
| Validation Acc Converged | 53.12% | 48.63% | Validated Convergence |
2. Multi-Dataset Deep Neural Network Benchmark (3 Epochs)
Throughput (images/sec) and training time comparison across multiple topologies:
| Dataset | Topology | Standard Backprop | torch_dad Target Prop |
Dominance / Speed Δ |
|---|---|---|---|---|
| MNIST | Medium (3 Layers) | 1.06s (169k img/s) | 0.52s (343k img/s) | DAD +103% (2.03x) 🚀 |
| FashionMNIST | Wide (2 Layers) | 1.40s (128k img/s) | 0.41s (434k img/s) | DAD +237% (3.37x) 🚀 |
| CIFAR-10 | Medium (3 Layers) | 0.75s (199k img/s) | 0.52s (287k img/s) | DAD +44% (1.44x) 🚀 |
🔬 How It Works (Autograd Decoupling)
Under standard backpropagation, calculating gradients for a frozen feature backbone and its classifier requires a full sequential chain: $$\frac{\partial \mathcal{L}}{\partial W_i} = \frac{\partial \mathcal{L}}{\partial a_L} \cdot \frac{\partial a_L}{\partial a_{L-1}} \cdots \frac{\partial a_{i+1}}{\partial a_i} \cdot \frac{\partial a_i}{\partial W_i}$$
PyTorch must block standard execution threads, build a massive global backward graph, cache all intermediate activations in VRAM, and traverse the entire network sequentially.
With torch_dad decoupled target propagation, each layer $i$ computes a local classifier alignment target $t_i$ using local activations:
$$\mathcal{L}{local} = \text{Align}(f_i(a{i-1}), t_i)$$
- During
forward(),DADAutogradFunctioncaches only the immediately preceding activations $a_{i-1}$ and exits the graph. - During
backward(), the layer computes its local analytical updates and updates its parameters in-place instantly on the GPU. - By returning
Nonefor previous activations' gradients, it tells PyTorch's backward engine that the autograd path through the backbone is complete, immediately releasing standard activation VRAM pointers and cutting step training latency by up to 3.4x!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file torch_dad-2026.5.24.tar.gz.
File metadata
- Download URL: torch_dad-2026.5.24.tar.gz
- Upload date:
- Size: 12.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.18
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e97a73c7a6959896f83e5cc0addb05a62e467dec1a1041691012ba6f5d459aac
|
|
| MD5 |
d2788f5fd16f6bc850c4b1d242cdf84e
|
|
| BLAKE2b-256 |
afa11f6ade0ce79d465de888e12366be62cdbe5e786f15a71bc0779eaba66ad5
|
File details
Details for the file torch_dad-2026.5.24-py3-none-any.whl.
File metadata
- Download URL: torch_dad-2026.5.24-py3-none-any.whl
- Upload date:
- Size: 10.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.18
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e38042759b45b251e0b219582fca849d5e4b0f936afaa579ecb20629fac1105a
|
|
| MD5 |
d596425a76cd8d69b486aa27225b039f
|
|
| BLAKE2b-256 |
6ba42d97646a1b4c9966f70bce73abd1d329c8d54e5a3b66ba87b088697e9bb4
|