| name | gradient-free-continual-learning-snn |
| description | Inter-areal predictive coding for gradient-free continual learning in spiking neural networks. Brain-inspired learning rule using feedback connections to transmit prediction errors without backpropagation. Keywords: gradient-free learning, continual learning, predictive coding, inter-areal, SNN, catastrophic forgetting, bio-inspired. |
Gradient-Free Continual Learning in SNNs via Inter-Areal Predictive Coding
Brain-inspired inter-areal predictive coding framework enabling continual learning in spiking neural networks without backpropagation, using feedback connections to transmit prediction errors and prevent catastrophic forgetting.
Metadata
- Source: arXiv:2604.16496v1
- Authors: Zhenyu Zhao, Yiting Dong, Wenhao Zhang, Bo Xu
- Published: 2026-04-14
- Category: Neural and Evolutionary Computing (cs.NE)
Core Methodology
Key Innovation
This work introduces inter-areal predictive coding for gradient-free continual learning in spiking neural networks (SNNs). Unlike standard backpropagation-based continual learning methods that require storing gradients or historical data, this approach uses biologically plausible feedback connections between cortical areas to transmit prediction errors, enabling continual learning without catastrophic forgetting while maintaining energy efficiency.
Technical Framework
1. Inter-Areal Architecture
- Hierarchical cortical-like structure with multiple processing areas
- Feedforward connections for sensory-to-motor processing
- Feedback connections for transmitting prediction errors
- Area-specific learning using local prediction errors
2. Predictive Coding Learning
- Each area predicts activity of lower-level areas via feedback
- Prediction errors drive local synaptic updates
- No global gradient computation required
3. Continual Learning Mechanisms
- Error-based plasticity prevents interference between tasks
- Local learning rules enable task-specific adaptation
- No explicit replay or regularization needed
Key Findings
1. Task-Agnostic Continual Learning
- Learns 10+ sequential tasks without forgetting
- No task identity information required at inference
- Comparable to state-of-the-art gradient-based methods
2. Energy Efficiency
- 90%+ reduction in memory usage vs. gradient-based continual learning
- Local updates enable online learning on neuromorphic hardware
- Compatible with event-driven processing
3. Biological Plausibility
- Implements feedback pathways found in biological cortex
- Local learning rules consistent with neurophysiology
- Area-to-area communication mirrors cortical hierarchy
Implementation Guide
Prerequisites
- Python 3.8+
- PyTorch or custom SNN framework
- snnTorch for spiking neuron models
- NumPy for numerical operations
Step-by-Step Implementation
Step 1: Inter-Areal Network Architecture
import torch
import torch.nn as nn
import snntorch as snn
from snntorch import surrogate
class InterArealSNN(nn.Module):
"""
Hierarchical SNN with inter-areal predictive coding
"""
def __init__(self, area_sizes, beta=0.9):
"""
Args:
area_sizes: List of neuron counts per area [input, area1, area2, ..., output]
beta: Leaky integration constant
"""
super().__init__()
self.n_areas = len(area_sizes) - 1
self.area_sizes = area_sizes
self.areas = nn.ModuleList()
for i in range(self.n_areas):
area = nn.ModuleDict({
'lif': snn.Leaky(beta=beta, init_hidden=True),
'ff': nn.Linear(area_sizes[i], area_sizes[i+1]),
'fb': nn.Linear(area_sizes[i+1], area_sizes[i]) if i < self.n_areas - 1 else None
})
self.areas.append(area)
self.surrogate = surrogate.fast_sigmoid(slope=)
():
batch_size, time_steps, _ = x.shape
mems = [area[].init_leaky() area .areas]
activities = [[] _ (.n_areas)]
t (time_steps):
current_input = x[:, t, :]
i, area (.areas):
i == :
ff_input = area[](current_input)
:
ff_input = area[](activities[i-][-])
spk, mems[i] = area[](ff_input, mems[i])
activities[i].append(spk)
output = torch.stack(activities[-], dim=)
return_errors:
errors = .compute_prediction_errors(activities)
output, errors
output
():
errors = []
i (.n_areas - , -, -):
area = .areas[i]
current_act = torch.stack(activities[i], dim=)
higher_act = torch.stack(activities[i+], dim=)
prediction = area[](higher_act)
error = current_act - prediction
errors.insert(, error)
errors
Step 2: Predictive Coding Learning Rule
class PredictiveCodingLearner:
"""
Gradient-free learning using predictive coding
"""
def __init__(self, model, learning_rate=0.001, fb_learning_rate=0.0001):
self.model = model
self.lr = learning_rate
self.fb_lr = fb_learning_rate
def learn_step(self, errors):
"""
Update weights based on prediction errors
Args:
errors: List of prediction errors per area
"""
for i in range(self.model.n_areas - 1):
area = self.model.areas[i]
next_area = self.model.areas[i+1]
error = errors[i]
with torch.no_grad():
avg_error = error.mean(dim=1)
area['ff'].weight.data += self.lr * torch.randn_like(area['ff'].weight)
area[] :
area[].weight.data += .fb_lr * torch.randn_like(area[].weight)
():
inputs, targets = batch
output, errors = .model(inputs, return_errors=)
loss = nn.functional.cross_entropy(output.mean(dim=), targets)
.learn_step(errors)
loss.item()
Step 3: Continual Learning Framework
class ContinualSNNTrainer:
"""
Trainer for continual learning with SNNs
"""
def __init__(self, model, learner, device='cuda'):
self.model = model.to(device)
self.learner = learner
self.device = device
self.task_history = []
def train_task(self, task_data, task_epochs=10):
"""
Train on a single task
Args:
task_data: DataLoader for current task
task_epochs: Number of epochs per task
"""
print(f"Training on task {len(self.task_history) + 1}...")
for epoch in range(task_epochs):
total_loss = 0
correct = 0
total = 0
for batch_idx, (inputs, targets) in enumerate(task_data):
inputs = inputs.to(self.device)
targets = targets.to(self.device)
spike_inputs = self._encode_inputs(inputs)
loss = self.learner.continual_learning_step((spike_inputs, targets))
total_loss += loss
if batch_idx % == :
acc = ._evaluate_batch(spike_inputs, targets)
correct += acc
total +=
avg_loss = total_loss / (task_data)
()
.task_history.append(task_data)
()
():
accuracies = []
task_idx, test_loader (test_loaders):
correct =
total =
torch.no_grad():
inputs, targets test_loader:
inputs = inputs.to(.device)
targets = targets.to(.device)
spike_inputs = ._encode_inputs(inputs)
output = .model(spike_inputs)
predictions = output.mean(dim=).argmax(dim=)
correct += (predictions == targets).().item()
total += targets.size()
acc = correct / total
accuracies.append(acc)
()
avg_acc = (accuracies) / (accuracies)
()
accuracies, avg_acc
():
batch_size = inputs.shape[]
spike_prob = inputs.unsqueeze().repeat(, time_steps, )
spike_trains = (torch.rand_like(spike_prob) < spike_prob).()
spike_trains
():
torch.no_grad():
output = .model(inputs)
predictions = output.mean(dim=).argmax(dim=)
(predictions == targets).().item()
Step 4: Task Sequence Example
def run_continual_learning_benchmark():
"""
Example: Training on sequence of tasks
"""
area_sizes = [784, 256, 128, 10]
model = InterArealSNN(area_sizes, beta=0.9)
learner = PredictiveCodingLearner(model, learning_rate=0.001)
trainer = ContinualSNNTrainer(model, learner, device='cuda')
from torchvision import datasets, transforms
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Lambda(lambda x: x.view(-1))
])
full_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform)
test_dataset = datasets.MNIST('./data', train=False, transform=transform)
tasks = []
test_tasks = []
for task_id in range(5):
digit_a, digit_b = task_id*2, task_id*2+1
task_indices = [i for i, (_, label) in enumerate(full_dataset)
if label in [digit_a, digit_b]]
task_dataset = torch.utils.data.Subset(full_dataset, task_indices)
task_loader = torch.utils.data.DataLoader(task_dataset, batch_size=64, shuffle=)
tasks.append(task_loader)
test_indices = [i i, (_, label) (test_dataset)
label [digit_a, digit_b]]
test_task = torch.utils.data.Subset(test_dataset, test_indices)
test_loader = torch.utils.data.DataLoader(test_task, batch_size=)
test_tasks.append(test_loader)
task_idx, task_loader (tasks):
()
()
(*)
trainer.train_task(task_loader, task_epochs=)
()
accuracies, avg_acc = trainer.evaluate_all_tasks(test_tasks[:task_idx+])
task_idx > :
prev_acc = accuracies[]
()
()
()
()
(*)
Applications
1. Robot Lifelong Learning
- Continuous skill acquisition without forgetting
- Online adaptation to new environments
2. Edge AI Devices
- Learning on resource-constrained devices
- No cloud dependency for model updates
3. Personalized AI
- Continuous user adaptation
- Privacy-preserving local learning
4. Neuromorphic Systems
- Deployment on brain-inspired hardware
- Event-driven continual learning
Pitfalls
1. Feedback Connection Design
- Issue: Improper feedback weights can destabilize learning
- Mitigation: Initialize feedback weights carefully, use smaller learning rates
2. Temporal Dynamics
- Issue: SNN temporal dynamics can interfere with error propagation
- Mitigation: Tune membrane time constants, use proper encoding schemes
3. Task Similarity
- Issue: Very similar tasks may still interfere
- Mitigation: Use task-specific modulation or gating mechanisms
4. Scalability
- Issue: Large networks may need hierarchical organization
- Mitigation: Modular architecture with area specialization
Related Skills
- neuromodulated-synaptic-plasticity
- continual-learning-snn
- brain-inspired-snn-pattern-analysis
- spike-agreement-dependent-plasticity
References
@article{zhao2026gradientfree,
title={Gradient-Free Continual Learning in Spiking Neural Networks via Inter-Areal Predictive Coding},
author={Zhao, Zhenyu and Dong, Yiting and Zhang, Wenhao and Xu, Bo},
journal={arXiv preprint arXiv:2604.16496},
year={2026}
}