-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtopic_ml_framework.html
More file actions
646 lines (596 loc) · 48.5 KB
/
Copy pathtopic_ml_framework.html
File metadata and controls
646 lines (596 loc) · 48.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The 6-Step ML Framework — Interactive Lesson</title>
<!-- ── Shared site chrome (global top bar, search, theme, glossary) ── -->
<link rel="stylesheet" href="site-polish.css">
<!-- ── The Lens-Switcher design system ── -->
<link rel="stylesheet" href="lens-template.css">
<!-- ── Pyodide code-runner (used by the Code lens metric mini-lab) ── -->
<link rel="stylesheet" href="code-runner.css">
<script src="catalog.js"></script>
<script src="theme.js" defer></script>
<script src="search.js" defer></script>
<script src="nav.js" defer></script>
<script src="glossary.js" defer></script>
<!-- The page config is set inline below (window.LESSON_DATA); lens-template.js renders it. -->
<script src="lens-template.js" defer></script>
<!-- code-runner.js MUST come AFTER lens-template.js: the lens engine injects every
lens's html (including the .code-runner block) into the DOM at boot, then
code-runner.js's DOMContentLoaded init() finds and wires it. -->
<script src="code-runner.js" defer></script>
<!-- ── Lesson-local styling for the two bespoke interactives this workflow lesson
adds on top of the shared template: the project-selector "walk a project"
panel, and the three-way data-split slider. Scoped under .lens-lesson so they
ride the template palette and never touch the shared chrome. Ported so the
interactives look + behave identically to the source lesson. ── -->
<style>
/* ── framework-step cards (the 6 steps) ── */
.lens-lesson .fw-steps { display: flex; flex-direction: column; gap: 12px; margin: 8px 0 4px; }
.lens-lesson .fw-step {
background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--accent);
border-radius: var(--radius-sm); padding: 15px 18px;
}
.lens-lesson .fw-step .fs-head { display: flex; align-items: center; gap: 11px; margin-bottom: 8px; }
.lens-lesson .fw-step .fs-num {
width: 27px; height: 27px; border-radius: 50%; background: var(--accent); color: #fff;
font-weight: 800; font-size: 13px; display: grid; place-items: center; flex-shrink: 0;
}
.lens-lesson .fw-step .fs-title { font-size: 15px; font-weight: 700; color: var(--accent-2); }
.lens-lesson .fw-step .fs-q { font-size: 12.5px; color: var(--muted); font-style: italic; }
.lens-lesson .fw-step p { color: var(--text); font-size: 14.5px; line-height: 1.65; margin: 0 0 8px; }
.lens-lesson .fw-step p:last-child { margin-bottom: 0; }
.lens-lesson .fs-tags { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 9px; }
.lens-lesson .fs-tag {
font-size: 11px; font-weight: 700; background: var(--surface-2); border: 1px solid var(--border);
border-radius: 999px; padding: 4px 11px; color: var(--muted);
}
/* ── project-selector "walk a project" ── */
.lens-lesson .walk-wrap { display: grid; grid-template-columns: 290px 1fr; gap: 16px; margin: 4px 0 6px; }
@media (max-width: 760px) { .lens-lesson .walk-wrap { grid-template-columns: 1fr; } }
.lens-lesson .walk-side { display: flex; flex-direction: column; gap: 10px; }
.lens-lesson .proj-btn {
background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--radius-sm);
padding: 12px 14px; cursor: pointer; text-align: left; color: var(--text); font: inherit; width: 100%;
transition: border-color .15s var(--ease), background .15s var(--ease), transform .12s var(--ease);
}
.lens-lesson .proj-btn:hover { border-color: var(--muted); background: #232b3a; }
.lens-lesson .proj-btn:active { transform: scale(.99); }
.lens-lesson .proj-btn.active { border-color: var(--accent); background: rgba(124,131,255,0.08); }
.lens-lesson .proj-btn .pb-title { font-size: 13px; font-weight: 700; color: var(--accent-2); margin-bottom: 3px; }
.lens-lesson .proj-btn .pb-sub { font-size: 12px; color: var(--muted); line-height: 1.45; }
.lens-lesson .walk-main { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.lens-lesson .walk-title {
background: var(--surface-2); border-bottom: 1px solid var(--border); padding: 11px 16px;
font-size: 12px; font-weight: 700; color: var(--accent-2);
display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
}
.lens-lesson .walk-body { padding: 6px 16px 8px; }
.lens-lesson .walk-row {
display: grid; grid-template-columns: 26px 120px 1fr; gap: 11px; align-items: start;
padding: 10px 0; border-bottom: 1px solid var(--border);
}
.lens-lesson .walk-row:last-child { border-bottom: none; }
.lens-lesson .wr-num {
width: 23px; height: 23px; border-radius: 50%; background: var(--surface-2); border: 1px solid var(--border);
color: var(--muted); font-size: 11px; font-weight: 800; display: grid; place-items: center;
}
.lens-lesson .wr-label { font-size: 12px; font-weight: 700; color: var(--accent); }
.lens-lesson .wr-val { font-size: 13.5px; color: var(--text); line-height: 1.55; }
@media (max-width: 600px) {
.lens-lesson .walk-row { grid-template-columns: 22px 1fr; }
.lens-lesson .walk-row .wr-label { grid-column: 2; }
.lens-lesson .walk-row .wr-val { grid-column: 2; }
}
.lens-lesson .ptag {
display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 10px; font-weight: 700;
text-transform: uppercase; letter-spacing: .05em;
}
.lens-lesson .ptag-clf { background: rgba(124,131,255,0.18); color: var(--accent); }
.lens-lesson .ptag-reg { background: rgba(57,211,83,0.18); color: var(--green); }
.lens-lesson .ptag-dl { background: rgba(240,136,62,0.18); color: var(--orange); }
/* ── three-way data-split slider ── */
.lens-lesson .split-controls { display: flex; flex-direction: column; gap: 13px; }
.lens-lesson .slider-row { display: flex; align-items: center; gap: 12px; }
.lens-lesson .slider-row label { font-size: 12px; font-weight: 700; color: var(--text); min-width: 150px; }
.lens-lesson .slider-row input[type="range"] { flex: 1; accent-color: var(--accent); min-width: 80px; }
.lens-lesson .slider-row .slider-val { font-size: 12px; color: var(--accent-2); min-width: 46px; text-align: right; font-weight: 700; }
.lens-lesson #split-bar { display: flex; height: 40px; border-radius: var(--radius-sm); overflow: hidden; border: 1px solid var(--border); margin: 4px 0; }
.lens-lesson .split-seg {
display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; color: #0d1117;
transition: width .25s var(--ease); min-width: 0; overflow: hidden; white-space: nowrap;
}
.lens-lesson .seg-train { background: var(--accent); }
.lens-lesson .seg-val { background: var(--yellow); }
.lens-lesson .seg-test { background: var(--green); }
.lens-lesson #split-readout { font-size: 13.5px; color: var(--text); line-height: 1.6; }
/* compare-table (reused look for the framework's tables) */
.lens-lesson .cmp { width: 100%; border-collapse: collapse; font-size: 14px; margin: 14px 0; max-width: none; }
.lens-lesson .cmp th {
background: var(--surface-2); border: 1px solid var(--border); padding: 9px 13px; text-align: left;
font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--muted);
}
.lens-lesson .cmp td { border: 1px solid var(--border); padding: 10px 13px; color: var(--text); line-height: 1.5; vertical-align: top; }
.lens-lesson .cmp tr:hover td { background: rgba(255,255,255,0.02); }
.lens-lesson .cmp .rowhead { font-weight: 700; color: #fff; }
.lens-lesson .cmp code { font-family: var(--font-mono); font-size: .88em; background: rgba(124,131,255,.12); color: #d7d9ff; padding: 1.5px 6px; border-radius: 5px; }
/* prereq rows */
.lens-lesson .prereq { display: flex; flex-direction: column; gap: 9px; margin: 6px 0; }
.lens-lesson .prereq-row { display: grid; grid-template-columns: 180px 1fr; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
@media (max-width: 600px) { .lens-lesson .prereq-row { grid-template-columns: 1fr; } .lens-lesson .prereq-row .pc { border-right: none; border-bottom: 1px solid var(--border); } }
.lens-lesson .prereq-row .pc { background: var(--surface-2); padding: 11px 14px; font-weight: 700; font-size: 12.5px; color: var(--accent); border-right: 1px solid var(--border); display: flex; align-items: center; }
.lens-lesson .prereq-row .pw { padding: 11px 14px; font-size: 13.5px; color: var(--text); line-height: 1.55; display: flex; align-items: center; }
.lens-lesson .runner-note { color: var(--muted); font-size: 13px; line-height: 1.6; margin: 8px 0 10px; }
.lens-lesson .runner-note code { color: var(--accent-2); font-family: var(--font-mono); }
</style>
</head>
<body>
<!--
This page is a THIN lesson: a config + content object (window.LESSON_DATA)
consumed by lens-template.js, which renders the entire Lens-Switcher UI.
The bespoke code is the Visual lens (two interactives — the "walk a project"
selector and the three-way data-split slider), attached via the Visual lens's
mount(root, ctx). The Code lens carries a Pyodide .code-runner (precision/recall
threshold demo). Everything else is declarative.
This is an APPLIED-ML lesson (the 6-step workflow): thinner internal theory,
more practical. There is no Math lens — the only quantitative content (the
metric tradeoff, the split arithmetic) lives in the Visual and Code lenses,
where it belongs.
-->
<script>
window.LESSON_DATA = {
meta: {
docTitle: "The 6-Step ML Framework",
title: "The 6-Step ML Framework",
titleHtml: "The 6-Step ML Framework, <span class=\"grad\">through any lens</span>",
kicker: "One workflow · five ways to see it",
subtitle: "How do you actually <em>approach</em> a machine-learning project — start to finish? Pick the lens that fits how you want to learn it right now, and switch any time. The same applied workflow, re-framed for your brain.",
prev: { href: "topic_ml_paradigms.html", title: "ML Paradigms" },
next: { href: "topic_data_tools.html", title: "Data Tools" }
},
lenses: [
/* ═══════════════ INTUITION ═══════════════ */
{
id: "intuition", icon: "🧠", name: "Intuition", sub: "what & why",
qword: "intuition", tag: "Intuition lens",
heading: "What the framework is, and the six questions",
lead: "Most ML projects fail not because the model was wrong, but because the problem was framed wrong, the data was leaky, or success was never defined. A framework forces you to answer those questions before you train anything. It's a way of thinking, not a recipe.",
html: `
<div class="prose">
<div class="callout note">
<span class="lbl">🎯 The one-sentence version</span>
The 6-step framework is a reusable <b>way of breaking any ML problem into smaller, answerable questions</b> — define the problem, survey the data, agree on what "success" means, build features, model, then experiment — in roughly the order you should ask them. Modelling, the part beginners obsess over, is just one of the six.
</div>
<h3 class="block-h">Before this — a few terms that recur</h3>
<div class="prereq">
<div class="prereq-row"><div class="pc">Model</div><div class="pw">A function with adjustable parameters that maps an input to an output. "Training" means tuning those parameters from data so the outputs are useful. You don't need to know <em>which</em> model yet — that's a later step.</div></div>
<div class="prereq-row"><div class="pc">Features & target</div><div class="pw">The <b>features</b> are the input columns you learn from (age, blood pressure, year built). The <b>target</b> is the column you want to predict (has-disease, sale price, dog breed). Almost every supervised project is "features in, target out."</div></div>
<div class="prereq-row"><div class="pc">Structured vs unstructured</div><div class="pw">Structured data lives in rows and columns (a spreadsheet, a database). Unstructured data does not — images, audio, free text. The distinction drives which tools and models you reach for.</div></div>
<div class="prereq-row"><div class="pc">Generalization</div><div class="pw">A model is only useful if it performs well on data it has <em>never seen</em>. Memorizing the training examples is worthless. The framework's later steps exist almost entirely to protect and measure generalization.</div></div>
</div>
<div class="card full">
<h4><span class="n">1</span>A framework, not a recipe</h4>
<p>There is no single sequence of commands that solves every ML problem. What transfers between projects is a <b>way of breaking the problem into smaller, answerable questions</b>. The 6 steps are those questions, in roughly the order you should ask them.</p>
<p>The big shift in mindset: machine learning is <b>experimental</b>. You will rarely get it right on the first try. The framework's job is to make each experiment cheap, honest, and reproducible so you can iterate quickly without fooling yourself.</p>
<div class="callout analogy">
<span class="lbl">🔖 Analogy — building before measuring</span>
Imagine a contractor who builds a house before checking whether the land can support it, what the client actually wants, or how they'll know the house is "good." That's an ML project with no framework. The 6 steps are the contractor's checklist: define the job, survey the materials, agree on what "done well" means, choose the right tools, build, then inspect and revise.
</div>
</div>
<div class="card full">
<h4><span class="n">2</span>The three things every project balances</h4>
<p>Across all six steps, three forces are in constant tension. Good ML practice is managing them, not eliminating them.</p>
<table class="cmp">
<tr><th>Force</th><th>What it means</th><th>Which step handles it</th></tr>
<tr><td class="rowhead">The problem</td><td>What business question are we really answering?</td><td>Step 1 (Problem definition)</td></tr>
<tr><td class="rowhead">The data</td><td>What do we have, and is it honest about the future?</td><td>Steps 2 & 4 (Data, Features)</td></tr>
<tr><td class="rowhead">The measure</td><td>How will we know if we succeeded?</td><td>Steps 3 & 6 (Evaluation, Experimentation)</td></tr>
</table>
<div class="callout good">
<span class="lbl">✓ Key takeaway</span>
Modelling (step 5) — the part beginners obsess over — is just one of six steps, and often the easiest. The leverage is in framing, data quality, and honest evaluation.
</div>
</div>
<h3 class="block-h">The 6 steps — each step is a question</h3>
<p style="color:var(--text)">Answer them roughly in order, but expect to loop back — especially between modelling and experimentation.</p>
<div class="fw-steps">
<div class="fw-step">
<div class="fs-head"><div class="fs-num">1</div><div><div class="fs-title">Problem Definition</div><div class="fs-q">"What problem are we trying to solve?"</div></div></div>
<p>Rephrase a business problem as a machine-learning problem. The first fork: is it <b>supervised</b> (you have labelled examples) or <b>unsupervised</b> (no labels, find structure)? If supervised, is it:</p>
<div class="fs-tags">
<span class="fs-tag">Classification — predict a category</span>
<span class="fs-tag">Regression — predict a number</span>
<span class="fs-tag">Recommendation — suggest items</span>
</div>
<p style="margin-top:8px;">Also ask whether ML is even the right tool. If a simple rule or lookup table solves it, use that. ML earns its complexity only when patterns are too rich for hand-written rules.</p>
</div>
<div class="fw-step" style="border-left-color:var(--accent-2);">
<div class="fs-head"><div class="fs-num" style="background:var(--accent-2);color:#0d1117;">2</div><div><div class="fs-title">Data</div><div class="fs-q">"What data do we have?"</div></div></div>
<p>ML extracts insight from data, so the data you have constrains everything. Characterize it on two axes:</p>
<div class="fs-tags">
<span class="fs-tag">Structured (rows & columns) vs Unstructured (images, audio, text)</span>
<span class="fs-tag">Static (a fixed export) vs Streaming (constantly updating)</span>
</div>
<p style="margin-top:8px;">Structured data → classical ML (trees, gradient boosting). Unstructured data → deep learning. This single classification quietly decides most of step 5.</p>
</div>
<div class="fw-step" style="border-left-color:var(--yellow);">
<div class="fs-head"><div class="fs-num" style="background:var(--yellow);color:#0d1117;">3</div><div><div class="fs-title">Evaluation</div><div class="fs-q">"What defines success?"</div></div></div>
<p>Decide the metric and target <em>before</em> modelling, so you can't move the goalposts later. "95% accurate" is meaningless until you know the cost of each kind of error.</p>
<div class="fs-tags">
<span class="fs-tag">Classification → accuracy, precision, recall, F1, ROC/AUC</span>
<span class="fs-tag">Regression → MAE, RMSE, R²</span>
<span class="fs-tag">Recommendation → precision@k</span>
</div>
<p style="margin-top:8px;">A medical screen cares about <b>recall</b> (don't miss a sick patient); a spam filter cares about <b>precision</b> (don't junk real mail). The metric encodes the real-world cost.</p>
</div>
<div class="fw-step" style="border-left-color:var(--green);">
<div class="fs-head"><div class="fs-num" style="background:var(--green);color:#0d1117;">4</div><div><div class="fs-title">Features</div><div class="fs-q">"What about the data can we use?"</div></div></div>
<p>Features are the parts of the data the model actually learns from. Types:</p>
<div class="fs-tags">
<span class="fs-tag">Categorical (sex, region)</span>
<span class="fs-tag">Numerical / continuous (age, price)</span>
<span class="fs-tag">Derived (engineered from existing columns)</span>
</div>
<p style="margin-top:8px;">Prefer features with high coverage (few missing values), consult domain experts, and keep training features <em>consistent</em> with what you'll have in production.</p>
<div class="callout warn" style="margin-top:10px;">
<span class="lbl">⚠ Feature leakage</span>
The deadliest, sneakiest bug in applied ML. If a feature secretly encodes the answer (e.g. "date account was closed" when predicting churn), your model looks brilliant offline and fails completely in production. Step 4 is where you hunt for it.
</div>
</div>
<div class="fw-step" style="border-left-color:var(--orange);">
<div class="fs-head"><div class="fs-num" style="background:var(--orange);color:#0d1117;">5</div><div><div class="fs-title">Modelling</div><div class="fs-q">"Which model should we choose?"</div></div></div>
<p>Pick a model based on the data, not on hype. The pragmatic order:</p>
<div class="fs-tags">
<span class="fs-tag">Start simple (linear / logistic regression)</span>
<span class="fs-tag">Then ensembles (Random Forest, XGBoost, CatBoost)</span>
<span class="fs-tag">Deep learning for unstructured data</span>
<span class="fs-tag">Transfer learning to reuse pretrained models</span>
</div>
<p style="margin-top:8px;">Weigh interpretability, how much data the model needs, and training/prediction time. A simple model you can debug often beats a complex one you can't.</p>
</div>
<div class="fw-step" style="border-left-color:var(--violet);">
<div class="fs-head"><div class="fs-num" style="background:var(--violet);color:#0d1117;">6</div><div><div class="fs-title">Experimentation</div><div class="fs-q">"What else could we try? Does it work in production?"</div></div></div>
<p>ML is a loop, not a line. Split your data, train, measure against the step-3 metric, change one thing, repeat. Diagnose by <em>where</em> performance is poor:</p>
<div class="fs-tags">
<span class="fs-tag">Poor on training → underfitting: bigger model / better data</span>
<span class="fs-tag">Poor on test → overfitting: simplify / more data</span>
<span class="fs-tag">Poor in production → revisit the problem & data</span>
</div>
<p style="margin-top:8px;">Document every change so results are reproducible. The data split that makes this honest is shown in the <b style="color:var(--accent-2)">👁️ Visual</b> lens.</p>
</div>
</div>
<h3 class="block-h">Where this comes from</h3>
<div class="callout note">
<span class="lbl">📚 Origin</span>
This is the practical, applied progression taught in Daniel Bourke's <em>Zero to Mastery: Machine Learning & Data Science</em> course. Many lessons in this suite are strong on the <em>theory</em> of how models work internally; this track adds the missing layer — <em>how you actually run a project end to end</em>. The framework is deliberately tool-agnostic: it tells you what to think about, not which library to import.
</div>
<p style="margin-top:22px;color:var(--muted);font-size:14px;">Want to see it move? Tap <b style="color:var(--accent-2)">👁️ Visual</b> to walk a real project through the six steps and play with the data split, or <b style="color:var(--accent-2)">💻 Code</b> for the metric tradeoff behind Step 3, runnable in your browser.</p>
</div>`
},
/* ═══════════════ VISUAL ═══════════════ */
{
id: "visual", icon: "👁️", name: "Visual", sub: "watch it",
qword: "visual walk-through", tag: "Visual lens",
heading: "Walk a project, then split the data",
lead: "Two interactives. First, pick one of the three milestone projects and watch the same six questions get completely different answers. Then drag the sliders to see how the three-way train / validation / test split changes — the single most important habit in applied ML.",
html: `
<h3 class="block-h">Walk a real project through the 6 steps</h3>
<p class="prose" style="color:var(--text);margin-bottom:14px;">Pick a project. See how the <em>same six questions</em> get completely different answers depending on the problem and the data. This is the framework's whole point — one process, any project.</p>
<div class="walk-wrap">
<div class="walk-side">
<button type="button" class="proj-btn active" data-proj="heart">
<div class="pb-title">❤ Heart Disease</div>
<div class="pb-sub">Predict whether a patient has heart disease from clinical measurements.</div>
</button>
<button type="button" class="proj-btn" data-proj="bulldozer">
<div class="pb-title">🚜 Bulldozer Price</div>
<div class="pb-sub">Predict the auction sale price of used heavy machinery.</div>
</button>
<button type="button" class="proj-btn" data-proj="dog">
<div class="pb-title">🐕 Dog Vision</div>
<div class="pb-sub">Identify the breed of a dog from a photograph.</div>
</button>
<div class="callout note" style="margin:2px 0 0;font-size:12.5px;">
These are the three end-to-end milestone projects from the Zero-to-Mastery course. Each is also walked in full detail in the <a href="topic_ml_projects.html">Milestone Projects</a> lesson.
</div>
</div>
<div class="walk-main">
<div class="walk-title">
<span id="walk-proj-name">Heart Disease Classification</span>
<span id="walk-proj-type"><span class="ptag ptag-clf">Classification</span></span>
</div>
<div class="walk-body">
<div class="walk-row"><div class="wr-num">1</div><div class="wr-label">Problem</div><div class="wr-val" id="w-problem">—</div></div>
<div class="walk-row"><div class="wr-num">2</div><div class="wr-label">Data</div><div class="wr-val" id="w-data">—</div></div>
<div class="walk-row"><div class="wr-num">3</div><div class="wr-label">Evaluation</div><div class="wr-val" id="w-eval">—</div></div>
<div class="walk-row"><div class="wr-num">4</div><div class="wr-label">Features</div><div class="wr-val" id="w-feat">—</div></div>
<div class="walk-row"><div class="wr-num">5</div><div class="wr-label">Modelling</div><div class="wr-val" id="w-model">—</div></div>
<div class="walk-row"><div class="wr-num">6</div><div class="wr-label">Experimentation</div><div class="wr-val" id="w-exp">—</div></div>
</div>
</div>
</div>
<div class="callout note" style="max-width:none">
<span class="lbl">👁 Notice the pattern</span>
Step 2 ("what data?") silently determines Step 5 ("which model?"). Heart disease and bulldozer prices are <em>structured</em> → classical ML and gradient boosting. Dog photos are <em>unstructured</em> → deep learning and transfer learning. You rarely "choose" the model freely; the data chooses it for you.
</div>
<h3 class="block-h">The three-way data split</h3>
<p class="prose" style="color:var(--text);margin-bottom:14px;">The single most important habit in applied ML: split your data into <b style="color:var(--accent)">training</b>, <b style="color:var(--yellow)">validation</b>, and <b style="color:var(--green)">test</b> sets — and never let the model learn from the test set. Drag the sliders to see how the split changes, and what each set is for.</p>
<div class="card full">
<div class="split-controls">
<div class="slider-row">
<label for="sl-train">Training set</label>
<input type="range" id="sl-train" min="40" max="90" value="70" step="5" aria-label="Training set percentage">
<span class="slider-val" id="val-train">70%</span>
</div>
<div class="slider-row">
<label for="sl-val">Validation set</label>
<input type="range" id="sl-val" min="5" max="40" value="15" step="5" aria-label="Validation set percentage">
<span class="slider-val" id="val-val">15%</span>
</div>
<div style="font-size:12px;color:var(--muted);">Test set takes whatever remains. The three always sum to 100%.</div>
<div id="split-bar">
<div class="split-seg seg-train" id="seg-train" style="width:70%;">Train</div>
<div class="split-seg seg-val" id="seg-val" style="width:15%;">Val</div>
<div class="split-seg seg-test" id="seg-test" style="width:15%;">Test</div>
</div>
<div id="split-readout"></div>
</div>
</div>
<h3 class="block-h">What each set is for</h3>
<table class="cmp">
<tr><th>Set</th><th>Typical size</th><th>The model is allowed to…</th><th>Used for</th></tr>
<tr><td class="rowhead" style="color:var(--accent);">Training</td><td>70–80%</td><td>Learn from it (update weights)</td><td>Fitting the model's parameters</td></tr>
<tr><td class="rowhead" style="color:var(--yellow);">Validation</td><td>10–15%</td><td>Be measured on it, repeatedly</td><td>Tuning hyperparameters, model selection, early stopping</td></tr>
<tr><td class="rowhead" style="color:var(--green);">Test</td><td>10–15%</td><td>Be measured on it <b>once</b>, at the very end</td><td>The honest final estimate of real-world performance</td></tr>
</table>
<div class="callout warn" style="max-width:none">
<span class="lbl">⚠ The golden rule</span>
If you tune anything based on the test set, it stops being a fair estimate — you've leaked information and your reported number is optimistic. The test set is opened once, like a sealed exam. This is exactly the overfitting story from the <a href="generalization_train_test.html">Generalization lesson</a>.
</div>`,
/* ── bespoke interactives: project-selector + data-split slider ── */
mount: function (root, ctx) {
/* ───── project walkthrough data ───── */
var PROJECTS = {
heart: {
name: 'Heart Disease Classification',
typeHtml: '<span class="ptag ptag-clf">Classification</span>',
problem: 'Binary classification — given a patient’s clinical measurements, predict whether they have heart disease (1) or not (0). A supervised problem with labelled patient records.',
data: 'Structured & static — a single CSV of ~300 patient rows from the UCI / Cleveland dataset. Rows and columns, fixed export. Structured + static points straight at classical ML.',
eval: 'The brief: reach 95% accuracy at predicting disease before deploying. But for a medical screen, recall matters most (a missed diagnosis is far costlier than a false alarm), so precision, recall and ROC/AUC are tracked too.',
feat: 'Mostly numerical (age, resting blood pressure, cholesterol, max heart rate) with a few categorical (sex, chest-pain type, fasting blood sugar). Small, clean, well-understood — little engineering needed.',
model: 'Start with Logistic Regression as a baseline, then compare K-Nearest-Neighbours and a Random Forest. Tune the best with cross-validated grid search. All interpretable enough for a clinical setting.',
exp: 'Train/test split, fit each model, compare on the chosen metrics, then GridSearchCV on the winner. Inspect a confusion matrix and ROC curve, and use feature importance to sanity-check against medical knowledge.'
},
bulldozer: {
name: 'Bulldozer Price Prediction',
typeHtml: '<span class="ptag ptag-reg">Regression</span>',
problem: 'Regression — predict the sale price (a continuous number) of a used bulldozer at auction from its configuration and sale details. Supervised, with historical sale prices as labels.',
data: 'Structured but large & time-ordered — the Kaggle "Blue Book for Bulldozers" set, ~400k sale records with many columns, including a sale date. Time ordering changes how you must split it.',
eval: 'The competition metric is RMSLE — Root Mean Squared Log Error — which penalizes proportional error, so being off by $5k on a $10k machine is worse than on a $100k one. Defined up front; never changed mid-project.',
feat: 'Heavy feature engineering: parse the sale date into year / month / day-of-week, handle dozens of categorical columns, and fill many missing values (adding "is-missing" flag columns so the model knows).',
model: 'A Random Forest Regressor is the workhorse for messy structured data — robust and needs little scaling (though in scikit-learn you still encode categoricals and impute missing values first). Tune n_estimators, max_depth, and friends.',
exp: 'Crucial twist: split by TIME, not randomly — validate on the most recent sales to mimic predicting future prices. A random split would leak future information and badly overstate performance.'
},
dog: {
name: 'Dog Vision (Deep Learning)',
typeHtml: '<span class="ptag ptag-dl">Deep Learning</span>',
problem: 'Multi-class image classification — given a photo of a dog, predict its breed out of 120 possible breeds. Supervised, with each training image labelled by breed.',
data: 'Unstructured & static — ~10,000+ JPEG images (the Kaggle dog-breed set). Pixels, not columns. Unstructured data is the signal to leave classical ML behind and reach for deep learning.',
eval: 'Multi-class log loss (the competition metric) plus top-1 accuracy for human-readable progress. Chosen before training so model comparisons stay honest.',
feat: 'You do NOT hand-engineer features from pixels — the network learns them. "Feature work" here is preprocessing: resize images to a fixed size, normalize pixel values, and batch them into tensors.',
model: 'Transfer learning — take a CNN (e.g. an EfficientNet / MobileNet) pretrained on ImageNet, freeze its learned visual features, and train a new classification head for the 120 breeds. Built in TensorFlow/Keras.',
exp: 'Train on a subset first for fast iteration, watch training vs validation loss for overfitting, add data augmentation and callbacks (early stopping, checkpoints), then scale to the full dataset and submit.'
}
};
function selectProject(key) {
if (!PROJECTS[key]) return;
var p = PROJECTS[key];
root.querySelectorAll('[data-proj]').forEach(function (b) {
b.classList.toggle('active', b.getAttribute('data-proj') === key);
});
root.querySelector('#walk-proj-name').textContent = p.name;
root.querySelector('#walk-proj-type').innerHTML = p.typeHtml;
root.querySelector('#w-problem').textContent = p.problem;
root.querySelector('#w-data').textContent = p.data;
root.querySelector('#w-eval').textContent = p.eval;
root.querySelector('#w-feat').textContent = p.feat;
root.querySelector('#w-model').textContent = p.model;
root.querySelector('#w-exp').textContent = p.exp;
}
root.querySelectorAll('[data-proj]').forEach(function (b) {
b.addEventListener('click', function () { selectProject(b.getAttribute('data-proj')); });
});
/* ───── data-split slider ───── */
function updateSplit() {
var slTrain = root.querySelector('#sl-train');
var slVal = root.querySelector('#sl-val');
var train = parseInt(slTrain.value, 10);
var val = parseInt(slVal.value, 10);
// clamp so train + val never exceeds 95 (leave >=5 for test)
if (train + val > 95) {
val = 95 - train;
if (val < 5) { val = 5; train = 90; slTrain.value = 90; }
slVal.value = val;
}
var test = 100 - train - val;
root.querySelector('#val-train').textContent = train + '%';
root.querySelector('#val-val').textContent = val + '%';
root.querySelector('#seg-train').style.width = train + '%';
root.querySelector('#seg-val').style.width = val + '%';
var segTest = root.querySelector('#seg-test');
segTest.style.width = test + '%';
segTest.textContent = test >= 8 ? 'Test' : '';
var rows = 1000;
root.querySelector('#split-readout').innerHTML =
'On a ' + rows + '-row dataset: <b style="color:var(--accent)">' + Math.round(rows * train / 100) + ' training rows</b> (learn from), ' +
'<b style="color:var(--yellow)">' + Math.round(rows * val / 100) + ' validation rows</b> (tune on), ' +
'<b style="color:var(--green)">' + Math.round(rows * test / 100) + ' test rows</b> (judge once). ' +
(test < 10 ? '<span style="color:var(--red)">⚠ A test set this small gives a noisy final estimate.</span>' :
train < 55 ? '<span style="color:var(--orange)">⚠ A small training set can underfit — the model has less to learn from.</span>' :
'<span style="color:var(--green)">✓ A balanced, healthy split.</span>');
}
root.querySelector('#sl-train').addEventListener('input', updateSplit);
root.querySelector('#sl-val').addEventListener('input', updateSplit);
/* initial render */
selectProject('heart');
updateSplit();
}
},
/* ═══════════════ CODE ═══════════════ */
{
id: "code", icon: "💻", name: "Code", sub: "run it",
qword: "code", tag: "Code lens",
heading: "The metric tradeoff behind Step 3, runnable",
lead: "Step 3 says: pick the metric before you model. Here is exactly why the metric matters — a live precision/recall demo you can run and edit in your browser. Move the decision threshold and watch the tradeoff between catching positives (recall) and being right when you do (precision).",
html: `
<p class="runner-note">This runs real Python (NumPy) in your browser via Pyodide — the runtime loads only when you press <b>Run</b>. Edit the thresholds and re-run to feel the tradeoff yourself.</p>
<div class="code-runner" data-runtime="pyodide" data-packages="numpy" data-title="Run the metric tradeoff behind Step 3">
<script type="text/plain" class="code-runner-source">
import numpy as np
# True label: 1 means "needs follow-up"; model score is estimated risk.
y_true = np.array([1, 0, 1, 1, 0, 0, 1, 0, 0, 1])
risk = np.array([0.91, 0.72, 0.68, 0.63, 0.55, 0.44, 0.41, 0.36, 0.22, 0.18])
def metrics_at(threshold):
pred = risk >= threshold
tp = int(np.sum((pred == 1) & (y_true == 1)))
fp = int(np.sum((pred == 1) & (y_true == 0)))
fn = int(np.sum((pred == 0) & (y_true == 1)))
precision = tp / max(tp + fp, 1)
recall = tp / max(tp + fn, 1)
return tp, fp, fn, precision, recall
print("threshold | TP FP FN | precision recall")
for threshold in [0.30, 0.50, 0.70]:
tp, fp, fn, precision, recall = metrics_at(threshold)
print(f"{threshold:>8.2f} | {tp:>2} {fp:>2} {fn:>2} | {precision:>8.2f} {recall:>6.2f}")
print("\\nLower threshold catches more positives; higher threshold makes positive predictions stricter.")
<\/script>
</div>
<div class="prose">
<div class="callout note">
<span class="lbl">💡 Reading it</span>
A medical screen lowers the threshold to maximize <b>recall</b> (catch every sick patient, even at the cost of false alarms). A spam filter raises it to maximize <b>precision</b> (never junk a real email). Same model, same scores — the <em>metric you optimize</em> changes the decision. That choice is Step 3, and you make it before you ever fit a model.
</div>
<h3 class="block-h">Cross-validation — when data is scarce</h3>
<div class="card full">
<p>Holding out a fixed validation set wastes data. <b>K-fold cross-validation</b> splits the training data into K parts, trains K times (each time validating on a different part), and averages the scores. You get a more stable estimate and use every example for both training and validation — at K× the compute cost. scikit-learn's <code>cross_val_score</code> does this in one line; it's covered in the <a href="topic_sklearn_workflow.html">scikit-learn workflow</a> lesson.</p>
</div>
<div class="callout good">
<span class="lbl">✓ In practice</span>
Frameworks do the bookkeeping for you: <code>train_test_split</code> makes the sealed three-way split, scoring functions (<code>precision_score</code>, <code>recall_score</code>, <code>roc_auc_score</code>) compute exactly what's above, and <code>GridSearchCV</code> ties cross-validation to hyperparameter search. The mental model in this lens is what they run under the hood.
</div>
<p style="color:var(--muted);font-size:14px;">Want this drawn instead of run? The <b style="color:var(--accent-2)">👁️ Visual</b> lens shows the three-way split that keeps these metrics honest.</p>
</div>`
},
/* ═══════════════ PRACTICE ═══════════════ */
{
id: "practice", icon: "🎯", name: "Practice", sub: "test me",
qword: "practice", tag: "Practice lens",
heading: "Quick self-check",
lead: "Three questions on the parts of the framework that trip people up most. Pick an answer to reveal whether it's right and why. No score is kept until you've tried them — this is for you, not a grade.",
quiz: [
{
q: "Your churn model scores 99% accuracy offline but is useless in production. A feature was \"date the account was closed.\" What went wrong, and which step prevents it?",
opts: [
"The model was too simple — use deep learning (Step 5)",
"Feature leakage: a feature encoded the answer — Step 4 hunts for it",
"The test set was too large — shrink it (Step 6)",
"The metric was wrong — switch to RMSE (Step 3)"
],
correct: 1,
why: "<b>Feature leakage.</b> \"Date account was closed\" is only known <em>after</em> churn has happened, so it secretly encodes the target. The model looks brilliant offline and collapses in production because that column won't exist at prediction time. Hunting for leakage is the job of <b>Step 4 (Features)</b> — it's the deadliest, sneakiest bug in applied ML."
},
{
q: "A teammate tunes hyperparameters by repeatedly checking the model's score on the test set, then reports that test score as the final result. Why is this wrong?",
opts: [
"Test sets should always be larger than training sets",
"You should never use cross-validation and a test set together",
"Tuning on the test set leaks information, so the reported number is optimistically biased",
"Hyperparameters should only be tuned on the training set's accuracy"
],
correct: 2,
why: "<b>The golden rule:</b> the test set is opened <em>once</em>, at the very end. If you tune anything based on it, it stops being a fair estimate of real-world performance — you've leaked information and your number is optimistic. Tuning belongs on the <b>validation</b> set (or via cross-validation); the test set is a sealed exam."
},
{
q: "You're predicting future bulldozer auction prices from time-stamped historical sales. How should you split the data?",
opts: [
"Randomly shuffle all rows, then split 80/20",
"Split by time — train on older sales, validate on the most recent",
"Put all expensive machines in the test set",
"Use the whole dataset for both training and testing"
],
correct: 1,
why: "<b>Split by time.</b> When the future is what you're predicting, a random split lets future sales leak into training and wildly overstates performance. Holding out the latest period mimics the real task — predict tomorrow from today. A random shuffle here is feature leakage in its most subtle form."
}
]
},
/* ═══════════════ GO DEEPER ═══════════════ */
{
id: "deeper", icon: "📺", name: "Go Deeper", sub: "full course",
qword: "full course", tag: "Go-deeper lens",
heading: "The authoritative course",
lead: "Ready for the rigorous treatment? Andrew Ng's CS229 is the definitive university course on machine learning, with the lectures most relevant to this applied framework — data splits, cross-validation, and debugging — called out first.",
html: `
<div class="gd-hero">
<div class="course">CS229 · Machine Learning</div>
<div class="by">Stanford · Andrew Ng · <a href="https://www.youtube.com/playlist?list=PLoROMvodv4rMiGQp3WXShtMGgzqpfVfbU" target="_blank" rel="noopener">watch the full course →</a></div>
<div class="gd-list">
<div class="gd-item">
<div class="ply">▶</div>
<div class="body">
<div class="t"><a href="https://youtu.be/rjbkWSTjHzM" target="_blank" rel="noopener">L8 · Data Splits, Models & Cross-Validation</a></div>
<div class="d">How the train / validation / test split protects generalization, and why cross-validation is the workhorse for model selection — the exact ideas in the Visual lens.</div>
</div>
</div>
<div class="gd-item">
<div class="ply">▶</div>
<div class="body">
<div class="t"><a href="https://youtu.be/ORrStCArmP4" target="_blank" rel="noopener">L13 · Debugging ML & Error Analysis</a></div>
<div class="d">Turning "it's not working" into a diagnosis: bias vs. variance, ablations, and where to spend your next hour — Step 6 made rigorous.</div>
</div>
</div>
</div>
</div>
<h3 class="block-h">The foundational reading</h3>
<div class="paper-card">
<div class="pt"><a href="https://homes.cs.washington.edu/~pedrod/papers/cacm12.pdf" target="_blank" rel="noopener">A Few Useful Things to Know about Machine Learning →</a></div>
<div class="pa">Pedro Domingos · Communications of the ACM 2012</div>
<div class="ps">A field-defining practitioner essay distilling the lessons that matter most when applying ML: learning = representation + evaluation + optimization, the central role of generalization, why "data beats a cleverer algorithm," the dangers of overfitting, and that feature engineering is where most real effort goes. This is the academic backbone behind practical "how to approach an ML project" frameworks.</div>
</div>
<div class="paper-card">
<div class="pt"><a href="https://www.jmlr.org/papers/v12/pedregosa11a.html" target="_blank" rel="noopener">Scikit-learn: Machine Learning in Python →</a></div>
<div class="pa">Pedregosa, Varoquaux, Gramfort et al. · JMLR 2011</div>
<div class="ps">The paper introducing scikit-learn, the library that standardized the practical ML workflow into a single consistent API: <code>estimator.fit(X, y)</code> then <code>estimator.predict(X)</code>, with shared tooling for train/test splitting, cross-validation, preprocessing pipelines, and hyperparameter search. It is the toolset the 6-step framework maps onto for structured-data problems.</div>
</div>
<h3 class="block-h">Why the framework matters</h3>
<div class="card full">
<h4><span class="n">↪</span>Where it fits in this suite</h4>
<p>This page is the entry point to the <b>Applied ML / Data Science</b> track. The natural reading order:</p>
<ol style="color:var(--text);font-size:14px;line-height:1.9;margin:8px 0 8px 18px;">
<li><b>This lesson</b> — the 6-step map.</li>
<li><a href="topic_data_tools.html">Data Tools</a> — NumPy, pandas, Matplotlib: how you actually touch the data in steps 2 & 4.</li>
<li><a href="topic_sklearn_workflow.html">Scikit-Learn Workflow</a> — split → fit → predict → evaluate → tune → save: steps 5 & 6 in code.</li>
<li><a href="topic_ml_projects.html">Milestone Projects</a> — all six steps, three full projects, start to finish.</li>
</ol>
<p>From there, the deep-learning project (Dog Vision) is the bridge into the suite's theory track (<a href="topic_cnn.html">CNNs</a>, transfer learning, and beyond).</p>
</div>
<h3 class="block-h">The mistakes the framework prevents</h3>
<table class="cmp">
<tr><th>Without a framework</th><th>The step that prevents it</th></tr>
<tr><td>"It's 99% accurate!" — on a dataset that's 99% one class</td><td>Step 3: pick a metric that fits the class balance</td></tr>
<tr><td>Brilliant offline, useless in production</td><td>Step 4: hunt for feature leakage</td></tr>
<tr><td>Spent three weeks on a deep net for a 500-row spreadsheet</td><td>Step 2 → Step 5: structured data wants a simple model first</td></tr>
<tr><td>Can't reproduce last week's best result</td><td>Step 6: document every experiment</td></tr>
<tr><td>Tuned on the test set; real performance is worse</td><td>Step 6: the sealed three-way split</td></tr>
</table>
<div class="callout good" style="max-width:var(--measure)">
<span class="lbl">✓ Bottom line</span>
The framework is cheap insurance. It costs a few minutes of upfront thinking and saves weeks of training the wrong model to optimize the wrong metric on leaky data.
</div>`
}
]
};
</script>
</body>
</html>