AashishAIHub commited on
Commit
0751fec
·
1 Parent(s): 6782452

update files

Browse files
ml_complete-all-topics/app.js CHANGED
@@ -107,8 +107,10 @@ function initSections() {
107
  if (section.id === 'optimal-k') initOptimalK();
108
  if (section.id === 'hyperparameter-tuning') initHyperparameterTuning();
109
  if (section.id === 'naive-bayes') initNaiveBayes();
 
110
  if (section.id === 'decision-trees') initDecisionTrees();
111
  if (section.id === 'ensemble-methods') initEnsembleMethods();
 
112
  }
113
  });
114
  });
@@ -204,82 +206,24 @@ function initLinearRegression() {
204
  drawLinearRegression();
205
  }
206
 
 
 
207
  function drawLinearRegression() {
208
  const canvas = document.getElementById('lr-canvas');
209
  if (!canvas) return;
210
 
211
- const ctx = canvas.getContext('2d');
212
- const width = canvas.width = canvas.offsetWidth;
213
- const height = canvas.height = 400;
214
-
215
- ctx.clearRect(0, 0, width, height);
216
-
217
- const padding = 60;
218
- const chartWidth = width - 2 * padding;
219
- const chartHeight = height - 2 * padding;
220
-
221
- const xMin = 0, xMax = 7;
222
- const yMin = 0, yMax = 100;
223
-
224
- // Draw axes
225
- ctx.strokeStyle = '#2a3544';
226
- ctx.lineWidth = 2;
227
- ctx.beginPath();
228
- ctx.moveTo(padding, padding);
229
- ctx.lineTo(padding, height - padding);
230
- ctx.lineTo(width - padding, height - padding);
231
- ctx.stroke();
232
-
233
- // Grid
234
- ctx.strokeStyle = 'rgba(42, 53, 68, 0.3)';
235
- ctx.lineWidth = 1;
236
- for (let i = 0; i <= 5; i++) {
237
- const x = padding + (chartWidth / 5) * i;
238
- ctx.beginPath();
239
- ctx.moveTo(x, padding);
240
- ctx.lineTo(x, height - padding);
241
- ctx.stroke();
242
-
243
- const y = height - padding - (chartHeight / 5) * i;
244
- ctx.beginPath();
245
- ctx.moveTo(padding, y);
246
- ctx.lineTo(width - padding, y);
247
- ctx.stroke();
248
  }
249
 
250
- const scaleX = (x) => padding + ((x - xMin) / (xMax - xMin)) * chartWidth;
251
- const scaleY = (y) => height - padding - ((y - yMin) / (yMax - yMin)) * chartHeight;
252
-
253
- // Draw data points
254
- ctx.fillStyle = '#6aa9ff';
255
- data.linearRegression.forEach(point => {
256
- const x = scaleX(point.experience);
257
- const y = scaleY(point.salary);
258
- ctx.beginPath();
259
- ctx.arc(x, y, 6, 0, 2 * Math.PI);
260
- ctx.fill();
261
- });
262
-
263
- // Draw regression line
264
- ctx.strokeStyle = '#ff8c6a';
265
- ctx.lineWidth = 3;
266
- ctx.beginPath();
267
- const y1 = state.slope * xMin + state.intercept;
268
- const y2 = state.slope * xMax + state.intercept;
269
- ctx.moveTo(scaleX(xMin), scaleY(y1));
270
- ctx.lineTo(scaleX(xMax), scaleY(y2));
271
- ctx.stroke();
272
 
273
- // Labels
274
- ctx.fillStyle = '#a9b4c2';
275
- ctx.font = '12px sans-serif';
276
- ctx.textAlign = 'center';
277
- ctx.fillText('Experience (years)', width / 2, height - 20);
278
- ctx.save();
279
- ctx.translate(20, height / 2);
280
- ctx.rotate(-Math.PI / 2);
281
- ctx.fillText('Salary ($k)', 0, 0);
282
- ctx.restore();
283
 
284
  // Calculate MSE
285
  let mse = 0;
@@ -290,11 +234,57 @@ function drawLinearRegression() {
290
  });
291
  mse /= data.linearRegression.length;
292
 
293
- // Display MSE
294
- ctx.fillStyle = '#7ef0d4';
295
- ctx.font = '14px sans-serif';
296
- ctx.textAlign = 'right';
297
- ctx.fillText(`MSE: ${mse.toFixed(2)}`, width - padding, padding + 20);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  }
299
 
300
  // Gradient Descent Visualization
@@ -386,87 +376,73 @@ function animateGradientDescent() {
386
  }, 50);
387
  }
388
 
 
 
389
  function drawGradientDescent(currentStep = -1) {
390
  const canvas = document.getElementById('gd-canvas');
391
  if (!canvas) return;
392
 
393
- const ctx = canvas.getContext('2d');
394
- const width = canvas.width = canvas.offsetWidth;
395
- const height = canvas.height = 400;
396
-
397
- ctx.clearRect(0, 0, width, height);
398
-
399
  if (state.gdIterations.length === 0) {
 
 
400
  ctx.fillStyle = '#a9b4c2';
401
  ctx.font = '16px sans-serif';
402
  ctx.textAlign = 'center';
403
- ctx.fillText('Click "Run Gradient Descent" to see the algorithm in action', width / 2, height / 2);
404
  return;
405
  }
406
 
407
- const padding = 60;
408
- const chartWidth = width - 2 * padding;
409
- const chartHeight = height - 2 * padding;
410
-
411
- const maxLoss = Math.max(...state.gdIterations.map(i => i.loss));
412
- const minLoss = Math.min(...state.gdIterations.map(i => i.loss));
413
-
414
- // Draw axes
415
- ctx.strokeStyle = '#2a3544';
416
- ctx.lineWidth = 2;
417
- ctx.beginPath();
418
- ctx.moveTo(padding, padding);
419
- ctx.lineTo(padding, height - padding);
420
- ctx.lineTo(width - padding, height - padding);
421
- ctx.stroke();
422
-
423
- const scaleX = (i) => padding + (i / (state.gdIterations.length - 1)) * chartWidth;
424
- const scaleY = (loss) => height - padding - ((loss - minLoss) / (maxLoss - minLoss)) * chartHeight;
425
 
426
- // Draw loss curve
427
- ctx.strokeStyle = '#7ef0d4';
428
- ctx.lineWidth = 2;
429
- ctx.beginPath();
430
- state.gdIterations.forEach((iter, i) => {
431
- const x = scaleX(i);
432
- const y = scaleY(iter.loss);
433
- if (i === 0) {
434
- ctx.moveTo(x, y);
435
- } else {
436
- ctx.lineTo(x, y);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
437
  }
438
  });
439
- ctx.stroke();
440
-
441
- // Highlight current step
442
- if (currentStep >= 0 && currentStep < state.gdIterations.length) {
443
- const iter = state.gdIterations[currentStep];
444
- const x = scaleX(currentStep);
445
- const y = scaleY(iter.loss);
446
-
447
- ctx.fillStyle = '#ff8c6a';
448
- ctx.beginPath();
449
- ctx.arc(x, y, 6, 0, 2 * Math.PI);
450
- ctx.fill();
451
-
452
- // Display current values
453
- ctx.fillStyle = '#e8eef6';
454
- ctx.font = '12px sans-serif';
455
- ctx.textAlign = 'left';
456
- ctx.fillText(`Step: ${currentStep + 1}`, padding + 10, padding + 20);
457
- ctx.fillText(`Loss: ${iter.loss.toFixed(2)}`, padding + 10, padding + 40);
458
- }
459
-
460
- // Labels
461
- ctx.fillStyle = '#a9b4c2';
462
- ctx.font = '12px sans-serif';
463
- ctx.textAlign = 'center';
464
- ctx.fillText('Iterations', width / 2, height - 20);
465
- ctx.save();
466
- ctx.translate(20, height / 2);
467
- ctx.rotate(-Math.PI / 2);
468
- ctx.fillText('Loss (MSE)', 0, 0);
469
- ctx.restore();
470
  }
471
 
472
  // Initialize everything when DOM is ready
@@ -766,10 +742,17 @@ function initSVMCParameter() {
766
  drawSVMCParameter();
767
  }
768
 
 
 
769
  function drawSVMCParameter() {
770
  const canvas = document.getElementById('svm-c-canvas');
771
  if (!canvas) return;
772
 
 
 
 
 
 
773
  const ctx = canvas.getContext('2d');
774
  const width = canvas.width = canvas.offsetWidth;
775
  const height = canvas.height = 450;
@@ -852,8 +835,10 @@ function drawSVMCParameter() {
852
  // Update info
853
  const wNorm = Math.sqrt(w1 * w1 + w2 * w2);
854
  const marginWidth = 2 / wNorm;
855
- document.getElementById('margin-width').textContent = marginWidth.toFixed(2);
856
- document.getElementById('violations-count').textContent = violations;
 
 
857
  }
858
 
859
  function initSVMTraining() {
@@ -1009,6 +994,8 @@ function drawSVMTraining() {
1009
  });
1010
  }
1011
 
 
 
1012
  function initSVMKernel() {
1013
  const canvas = document.getElementById('svm-kernel-canvas');
1014
  if (!canvas || canvas.dataset.initialized) return;
@@ -1030,7 +1017,8 @@ function initSVMKernel() {
1030
  if (paramSlider) {
1031
  paramSlider.addEventListener('input', (e) => {
1032
  state.svm.kernelParam = parseFloat(e.target.value);
1033
- document.getElementById('kernel-param-val').textContent = state.svm.kernelParam.toFixed(1);
 
1034
  drawSVMKernel();
1035
  });
1036
  }
@@ -2357,6 +2345,9 @@ function drawLossCurves() {
2357
  }
2358
 
2359
  // Topic 13: Finding Optimal K in KNN
 
 
 
2360
  function initOptimalK() {
2361
  const canvas1 = document.getElementById('elbow-canvas');
2362
  if (canvas1 && !canvas1.dataset.initialized) {
@@ -2404,57 +2395,72 @@ function drawElbowCurve() {
2404
  ctx.lineTo(width - padding, height - padding);
2405
  ctx.stroke();
2406
 
2407
- // Draw curve
2408
- ctx.strokeStyle = '#6aa9ff';
2409
- ctx.lineWidth = 3;
2410
- ctx.beginPath();
2411
- kValues.forEach((k, i) => {
2412
- const x = scaleX(k);
2413
- const y = scaleY(accuracies[i]);
2414
- if (i === 0) ctx.moveTo(x, y);
2415
- else ctx.lineTo(x, y);
2416
- });
2417
- ctx.stroke();
2418
 
2419
- // Draw points
2420
- kValues.forEach((k, i) => {
2421
- const x = scaleX(k);
2422
- const y = scaleY(accuracies[i]);
2423
- ctx.fillStyle = k === optimalK ? '#7ef0d4' : '#6aa9ff';
2424
- ctx.beginPath();
2425
- ctx.arc(x, y, k === optimalK ? 8 : 4, 0, 2 * Math.PI);
2426
- ctx.fill();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2427
  });
2428
-
2429
- // Highlight optimal K
2430
- const optX = scaleX(optimalK);
2431
- const optY = scaleY(accuracies[optimalK - 1]);
2432
- ctx.strokeStyle = '#7ef0d4';
2433
- ctx.lineWidth = 2;
2434
- ctx.setLineDash([5, 5]);
2435
- ctx.beginPath();
2436
- ctx.moveTo(optX, optY);
2437
- ctx.lineTo(optX, height - padding);
2438
- ctx.stroke();
2439
- ctx.setLineDash([]);
2440
-
2441
- // Labels
2442
- ctx.fillStyle = '#a9b4c2';
2443
- ctx.font = '12px sans-serif';
2444
- ctx.textAlign = 'center';
2445
- ctx.fillText('K (Number of Neighbors)', width / 2, height - 20);
2446
- ctx.save();
2447
- ctx.translate(20, height / 2);
2448
- ctx.rotate(-Math.PI / 2);
2449
- ctx.fillText('Accuracy', 0, 0);
2450
- ctx.restore();
2451
-
2452
- // Optimal K label
2453
- ctx.fillStyle = '#7ef0d4';
2454
- ctx.font = 'bold 14px sans-serif';
2455
- ctx.textAlign = 'center';
2456
- ctx.fillText(`Optimal K = ${optimalK}`, optX, padding + 30);
2457
- ctx.fillText(`Accuracy: ${accuracies[optimalK - 1].toFixed(2)}`, optX, padding + 50);
2458
  }
2459
 
2460
  function drawCVKHeatmap() {
@@ -2541,6 +2547,8 @@ function drawCVKHeatmap() {
2541
  }
2542
 
2543
  // Topic 14: Hyperparameter Tuning
 
 
2544
  function initHyperparameterTuning() {
2545
  const canvas1 = document.getElementById('gridsearch-heatmap');
2546
  if (canvas1 && !canvas1.dataset.initialized) {
@@ -2566,6 +2574,11 @@ function drawGridSearchHeatmap() {
2566
  const canvas = document.getElementById('gridsearch-heatmap');
2567
  if (!canvas) return;
2568
 
 
 
 
 
 
2569
  const ctx = canvas.getContext('2d');
2570
  const width = canvas.width = canvas.offsetWidth;
2571
  const height = canvas.height = 450;
@@ -2660,7 +2673,24 @@ function drawGridSearchHeatmap() {
2660
  ctx.fillText('Gamma Parameter', 0, 0);
2661
  ctx.restore();
2662
 
2663
- // Best params
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2664
  ctx.fillStyle = '#7ef0d4';
2665
  ctx.font = 'bold 14px sans-serif';
2666
  ctx.textAlign = 'left';
@@ -2731,6 +2761,10 @@ function drawParamSurface() {
2731
  }
2732
 
2733
  // Topic 15: Naive Bayes
 
 
 
 
2734
  function initNaiveBayes() {
2735
  const canvas1 = document.getElementById('bayes-theorem-viz');
2736
  if (canvas1 && !canvas1.dataset.initialized) {
@@ -2743,6 +2777,18 @@ function initNaiveBayes() {
2743
  canvas2.dataset.initialized = 'true';
2744
  drawSpamClassification();
2745
  }
 
 
 
 
 
 
 
 
 
 
 
 
2746
  }
2747
 
2748
  function drawBayesTheorem() {
@@ -2799,6 +2845,183 @@ function drawBayesTheorem() {
2799
  ctx.fillText("Bayes' Theorem Breakdown", centerX, 40);
2800
  }
2801
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2802
  function drawSpamClassification() {
2803
  const canvas = document.getElementById('spam-classification');
2804
  if (!canvas) return;
@@ -2859,20 +3082,52 @@ function drawSpamClassification() {
2859
  ctx.font = 'bold 18px sans-serif';
2860
  ctx.fillText('→ SPAM! 📧❌', padding, y4 + 30);
2861
 
2862
- // Visual comparison
2863
- const barY = y4 + 60;
2864
- const barMaxWidth = width - 2 * padding - 100;
2865
- ctx.fillStyle = '#7ef0d4';
2866
- ctx.fillRect(padding, barY, 0.1008 / 0.1008 * barMaxWidth, 20);
2867
- ctx.fillStyle = '#e8eef6';
2868
- ctx.font = '11px sans-serif';
2869
- ctx.textAlign = 'right';
2870
- ctx.fillText('Spam', padding + barMaxWidth + 80, barY + 15);
2871
-
2872
- ctx.fillStyle = '#ff8c6a';
2873
- ctx.fillRect(padding, barY + 30, 0.0007 / 0.1008 * barMaxWidth, 20);
2874
- ctx.fillStyle = '#e8eef6';
2875
- ctx.fillText('Not Spam', padding + barMaxWidth + 80, barY + 45);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2876
  }
2877
 
2878
  // Topic 16: Decision Trees
@@ -3552,6 +3807,326 @@ function drawRandomForestViz() {
3552
  ctx.fillText('Random Forest: Ensemble of Decision Trees', width / 2, 25);
3553
  }
3554
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3555
  // Handle window resize
3556
  let resizeTimer;
3557
  window.addEventListener('resize', () => {
@@ -3579,18 +4154,23 @@ window.addEventListener('resize', () => {
3579
  drawSVMTraining();
3580
  drawSVMKernel();
3581
  // New topics
3582
- drawElbowCurve();
3583
- drawCVKHeatmap();
3584
- drawGridSearchHeatmap();
3585
- drawParamSurface();
3586
- drawBayesTheorem();
3587
- drawSpamClassification();
3588
- drawDecisionTree();
3589
- drawEntropyViz();
3590
- drawSplitComparison();
3591
- drawTreeBoundary();
3592
- drawBaggingViz();
3593
- drawBoostingViz();
3594
- drawRandomForestViz();
 
 
 
 
 
3595
  }, 250);
3596
  });
 
107
  if (section.id === 'optimal-k') initOptimalK();
108
  if (section.id === 'hyperparameter-tuning') initHyperparameterTuning();
109
  if (section.id === 'naive-bayes') initNaiveBayes();
110
+ if (section.id === 'kmeans') initKMeans();
111
  if (section.id === 'decision-trees') initDecisionTrees();
112
  if (section.id === 'ensemble-methods') initEnsembleMethods();
113
+ if (section.id === 'algorithm-comparison') initAlgorithmComparison();
114
  }
115
  });
116
  });
 
206
  drawLinearRegression();
207
  }
208
 
209
+ let lrChart = null;
210
+
211
  function drawLinearRegression() {
212
  const canvas = document.getElementById('lr-canvas');
213
  if (!canvas) return;
214
 
215
+ // Destroy existing chart
216
+ if (lrChart) {
217
+ lrChart.destroy();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  }
219
 
220
+ const ctx = canvas.getContext('2d');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
+ // Calculate fitted line points
223
+ const fittedLine = [];
224
+ for (let x = 0; x <= 7; x += 0.1) {
225
+ fittedLine.push({ x: x, y: state.slope * x + state.intercept });
226
+ }
 
 
 
 
 
227
 
228
  // Calculate MSE
229
  let mse = 0;
 
234
  });
235
  mse /= data.linearRegression.length;
236
 
237
+ lrChart = new Chart(ctx, {
238
+ type: 'scatter',
239
+ data: {
240
+ datasets: [
241
+ {
242
+ label: 'Data Points',
243
+ data: data.linearRegression.map(p => ({ x: p.experience, y: p.salary })),
244
+ backgroundColor: '#6aa9ff',
245
+ pointRadius: 8,
246
+ pointHoverRadius: 10
247
+ },
248
+ {
249
+ label: 'Fitted Line',
250
+ data: fittedLine,
251
+ type: 'line',
252
+ borderColor: '#ff8c6a',
253
+ borderWidth: 3,
254
+ fill: false,
255
+ pointRadius: 0,
256
+ tension: 0
257
+ }
258
+ ]
259
+ },
260
+ options: {
261
+ responsive: true,
262
+ maintainAspectRatio: false,
263
+ plugins: {
264
+ title: {
265
+ display: true,
266
+ text: `Experience vs Salary (MSE: ${mse.toFixed(2)})`,
267
+ color: '#e8eef6',
268
+ font: { size: 16 }
269
+ },
270
+ legend: {
271
+ labels: { color: '#a9b4c2' }
272
+ }
273
+ },
274
+ scales: {
275
+ x: {
276
+ title: { display: true, text: 'Years of Experience', color: '#a9b4c2' },
277
+ grid: { color: '#2a3544' },
278
+ ticks: { color: '#a9b4c2' }
279
+ },
280
+ y: {
281
+ title: { display: true, text: 'Salary ($k)', color: '#a9b4c2' },
282
+ grid: { color: '#2a3544' },
283
+ ticks: { color: '#a9b4c2' }
284
+ }
285
+ }
286
+ }
287
+ });
288
  }
289
 
290
  // Gradient Descent Visualization
 
376
  }, 50);
377
  }
378
 
379
+ let gdChart = null;
380
+
381
  function drawGradientDescent(currentStep = -1) {
382
  const canvas = document.getElementById('gd-canvas');
383
  if (!canvas) return;
384
 
 
 
 
 
 
 
385
  if (state.gdIterations.length === 0) {
386
+ const ctx = canvas.getContext('2d');
387
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
388
  ctx.fillStyle = '#a9b4c2';
389
  ctx.font = '16px sans-serif';
390
  ctx.textAlign = 'center';
391
+ ctx.fillText('Click "Run Gradient Descent" to see the algorithm in action', canvas.width / 2, canvas.height / 2);
392
  return;
393
  }
394
 
395
+ // Destroy existing chart
396
+ if (gdChart) {
397
+ gdChart.destroy();
398
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
 
400
+ const ctx = canvas.getContext('2d');
401
+ const lossData = state.gdIterations.map((iter, i) => ({ x: i + 1, y: iter.loss }));
402
+
403
+ gdChart = new Chart(ctx, {
404
+ type: 'line',
405
+ data: {
406
+ datasets: [{
407
+ label: 'Training Loss',
408
+ data: lossData,
409
+ borderColor: '#7ef0d4',
410
+ backgroundColor: 'rgba(126, 240, 212, 0.1)',
411
+ borderWidth: 3,
412
+ fill: true,
413
+ tension: 0.4,
414
+ pointRadius: currentStep >= 0 ? lossData.map((_, i) => i === currentStep ? 8 : 2) : 4,
415
+ pointBackgroundColor: currentStep >= 0 ? lossData.map((_, i) => i === currentStep ? '#ff8c6a' : '#7ef0d4') : '#7ef0d4'
416
+ }]
417
+ },
418
+ options: {
419
+ responsive: true,
420
+ maintainAspectRatio: false,
421
+ plugins: {
422
+ title: {
423
+ display: true,
424
+ text: currentStep >= 0 ? `Gradient Descent Progress (Step ${currentStep + 1}/${state.gdIterations.length})` : 'Gradient Descent Progress',
425
+ color: '#e8eef6',
426
+ font: { size: 16 }
427
+ },
428
+ legend: {
429
+ labels: { color: '#a9b4c2' }
430
+ }
431
+ },
432
+ scales: {
433
+ x: {
434
+ title: { display: true, text: 'Iterations', color: '#a9b4c2' },
435
+ grid: { color: '#2a3544' },
436
+ ticks: { color: '#a9b4c2' }
437
+ },
438
+ y: {
439
+ title: { display: true, text: 'Loss (MSE)', color: '#a9b4c2' },
440
+ grid: { color: '#2a3544' },
441
+ ticks: { color: '#a9b4c2' }
442
+ }
443
+ }
444
  }
445
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
  }
447
 
448
  // Initialize everything when DOM is ready
 
742
  drawSVMCParameter();
743
  }
744
 
745
+ let svmCChart = null;
746
+
747
  function drawSVMCParameter() {
748
  const canvas = document.getElementById('svm-c-canvas');
749
  if (!canvas) return;
750
 
751
+ // Destroy existing chart
752
+ if (svmCChart) {
753
+ svmCChart.destroy();
754
+ }
755
+
756
  const ctx = canvas.getContext('2d');
757
  const width = canvas.width = canvas.offsetWidth;
758
  const height = canvas.height = 450;
 
835
  // Update info
836
  const wNorm = Math.sqrt(w1 * w1 + w2 * w2);
837
  const marginWidth = 2 / wNorm;
838
+ const marginEl = document.getElementById('margin-width');
839
+ const violEl = document.getElementById('violations-count');
840
+ if (marginEl) marginEl.textContent = marginWidth.toFixed(2);
841
+ if (violEl) violEl.textContent = violations;
842
  }
843
 
844
  function initSVMTraining() {
 
994
  });
995
  }
996
 
997
+ let svmKernelChart = null;
998
+
999
  function initSVMKernel() {
1000
  const canvas = document.getElementById('svm-kernel-canvas');
1001
  if (!canvas || canvas.dataset.initialized) return;
 
1017
  if (paramSlider) {
1018
  paramSlider.addEventListener('input', (e) => {
1019
  state.svm.kernelParam = parseFloat(e.target.value);
1020
+ const paramVal = document.getElementById('kernel-param-val');
1021
+ if (paramVal) paramVal.textContent = state.svm.kernelParam.toFixed(1);
1022
  drawSVMKernel();
1023
  });
1024
  }
 
2345
  }
2346
 
2347
  // Topic 13: Finding Optimal K in KNN
2348
+ let elbowChart = null;
2349
+ let cvKChart = null;
2350
+
2351
  function initOptimalK() {
2352
  const canvas1 = document.getElementById('elbow-canvas');
2353
  if (canvas1 && !canvas1.dataset.initialized) {
 
2395
  ctx.lineTo(width - padding, height - padding);
2396
  ctx.stroke();
2397
 
2398
+ // Destroy existing chart
2399
+ if (elbowChart) {
2400
+ elbowChart.destroy();
2401
+ }
 
 
 
 
 
 
 
2402
 
2403
+ // Use Chart.js
2404
+ elbowChart = new Chart(ctx, {
2405
+ type: 'line',
2406
+ data: {
2407
+ labels: kValues,
2408
+ datasets: [{
2409
+ label: 'Accuracy',
2410
+ data: accuracies,
2411
+ borderColor: '#6aa9ff',
2412
+ backgroundColor: 'rgba(106, 169, 255, 0.1)',
2413
+ borderWidth: 3,
2414
+ fill: true,
2415
+ tension: 0.4,
2416
+ pointRadius: kValues.map(k => k === optimalK ? 10 : 5),
2417
+ pointBackgroundColor: kValues.map(k => k === optimalK ? '#7ef0d4' : '#6aa9ff'),
2418
+ pointBorderColor: kValues.map(k => k === optimalK ? '#7ef0d4' : '#6aa9ff'),
2419
+ pointBorderWidth: kValues.map(k => k === optimalK ? 3 : 2)
2420
+ }]
2421
+ },
2422
+ options: {
2423
+ responsive: true,
2424
+ maintainAspectRatio: false,
2425
+ plugins: {
2426
+ title: {
2427
+ display: true,
2428
+ text: `Elbow Method: Optimal K = ${optimalK} (Accuracy: ${accuracies[optimalK - 1].toFixed(2)})`,
2429
+ color: '#7ef0d4',
2430
+ font: { size: 16, weight: 'bold' }
2431
+ },
2432
+ legend: {
2433
+ labels: { color: '#a9b4c2' }
2434
+ },
2435
+ annotation: {
2436
+ annotations: {
2437
+ line1: {
2438
+ type: 'line',
2439
+ xMin: optimalK,
2440
+ xMax: optimalK,
2441
+ borderColor: '#7ef0d4',
2442
+ borderWidth: 2,
2443
+ borderDash: [5, 5]
2444
+ }
2445
+ }
2446
+ }
2447
+ },
2448
+ scales: {
2449
+ x: {
2450
+ title: { display: true, text: 'K (Number of Neighbors)', color: '#a9b4c2' },
2451
+ grid: { color: '#2a3544' },
2452
+ ticks: { color: '#a9b4c2' }
2453
+ },
2454
+ y: {
2455
+ title: { display: true, text: 'Accuracy', color: '#a9b4c2' },
2456
+ grid: { color: '#2a3544' },
2457
+ ticks: { color: '#a9b4c2' },
2458
+ min: 0.7,
2459
+ max: 1.0
2460
+ }
2461
+ }
2462
+ }
2463
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2464
  }
2465
 
2466
  function drawCVKHeatmap() {
 
2547
  }
2548
 
2549
  // Topic 14: Hyperparameter Tuning
2550
+ let gridSearchChart = null;
2551
+
2552
  function initHyperparameterTuning() {
2553
  const canvas1 = document.getElementById('gridsearch-heatmap');
2554
  if (canvas1 && !canvas1.dataset.initialized) {
 
2574
  const canvas = document.getElementById('gridsearch-heatmap');
2575
  if (!canvas) return;
2576
 
2577
+ // Destroy existing chart
2578
+ if (gridSearchChart) {
2579
+ gridSearchChart.destroy();
2580
+ }
2581
+
2582
  const ctx = canvas.getContext('2d');
2583
  const width = canvas.width = canvas.offsetWidth;
2584
  const height = canvas.height = 450;
 
2673
  ctx.fillText('Gamma Parameter', 0, 0);
2674
  ctx.restore();
2675
 
2676
+ // Best params - Use Chart.js for bar comparison instead
2677
+ const compareData = [];
2678
+ cValues.forEach((c, j) => {
2679
+ gammaValues.forEach((g, i) => {
2680
+ compareData.push({
2681
+ c: c,
2682
+ gamma: g,
2683
+ acc: accuracies[i][j],
2684
+ label: `C=${c}, γ=${g}`
2685
+ });
2686
+ });
2687
+ });
2688
+
2689
+ // Sort and get top 5
2690
+ compareData.sort((a, b) => b.acc - a.acc);
2691
+ const top5 = compareData.slice(0, 5);
2692
+
2693
+ // Add annotation for best
2694
  ctx.fillStyle = '#7ef0d4';
2695
  ctx.font = 'bold 14px sans-serif';
2696
  ctx.textAlign = 'left';
 
2761
  }
2762
 
2763
  // Topic 15: Naive Bayes
2764
+ let bayesComparisonChart = null;
2765
+ let categoricalNBChart = null;
2766
+ let gaussianNBChart = null;
2767
+
2768
  function initNaiveBayes() {
2769
  const canvas1 = document.getElementById('bayes-theorem-viz');
2770
  if (canvas1 && !canvas1.dataset.initialized) {
 
2777
  canvas2.dataset.initialized = 'true';
2778
  drawSpamClassification();
2779
  }
2780
+
2781
+ const canvas3 = document.getElementById('categorical-nb-canvas');
2782
+ if (canvas3 && !canvas3.dataset.initialized) {
2783
+ canvas3.dataset.initialized = 'true';
2784
+ drawCategoricalNB();
2785
+ }
2786
+
2787
+ const canvas4 = document.getElementById('gaussian-nb-canvas');
2788
+ if (canvas4 && !canvas4.dataset.initialized) {
2789
+ canvas4.dataset.initialized = 'true';
2790
+ drawGaussianNB();
2791
+ }
2792
  }
2793
 
2794
  function drawBayesTheorem() {
 
2845
  ctx.fillText("Bayes' Theorem Breakdown", centerX, 40);
2846
  }
2847
 
2848
+ function drawCategoricalNB() {
2849
+ const canvas = document.getElementById('categorical-nb-canvas');
2850
+ if (!canvas) return;
2851
+
2852
+ if (categoricalNBChart) {
2853
+ categoricalNBChart.destroy();
2854
+ }
2855
+
2856
+ const ctx = canvas.getContext('2d');
2857
+
2858
+ categoricalNBChart = new Chart(ctx, {
2859
+ type: 'bar',
2860
+ data: {
2861
+ labels: ['P(Yes|Rainy,Hot)', 'P(No|Rainy,Hot)'],
2862
+ datasets: [{
2863
+ label: 'Without Smoothing',
2864
+ data: [0.0833, 0],
2865
+ backgroundColor: 'rgba(255, 140, 106, 0.6)',
2866
+ borderColor: '#ff8c6a',
2867
+ borderWidth: 2
2868
+ }, {
2869
+ label: 'With Laplace Smoothing',
2870
+ data: [0.0818, 0.0266],
2871
+ backgroundColor: 'rgba(126, 240, 212, 0.6)',
2872
+ borderColor: '#7ef0d4',
2873
+ borderWidth: 2
2874
+ }, {
2875
+ label: 'Normalized Probability',
2876
+ data: [0.755, 0.245],
2877
+ backgroundColor: 'rgba(106, 169, 255, 0.8)',
2878
+ borderColor: '#6aa9ff',
2879
+ borderWidth: 2
2880
+ }]
2881
+ },
2882
+ options: {
2883
+ responsive: true,
2884
+ maintainAspectRatio: false,
2885
+ plugins: {
2886
+ title: {
2887
+ display: true,
2888
+ text: 'Categorical Naive Bayes: Probability Comparison',
2889
+ color: '#e8eef6',
2890
+ font: { size: 16, weight: 'bold' }
2891
+ },
2892
+ legend: {
2893
+ labels: { color: '#a9b4c2' }
2894
+ }
2895
+ },
2896
+ scales: {
2897
+ x: {
2898
+ grid: { color: '#2a3544' },
2899
+ ticks: { color: '#a9b4c2' }
2900
+ },
2901
+ y: {
2902
+ title: { display: true, text: 'Probability', color: '#a9b4c2' },
2903
+ grid: { color: '#2a3544' },
2904
+ ticks: { color: '#a9b4c2' },
2905
+ min: 0,
2906
+ max: 1
2907
+ }
2908
+ }
2909
+ }
2910
+ });
2911
+ }
2912
+
2913
+ function drawGaussianNB() {
2914
+ const canvas = document.getElementById('gaussian-nb-canvas');
2915
+ if (!canvas) return;
2916
+
2917
+ const ctx = canvas.getContext('2d');
2918
+ const width = canvas.width = canvas.offsetWidth;
2919
+ const height = canvas.height = 400;
2920
+
2921
+ ctx.clearRect(0, 0, width, height);
2922
+ ctx.fillStyle = '#1a2332';
2923
+ ctx.fillRect(0, 0, width, height);
2924
+
2925
+ const padding = 60;
2926
+ const chartWidth = width - 2 * padding;
2927
+ const chartHeight = height - 2 * padding;
2928
+
2929
+ const xMin = 0, xMax = 5, yMin = 0, yMax = 4;
2930
+ const scaleX = (x) => padding + (x / xMax) * chartWidth;
2931
+ const scaleY = (y) => height - padding - (y / yMax) * chartHeight;
2932
+
2933
+ // Draw decision boundary (approximate)
2934
+ ctx.strokeStyle = '#6aa9ff';
2935
+ ctx.lineWidth = 3;
2936
+ ctx.setLineDash([5, 5]);
2937
+ ctx.beginPath();
2938
+ ctx.moveTo(scaleX(2.5), scaleY(0));
2939
+ ctx.lineTo(scaleX(2.5), scaleY(4));
2940
+ ctx.stroke();
2941
+ ctx.setLineDash([]);
2942
+
2943
+ // Draw "Yes" points
2944
+ const yesPoints = [{x: 1.0, y: 2.0}, {x: 2.0, y: 1.0}, {x: 1.5, y: 1.8}];
2945
+ yesPoints.forEach(p => {
2946
+ ctx.fillStyle = '#7ef0d4';
2947
+ ctx.beginPath();
2948
+ ctx.arc(scaleX(p.x), scaleY(p.y), 8, 0, 2 * Math.PI);
2949
+ ctx.fill();
2950
+ ctx.strokeStyle = '#1a2332';
2951
+ ctx.lineWidth = 2;
2952
+ ctx.stroke();
2953
+ });
2954
+
2955
+ // Draw "No" points
2956
+ const noPoints = [{x: 3.0, y: 3.0}, {x: 3.5, y: 2.8}, {x: 2.9, y: 3.2}];
2957
+ noPoints.forEach(p => {
2958
+ ctx.fillStyle = '#ff8c6a';
2959
+ ctx.beginPath();
2960
+ ctx.arc(scaleX(p.x), scaleY(p.y), 8, 0, 2 * Math.PI);
2961
+ ctx.fill();
2962
+ ctx.strokeStyle = '#1a2332';
2963
+ ctx.lineWidth = 2;
2964
+ ctx.stroke();
2965
+ });
2966
+
2967
+ // Draw test point
2968
+ ctx.fillStyle = '#ffeb3b';
2969
+ ctx.beginPath();
2970
+ ctx.arc(scaleX(2.0), scaleY(2.0), 12, 0, 2 * Math.PI);
2971
+ ctx.fill();
2972
+ ctx.strokeStyle = '#6aa9ff';
2973
+ ctx.lineWidth = 3;
2974
+ ctx.stroke();
2975
+
2976
+ // Label test point
2977
+ ctx.fillStyle = '#e8eef6';
2978
+ ctx.font = 'bold 12px sans-serif';
2979
+ ctx.textAlign = 'center';
2980
+ ctx.fillText('Test [2.0, 2.0]', scaleX(2.0), scaleY(2.0) - 20);
2981
+ ctx.fillStyle = '#7ef0d4';
2982
+ ctx.fillText('→ YES', scaleX(2.0), scaleY(2.0) + 30);
2983
+
2984
+ // Axes
2985
+ ctx.strokeStyle = '#2a3544';
2986
+ ctx.lineWidth = 2;
2987
+ ctx.beginPath();
2988
+ ctx.moveTo(padding, padding);
2989
+ ctx.lineTo(padding, height - padding);
2990
+ ctx.lineTo(width - padding, height - padding);
2991
+ ctx.stroke();
2992
+
2993
+ // Labels
2994
+ ctx.fillStyle = '#a9b4c2';
2995
+ ctx.font = '12px sans-serif';
2996
+ ctx.textAlign = 'center';
2997
+ ctx.fillText('X₁', width / 2, height - 20);
2998
+ ctx.save();
2999
+ ctx.translate(20, height / 2);
3000
+ ctx.rotate(-Math.PI / 2);
3001
+ ctx.fillText('X₂', 0, 0);
3002
+ ctx.restore();
3003
+
3004
+ // Legend
3005
+ ctx.fillStyle = '#7ef0d4';
3006
+ ctx.beginPath();
3007
+ ctx.arc(padding + 20, 30, 6, 0, 2 * Math.PI);
3008
+ ctx.fill();
3009
+ ctx.fillStyle = '#e8eef6';
3010
+ ctx.font = '11px sans-serif';
3011
+ ctx.textAlign = 'left';
3012
+ ctx.fillText('Class: Yes', padding + 30, 35);
3013
+
3014
+ ctx.fillStyle = '#ff8c6a';
3015
+ ctx.beginPath();
3016
+ ctx.arc(padding + 120, 30, 6, 0, 2 * Math.PI);
3017
+ ctx.fill();
3018
+ ctx.fillStyle = '#e8eef6';
3019
+ ctx.fillText('Class: No', padding + 130, 35);
3020
+
3021
+ ctx.fillStyle = '#6aa9ff';
3022
+ ctx.fillText('| Decision Boundary', padding + 210, 35);
3023
+ }
3024
+
3025
  function drawSpamClassification() {
3026
  const canvas = document.getElementById('spam-classification');
3027
  if (!canvas) return;
 
3082
  ctx.font = 'bold 18px sans-serif';
3083
  ctx.fillText('→ SPAM! 📧❌', padding, y4 + 30);
3084
 
3085
+ // Create comparison chart at bottom
3086
+ if (!bayesComparisonChart) {
3087
+ const compCanvas = document.createElement('canvas');
3088
+ compCanvas.id = 'bayes-comparison-chart';
3089
+ compCanvas.style.marginTop = '20px';
3090
+ canvas.parentElement.appendChild(compCanvas);
3091
+
3092
+ bayesComparisonChart = new Chart(compCanvas.getContext('2d'), {
3093
+ type: 'bar',
3094
+ data: {
3095
+ labels: ['Spam Probability', 'Not-Spam Probability'],
3096
+ datasets: [{
3097
+ label: 'Probability',
3098
+ data: [0.1008, 0.0007],
3099
+ backgroundColor: ['#7ef0d4', '#ff8c6a'],
3100
+ borderColor: ['#7ef0d4', '#ff8c6a'],
3101
+ borderWidth: 2
3102
+ }]
3103
+ },
3104
+ options: {
3105
+ responsive: true,
3106
+ maintainAspectRatio: false,
3107
+ indexAxis: 'y',
3108
+ plugins: {
3109
+ title: {
3110
+ display: true,
3111
+ text: 'Probability Comparison',
3112
+ color: '#e8eef6',
3113
+ font: { size: 14 }
3114
+ },
3115
+ legend: { display: false }
3116
+ },
3117
+ scales: {
3118
+ x: {
3119
+ grid: { color: '#2a3544' },
3120
+ ticks: { color: '#a9b4c2' }
3121
+ },
3122
+ y: {
3123
+ grid: { display: false },
3124
+ ticks: { color: '#a9b4c2' }
3125
+ }
3126
+ }
3127
+ }
3128
+ });
3129
+ compCanvas.style.height = '150px';
3130
+ }
3131
  }
3132
 
3133
  // Topic 16: Decision Trees
 
3807
  ctx.fillText('Random Forest: Ensemble of Decision Trees', width / 2, 25);
3808
  }
3809
 
3810
+ // Topic 16: K-means Clustering
3811
+ let kmeansVizChart = null;
3812
+ let kmeansElbowChart = null;
3813
+
3814
+ function initKMeans() {
3815
+ const canvas1 = document.getElementById('kmeans-viz-canvas');
3816
+ if (canvas1 && !canvas1.dataset.initialized) {
3817
+ canvas1.dataset.initialized = 'true';
3818
+ drawKMeansVisualization();
3819
+ }
3820
+
3821
+ const canvas2 = document.getElementById('kmeans-elbow-canvas');
3822
+ if (canvas2 && !canvas2.dataset.initialized) {
3823
+ canvas2.dataset.initialized = 'true';
3824
+ drawKMeansElbow();
3825
+ }
3826
+ }
3827
+
3828
+ function drawKMeansVisualization() {
3829
+ const canvas = document.getElementById('kmeans-viz-canvas');
3830
+ if (!canvas) return;
3831
+
3832
+ const ctx = canvas.getContext('2d');
3833
+ const width = canvas.width = canvas.offsetWidth;
3834
+ const height = canvas.height = 450;
3835
+
3836
+ ctx.clearRect(0, 0, width, height);
3837
+ ctx.fillStyle = '#1a2332';
3838
+ ctx.fillRect(0, 0, width, height);
3839
+
3840
+ const padding = 60;
3841
+ const chartWidth = width - 2 * padding;
3842
+ const chartHeight = height - 2 * padding;
3843
+
3844
+ const xMin = 0, xMax = 10, yMin = 0, yMax = 12;
3845
+ const scaleX = (x) => padding + (x / xMax) * chartWidth;
3846
+ const scaleY = (y) => height - padding - (y / yMax) * chartHeight;
3847
+
3848
+ // Data points
3849
+ const points = [
3850
+ {id: 'A', x: 1, y: 2, cluster: 1},
3851
+ {id: 'B', x: 1.5, y: 1.8, cluster: 1},
3852
+ {id: 'C', x: 5, y: 8, cluster: 2},
3853
+ {id: 'D', x: 8, y: 8, cluster: 2},
3854
+ {id: 'E', x: 1, y: 0.6, cluster: 1},
3855
+ {id: 'F', x: 9, y: 11, cluster: 2}
3856
+ ];
3857
+
3858
+ // Final centroids
3859
+ const centroids = [
3860
+ {x: 1.17, y: 1.47, color: '#7ef0d4'},
3861
+ {x: 7.33, y: 9.0, color: '#ff8c6a'}
3862
+ ];
3863
+
3864
+ // Draw lines from points to centroids
3865
+ points.forEach(p => {
3866
+ const c = centroids[p.cluster - 1];
3867
+ ctx.strokeStyle = p.cluster === 1 ? 'rgba(126, 240, 212, 0.3)' : 'rgba(255, 140, 106, 0.3)';
3868
+ ctx.lineWidth = 1;
3869
+ ctx.beginPath();
3870
+ ctx.moveTo(scaleX(p.x), scaleY(p.y));
3871
+ ctx.lineTo(scaleX(c.x), scaleY(c.y));
3872
+ ctx.stroke();
3873
+ });
3874
+
3875
+ // Draw points
3876
+ points.forEach(p => {
3877
+ ctx.fillStyle = p.cluster === 1 ? '#7ef0d4' : '#ff8c6a';
3878
+ ctx.beginPath();
3879
+ ctx.arc(scaleX(p.x), scaleY(p.y), 8, 0, 2 * Math.PI);
3880
+ ctx.fill();
3881
+ ctx.strokeStyle = '#1a2332';
3882
+ ctx.lineWidth = 2;
3883
+ ctx.stroke();
3884
+
3885
+ // Label
3886
+ ctx.fillStyle = '#e8eef6';
3887
+ ctx.font = 'bold 12px sans-serif';
3888
+ ctx.textAlign = 'center';
3889
+ ctx.fillText(p.id, scaleX(p.x), scaleY(p.y) - 15);
3890
+ });
3891
+
3892
+ // Draw centroids
3893
+ centroids.forEach((c, i) => {
3894
+ ctx.fillStyle = c.color;
3895
+ ctx.beginPath();
3896
+ ctx.arc(scaleX(c.x), scaleY(c.y), 12, 0, 2 * Math.PI);
3897
+ ctx.fill();
3898
+ ctx.strokeStyle = '#e8eef6';
3899
+ ctx.lineWidth = 3;
3900
+ ctx.stroke();
3901
+
3902
+ // Draw X
3903
+ ctx.strokeStyle = '#1a2332';
3904
+ ctx.lineWidth = 2;
3905
+ ctx.beginPath();
3906
+ ctx.moveTo(scaleX(c.x) - 6, scaleY(c.y) - 6);
3907
+ ctx.lineTo(scaleX(c.x) + 6, scaleY(c.y) + 6);
3908
+ ctx.moveTo(scaleX(c.x) + 6, scaleY(c.y) - 6);
3909
+ ctx.lineTo(scaleX(c.x) - 6, scaleY(c.y) + 6);
3910
+ ctx.stroke();
3911
+
3912
+ // Label
3913
+ ctx.fillStyle = '#e8eef6';
3914
+ ctx.font = 'bold 13px sans-serif';
3915
+ ctx.textAlign = 'center';
3916
+ ctx.fillText(`c${i+1}`, scaleX(c.x), scaleY(c.y) + 25);
3917
+ });
3918
+
3919
+ // Axes
3920
+ ctx.strokeStyle = '#2a3544';
3921
+ ctx.lineWidth = 2;
3922
+ ctx.beginPath();
3923
+ ctx.moveTo(padding, padding);
3924
+ ctx.lineTo(padding, height - padding);
3925
+ ctx.lineTo(width - padding, height - padding);
3926
+ ctx.stroke();
3927
+
3928
+ // Labels
3929
+ ctx.fillStyle = '#a9b4c2';
3930
+ ctx.font = '12px sans-serif';
3931
+ ctx.textAlign = 'center';
3932
+ ctx.fillText('X', width / 2, height - 20);
3933
+ ctx.save();
3934
+ ctx.translate(20, height / 2);
3935
+ ctx.rotate(-Math.PI / 2);
3936
+ ctx.fillText('Y', 0, 0);
3937
+ ctx.restore();
3938
+
3939
+ // Title
3940
+ ctx.fillStyle = '#7ef0d4';
3941
+ ctx.font = 'bold 16px sans-serif';
3942
+ ctx.textAlign = 'center';
3943
+ ctx.fillText('K-means Clustering (K=2) - Final State', width / 2, 30);
3944
+
3945
+ // WCSS
3946
+ ctx.fillStyle = '#6aa9ff';
3947
+ ctx.font = '14px sans-serif';
3948
+ ctx.textAlign = 'left';
3949
+ ctx.fillText('WCSS = 15.984', padding, height - padding + 30);
3950
+ }
3951
+
3952
+ function drawKMeansElbow() {
3953
+ const canvas = document.getElementById('kmeans-elbow-canvas');
3954
+ if (!canvas) return;
3955
+
3956
+ if (kmeansElbowChart) {
3957
+ kmeansElbowChart.destroy();
3958
+ }
3959
+
3960
+ const ctx = canvas.getContext('2d');
3961
+
3962
+ const kValues = [1, 2, 3, 4, 5];
3963
+ const wcssValues = [50, 18, 10, 8, 7];
3964
+
3965
+ kmeansElbowChart = new Chart(ctx, {
3966
+ type: 'line',
3967
+ data: {
3968
+ labels: kValues,
3969
+ datasets: [{
3970
+ label: 'WCSS',
3971
+ data: wcssValues,
3972
+ borderColor: '#6aa9ff',
3973
+ backgroundColor: 'rgba(106, 169, 255, 0.1)',
3974
+ borderWidth: 3,
3975
+ fill: true,
3976
+ tension: 0.4,
3977
+ pointRadius: kValues.map(k => k === 3 ? 10 : 6),
3978
+ pointBackgroundColor: kValues.map(k => k === 3 ? '#7ef0d4' : '#6aa9ff'),
3979
+ pointBorderColor: kValues.map(k => k === 3 ? '#7ef0d4' : '#6aa9ff'),
3980
+ pointBorderWidth: kValues.map(k => k === 3 ? 3 : 2)
3981
+ }]
3982
+ },
3983
+ options: {
3984
+ responsive: true,
3985
+ maintainAspectRatio: false,
3986
+ plugins: {
3987
+ title: {
3988
+ display: true,
3989
+ text: 'Elbow Method: Optimal K = 3 (Elbow Point)',
3990
+ color: '#7ef0d4',
3991
+ font: { size: 16, weight: 'bold' }
3992
+ },
3993
+ legend: {
3994
+ labels: { color: '#a9b4c2' }
3995
+ },
3996
+ annotation: {
3997
+ annotations: {
3998
+ line1: {
3999
+ type: 'line',
4000
+ xMin: 3,
4001
+ xMax: 3,
4002
+ borderColor: '#7ef0d4',
4003
+ borderWidth: 2,
4004
+ borderDash: [5, 5],
4005
+ label: {
4006
+ display: true,
4007
+ content: 'Elbow!',
4008
+ position: 'start'
4009
+ }
4010
+ }
4011
+ }
4012
+ }
4013
+ },
4014
+ scales: {
4015
+ x: {
4016
+ title: { display: true, text: 'Number of Clusters (K)', color: '#a9b4c2' },
4017
+ grid: { color: '#2a3544' },
4018
+ ticks: { color: '#a9b4c2', stepSize: 1 }
4019
+ },
4020
+ y: {
4021
+ title: { display: true, text: 'Within-Cluster Sum of Squares (WCSS)', color: '#a9b4c2' },
4022
+ grid: { color: '#2a3544' },
4023
+ ticks: { color: '#a9b4c2' },
4024
+ min: 0
4025
+ }
4026
+ }
4027
+ }
4028
+ });
4029
+ }
4030
+
4031
+ // Topic 19: Algorithm Comparison
4032
+ function initAlgorithmComparison() {
4033
+ const canvas = document.getElementById('decision-flowchart');
4034
+ if (!canvas || canvas.dataset.initialized) return;
4035
+ canvas.dataset.initialized = 'true';
4036
+ drawDecisionFlowchart();
4037
+ }
4038
+
4039
+ function drawDecisionFlowchart() {
4040
+ const canvas = document.getElementById('decision-flowchart');
4041
+ if (!canvas) return;
4042
+
4043
+ const ctx = canvas.getContext('2d');
4044
+ const width = canvas.width = canvas.offsetWidth;
4045
+ const height = canvas.height = 500;
4046
+
4047
+ ctx.clearRect(0, 0, width, height);
4048
+ ctx.fillStyle = '#1a2332';
4049
+ ctx.fillRect(0, 0, width, height);
4050
+
4051
+ const nodes = [
4052
+ { x: width/2, y: 50, text: 'Start:\nWhat problem?', w: 140, h: 60, color: '#7ef0d4', type: 'start' },
4053
+ { x: width/4, y: 160, text: 'Classification', w: 120, h: 50, color: '#6aa9ff', type: 'decision' },
4054
+ { x: width/2, y: 160, text: 'Regression', w: 120, h: 50, color: '#6aa9ff', type: 'decision' },
4055
+ { x: 3*width/4, y: 160, text: 'Clustering', w: 120, h: 50, color: '#6aa9ff', type: 'decision' },
4056
+ { x: width/8, y: 270, text: 'Linear?', w: 100, h: 50, color: '#ffb490', type: 'question' },
4057
+ { x: 3*width/8, y: 270, text: 'Fast?', w: 100, h: 50, color: '#ffb490', type: 'question' },
4058
+ { x: width/2, y: 270, text: 'Linear?', w: 100, h: 50, color: '#ffb490', type: 'question' },
4059
+ { x: 3*width/4, y: 270, text: 'Known K?', w: 100, h: 50, color: '#ffb490', type: 'question' },
4060
+ { x: width/16, y: 380, text: 'Logistic\nRegression', w: 90, h: 50, color: '#7ef0d4', type: 'result' },
4061
+ { x: 3*width/16, y: 380, text: 'SVM', w: 90, h: 50, color: '#7ef0d4', type: 'result' },
4062
+ { x: 5*width/16, y: 380, text: 'Naive\nBayes', w: 90, h: 50, color: '#7ef0d4', type: 'result' },
4063
+ { x: 7*width/16, y: 380, text: 'Random\nForest', w: 90, h: 50, color: '#7ef0d4', type: 'result' },
4064
+ { x: 9*width/16, y: 380, text: 'Linear\nRegression', w: 90, h: 50, color: '#7ef0d4', type: 'result' },
4065
+ { x: 11*width/16, y: 380, text: 'XGBoost', w: 90, h: 50, color: '#7ef0d4', type: 'result' },
4066
+ { x: 13*width/16, y: 380, text: 'K-means', w: 90, h: 50, color: '#7ef0d4', type: 'result' },
4067
+ { x: 15*width/16, y: 380, text: 'DBSCAN', w: 90, h: 50, color: '#7ef0d4', type: 'result' }
4068
+ ];
4069
+
4070
+ const edges = [
4071
+ { from: 0, to: 1 }, { from: 0, to: 2 }, { from: 0, to: 3 },
4072
+ { from: 1, to: 4 }, { from: 1, to: 5 },
4073
+ { from: 2, to: 6 },
4074
+ { from: 3, to: 7 },
4075
+ { from: 4, to: 8, label: 'Yes' }, { from: 4, to: 9, label: 'No' },
4076
+ { from: 5, to: 10, label: 'Yes' }, { from: 5, to: 11, label: 'No' },
4077
+ { from: 6, to: 12, label: 'Yes' }, { from: 6, to: 13, label: 'No' },
4078
+ { from: 7, to: 14, label: 'Yes' }, { from: 7, to: 15, label: 'No' }
4079
+ ];
4080
+
4081
+ // Draw edges
4082
+ ctx.strokeStyle = '#6aa9ff';
4083
+ ctx.lineWidth = 2;
4084
+ edges.forEach(edge => {
4085
+ const from = nodes[edge.from];
4086
+ const to = nodes[edge.to];
4087
+
4088
+ ctx.beginPath();
4089
+ ctx.moveTo(from.x, from.y + from.h/2);
4090
+ ctx.lineTo(to.x, to.y - to.h/2);
4091
+ ctx.stroke();
4092
+
4093
+ if (edge.label) {
4094
+ ctx.fillStyle = '#7ef0d4';
4095
+ ctx.font = '10px sans-serif';
4096
+ ctx.textAlign = 'center';
4097
+ const midX = (from.x + to.x) / 2;
4098
+ const midY = (from.y + to.y) / 2;
4099
+ ctx.fillText(edge.label, midX + 12, midY);
4100
+ }
4101
+ });
4102
+
4103
+ // Draw nodes
4104
+ nodes.forEach(node => {
4105
+ const x = node.x - node.w/2;
4106
+ const y = node.y - node.h/2;
4107
+
4108
+ ctx.fillStyle = node.color + '33';
4109
+ ctx.fillRect(x, y, node.w, node.h);
4110
+ ctx.strokeStyle = node.color;
4111
+ ctx.lineWidth = 2;
4112
+ ctx.strokeRect(x, y, node.w, node.h);
4113
+
4114
+ ctx.fillStyle = '#e8eef6';
4115
+ ctx.font = node.type === 'result' ? 'bold 11px sans-serif' : '11px sans-serif';
4116
+ ctx.textAlign = 'center';
4117
+ const lines = node.text.split('\n');
4118
+ lines.forEach((line, i) => {
4119
+ ctx.fillText(line, node.x, node.y - (lines.length - 1) * 6 + i * 12);
4120
+ });
4121
+ });
4122
+
4123
+ // Title
4124
+ ctx.fillStyle = '#7ef0d4';
4125
+ ctx.font = 'bold 16px sans-serif';
4126
+ ctx.textAlign = 'center';
4127
+ ctx.fillText('Algorithm Selection Flowchart', width/2, 25);
4128
+ }
4129
+
4130
  // Handle window resize
4131
  let resizeTimer;
4132
  window.addEventListener('resize', () => {
 
4154
  drawSVMTraining();
4155
  drawSVMKernel();
4156
  // New topics
4157
+ if (document.getElementById('elbow-canvas')) drawElbowCurve();
4158
+ if (document.getElementById('cv-k-canvas')) drawCVKHeatmap();
4159
+ if (document.getElementById('gridsearch-heatmap')) drawGridSearchHeatmap();
4160
+ if (document.getElementById('param-surface')) drawParamSurface();
4161
+ if (document.getElementById('bayes-theorem-viz')) drawBayesTheorem();
4162
+ if (document.getElementById('spam-classification')) drawSpamClassification();
4163
+ if (document.getElementById('decision-tree-viz')) drawDecisionTree();
4164
+ if (document.getElementById('entropy-viz')) drawEntropyViz();
4165
+ if (document.getElementById('split-comparison')) drawSplitComparison();
4166
+ if (document.getElementById('tree-boundary')) drawTreeBoundary();
4167
+ if (document.getElementById('bagging-viz')) drawBaggingViz();
4168
+ if (document.getElementById('boosting-viz')) drawBoostingViz();
4169
+ if (document.getElementById('random-forest-viz')) drawRandomForestViz();
4170
+ if (document.getElementById('categorical-nb-canvas')) drawCategoricalNB();
4171
+ if (document.getElementById('gaussian-nb-canvas')) drawGaussianNB();
4172
+ if (document.getElementById('kmeans-viz-canvas')) drawKMeansVisualization();
4173
+ if (document.getElementById('kmeans-elbow-canvas')) drawKMeansElbow();
4174
+ if (document.getElementById('decision-flowchart')) drawDecisionFlowchart();
4175
  }, 250);
4176
  });
ml_complete-all-topics/index.html CHANGED
@@ -4,6 +4,7 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Machine Learning: Complete Educational Guide</title>
 
7
  <style>
8
  @font-face {
9
  font-family: 'FKGroteskNeue';
@@ -499,8 +500,10 @@ canvas {
499
  <a href="#optimal-k" class="toc-link">13. Finding Optimal K in KNN</a>
500
  <a href="#hyperparameter-tuning" class="toc-link">14. Hyperparameter Tuning</a>
501
  <a href="#naive-bayes" class="toc-link">15. Naive Bayes</a>
502
- <a href="#decision-trees" class="toc-link">16. Decision Trees</a>
503
- <a href="#ensemble-methods" class="toc-link">17. Ensemble Methods</a>
 
 
504
  </nav>
505
  </aside>
506
 
@@ -616,8 +619,8 @@ canvas {
616
  <p>We can find a line (y = 7.5x + 32) that predicts: Someone with 7 years experience will earn approximately $84.5k.</p>
617
 
618
  <div class="figure">
619
- <div class="figure-placeholder" style="height: 400px">
620
- <canvas id="lr-canvas"></canvas>
621
  </div>
622
  <p class="figure-caption"><strong>Figure 1:</strong> Scatter plot showing experience vs. salary with the best fit line</p>
623
  </div>
@@ -710,8 +713,8 @@ canvas {
710
  </ul>
711
 
712
  <div class="figure">
713
- <div class="figure-placeholder" style="height: 400px">
714
- <canvas id="gd-canvas"></canvas>
715
  </div>
716
  <p class="figure-caption"><strong>Figure 2:</strong> Loss surface showing gradient descent path to minimum</p>
717
  </div>
@@ -2402,8 +2405,8 @@ Actual Pos TP FN
2402
  <p>Test different K values and plot performance. Look for the "elbow" where adding more neighbors doesn't help much.</p>
2403
 
2404
  <div class="figure">
2405
- <div class="figure-placeholder" style="height: 400px">
2406
- <canvas id="elbow-canvas"></canvas>
2407
  </div>
2408
  <p class="figure-caption"><strong>Figure 1:</strong> Elbow curve showing optimal K at the bend</p>
2409
  </div>
@@ -2550,14 +2553,14 @@ Actual Pos TP FN
2550
  </div>
2551
  </div>
2552
 
2553
- <!-- Section 15: Naive Bayes -->
2554
  <div class="section" id="naive-bayes">
2555
  <div class="section-header">
2556
  <h2>15. Naive Bayes Classification</h2>
2557
  <button class="section-toggle">▼</button>
2558
  </div>
2559
  <div class="section-body">
2560
- <p>Naive Bayes is a probabilistic classifier based on Bayes' Theorem. Despite its "naive" independence assumption, it works surprisingly well for text classification and other tasks!</p>
2561
 
2562
  <div class="info-card">
2563
  <div class="info-card-title">Key Concepts</div>
@@ -2668,9 +2671,271 @@ Actual Pos TP FN
2668
  </tbody>
2669
  </table>
2670
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2671
  <div class="callout success">
2672
  <div class="callout-title">✅ When to Use Naive Bayes</div>
2673
  <div class="callout-content">
 
 
 
2674
  <strong>Perfect for:</strong><br>
2675
  • Text classification (spam detection, sentiment analysis)<br>
2676
  • Document categorization<br>
@@ -2687,7 +2952,187 @@ Actual Pos TP FN
2687
  </div>
2688
  </div>
2689
 
2690
- <!-- Section 16: Decision Trees -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2691
  <div class="section" id="decision-trees">
2692
  <div class="section-header">
2693
  <h2>16. Decision Trees</h2>
@@ -2845,10 +3290,10 @@ Actual Pos TP FN
2845
  </div>
2846
  </div>
2847
 
2848
- <!-- Section 17: Ensemble Methods -->
2849
  <div class="section" id="ensemble-methods">
2850
  <div class="section-header">
2851
- <h2>17. Ensemble Methods</h2>
2852
  <button class="section-toggle">▼</button>
2853
  </div>
2854
  <div class="section-body">
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Machine Learning: Complete Educational Guide</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
8
  <style>
9
  @font-face {
10
  font-family: 'FKGroteskNeue';
 
500
  <a href="#optimal-k" class="toc-link">13. Finding Optimal K in KNN</a>
501
  <a href="#hyperparameter-tuning" class="toc-link">14. Hyperparameter Tuning</a>
502
  <a href="#naive-bayes" class="toc-link">15. Naive Bayes</a>
503
+ <a href="#kmeans" class="toc-link">16. K-means Clustering</a>
504
+ <a href="#decision-trees" class="toc-link">17. Decision Trees</a>
505
+ <a href="#ensemble-methods" class="toc-link">18. Ensemble Methods</a>
506
+ <a href="#algorithm-comparison" class="toc-link">19. Algorithm Comparison</a>
507
  </nav>
508
  </aside>
509
 
 
619
  <p>We can find a line (y = 7.5x + 32) that predicts: Someone with 7 years experience will earn approximately $84.5k.</p>
620
 
621
  <div class="figure">
622
+ <div class="figure-placeholder" style="height: 400px; position: relative;">
623
+ <canvas id="lr-canvas" style="width: 100%; height: 100%;"></canvas>
624
  </div>
625
  <p class="figure-caption"><strong>Figure 1:</strong> Scatter plot showing experience vs. salary with the best fit line</p>
626
  </div>
 
713
  </ul>
714
 
715
  <div class="figure">
716
+ <div class="figure-placeholder" style="height: 400px; position: relative;">
717
+ <canvas id="gd-canvas" style="width: 100%; height: 100%;"></canvas>
718
  </div>
719
  <p class="figure-caption"><strong>Figure 2:</strong> Loss surface showing gradient descent path to minimum</p>
720
  </div>
 
2405
  <p>Test different K values and plot performance. Look for the "elbow" where adding more neighbors doesn't help much.</p>
2406
 
2407
  <div class="figure">
2408
+ <div class="figure-placeholder" style="height: 400px; position: relative;">
2409
+ <canvas id="elbow-canvas" style="width: 100%; height: 100%;"></canvas>
2410
  </div>
2411
  <p class="figure-caption"><strong>Figure 1:</strong> Elbow curve showing optimal K at the bend</p>
2412
  </div>
 
2553
  </div>
2554
  </div>
2555
 
2556
+ <!-- Section 15: Naive Bayes (COMPREHENSIVE WITH MATH) -->
2557
  <div class="section" id="naive-bayes">
2558
  <div class="section-header">
2559
  <h2>15. Naive Bayes Classification</h2>
2560
  <button class="section-toggle">▼</button>
2561
  </div>
2562
  <div class="section-body">
2563
+ <p>Naive Bayes is a probabilistic classifier based on Bayes' Theorem. Despite its "naive" independence assumption, it works surprisingly well for text classification and other tasks! We'll cover both Categorical and Gaussian Naive Bayes with complete mathematical solutions.</p>
2564
 
2565
  <div class="info-card">
2566
  <div class="info-card-title">Key Concepts</div>
 
2671
  </tbody>
2672
  </table>
2673
 
2674
+ <h3>🎯 PART A: Categorical Naive Bayes (Step-by-Step from PDF)</h3>
2675
+
2676
+ <h4>Dataset: Tennis Play Prediction</h4>
2677
+ <table class="data-table">
2678
+ <thead>
2679
+ <tr><th>Outlook</th><th>Temperature</th><th>Play</th></tr>
2680
+ </thead>
2681
+ <tbody>
2682
+ <tr><td>Sunny</td><td>Hot</td><td>No</td></tr>
2683
+ <tr><td>Sunny</td><td>Mild</td><td>No</td></tr>
2684
+ <tr><td>Cloudy</td><td>Hot</td><td>Yes</td></tr>
2685
+ <tr><td>Rainy</td><td>Mild</td><td>Yes</td></tr>
2686
+ <tr><td>Rainy</td><td>Cool</td><td>Yes</td></tr>
2687
+ <tr><td>Cloudy</td><td>Cool</td><td>Yes</td></tr>
2688
+ </tbody>
2689
+ </table>
2690
+
2691
+ <p><strong>Problem:</strong> Predict whether to play tennis when Outlook=Rainy and Temperature=Hot</p>
2692
+
2693
+ <div class="step">
2694
+ <div class="step-title">STEP 1: Calculate Prior Probabilities</div>
2695
+ <div class="step-calculation">
2696
+ Count occurrences in training data:<br>
2697
+ • Play=Yes appears 4 times out of 6 total<br>
2698
+ • Play=No appears 2 times out of 6 total<br>
2699
+ <br>
2700
+ <strong>Calculation:</strong><br>
2701
+ P(Yes) = 4/6 = <strong>0.667 (66.7%)</strong><br>
2702
+ P(No) = 2/6 = <strong>0.333 (33.3%)</strong>
2703
+ </div>
2704
+ </div>
2705
+
2706
+ <div class="step">
2707
+ <div class="step-title">STEP 2: Calculate Conditional Probabilities (Before Smoothing)</div>
2708
+ <div class="step-calculation">
2709
+ <strong>For Outlook = "Rainy":</strong><br>
2710
+ • Count (Rainy AND Yes) = 2 examples<br>
2711
+ • Count (Yes) = 4 total<br>
2712
+ • P(Rainy|Yes) = 2/4 = <strong>0.5</strong><br>
2713
+ <br>
2714
+ • Count (Rainy AND No) = 0 examples ❌<br>
2715
+ • Count (No) = 2 total<br>
2716
+ • P(Rainy|No) = 0/2 = <strong>0</strong> ⚠️ <span style="color: #ff8c6a;">ZERO PROBABILITY PROBLEM!</span><br>
2717
+ <br>
2718
+ <strong>For Temperature = "Hot":</strong><br>
2719
+ • P(Hot|Yes) = 1/4 = <strong>0.25</strong><br>
2720
+ • P(Hot|No) = 1/2 = <strong>0.5</strong>
2721
+ </div>
2722
+ </div>
2723
+
2724
+ <div class="formula">
2725
+ <strong>Step 3: Apply Bayes' Theorem (Initial)</strong><br>
2726
+ <br>
2727
+ P(Yes|Rainy,Hot) = P(Yes) × P(Rainy|Yes) × P(Hot|Yes)<br>
2728
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.667 × 0.5 × 0.25<br>
2729
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.0833<br>
2730
+ <br>
2731
+ P(No|Rainy,Hot) = P(No) × P(Rainy|No) × P(Hot|No)<br>
2732
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.333 × 0 × 0.5<br>
2733
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0 ❌ Problem!
2734
+ </div>
2735
+
2736
+ <div class="callout warning">
2737
+ <div class="callout-title">⚠️ Zero Probability Problem</div>
2738
+ <div class="callout-content">
2739
+ When P(Rainy|No) = 0, the entire probability becomes 0! This is unrealistic - just because we haven't seen "Rainy" with "No" in our training data doesn't mean it's impossible. We need <strong>Laplace Smoothing</strong>!
2740
+ </div>
2741
+ </div>
2742
+
2743
+ <div class="step">
2744
+ <div class="step-title">STEP 4: Apply Laplace Smoothing (α = 1)</div>
2745
+ <div class="step-calculation">
2746
+ <strong>Smoothed formula:</strong><br>
2747
+ P(x|c) = (count(x,c) + α) / (count(c) + α × num_categories)<br>
2748
+ <br>
2749
+ <strong>For Outlook</strong> (3 categories: Sunny, Cloudy, Rainy):<br>
2750
+ P(Rainy|Yes) = (2 + 1) / (4 + 1×3)<br>
2751
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 3/7<br>
2752
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= <strong>0.429</strong> ✓<br>
2753
+ <br>
2754
+ P(Rainy|No) = (0 + 1) / (2 + 1×3)<br>
2755
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 1/5<br>
2756
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= <strong>0.2</strong> ✓ <span style="color: #7ef0d4;">Fixed the zero!</span><br>
2757
+ <br>
2758
+ <strong>For Temperature</strong> (3 categories: Hot, Mild, Cool):<br>
2759
+ P(Hot|Yes) = (1 + 1) / (4 + 1×3) = 2/7 = <strong>0.286</strong><br>
2760
+ P(Hot|No) = (1 + 1) / (2 + 1×3) = 2/5 = <strong>0.4</strong>
2761
+ </div>
2762
+ </div>
2763
+
2764
+ <div class="step">
2765
+ <div class="step-title">STEP 5: Recalculate with Smoothing</div>
2766
+ <div class="step-calculation">
2767
+ <strong>P(Yes|Rainy,Hot):</strong><br>
2768
+ = P(Yes) × P(Rainy|Yes) × P(Hot|Yes)<br>
2769
+ = 0.667 × 0.429 × 0.286<br>
2770
+ = <strong>0.0818</strong><br>
2771
+ <br>
2772
+ <strong>P(No|Rainy,Hot):</strong><br>
2773
+ = P(No) × P(Rainy|No) × P(Hot|No)<br>
2774
+ = 0.333 × 0.2 × 0.4<br>
2775
+ = <strong>0.0266</strong>
2776
+ </div>
2777
+ </div>
2778
+
2779
+ <div class="step">
2780
+ <div class="step-title">STEP 6: Normalize to Get Final Probabilities</div>
2781
+ <div class="step-calculation">
2782
+ <strong>Sum of probabilities:</strong><br>
2783
+ Sum = 0.0818 + 0.0266 = <strong>0.1084</strong><br>
2784
+ <br>
2785
+ <strong>Normalize:</strong><br>
2786
+ P(Yes|Rainy,Hot) = 0.0818 / 0.1084<br>
2787
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= <strong style="color: #7ef0d4;">0.755 (75.5%)</strong><br>
2788
+ <br>
2789
+ P(No|Rainy,Hot) = 0.0266 / 0.1084<br>
2790
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= <strong style="color: #ff8c6a;">0.245 (24.5%)</strong><br>
2791
+ <br>
2792
+ <div style="background: rgba(126, 240, 212, 0.2); padding: 16px; border-radius: 8px; margin-top: 12px;">
2793
+ <strong style="color: #7ef0d4; font-size: 20px;">✅ FINAL PREDICTION: YES (Play Tennis!)</strong><br>
2794
+ <span style="color: #a9b4c2; font-size: 14px;">Confidence: 75.5%</span>
2795
+ </div>
2796
+ </div>
2797
+ </div>
2798
+
2799
+ <div class="figure">
2800
+ <div class="figure-placeholder" style="height: 400px">
2801
+ <canvas id="categorical-nb-canvas"></canvas>
2802
+ </div>
2803
+ <p class="figure-caption"><strong>Figure:</strong> Categorical Naive Bayes calculation visualization</p>
2804
+ </div>
2805
+
2806
+ <h3>🎯 PART B: Gaussian Naive Bayes (Step-by-Step from PDF)</h3>
2807
+
2808
+ <h4>Dataset: 2D Classification</h4>
2809
+ <table class="data-table">
2810
+ <thead>
2811
+ <tr><th>ID</th><th>X₁</th><th>X₂</th><th>Class</th></tr>
2812
+ </thead>
2813
+ <tbody>
2814
+ <tr><td>A</td><td>1.0</td><td>2.0</td><td>Yes</td></tr>
2815
+ <tr><td>B</td><td>2.0</td><td>1.0</td><td>Yes</td></tr>
2816
+ <tr><td>C</td><td>1.5</td><td>1.8</td><td>Yes</td></tr>
2817
+ <tr><td>D</td><td>3.0</td><td>3.0</td><td>No</td></tr>
2818
+ <tr><td>E</td><td>3.5</td><td>2.8</td><td>No</td></tr>
2819
+ <tr><td>F</td><td>2.9</td><td>3.2</td><td>No</td></tr>
2820
+ </tbody>
2821
+ </table>
2822
+
2823
+ <p><strong>Problem:</strong> Classify test point [X₁=2.0, X₂=2.0]</p>
2824
+
2825
+ <div class="step">
2826
+ <div class="step-title">STEP 1: Calculate Mean and Variance for Each Class</div>
2827
+ <div class="step-calculation">
2828
+ <strong>Class "Yes" (samples A, B, C):</strong><br>
2829
+ X₁ values: [1.0, 2.0, 1.5]<br>
2830
+ μ₁(Yes) = (1.0 + 2.0 + 1.5) / 3 = <strong>1.5</strong><br>
2831
+ σ₁²(Yes) = [(1-1.5)² + (2-1.5)² + (1.5-1.5)²] / 3<br>
2832
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= [0.25 + 0.25 + 0] / 3<br>
2833
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= <strong>0.166</strong><br>
2834
+ <br>
2835
+ X₂ values: [2.0, 1.0, 1.8]<br>
2836
+ μ₂(Yes) = (2.0 + 1.0 + 1.8) / 3 = <strong>1.6</strong><br>
2837
+ σ₂²(Yes) = [(2-1.6)² + (1-1.6)² + (1.8-1.6)²] / 3<br>
2838
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= [0.16 + 0.36 + 0.04] / 3<br>
2839
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= <strong>0.186</strong><br>
2840
+ <br>
2841
+ <strong>Class "No" (samples D, E, F):</strong><br>
2842
+ X₁ values: [3.0, 3.5, 2.9]<br>
2843
+ μ₁(No) = (3.0 + 3.5 + 2.9) / 3 = <strong>3.133</strong><br>
2844
+ σ₁²(No) = <strong>0.0688</strong><br>
2845
+ <br>
2846
+ X₂ values: [3.0, 2.8, 3.2]<br>
2847
+ μ₂(No) = (3.0 + 2.8 + 3.2) / 3 = <strong>3.0</strong><br>
2848
+ σ₂²(No) = <strong>0.0266</strong>
2849
+ </div>
2850
+ </div>
2851
+
2852
+ <div class="formula">
2853
+ <strong>Step 2: Gaussian Probability Density Function</strong><br>
2854
+ <br>
2855
+ P(x|μ,σ²) = (1/√(2πσ²)) × exp(-(x-μ)²/(2σ²))<br>
2856
+ <br>
2857
+ This gives us the probability density at point x given mean μ and variance σ²
2858
+ </div>
2859
+
2860
+ <div class="step">
2861
+ <div class="step-title">STEP 3: Calculate P(X₁=2.0 | Class) using Gaussian PDF</div>
2862
+ <div class="step-calculation">
2863
+ <strong>For Class "Yes" (μ=1.5, σ²=0.166):</strong><br>
2864
+ P(2.0|Yes) = (1/√(2π × 0.166)) × exp(-(2.0-1.5)²/(2 × 0.166))<br>
2865
+ <br>
2866
+ Step-by-step:<br>
2867
+ • Normalization: 1/√(2π × 0.166) = 1/√1.043 = 1/1.021 = <strong>0.9772</strong><br>
2868
+ • Exponent: -(2.0-1.5)²/(2 × 0.166) = -(0.5)²/0.332 = -0.25/0.332 = <strong>-0.753</strong><br>
2869
+ • e^(-0.753) = <strong>0.471</strong><br>
2870
+ • Final: 0.9772 × 0.471 = <strong style="color: #7ef0d4;">0.460</strong><br>
2871
+ <br>
2872
+ <strong>For Class "No" (μ=3.133, σ²=0.0688):</strong><br>
2873
+ P(2.0|No) = (1/√(2π × 0.0688)) × exp(-(2.0-3.133)²/(2 × 0.0688))<br>
2874
+ <br>
2875
+ Step-by-step:<br>
2876
+ • Normalization: 1/√(2π × 0.0688) = <strong>1.523</strong><br>
2877
+ • Exponent: -(2.0-3.133)²/(2 × 0.0688) = -(-1.133)²/0.1376 = -1.283/0.1376 = <strong>-9.333</strong><br>
2878
+ • e^(-9.333) = <strong>0.000088</strong><br>
2879
+ • Final: 1.523 × 0.000088 = <strong style="color: #ff8c6a;">0.000134</strong><br>
2880
+ <br>
2881
+ <span style="color: #7ef0d4;">• Point (2.0, ?) is MUCH more likely to be "Yes"!</span>
2882
+ </div>
2883
+ </div>
2884
+
2885
+ <div class="formula">
2886
+ <strong>Step 4: Calculate P(X₂=2.0 | Class)</strong><br>
2887
+ <br>
2888
+ <strong>For "Yes":</strong><br>
2889
+ P(2.0|Yes) = (1/√(2π×0.186)) × exp(-(2.0-1.6)²/(2×0.186))<br>
2890
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.923 × exp(-0.430)<br>
2891
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.923 × 0.651<br>
2892
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.601<br>
2893
+ <br>
2894
+ <strong>For "No":</strong><br>
2895
+ P(2.0|No) = (1/√(2π×0.0266)) × exp(-(2.0-3.0)²/(2×0.0266))<br>
2896
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 2.449 × exp(-18.797)<br>
2897
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 2.449 × 0.0000000614<br>
2898
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.00000015
2899
+ </div>
2900
+
2901
+ <div class="formula">
2902
+ <strong>Step 5: Combine with Prior (assume equal priors)</strong><br>
2903
+ <br>
2904
+ P(Yes) = P(No) = 0.5<br>
2905
+ <br>
2906
+ P(Yes|X) ∝ P(Yes) × P(X₁=2.0|Yes) × P(X₂=2.0|Yes)<br>
2907
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.5 × 0.460 × 0.601<br>
2908
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.138<br>
2909
+ <br>
2910
+ P(No|X) ∝ P(No) × P(X₁=2.0|No) × P(X₂=2.0|No)<br>
2911
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.5 × 0.000134 × 0.00000015<br>
2912
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.00000000001
2913
+ </div>
2914
+
2915
+ <div class="formula">
2916
+ <strong>Step 6: Normalize</strong><br>
2917
+ <br>
2918
+ Sum = 0.138 + 0.00000000001 ≈ 0.138<br>
2919
+ <br>
2920
+ P(Yes|X) = 0.138 / 0.138 ≈ 1.0 (99.99%)<br>
2921
+ P(No|X) ≈ 0.0 (0.01%)<br>
2922
+ <br>
2923
+ <strong style="color: #7ef0d4; font-size: 18px;">Prediction: YES ✅</strong>
2924
+ </div>
2925
+
2926
+ <div class="figure">
2927
+ <div class="figure-placeholder" style="height: 400px">
2928
+ <canvas id="gaussian-nb-canvas"></canvas>
2929
+ </div>
2930
+ <p class="figure-caption"><strong>Figure:</strong> Gaussian Naive Bayes with decision boundary</p>
2931
+ </div>
2932
+
2933
  <div class="callout success">
2934
  <div class="callout-title">✅ When to Use Naive Bayes</div>
2935
  <div class="callout-content">
2936
+ <strong>Categorical NB:</strong> Discrete features (text, categories)<br>
2937
+ <strong>Gaussian NB:</strong> Continuous features (measurements, coordinates)<br>
2938
+ <br>
2939
  <strong>Perfect for:</strong><br>
2940
  • Text classification (spam detection, sentiment analysis)<br>
2941
  • Document categorization<br>
 
2952
  </div>
2953
  </div>
2954
 
2955
+ <!-- Section 16: K-means Clustering -->
2956
+ <div class="section" id="kmeans">
2957
+ <div class="section-header">
2958
+ <h2>16. K-means Clustering</h2>
2959
+ <button class="section-toggle">▼</button>
2960
+ </div>
2961
+ <div class="section-body">
2962
+ <p>K-means is an unsupervised learning algorithm that groups data into K clusters. Each cluster has a centroid (center point), and points are assigned to the nearest centroid. Perfect for customer segmentation, image compression, and pattern discovery!</p>
2963
+
2964
+ <div class="info-card">
2965
+ <div class="info-card-title">Key Concepts</div>
2966
+ <ul class="info-card-list">
2967
+ <li>Unsupervised: No labels needed!</li>
2968
+ <li>K = number of clusters (you choose)</li>
2969
+ <li>Minimizes Within-Cluster Sum of Squares (WCSS)</li>
2970
+ <li>Iterative: Updates centroids until convergence</li>
2971
+ </ul>
2972
+ </div>
2973
+
2974
+ <h3>🎯 Step-by-Step K-means Algorithm (from PDF)</h3>
2975
+
2976
+ <h4>Dataset: 6 Points in 2D Space</h4>
2977
+ <table class="data-table">
2978
+ <thead>
2979
+ <tr><th>Point</th><th>X</th><th>Y</th></tr>
2980
+ </thead>
2981
+ <tbody>
2982
+ <tr><td>A</td><td>1</td><td>2</td></tr>
2983
+ <tr><td>B</td><td>1.5</td><td>1.8</td></tr>
2984
+ <tr><td>C</td><td>5</td><td>8</td></tr>
2985
+ <tr><td>D</td><td>8</td><td>8</td></tr>
2986
+ <tr><td>E</td><td>1</td><td>0.6</td></tr>
2987
+ <tr><td>F</td><td>9</td><td>11</td></tr>
2988
+ </tbody>
2989
+ </table>
2990
+
2991
+ <p><strong>Goal:</strong> Group into K=2 clusters</p>
2992
+ <p><strong>Initial Centroids:</strong> c₁ = [3, 4], c₂ = [5, 1]</p>
2993
+
2994
+ <div class="formula">
2995
+ <strong>Distance Formula (Euclidean):</strong><br>
2996
+ d(point, centroid) = √[(x₁-x₂)² + (y₁-y₂)²]
2997
+ </div>
2998
+
2999
+ <h4>Iteration 1</h4>
3000
+
3001
+ <div class="formula">
3002
+ <strong>Step 1: Calculate Distances to All Centroids</strong><br>
3003
+ <br>
3004
+ <strong>Point A (1, 2):</strong><br>
3005
+ d(A, c₁) = √[(1-3)² + (2-4)²] = √[4+4] = √8 = 2.83<br>
3006
+ d(A, c₂) = √[(1-5)² + (2-1)²] = √[16+1] = √17 = 4.12<br>
3007
+ → Assign to c₁ (closer)<br>
3008
+ <br>
3009
+ <strong>Point B (1.5, 1.8):</strong><br>
3010
+ d(B, c₁) = √[(1.5-3)² + (1.8-4)²] = √[2.25+4.84] = 2.66<br>
3011
+ d(B, c₂) = √[(1.5-5)² + (1.8-1)²] = √[12.25+0.64] = 3.59<br>
3012
+ → Assign to c₁<br>
3013
+ <br>
3014
+ <strong>Point C (5, 8):</strong><br>
3015
+ d(C, c₁) = √[(5-3)² + (8-4)²] = √[4+16] = 4.47<br>
3016
+ d(C, c₂) = √[(5-5)² + (8-1)²] = √[0+49] = 7.0<br>
3017
+ → Assign to c₁<br>
3018
+ <br>
3019
+ <strong>Point D (8, 8):</strong><br>
3020
+ d(D, c₁) = √[(8-3)² + (8-4)²] = √[25+16] = 6.40<br>
3021
+ d(D, c₂) = √[(8-5)² + (8-1)²] = √[9+49] = 7.62<br>
3022
+ → Assign to c₁<br>
3023
+ <br>
3024
+ <strong>Point E (1, 0.6):</strong><br>
3025
+ d(E, c₁) = √[(1-3)² + (0.6-4)²] = √[4+11.56] = 3.94<br>
3026
+ d(E, c₂) = √[(1-5)² + (0.6-1)²] = √[16+0.16] = 4.02<br>
3027
+ → Assign to c₁<br>
3028
+ <br>
3029
+ <strong>Point F (9, 11):</strong><br>
3030
+ d(F, c₁) = √[(9-3)² + (11-4)²] = √[36+49] = 9.22<br>
3031
+ d(F, c₂) = √[(9-5)² + (11-1)²] = √[16+100] = 10.77<br>
3032
+ → Assign to c₁<br>
3033
+ <br>
3034
+ <strong>Result:</strong> Cluster 1 = {A, B, C, D, E, F}, Cluster 2 = {}
3035
+ </div>
3036
+
3037
+ <div class="callout warning">
3038
+ <div class="callout-title">⚠️ Poor Initial Centroids!</div>
3039
+ <div class="callout-content">
3040
+ All points assigned to c₁! This happens with bad initialization. Let's try better initial centroids for the algorithm to work properly.
3041
+ </div>
3042
+ </div>
3043
+
3044
+ <p><strong>Better Initial Centroids:</strong> c₁ = [1, 1], c₂ = [8, 9]</p>
3045
+
3046
+ <div class="formula">
3047
+ <strong>Iteration 1 (Revised):</strong><br>
3048
+ <br>
3049
+ Cluster 1: {A, B, E} → c₁_new = mean = [(1+1.5+1)/3, (2+1.8+0.6)/3] = [1.17, 1.47]<br>
3050
+ Cluster 2: {C, D, F} → c₂_new = mean = [(5+8+9)/3, (8+8+11)/3] = [7.33, 9.00]<br>
3051
+ <br>
3052
+ <strong>WCSS Calculation:</strong><br>
3053
+ WCSS₁ = d²(A,c₁) + d²(B,c₁) + d²(E,c₁)<br>
3054
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= (1-1.17)²+(2-1.47)² + (1.5-1.17)²+(1.8-1.47)² + (1-1.17)²+(0.6-1.47)²<br>
3055
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 0.311 + 0.218 + 0.786 = 1.315<br>
3056
+ <br>
3057
+ WCSS₂ = d²(C,c₂) + d²(D,c₂) + d²(F,c₂)<br>
3058
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= (5-7.33)²+(8-9)² + (8-7.33)²+(8-9)² + (9-7.33)²+(11-9)²<br>
3059
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;= 6.433 + 1.447 + 6.789 = 14.669<br>
3060
+ <br>
3061
+ <strong>Total WCSS = 1.315 + 14.669 = 15.984</strong>
3062
+ </div>
3063
+
3064
+ <div class="formula">
3065
+ <strong>Iteration 2:</strong><br>
3066
+ <br>
3067
+ Using c₁ = [1.17, 1.47] and c₂ = [7.33, 9.00], recalculate distances...<br>
3068
+ <br>
3069
+ Result: Same assignments! Centroids don't change.<br>
3070
+ <strong>✓ Converged!</strong>
3071
+ </div>
3072
+
3073
+ <div class="figure">
3074
+ <div class="figure-placeholder" style="height: 450px">
3075
+ <canvas id="kmeans-viz-canvas"></canvas>
3076
+ </div>
3077
+ <p class="figure-caption"><strong>Figure:</strong> K-means clustering visualization with centroid movement</p>
3078
+ </div>
3079
+
3080
+ <h3>Finding Optimal K: The Elbow Method</h3>
3081
+
3082
+ <p>How do we choose K? Try different values and plot WCSS!</p>
3083
+
3084
+ <div class="formula">
3085
+ <strong>WCSS for Different K Values:</strong><br>
3086
+ <br>
3087
+ K=1: WCSS = 50.0 (all in one cluster)<br>
3088
+ K=2: WCSS = 18.0<br>
3089
+ K=3: WCSS = 10.0 ← Elbow point!<br>
3090
+ K=4: WCSS = 8.0<br>
3091
+ K=5: WCSS = 7.0<br>
3092
+ <br>
3093
+ <strong>Rule:</strong> Choose K at the "elbow" where WCSS stops decreasing rapidly
3094
+ </div>
3095
+
3096
+ <div class="figure">
3097
+ <div class="figure-placeholder" style="height: 400px">
3098
+ <canvas id="kmeans-elbow-canvas"></canvas>
3099
+ </div>
3100
+ <p class="figure-caption"><strong>Figure:</strong> Elbow method - optimal K is where the curve bends</p>
3101
+ </div>
3102
+
3103
+ <div class="callout info">
3104
+ <div class="callout-title">💡 K-means Tips</div>
3105
+ <div class="callout-content">
3106
+ <strong>Advantages:</strong><br>
3107
+ ✓ Simple and fast<br>
3108
+ ✓ Works well with spherical clusters<br>
3109
+ ✓ Scales to large datasets<br>
3110
+ <br>
3111
+ <strong>Disadvantages:</strong><br>
3112
+ ✗ Need to specify K in advance<br>
3113
+ ✗ Sensitive to initial centroids (use K-means++!)<br>
3114
+ ✗ Assumes spherical clusters<br>
3115
+ ✗ Sensitive to outliers<br>
3116
+ <br>
3117
+ <strong>Solutions:</strong><br>
3118
+ • Use elbow method for K<br>
3119
+ • Use K-means++ initialization<br>
3120
+ • Run multiple times with different initializations
3121
+ </div>
3122
+ </div>
3123
+
3124
+ <h3>Real-World Applications</h3>
3125
+ <ul>
3126
+ <li><strong>Customer Segmentation:</strong> Group customers by behavior</li>
3127
+ <li><strong>Image Compression:</strong> Reduce colors in images</li>
3128
+ <li><strong>Document Clustering:</strong> Group similar articles</li>
3129
+ <li><strong>Anomaly Detection:</strong> Points far from centroids are outliers</li>
3130
+ <li><strong>Feature Learning:</strong> Learn representations for neural networks</li>
3131
+ </ul>
3132
+ </div>
3133
+ </div>
3134
+
3135
+ <!-- Section 17: Decision Trees -->
3136
  <div class="section" id="decision-trees">
3137
  <div class="section-header">
3138
  <h2>16. Decision Trees</h2>
 
3290
  </div>
3291
  </div>
3292
 
3293
+ <!-- Section 18: Ensemble Methods -->
3294
  <div class="section" id="ensemble-methods">
3295
  <div class="section-header">
3296
+ <h2>18. Ensemble Methods</h2>
3297
  <button class="section-toggle">▼</button>
3298
  </div>
3299
  <div class="section-body">