Skip to content

Commit 935d23d

Browse files
committed
feat: add max pooling animation
1 parent 99d8732 commit 935d23d

File tree

11 files changed

+605
-0
lines changed

11 files changed

+605
-0
lines changed

max-pooling-animation/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Max Pooling Animation
2+
3+
A visual demonstration of Max Pooling, built with React, Three.js, and Tailwind CSS.
4+
5+
## Overview
6+
7+
This project visualizes how Max Pooling downsamples feature maps in Convolutional Neural Networks (CNNs). It shows:
8+
9+
- **Sliding Window**: A window that moves across the input feature map
10+
- **Max Selection**: Highlighting the maximum value in each window region
11+
- **Output Creation**: Building the downsampled output feature map
12+
- **Dimension Reduction**: Visualizing how pooling reduces spatial dimensions
13+
14+
## Components
15+
16+
- **Max Pooling Panel**: Animated visualization with sliding window and max value highlighting
17+
- **Config Panel**: Interactive controls for pool size, stride, and input randomization
18+
19+
## Getting Started
20+
21+
1. Install dependencies:
22+
```bash
23+
npm install
24+
```
25+
26+
2. Run the development server:
27+
```bash
28+
npm run dev
29+
```
30+
31+
3. Open your browser to the URL shown (usually `http://localhost:5173`).
32+
33+
## Features
34+
35+
- **Configurable Pool Size**: Try 2×2 or 3×3 pooling windows
36+
- **Adjustable Stride**: Test with stride 1 (overlapping) or stride 2 (non-overlapping)
37+
- **Random Input**: Generate different feature maps to see how pooling behaves
38+
39+
## Why Max Pooling?
40+
41+
- Reduces spatial dimensions (downsampling)
42+
- Decreases computational cost
43+
- Provides translation invariance
44+
- Keeps the strongest features (maximum values)
45+
46+
## License
47+
48+
MIT

max-pooling-animation/index.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Max Pooling Animation</title>
8+
</head>
9+
10+
<body>
11+
<div id="root"></div>
12+
<script type="module" src="/src/main.jsx"></script>
13+
</body>
14+
15+
</html>

max-pooling-animation/package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "max-pooling-animation",
3+
"version": "1.0.0",
4+
"type": "module",
5+
"scripts": {
6+
"dev": "vite",
7+
"build": "vite build",
8+
"preview": "vite preview"
9+
},
10+
"dependencies": {
11+
"gsap": "^3.12.2",
12+
"react": "^18.2.0",
13+
"react-dom": "^18.2.0",
14+
"three": "^0.158.0"
15+
},
16+
"devDependencies": {
17+
"@types/react": "^18.2.15",
18+
"@types/react-dom": "^18.2.7",
19+
"@vitejs/plugin-react": "^4.0.3",
20+
"autoprefixer": "^10.4.16",
21+
"postcss": "^8.4.31",
22+
"tailwindcss": "^3.3.5",
23+
"vite": "^5.0.0"
24+
}
25+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
}

max-pooling-animation/src/App.jsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React, { useState } from 'react';
2+
import MaxPoolingPanel from './MaxPoolingPanel';
3+
import ConfigPanel from './ConfigPanel';
4+
5+
const generateRandomMatrix = () => {
6+
const size = 4;
7+
const matrix = [];
8+
for (let i = 0; i < size; i++) {
9+
const row = [];
10+
for (let j = 0; j < size; j++) {
11+
row.push(Math.floor(Math.random() * 10));
12+
}
13+
matrix.push(row);
14+
}
15+
return matrix;
16+
};
17+
18+
export default function App() {
19+
const [inputMatrix, setInputMatrix] = useState(generateRandomMatrix());
20+
const [poolSize, setPoolSize] = useState(2);
21+
const [stride, setStride] = useState(2);
22+
23+
const handleParamsChange = (matrix, newPoolSize, newStride) => {
24+
setInputMatrix(matrix);
25+
setPoolSize(newPoolSize);
26+
setStride(newStride);
27+
};
28+
29+
return (
30+
<div className="min-h-screen bg-gray-100 p-4">
31+
<h1 className="text-3xl font-bold text-gray-800 text-center mb-4">Max Pooling</h1>
32+
<p className="text-center text-gray-600 mb-4">
33+
Visualizing feature map downsampling in CNNs
34+
</p>
35+
36+
<div className="flex flex-col gap-4 max-w-7xl mx-auto">
37+
{/* Top Row - Animation and Config */}
38+
<div className="flex flex-col lg:flex-row gap-4">
39+
{/* Left Panel - Animation */}
40+
<div className="flex-1 bg-gray-50 rounded-xl shadow-lg overflow-hidden">
41+
<MaxPoolingPanel
42+
inputMatrix={inputMatrix}
43+
poolSize={poolSize}
44+
stride={stride}
45+
/>
46+
</div>
47+
48+
{/* Right Panel - Config */}
49+
<div className="flex-1 bg-gray-50 rounded-xl shadow-lg overflow-hidden">
50+
<ConfigPanel onParamsChange={handleParamsChange} />
51+
</div>
52+
</div>
53+
</div>
54+
</div>
55+
);
56+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import React, { useState } from 'react';
2+
3+
export default function ConfigPanel({ onParamsChange }) {
4+
const [poolSize, setPoolSize] = useState(2);
5+
const [stride, setStride] = useState(2);
6+
7+
const generateRandomMatrix = () => {
8+
const size = 4;
9+
const matrix = [];
10+
for (let i = 0; i < size; i++) {
11+
const row = [];
12+
for (let j = 0; j < size; j++) {
13+
row.push(Math.floor(Math.random() * 10));
14+
}
15+
matrix.push(row);
16+
}
17+
return matrix;
18+
};
19+
20+
const [inputMatrix, setInputMatrix] = useState(generateRandomMatrix());
21+
22+
const handlePoolSizeChange = (value) => {
23+
setPoolSize(parseInt(value));
24+
if (onParamsChange) {
25+
onParamsChange(inputMatrix, parseInt(value), stride);
26+
}
27+
};
28+
29+
const handleStrideChange = (value) => {
30+
setStride(parseInt(value));
31+
if (onParamsChange) {
32+
onParamsChange(inputMatrix, poolSize, parseInt(value));
33+
}
34+
};
35+
36+
const handleRandomize = () => {
37+
const newMatrix = generateRandomMatrix();
38+
setInputMatrix(newMatrix);
39+
if (onParamsChange) {
40+
onParamsChange(newMatrix, poolSize, stride);
41+
}
42+
};
43+
44+
const inputSize = inputMatrix.length;
45+
const outputSize = Math.floor((inputSize - poolSize) / stride) + 1;
46+
47+
return (
48+
<div className="flex flex-col items-center p-4 h-full">
49+
<h2 className="text-xl font-bold text-gray-800 mb-4">Configuration</h2>
50+
51+
<div className="flex flex-col gap-6 w-full max-w-md">
52+
{/* Pool Size */}
53+
<div className="flex flex-col gap-2">
54+
<label className="font-bold text-gray-700">
55+
Pool Size:
56+
</label>
57+
<div className="flex gap-2">
58+
{[2, 3].map((size) => (
59+
<button
60+
key={size}
61+
onClick={() => handlePoolSizeChange(size)}
62+
className={`px-4 py-2 rounded-lg font-bold transition-colors ${poolSize === size
63+
? 'bg-blue-500 text-white'
64+
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
65+
}`}
66+
>
67+
{size}×{size}
68+
</button>
69+
))}
70+
</div>
71+
</div>
72+
73+
{/* Stride */}
74+
<div className="flex flex-col gap-2">
75+
<label className="font-bold text-gray-700">
76+
Stride:
77+
</label>
78+
<div className="flex gap-2">
79+
{[1, 2].map((s) => (
80+
<button
81+
key={s}
82+
onClick={() => handleStrideChange(s)}
83+
className={`px-4 py-2 rounded-lg font-bold transition-colors ${stride === s
84+
? 'bg-blue-500 text-white'
85+
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
86+
}`}
87+
>
88+
{s}
89+
</button>
90+
))}
91+
</div>
92+
</div>
93+
94+
{/* Dimensions */}
95+
<div className="bg-purple-50 p-4 rounded-lg border border-purple-200">
96+
<p className="text-sm font-bold text-gray-700 mb-2">Dimensions:</p>
97+
<p className="text-sm text-gray-600">
98+
Input: <span className="font-mono font-bold">{inputSize}×{inputSize}</span>
99+
</p>
100+
<p className="text-sm text-gray-600">
101+
Output: <span className="font-mono font-bold">{outputSize}×{outputSize}</span>
102+
</p>
103+
<p className="text-xs text-gray-500 mt-2">
104+
Formula: (Input - Pool) / Stride + 1
105+
</p>
106+
</div>
107+
108+
{/* Randomize Button */}
109+
<button
110+
onClick={handleRandomize}
111+
className="px-4 py-2 bg-orange-500 hover:bg-orange-600 text-white font-bold rounded-lg transition-colors"
112+
>
113+
🎲 Randomize Input
114+
</button>
115+
116+
<div className="bg-blue-50 p-4 rounded-lg border border-blue-200 text-sm text-gray-700 space-y-2">
117+
<p><strong>How Max Pooling Works:</strong></p>
118+
<ul className="list-disc list-inside space-y-1">
119+
<li>Slide window across input</li>
120+
<li>Take maximum value in each window</li>
121+
<li>Downsamples feature maps (reduces size)</li>
122+
<li>Preserves strongest features</li>
123+
</ul>
124+
</div>
125+
</div>
126+
</div>
127+
);
128+
}

0 commit comments

Comments
 (0)