Prompt utilise pour regenerer cette page :
Page: Perceptron
Description: "Handwritten digit recognition with neural networks"
Category: artificial-intelligence
Icon: brain
Tags: neural, mnist
Status: validated
Front matter (index.md):
title: "Perceptron"
description: "Handwritten digit recognition with neural networks"
icon: "brain"
tags: ["neural", "mnist"]
status: ["validated"]
HTML structure (index.md):
<!-- Gauges: 10 vertical bars for digits 0-9 -->
<section class="gauges">
10x <div class="gauge" data-digit="N"><div class="gauge-fill"></div><span>N</span></div>
(N = 0 through 9)
</section>
<!-- Layer activations: dynamic canvases injected by JS -->
<section id="layer-activations"></section>
<!-- Drawing canvas: 20x20 grid of div cells -->
<section class="canvas-container">
<div class="canvas" id="drawing-canvas"></div>
</section>
<!-- Control buttons using Hugo shortcodes -->
<section class="controls">
{{< button id="sample-btn" label="Sample" >}}
{{< button id="reset-btn" label="Reset" >}}
</section>
Widget files:
- _models.left.md (weight: 10): ##### Models
<nav id="model-list"></nav>
(Populated dynamically by JS with clickable model links)
- _stats.right.md (weight: 20): ##### Details
<dl id="model-details"> with:
- Topology: dd#detail-topology (initial "-")
- Geometry: dd#detail-geometry (initial "-")
- Epochs: dd#detail-epochs (initial "-")
- Accuracy: dd#detail-accuracy (initial "-")
- Trained: dd#detail-trained (initial "-")
- _options.right.md (weight: 30): ##### Options
<div id="drawing-options">
- Size: label.control-group with input#brush-size type=range min=1 max=5 value=2
- Eraser: label.control-toggle with checkbox#eraser-toggle
- _algorithm.after.md (weight: 85): Explains MLP architecture (400 inputs = 20x20 grid, configurable hidden layers, 10 outputs with softmax). Training details: MNIST dataset, center-cropped to 20x20, augmentation, Adam optimizer, ~97% accuracy.
Architecture (2 JS files):
- default.js: drawing canvas controller (IIFE)
- app.js: MLP inference engine (IIFE, exposes window.MLP)
=== default.js (drawing canvas) ===
IIFE, imports: panic from '/_lib/panic_v3.js'
Constants: CELL_SIZE=20px, GAP_SIZE=1px
State: gridSize=20 (dynamic from model), isDrawing, cells[], cellValues[] (0-1 per cell), lastPos, canvasElement, isInitialized, gaugeElements (cached), layerCanvases[], currentSample=0, brushRadius=2.5, isErasing=false
destroyCanvas(): removes all cells, removes event listeners (mousedown/mousemove/mouseup/touchstart/touchmove/touchend), resets state
initCanvas(size=20): creates gridSize x gridSize div.cell elements inside #drawing-canvas. Sets CSS grid template. Binds mouse events on canvas, document mouseup. Touch events with {passive: false}. Binds reset-btn, sample-btn, eraser-toggle, brush-size. Keyboard shortcuts bound once (not re-bound on re-init).
Drawing system:
getCellCoords(index): returns {x, y} from flat index
getCellIndex(x, y): returns index or -1 if out of bounds
setCellValue(index, value): only increases value (anti-aliasing), sets data-fill attribute and --fill CSS variable
eraseCellValue(index): resets to 0, removes data-fill and --fill
fillCellWithAA(px, py): fills cells in square region around point within brushRadius. Distance-based intensity: value = 1 - (distance/radius). Supports erasing mode.
drawLine(x0, y0, x1, y1): interpolates points along line with steps = max(1, ceil(distance*2)), calls fillCellWithAA at each step
clientToGrid(clientX, clientY): converts client coords to float grid coords using getBoundingClientRect and cellStep (CELL_SIZE + GAP_SIZE)
fillCellAt(clientX, clientY): converts to grid coords, draws line from lastPos (or single point if first), calls onCanvasChanged()
Event handlers: handleMouseDown/Move/Up, handleTouchStart/Move/End, handleKeyDown (R=reset, 0-9=load sample digit)
resetCanvas(): clears all cells, resets gauges to 0, clears activations
loadSample(digit): fetches /_api/perceptron/samples/{digit}.json, draws strokes (array of [x,y] point sequences), scales for non-20x20 grids, triggers inference
nextSample(): cycles 0-9, calls loadSample(currentSample++)
createLayerCanvases(topology): creates canvas elements in #layer-activations for each hidden layer (reverse order: last layer first = top). Canvas width = neuron count, height = 18px.
clearActivations(): fills all layer canvases with --background-color-surface color
getCanvasData(): returns copy of cellValues array
centerDrawing(data): finds bounding box, calculates offset to center content in grid
drawActivations(): gets layer activations from window.MLP.getActivations(), draws 1px vertical lines per neuron with quadratic brightness scale (white=0 to black=1)
onCanvasChanged(): calls window.MLP.predict(), updates gauges and activations
updateGauges(probabilities): sets --probability CSS variable per gauge, data-confidence attribute (high >80%: #27ae60, medium 50-80%: #f39c12, low <50%: #e74c3c) only on winning gauge
Initialization: listens for 'mlp:ready' custom event (dispatched by app.js), receives gridSize and topology, calls initCanvas() and createLayerCanvases()
Exposes window.perceptron = { getCanvasData, resetCanvas, updateGauges }
=== app.js (MLP inference engine) ===
IIFE, imports: panic from '/_lib/panic_v3.js'
STORAGE_KEY = 'org.cylian.perceptron.model'
Storage module: get/set with localStorage, try/catch for errors
Network state: topology (array), activations (string array), layers (array of {weights, biases}), isLoaded, layerActivations[]
Activation functions:
relu(values): element-wise max(0, x)
softmax(values): exp(x - max) / sum, numerically stable
getActivation(name): lookup table for 'relu', 'softmax', 'linear'
dense(weights, input, biases): matrix-vector multiply + bias
forward(input): iterates layers applying dense + activation, stores layerActivations for visualization
loadWeights(url): fetches JSON model, validates Content-Type (application/json check for Safari), validates structure (topology, layers, activations arrays), validates each layer dimensions against topology. Stores model and sets isLoaded=true.
predict(inputData): forward pass, returns {probabilities, prediction (argmax), confidence (max probability)}
isReady(): returns isLoaded
getTopology(): returns topology array
getActivations(): returns layerActivations from last forward pass
parseGeometry(geometry): parses "20x20" string to grid size number (default 20)
Model management:
modelsIndex: loaded from /_api/perceptron/index.json
populateModelList(models): creates <a> links in #model-list with model.options.name
updateActiveModel(modelId): toggles .active class on model links
updateModelDetails(model): updates detail-topology/geometry/epochs/accuracy/trained elements
loadModelById(modelId): skips if already loaded, finds in modelsIndex, parses geometry, loads weights from /_api/perceptron/{model.file}, updates details, saves to localStorage, dispatches 'mlp:ready' custom event with {topology, gridSize}
init(): fetches /_api/perceptron/index.json, populates model list, loads saved model from localStorage (or first model), updates active state
Exposes window.MLP = { loadWeights, predict, isReady, getTopology, getActivations }
Auto-init on DOMContentLoaded
SCSS file (default.scss):
Variables: $cell-size: 20px, $grid-size: 20, $gap-size: 1px, $gauge-height: 100px, $gauge-width: 24px
layout-main scope:
main: flex column centered, padding 2rem
section.gauges: flex row centered, gap 8px, margin-bottom 1rem
.gauge: flex column centered, width $gauge-width
.gauge-fill: 100% width, $gauge-height height, surface background/border, 4px radius, relative+overflow hidden
::after pseudo-element: absolute bottom-0, height controlled by --probability CSS var (calc(var(--probability, 0) * 100%)), transition 150ms, default color var(--draw-color-primary)
Confidence colors on .gauge-fill::after:
[data-confidence="high"]: #27ae60 (green, >80%)
[data-confidence="medium"]: #f39c12 (orange, 50-80%)
[data-confidence="low"]: #e74c3c (red, <50%)
.gauge span: margin-top 0.5rem, 0.875rem, weight 600
#layer-activations: flex column centered, gap 2px, margin-bottom 1rem. Canvas children with 1px border.
section.canvas-container: flex centered
.canvas: CSS grid, gridTemplateColumns/Rows repeat($grid-size, $cell-size), gap $gap-size, cursor crosshair, touch-action none, user-select none
.cell: $cell-size square, surface background, 1px border, box-sizing border-box, 50ms transition
&[data-fill]: background uses color-mix(in srgb, text-color calc(var(--fill)*100%), background-color)
section.controls: flex centered, gap 1.5rem, margin-top 1.5rem
#reset-btn, #sample-btn: styled buttons with primary color, hover/active states
#drawing-options: flex column, gap 0.75rem
.control-group: flex row between, range input 80px
.control-toggle: flex row with checkbox
#model-list: flex column, gap 0.25rem
a.item: block, padding, hover background, .active with primary bg and inverted text color
Page entierement generee et maintenue par IA, sans intervention humaine.