teachablellm / index.html
natbutter's picture
first commit
9c40bae
Raw
History Blame Contribute Delete
19.3 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Teachable LLM - In-Browser Transfer Learning w/ Transformers.js</title>
<meta name="description"
content="Interactive demonstration of in-browser text classification using Transformers.js feature extraction and k-Nearest Neighbors with customizable model selection." />
<!-- Google Fonts: Google Sans, Google Sans Text, Roboto, Roboto Mono -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&family=Google+Sans+Text:wght@400;500;700&family=Roboto:wght@400;500;700&family=Roboto+Mono:wght@400;500&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="style.css" />
<!-- Importmap for @xenova/transformers browser module resolution -->
<script type="importmap">
{
"imports": {
"@xenova/transformers": "https://cdn.jsdelivr.net/npm/@xenova/transformers@2.17.2"
}
}
</script>
</head>
<body>
<!-- Top Google Four-Color Accent Bar -->
<div class="google-color-bar">
<div class="bar-blue"></div>
<div class="bar-red"></div>
<div class="bar-yellow"></div>
<div class="bar-green"></div>
</div>
<div class="app-container">
<!-- Header -->
<header class="app-header">
<div class="brand">
<div class="brand-icon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none">
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"
fill="#4285F4" />
<circle cx="12" cy="8" r="2.5" fill="#EA4335" />
<circle cx="8" cy="14" r="2.5" fill="#FBBC04" />
<circle cx="16" cy="14" r="2.5" fill="#34A853" />
</svg>
</div>
<div>
<h1 class="brand-title">Teachable LLM</h1>
<p class="brand-subtitle">Transfer Learning via Transformers.js & k-NN in WebAssembly</p>
</div>
</div>
<!-- Model Selector & Status Controls -->
<div class="header-controls">
<div class="model-selector-card">
<label for="model-select">Transformer Model:</label>
<select id="model-select" class="model-select">
<option value="Xenova/all-MiniLM-L6-v2" selected>all-MiniLM-L6-v2 (384-d, ~23MB - Default)</option>
<option value="Xenova/all-mpnet-base-v2">all-mpnet-base-v2 (768-d, ~110MB - High Accuracy)</option>
<option value="Xenova/bge-small-en-v1.5">bge-small-en-v1.5 (384-d, ~67MB - BAAI BGE)</option>
<option value="Xenova/gte-small">gte-small (384-d, ~67MB - General Embeddings)</option>
<option value="Xenova/distilbert-base-uncased">distilbert-base-uncased (768-d, ~67MB - DistilBERT)</option>
</select>
</div>
<!-- Model Load Status Badge -->
<div class="status-card">
<div class="status-indicator">
<span id="status-dot" class="status-dot loading"></span>
<span id="status-text">Initializing pipeline...</span>
</div>
<div id="progress-container" class="progress-container" style="display: none;">
<div id="progress-bar" class="progress-bar" style="width: 0%;"></div>
</div>
</div>
</div>
</header>
<!-- Concept Overview Hero & Pipeline Stepper Card -->
<section class="hero-card">
<div class="hero-content">
<div class="hero-header-row">
<div>
<h2>Interactive Architecture Pipeline</h2>
<p class="hero-subtitle">Explore how Transformers convert text into high-dimensional vectors and perform
transfer learning in-browser.</p>
</div>
<button type="button" id="animate-pipeline-btn" class="btn btn-secondary btn-sm">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" style="margin-right:4px;">
<polygon points="5,3 19,12 5,21" fill="currentColor" />
</svg>
Animate Forward Pass
</button>
</div>
<div class="stepper-grid">
<div class="stepper-step active" data-step="1">
<div class="step-num step-blue">1</div>
<div class="step-details">
<h3>Tokenization</h3>
<p>Subword splitting & position IDs</p>
</div>
</div>
<div class="stepper-step" data-step="2">
<div class="step-num step-red">2</div>
<div class="step-details">
<h3>Self-Attention</h3>
<p>Multi-Head token interaction</p>
</div>
</div>
<div class="stepper-step" data-step="3">
<div class="step-num step-yellow">3</div>
<div class="step-details">
<h3>Mean Pooling</h3>
<p>Dense vector fingerprint</p>
</div>
</div>
<div class="stepper-step" data-step="4">
<div class="step-num step-green">4</div>
<div class="step-details">
<h3>2D Space & k-NN</h3>
<p>PCA Manifold & Class Regions</p>
</div>
</div>
</div>
</div>
</section>
<!-- Main Content Grid -->
<main class="main-grid">
<!-- Left Column: Interactive Controls -->
<div class="left-column">
<!-- Training Dataset Management Card -->
<section class="panel-card">
<div class="panel-header">
<h2>1. Training Dataset</h2>
<span id="dataset-count" class="badge-neutral">0 examples</span>
</div>
<!-- Add Example Form -->
<form id="add-example-form" class="add-form">
<div class="form-group">
<label for="example-text">Text Example</label>
<input type="text" id="example-text" placeholder="e.g. The service was fast and super friendly!"
required />
</div>
<div class="form-group">
<label for="example-label">Class Label</label>
<div class="label-input-group">
<input type="text" id="example-label" placeholder="e.g. positive" required />
<div class="preset-badges">
<button type="button" class="preset-badge badge-positive"
data-label="positive">positive</button>
<button type="button" class="preset-badge badge-negative"
data-label="negative">negative</button>
<button type="button" class="preset-badge badge-neutral"
data-label="neutral">neutral</button>
</div>
</div>
</div>
<button type="submit" class="btn btn-secondary">+ Add Example</button>
</form>
<!-- Dataset Example List -->
<div class="dataset-section">
<div class="dataset-actions">
<h3>Current Memory Dataset</h3>
<div class="btn-group">
<button type="button" id="reset-demo-btn" class="btn btn-sm btn-outline">Reset Demo
Dataset</button>
<button type="button" id="clear-dataset-btn" class="btn btn-sm btn-outline-danger">Clear
All</button>
</div>
</div>
<div id="dataset-list" class="dataset-list">
<!-- Populated dynamically by demo.js -->
</div>
</div>
</section>
<!-- Inference & Prediction Card -->
<section class="panel-card">
<div class="panel-header">
<h2>2. Run Inference</h2>
</div>
<div class="inference-form">
<div class="form-group">
<label for="test-text">Test Input Sentence</label>
<textarea id="test-text" rows="2"
placeholder="Type any sentence to test classification..."></textarea>
</div>
<div class="form-row">
<div class="form-group inline">
<label for="k-value">Neighbors (k):</label>
<input type="number" id="k-value" value="3" min="1" max="10" />
</div>
<button type="button" id="predict-btn" class="btn btn-primary">Classify Text</button>
</div>
</div>
<!-- Prediction Result Display -->
<div id="result-box" class="result-box" style="display: none;">
<div class="result-header">
<div class="result-main">
<span class="result-title">Predicted Class:</span>
<div id="predicted-label"></div>
</div>
<div class="confidence-box">
<span class="confidence-label">Confidence: <strong id="confidence-val">--</strong></span>
<div class="confidence-track">
<div id="confidence-fill" class="confidence-fill" style="width: 0%;"></div>
</div>
</div>
</div>
<div id="vector-preview" class="vector-preview"></div>
<!-- Top k Neighbors Breakdown -->
<div class="neighbors-section">
<h4>Nearest Neighbor Votes</h4>
<div id="neighbors-list" class="neighbors-list"></div>
</div>
</div>
</section>
</div>
<!-- Right Column: Architecture & Logs -->
<div class="right-column">
<!-- Activity Log Card -->
<section class="panel-card log-panel">
<div class="panel-header">
<h2>Activity Console</h2>
<span class="live-tag">LIVE</span>
</div>
<div id="log-output" class="log-output">
<!-- Streamed console output -->
</div>
</section>
<!-- Architecture Summary Card -->
<section class="panel-card info-card">
<div class="panel-header">
<h2>Architecture Specs</h2>
</div>
<ul class="specs-list">
<li><strong>Active Model:</strong> <code id="spec-model-name">Xenova/all-MiniLM-L6-v2</code></li>
<li><strong>Runtime:</strong> ONNX Runtime Web via Transformers.js</li>
<li><strong>Embedding Dimension:</strong> <span id="spec-embed-dim">384</span></li>
<li><strong>Pooling Strategy:</strong> Mean pooling with L2 normalization</li>
<li><strong>Classifier:</strong> k-Nearest Neighbors (Euclidean Distance)</li>
</ul>
</section>
</div>
</main>
<!-- Full-Width Interactive Architecture & Storytelling Visualizer Section -->
<section class="viz-storytelling-section">
<div class="section-title-row">
<div>
<h2>Deep Learning Visual Explorers</h2>
<p>Interactive step-by-step breakdown inspired by Transformer Explainer & GAN Lab</p>
</div>
</div>
<div class="viz-grid">
<!-- Viz 1: Subword Tokenizer -->
<div class="panel-card viz-card" id="viz-step-1">
<div class="panel-header">
<h2>Stage 1: Subword Tokenization</h2>
<span class="badge label-custom">WordPiece / BPE</span>
</div>
<p class="viz-desc">Input text is mapped into discrete subword token IDs and positional indices.</p>
<div id="tokenizer-viz-container" class="viz-container"></div>
</div>
<!-- Viz 2: Self-Attention Matrix -->
<div class="panel-card viz-card" id="viz-step-2">
<div class="panel-header">
<h2>Stage 2: Self-Attention Matrix & QKV Arcs</h2>
<span class="badge badge-negative">Softmax(QK<sup>T</sup>/&radic;d)V</span>
</div>
<p class="viz-desc">Tokens attend to each other across Multi-Head Self-Attention layers to capture
contextual meaning.</p>
<div id="attn-arcs-container" class="attn-arcs-wrapper"></div>
<div id="attention-viz-container" class="viz-container"></div>
</div>
<!-- Viz 3: Pooling & Embedding Fingerprint Barcode -->
<div class="panel-card viz-card" id="viz-step-3">
<div class="panel-header">
<h2>Stage 3: Sentence Embedding Fingerprint</h2>
<span class="badge badge-neutral">Mean Pooling & L2 Norm</span>
</div>
<p class="viz-desc">Token vectors are collapsed into a dense 1D fingerprint representing the entire
sentence's semantics.</p>
<div id="pooling-viz-container" class="viz-container"></div>
</div>
<!-- Viz 4: 2D Embedding Space & Decision Boundary Canvas (GAN Lab style) -->
<div class="panel-card viz-card full-width-viz" id="viz-step-4">
<div class="panel-header">
<div>
<h2>Stage 4: 2D Embedding Space & k-NN Decision Boundaries</h2>
<p class="viz-desc">High-dimensional embeddings projected to 2D via PCA. Background shows memory
class zones; click canvas to query!</p>
</div>
<div id="pca-legend" class="pca-legend"></div>
</div>
<div class="canvas-wrapper">
<canvas id="pca-canvas" width="760" height="420"></canvas>
<div class="canvas-hint-overlay">
<span>💡 Click anywhere on canvas to test a 2D vector coordinate query</span>
</div>
</div>
</div>
</div>
</section>
<footer class="app-footer">
<p>Built with <a href="https://github.com/xenova/transformers.js" target="_blank" rel="noopener">Transformers.js</a>
</p>
</footer>
</div>
<!-- Main Module Script -->
<script type="module" src="demo.js"></script>
</body>
</html>