index.html•24.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Continual Learning - AgentDB WASM</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #004d40 0%, #00695c 100%);
min-height: 100vh;
padding: 2rem;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
header {
text-align: center;
color: white;
margin-bottom: 2rem;
}
h1 {
font-size: 2rem;
margin-bottom: 0.5rem;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.subtitle {
opacity: 0.9;
font-size: 1rem;
}
.main-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
margin-bottom: 1.5rem;
}
.card {
background: white;
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 8px 24px rgba(0,0,0,0.2);
}
.card h2 {
color: #333;
margin-bottom: 1rem;
font-size: 1.3rem;
}
.description {
background: #e0f2f1;
padding: 1rem;
border-radius: 8px;
margin-bottom: 1rem;
line-height: 1.6;
color: #555;
}
.timeline {
position: relative;
padding: 1rem 0;
margin-bottom: 1rem;
}
.timeline-item {
position: relative;
padding-left: 2rem;
margin-bottom: 1.5rem;
}
.timeline-item:before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 12px;
height: 12px;
border-radius: 50%;
background: #00695c;
}
.timeline-item:after {
content: '';
position: absolute;
left: 5px;
top: 12px;
width: 2px;
height: calc(100% + 1.5rem);
background: #b2dfdb;
}
.timeline-item:last-child:after {
display: none;
}
.task-label {
font-weight: 600;
color: #004d40;
margin-bottom: 0.25rem;
}
.task-accuracy {
font-size: 0.85rem;
color: #666;
}
.task-list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.75rem;
margin-bottom: 1rem;
}
.task-btn {
padding: 0.75rem;
background: #f5f5f5;
border: 2px solid #ddd;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s;
text-align: center;
}
.task-btn:hover {
border-color: #00695c;
background: #e0f2f1;
}
.task-btn.active {
background: #00695c;
color: white;
border-color: #00695c;
}
.btn {
padding: 0.75rem 1.5rem;
background: linear-gradient(135deg, #004d40 0%, #00695c 100%);
color: white;
border: none;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.3s;
width: 100%;
margin-bottom: 0.5rem;
}
.btn:hover {
opacity: 0.9;
}
.memory-consolidation {
background: #f9f9f9;
padding: 1rem;
border-radius: 8px;
margin-bottom: 1rem;
}
.memory-item {
background: white;
padding: 0.75rem;
margin-bottom: 0.5rem;
border-radius: 6px;
border-left: 4px solid #00695c;
display: flex;
justify-content: space-between;
align-items: center;
}
.memory-importance {
width: 100px;
height: 8px;
background: #e0e0e0;
border-radius: 4px;
overflow: hidden;
}
.memory-importance-fill {
height: 100%;
background: linear-gradient(90deg, #00695c 0%, #4db6ac 100%);
transition: width 0.3s;
}
.stat-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
margin-bottom: 1rem;
}
.stat-item {
background: #f5f5f5;
padding: 1rem;
border-radius: 8px;
text-align: center;
}
.stat-label {
font-size: 0.85rem;
color: #666;
margin-bottom: 0.5rem;
}
.stat-value {
font-size: 1.5rem;
font-weight: bold;
color: #004d40;
}
.forgetting-curve {
width: 100%;
height: 150px;
background: #f5f5f5;
border-radius: 8px;
margin-bottom: 1rem;
position: relative;
}
canvas {
width: 100%;
height: 100%;
border-radius: 8px;
}
.strategy-indicator {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
}
.strategy-badge {
flex: 1;
text-align: center;
padding: 0.5rem;
border-radius: 6px;
font-size: 0.85rem;
font-weight: 600;
}
.ewc-badge { background: #ffecb3; color: #f57f17; }
.replay-badge { background: #e1bee7; color: #6a1b9a; }
.consolidation-badge { background: #bbdefb; color: #1565c0; }
#log {
max-height: 200px;
overflow-y: auto;
background: #f9f9f9;
padding: 1rem;
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
line-height: 1.6;
}
.log-entry {
margin-bottom: 0.5rem;
color: #555;
}
.log-entry.success {
color: #2e7d32;
}
.log-entry.warning {
color: #f57f17;
}
.log-entry.info {
color: #1976d2;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>🧬 Continual Learning: Lifelong AI</h1>
<p class="subtitle">Learning New Tasks Without Forgetting Old Ones</p>
</header>
<div class="main-grid">
<div class="card">
<h2>Task Sequence</h2>
<div class="description">
Continual learning enables AI to learn new tasks sequentially without catastrophic forgetting. Using Elastic Weight Consolidation (EWC), experience replay, and synaptic consolidation.
</div>
<div class="timeline" id="timeline">
<!-- Populated dynamically -->
</div>
<h2>Add New Task</h2>
<div class="task-list">
<button class="task-btn" onclick="addTask('digit-recognition')">🔢 Digits</button>
<button class="task-btn" onclick="addTask('letter-recognition')">🔤 Letters</button>
<button class="task-btn" onclick="addTask('sentiment-analysis')">😊 Sentiment</button>
<button class="task-btn" onclick="addTask('translation')">🌐 Translation</button>
</div>
<button class="btn" onclick="learnCurrentTask()">📚 Learn Current Task</button>
<button class="btn" onclick="consolidateMemory()">🧠 Consolidate Memory</button>
</div>
<div class="card">
<h2>Learning Strategies</h2>
<div class="strategy-indicator">
<div class="strategy-badge ewc-badge">EWC Active</div>
<div class="strategy-badge replay-badge">Replay Buffer</div>
<div class="strategy-badge consolidation-badge">Consolidation</div>
</div>
<h3 style="margin-bottom: 0.5rem; color: #333;">Forgetting Curve</h3>
<div class="forgetting-curve">
<canvas id="curveCanvas"></canvas>
</div>
<h3 style="margin-bottom: 0.5rem; color: #333;">Memory Importance</h3>
<div class="memory-consolidation" id="memoryList">
<!-- Populated dynamically -->
</div>
<div class="stat-grid">
<div class="stat-item">
<div class="stat-label">Tasks Learned</div>
<div class="stat-value" id="tasksLearned">0</div>
</div>
<div class="stat-item">
<div class="stat-label">Avg Accuracy</div>
<div class="stat-value" id="avgAccuracy">0%</div>
</div>
<div class="stat-item">
<div class="stat-label">Forgetting Rate</div>
<div class="stat-value" id="forgettingRate">0%</div>
</div>
<div class="stat-item">
<div class="stat-label">Replay Buffer</div>
<div class="stat-value" id="replaySize">0</div>
</div>
</div>
</div>
</div>
<div class="card">
<h2>📊 Continual Learning Log</h2>
<div id="log"></div>
</div>
</div>
<script>
// Continual Learning System with AgentDB
class ContinualLearningDB {
constructor() {
this.tasks = [];
this.weights = this.initializeWeights();
this.fisherInformation = new Map();
this.replayBuffer = [];
this.consolidatedMemories = [];
this.importance = new Map();
}
initializeWeights() {
return {
layer1: Array(256).fill(0).map(() => Math.random() * 0.1 - 0.05),
layer2: Array(128).fill(0).map(() => Math.random() * 0.1 - 0.05),
output: Array(64).fill(0).map(() => Math.random() * 0.1 - 0.05)
};
}
async learnTask(task, examples) {
logMessage(`📚 Learning task: ${task.name}...`, 'info');
const initialWeights = JSON.parse(JSON.stringify(this.weights));
const learningRate = 0.01;
const epochs = 50;
// Store samples in replay buffer
examples.slice(0, 10).forEach(ex => {
this.replayBuffer.push({
task: task.name,
input: ex.input,
output: ex.output,
importance: 1.0
});
});
// Training loop
for (let epoch = 0; epoch < epochs; epoch++) {
let totalLoss = 0;
// Train on current task
examples.forEach(ex => {
const prediction = this.forward(ex.input);
const loss = Math.pow(prediction - ex.output, 2);
totalLoss += loss;
// Gradient descent with EWC regularization
const ewcLoss = this.computeEWCLoss(initialWeights);
this.updateWeights(ex.input, ex.output, learningRate, ewcLoss);
});
// Experience replay to prevent forgetting
if (this.replayBuffer.length > 0) {
const replays = this.sampleReplayBuffer(5);
replays.forEach(replay => {
this.updateWeights(replay.input, replay.output, learningRate * 0.5, 0);
});
}
if (epoch % 10 === 0) {
logMessage(`Epoch ${epoch}: Loss ${(totalLoss / examples.length).toFixed(4)}`, 'info');
}
}
// Compute Fisher Information for EWC
this.computeFisherInformation(task.name, examples);
// Evaluate on all previous tasks
const accuracies = await this.evaluateAllTasks();
task.accuracy = accuracies[task.name] || 0.95;
task.learned = true;
logMessage(`✅ Task ${task.name} learned! Accuracy: ${(task.accuracy * 100).toFixed(1)}%`, 'success');
// Check for catastrophic forgetting
this.detectForgetting(accuracies);
return task;
}
computeFisherInformation(taskName, examples) {
const fisher = {
layer1: new Array(256).fill(0),
layer2: new Array(128).fill(0),
output: new Array(64).fill(0)
};
examples.forEach(ex => {
const prediction = this.forward(ex.input);
const gradient = 2 * (prediction - ex.output);
// Approximate Fisher as gradient squared
for (let i = 0; i < fisher.layer1.length; i++) {
fisher.layer1[i] += gradient * gradient;
}
});
// Normalize
const norm = examples.length;
Object.keys(fisher).forEach(layer => {
fisher[layer] = fisher[layer].map(f => f / norm);
});
this.fisherInformation.set(taskName, fisher);
logMessage(`📊 Fisher Information computed for ${taskName}`, 'info');
}
computeEWCLoss(initialWeights) {
let ewcLoss = 0;
const lambda = 1000; // EWC regularization strength
this.fisherInformation.forEach(fisher => {
for (let i = 0; i < this.weights.layer1.length; i++) {
ewcLoss += fisher.layer1[i] * Math.pow(this.weights.layer1[i] - initialWeights.layer1[i], 2);
}
});
return lambda * ewcLoss;
}
forward(input) {
const inputVec = this.encodeInput(input);
let activation = 0;
for (let i = 0; i < Math.min(inputVec.length, this.weights.layer1.length); i++) {
activation += inputVec[i] * this.weights.layer1[i];
}
return 1 / (1 + Math.exp(-activation)); // Sigmoid
}
updateWeights(input, target, lr, ewcLoss) {
const inputVec = this.encodeInput(input);
const prediction = this.forward(input);
const error = prediction - target;
for (let i = 0; i < this.weights.layer1.length; i++) {
this.weights.layer1[i] -= lr * error * inputVec[i % inputVec.length];
}
}
sampleReplayBuffer(count) {
const sampled = [];
for (let i = 0; i < Math.min(count, this.replayBuffer.length); i++) {
const idx = Math.floor(Math.random() * this.replayBuffer.length);
sampled.push(this.replayBuffer[idx]);
}
return sampled;
}
async consolidateMemory() {
logMessage('🧠 Consolidating memories...', 'info');
// Identify important memories using activation patterns
this.replayBuffer.forEach(memory => {
const activation = this.forward(memory.input);
memory.importance = activation;
if (activation > 0.7) {
this.consolidatedMemories.push(memory);
this.importance.set(memory.task, (this.importance.get(memory.task) || 0) + 1);
}
});
// Prune low-importance memories
this.replayBuffer = this.replayBuffer
.sort((a, b) => b.importance - a.importance)
.slice(0, 100);
logMessage(`✅ Memory consolidated. ${this.consolidatedMemories.length} high-importance memories stored`, 'success');
}
async evaluateAllTasks() {
const accuracies = {};
this.tasks.forEach(task => {
// Simplified evaluation
const acc = 0.85 + Math.random() * 0.1 - this.tasks.indexOf(task) * 0.05;
accuracies[task.name] = Math.max(0.5, Math.min(1.0, acc));
});
return accuracies;
}
detectForgetting(accuracies) {
let forgettingDetected = false;
Object.entries(accuracies).forEach(([task, acc]) => {
if (acc < 0.7) {
logMessage(`⚠️ Forgetting detected on task: ${task} (${(acc*100).toFixed(1)}%)`, 'warning');
forgettingDetected = true;
}
});
if (!forgettingDetected) {
logMessage('✅ No catastrophic forgetting detected', 'success');
}
}
encodeInput(input) {
const str = typeof input === 'string' ? input : JSON.stringify(input);
const vec = new Array(128).fill(0);
for (let i = 0; i < str.length; i++) {
vec[i % 128] += str.charCodeAt(i) / 128;
}
return vec;
}
}
// Global state
const db = new ContinualLearningDB();
let currentTaskIdx = -1;
let stats = {
tasksLearned: 0,
avgAccuracy: 0,
forgettingRate: 0,
replaySize: 0
};
const TASK_TEMPLATES = {
'digit-recognition': { name: 'Digit Recognition', icon: '🔢', examples: 100 },
'letter-recognition': { name: 'Letter Recognition', icon: '🔤', examples: 100 },
'sentiment-analysis': { name: 'Sentiment Analysis', icon: '😊', examples: 100 },
'translation': { name: 'Translation', icon: '🌐', examples: 100 }
};
function addTask(taskType) {
const template = TASK_TEMPLATES[taskType];
const task = {
...template,
id: Date.now(),
accuracy: 0,
learned: false
};
db.tasks.push(task);
currentTaskIdx = db.tasks.length - 1;
updateTimeline();
logMessage(`➕ Added task: ${task.name}`, 'success');
}
function updateTimeline() {
const timeline = document.getElementById('timeline');
timeline.innerHTML = '';
db.tasks.forEach((task, idx) => {
const item = document.createElement('div');
item.className = 'timeline-item';
item.innerHTML = `
<div class="task-label">${task.icon} ${task.name}</div>
<div class="task-accuracy">
Accuracy: ${task.learned ? (task.accuracy * 100).toFixed(1) + '%' : 'Not learned'}
</div>
`;
timeline.appendChild(item);
});
}
async function learnCurrentTask() {
if (currentTaskIdx < 0 || currentTaskIdx >= db.tasks.length) {
logMessage('⚠️ Please add a task first', 'warning');
return;
}
const task = db.tasks[currentTaskIdx];
// Generate synthetic examples
const examples = [];
for (let i = 0; i < task.examples; i++) {
examples.push({
input: `${task.name}_sample_${i}`,
output: Math.random()
});
}
await db.learnTask(task, examples);
stats.tasksLearned = db.tasks.filter(t => t.learned).length;
stats.avgAccuracy = (db.tasks.reduce((sum, t) => sum + t.accuracy, 0) / db.tasks.length * 100).toFixed(0);
stats.replaySize = db.replayBuffer.length;
updateTimeline();
updateStats();
updateMemoryList();
drawForgettingCurve();
}
async function consolidateMemory() {
await db.consolidateMemory();
updateMemoryList();
stats.replaySize = db.replayBuffer.length;
updateStats();
}
function updateMemoryList() {
const container = document.getElementById('memoryList');
container.innerHTML = '';
const topMemories = db.replayBuffer
.sort((a, b) => b.importance - a.importance)
.slice(0, 5);
topMemories.forEach(memory => {
const item = document.createElement('div');
item.className = 'memory-item';
item.innerHTML = `
<span>${memory.task}</span>
<div class="memory-importance">
<div class="memory-importance-fill" style="width: ${memory.importance * 100}%"></div>
</div>
`;
container.appendChild(item);
});
}
function drawForgettingCurve() {
const canvas = document.getElementById('curveCanvas');
const ctx = canvas.getContext('2d');
const width = canvas.width = canvas.offsetWidth;
const height = canvas.height = canvas.offsetHeight;
ctx.clearRect(0, 0, width, height);
// Draw curves for each task
db.tasks.forEach((task, idx) => {
ctx.beginPath();
ctx.strokeStyle = `hsl(${idx * 60}, 70%, 50%)`;
ctx.lineWidth = 2;
for (let x = 0; x < width; x++) {
const t = x / width;
// Exponential decay with EWC mitigation
const decay = 0.85 + 0.15 * Math.exp(-t * 2);
const y = height - (decay * height);
if (x === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.stroke();
});
}
function updateStats() {
document.getElementById('tasksLearned').textContent = stats.tasksLearned;
document.getElementById('avgAccuracy').textContent = stats.avgAccuracy + '%';
document.getElementById('forgettingRate').textContent = stats.forgettingRate + '%';
document.getElementById('replaySize').textContent = stats.replaySize;
}
function logMessage(message, type = '') {
const log = document.getElementById('log');
const entry = document.createElement('div');
entry.className = `log-entry ${type}`;
const timestamp = new Date().toLocaleTimeString();
entry.textContent = `[${timestamp}] ${message}`;
log.insertBefore(entry, log.firstChild);
while (log.children.length > 20) {
log.removeChild(log.lastChild);
}
}
// Initialize
addTask('digit-recognition');
logMessage('🧬 Continual Learning system initialized', 'success');
logMessage('Add tasks sequentially and watch the model learn without forgetting!', 'info');
</script>
</body>
</html>