Skip to content

Commit 76edbce

Browse files
feat(ai): add quantization and vectorization explainers
Add interactive Chinese-language explainers for model quantization and vector embeddings. These visual guides make memory reduction and semantic vector spaces easier to understand through concrete examples and interactive demonstrations.
1 parent a933a52 commit 76edbce

2 files changed

Lines changed: 766 additions & 0 deletions

File tree

quantization.html

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
<!DOCTYPE html>
2+
<html lang="zh-CN">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>量化:让大模型塞进小显卡</title>
7+
<link rel="preconnect" href="https://fonts.googleapis.com">
8+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9+
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@700;900&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
10+
<style>
11+
:root{
12+
--paper:#EDEFE6;
13+
--ink:#1B3B36;
14+
--ink-soft:#4B6660;
15+
--line:#B9C7B4;
16+
--pin:#C33C54;
17+
--sea:#3E7C8C;
18+
--sun:#D9A227;
19+
--serif:"Noto Serif SC","Songti SC","Source Han Serif SC","SimSun",serif;
20+
--sans:"PingFang SC","Hiragino Sans GB","Microsoft YaHei",system-ui,-apple-system,sans-serif;
21+
--mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
22+
}
23+
*{box-sizing:border-box}
24+
html{-webkit-text-size-adjust:100%}
25+
body{
26+
margin:0;background-color:var(--paper);
27+
background-image:
28+
linear-gradient(rgba(27,59,54,.045) 1px,transparent 1px),
29+
linear-gradient(90deg,rgba(27,59,54,.045) 1px,transparent 1px);
30+
background-size:44px 44px;
31+
color:var(--ink);font-family:var(--sans);line-height:1.7;
32+
}
33+
.wrap{max-width:1040px;margin:0 auto;padding:0 24px}
34+
35+
.hero{padding:clamp(56px,12vw,120px) 0 clamp(30px,6vw,60px)}
36+
.hero__tag{
37+
font-family:var(--mono);font-size:12px;letter-spacing:.22em;color:var(--ink-soft);
38+
margin:0 0 22px;display:flex;align-items:center;gap:12px;
39+
}
40+
.hero__tag::after{content:"";flex:1;height:1px;background:var(--line)}
41+
.hero__title{
42+
font-family:var(--serif);font-weight:900;
43+
font-size:clamp(36px,8.4vw,80px);line-height:1.14;margin:0 0 18px;
44+
}
45+
.hero__title em{font-style:normal;color:var(--pin)}
46+
.hero__sub{font-size:clamp(15px,2.6vw,19px);color:var(--ink-soft);margin:0;max-width:32em}
47+
48+
.plate{padding:clamp(52px,10vw,110px) 0;border-top:1px solid var(--line)}
49+
.plate__eyebrow{font-family:var(--mono);font-size:12px;letter-spacing:.2em;color:var(--pin);margin:0 0 14px}
50+
.plate__title{font-family:var(--serif);font-weight:700;font-size:clamp(27px,6vw,48px);line-height:1.25;margin:0 0 12px}
51+
.plate__note{color:var(--ink-soft);margin:0;font-size:clamp(14px,2.4vw,17px)}
52+
.figure{margin:clamp(28px,6vw,48px) 0 0}
53+
.figure svg{width:100%;height:auto;display:block}
54+
55+
/* ---- 精度:数字被削短 ---- */
56+
.digits{
57+
font-family:var(--mono);font-weight:600;
58+
font-size:clamp(26px,7vw,64px);letter-spacing:.02em;
59+
background:#fff;border:2px solid var(--ink);border-radius:5px;
60+
padding:clamp(16px,4vw,30px);box-shadow:6px 6px 0 rgba(27,59,54,.12);
61+
overflow:hidden;white-space:nowrap;text-align:center;
62+
}
63+
.digits .keep{color:var(--ink)}
64+
.digits .cut{color:var(--line);transition:color .35s ease}
65+
.digits .gone{color:transparent}
66+
67+
/* ---- 控制台 ---- */
68+
.rig{
69+
background:#F5F6EF;border:2px solid var(--ink);border-radius:6px;
70+
box-shadow:8px 8px 0 rgba(27,59,54,.1);
71+
padding:clamp(18px,4vw,32px);margin-top:clamp(28px,6vw,44px);
72+
}
73+
.rig__grid{display:grid;grid-template-columns:1fr 1.05fr;gap:clamp(20px,4vw,44px);align-items:end}
74+
@media(max-width:680px){.rig__grid{grid-template-columns:1fr;align-items:stretch}}
75+
76+
.tank{position:relative;height:320px;border-left:2px solid var(--ink);border-bottom:2px solid var(--ink)}
77+
.tank__limit{
78+
position:absolute;left:0;right:0;bottom:80px;
79+
border-top:2px dashed var(--pin);
80+
}
81+
.tank__limit span{
82+
position:absolute;right:0;top:-24px;font-family:var(--mono);
83+
font-size:12px;color:var(--pin);letter-spacing:.08em;
84+
}
85+
.tank__bar{
86+
position:absolute;left:22%;width:56%;bottom:0;
87+
background:var(--sea);border:2px solid var(--ink);border-bottom:0;
88+
transition:height .45s cubic-bezier(.4,1.3,.5,1),background .45s ease;
89+
display:flex;align-items:flex-start;justify-content:center;
90+
}
91+
.tank__bar b{
92+
font-family:var(--mono);font-size:clamp(13px,2.6vw,17px);color:#fff;
93+
padding-top:8px;font-weight:500;
94+
}
95+
.tank__bar[data-fit="0"]{background:var(--pin)}
96+
97+
.readout{display:flex;flex-direction:column;gap:clamp(12px,2.4vw,18px)}
98+
.row{display:flex;justify-content:space-between;align-items:baseline;border-bottom:1px solid var(--line);padding-bottom:10px}
99+
.row i{font-style:normal;font-family:var(--mono);font-size:12px;letter-spacing:.14em;color:var(--ink-soft)}
100+
.row strong{font-family:var(--serif);font-weight:700;font-size:clamp(20px,4.4vw,32px)}
101+
.verdict{font-family:var(--serif);font-weight:700;font-size:clamp(18px,4vw,28px)}
102+
.verdict[data-fit="1"]{color:var(--sea)}
103+
.verdict[data-fit="0"]{color:var(--pin)}
104+
.verdict[data-fit="2"]{color:var(--sun)}
105+
106+
.quality{margin-top:6px}
107+
.quality__track{height:16px;background:#E3E8DC;border:1px solid var(--line);border-radius:9px;overflow:hidden}
108+
.quality__fill{height:100%;background:var(--sun);transition:width .45s ease}
109+
110+
.slider{margin-top:clamp(24px,5vw,38px)}
111+
.slider input{
112+
-webkit-appearance:none;appearance:none;width:100%;height:6px;
113+
background:var(--line);border-radius:3px;outline:none;margin:0;
114+
}
115+
.slider input::-webkit-slider-thumb{
116+
-webkit-appearance:none;appearance:none;width:30px;height:30px;border-radius:50%;
117+
background:var(--pin);border:3px solid var(--paper);cursor:pointer;
118+
box-shadow:0 0 0 2px var(--ink);
119+
}
120+
.slider input::-moz-range-thumb{
121+
width:24px;height:24px;border-radius:50%;background:var(--pin);
122+
border:3px solid var(--paper);cursor:pointer;box-shadow:0 0 0 2px var(--ink);
123+
}
124+
.slider input:focus-visible{box-shadow:0 0 0 3px rgba(195,60,84,.35)}
125+
.ticks{display:flex;justify-content:space-between;margin-top:12px;font-family:var(--mono);font-size:clamp(11px,2.4vw,14px);color:var(--ink-soft)}
126+
.ticks span{cursor:pointer;padding:2px 4px}
127+
.ticks span[data-on="1"]{color:var(--pin);font-weight:600}
128+
129+
/* ---- 名字卡 ---- */
130+
.tags{display:flex;flex-wrap:wrap;gap:10px;margin-top:clamp(24px,5vw,40px)}
131+
.tag{
132+
font-family:var(--mono);font-size:clamp(12px,2.4vw,15px);
133+
border:1px solid var(--line);background:#fff;border-radius:4px;padding:9px 14px;color:var(--ink-soft);
134+
}
135+
.tag b{color:var(--ink);font-weight:600}
136+
137+
.end{padding:clamp(60px,12vw,130px) 0;border-top:2px solid var(--ink);text-align:center}
138+
.end__line{font-family:var(--serif);font-weight:900;font-size:clamp(23px,5.4vw,44px);line-height:1.45;margin:0}
139+
.end__line em{font-style:normal;background:linear-gradient(transparent 62%,rgba(217,162,39,.55) 62%)}
140+
.end__foot{font-family:var(--mono);font-size:12px;color:var(--ink-soft);letter-spacing:.2em;margin-top:26px}
141+
142+
.rise{opacity:0;transform:translateY(22px);transition:opacity .7s ease,transform .7s ease}
143+
.rise.on{opacity:1;transform:none}
144+
@media(prefers-reduced-motion:reduce){
145+
.rise{opacity:1;transform:none;transition:none}
146+
.tank__bar,.quality__fill{transition:none}
147+
}
148+
</style>
149+
</head>
150+
<body>
151+
152+
<header class="hero wrap">
153+
<p class="hero__tag">量化 · QUANTIZATION</p>
154+
<h1 class="hero__title">让大模型,<br>塞进<em>小显卡</em></h1>
155+
<p class="hero__sub">办法不是让模型变聪明,是让它每个数字占的地方变小。</p>
156+
</header>
157+
158+
<main>
159+
160+
<section class="plate wrap rise">
161+
<p class="plate__eyebrow">01 / 模型是什么</p>
162+
<h2 class="plate__title">一个模型 = 几十亿个数字。</h2>
163+
<p class="plate__note">这些数字叫"权重"。跑模型,先要把它们全搬进显存。</p>
164+
<div class="figure">
165+
<svg viewBox="0 0 800 220" role="img" aria-label="模型由大量权重数字组成">
166+
<g font-family="var(--mono)" font-size="17" fill="#3E7C8C">
167+
<text x="0" y="30">0.0231</text><text x="130" y="30">-1.4420</text><text x="280" y="30">0.8817</text><text x="420" y="30">-0.0093</text><text x="560" y="30">0.3364</text><text x="690" y="30">1.0025</text>
168+
<text x="0" y="80">-0.7710</text><text x="130" y="80">0.4508</text><text x="280" y="80">-0.1192</text><text x="420" y="80">0.9931</text><text x="560" y="80">-0.5507</text><text x="690" y="80">0.2140</text>
169+
<text x="0" y="130" opacity=".7">0.6653</text><text x="130" y="130" opacity=".7">-0.3081</text><text x="280" y="130" opacity=".7">0.1274</text><text x="420" y="130" opacity=".7">-0.8846</text><text x="560" y="130" opacity=".7">0.7028</text><text x="690" y="130" opacity=".7">-0.0417</text>
170+
<text x="0" y="180" opacity=".35">0.1590</text><text x="130" y="180" opacity=".35">0.9265</text><text x="280" y="180" opacity=".35">-0.3589</text><text x="420" y="180" opacity=".35">0.7932</text><text x="560" y="180" opacity=".35">-0.6264</text><text x="690" y="180" opacity=".35">0.3383</text>
171+
</g>
172+
<text x="400" y="214" text-anchor="middle" font-family="var(--serif)" font-weight="700" font-size="20" fill="#C33C54">…… 七十亿个 ……</text>
173+
</svg>
174+
</div>
175+
</section>
176+
177+
<section class="plate wrap rise">
178+
<p class="plate__eyebrow">02 / 量化做的事</p>
179+
<h2 class="plate__title">把每个数字,记粗一点。</h2>
180+
<p class="plate__note">3.14159265 记成 3.1 —— 差不多,但省一大截地方。</p>
181+
<div class="figure">
182+
<div class="digits" id="digits" aria-live="polite"></div>
183+
</div>
184+
</section>
185+
186+
<section class="plate wrap rise">
187+
<p class="plate__eyebrow">03 / 拉一下试试</p>
188+
<h2 class="plate__title">记得越粗,模型越小。</h2>
189+
<p class="plate__note">以一个 70 亿参数的模型为例。虚线是一张普通显卡(8GB)。</p>
190+
191+
<div class="rig">
192+
<div class="rig__grid">
193+
<div class="tank" aria-hidden="true">
194+
<div class="tank__limit"><span>普通显卡 8GB</span></div>
195+
<div class="tank__bar" id="bar" data-fit="0"><b id="barLabel">28 GB</b></div>
196+
</div>
197+
198+
<div class="readout">
199+
<div class="row"><i>每个数字占</i><strong id="bits">32 位</strong></div>
200+
<div class="row"><i>模型体积</i><strong id="size">28 GB</strong></div>
201+
<div class="row"><i>普通显卡</i><span class="verdict" id="verdict" data-fit="0">装不下</span></div>
202+
<div class="quality">
203+
<div class="row" style="border:0;padding-bottom:6px"><i>回答质量</i><strong id="q">100%</strong></div>
204+
<div class="quality__track"><div class="quality__fill" id="qfill" style="width:100%"></div></div>
205+
</div>
206+
</div>
207+
</div>
208+
209+
<div class="slider">
210+
<input type="range" id="range" min="0" max="3" step="1" value="0" aria-label="选择精度">
211+
<div class="ticks" id="ticks">
212+
<span data-i="0">FP32</span><span data-i="1">FP16</span><span data-i="2">INT8</span><span data-i="3">INT4</span>
213+
</div>
214+
</div>
215+
</div>
216+
</section>
217+
218+
<section class="plate wrap rise">
219+
<p class="plate__eyebrow">04 / 代价</p>
220+
<h2 class="plate__title">会笨一点点。</h2>
221+
<p class="plate__note">像照片压缩:远看一样,凑近看有点糊。压太狠就明显了。</p>
222+
<div class="figure">
223+
<svg viewBox="0 0 800 260" role="img" aria-label="压缩越狠画面越糊的类比">
224+
<g>
225+
<rect x="10" y="20" width="180" height="180" fill="#3E7C8C"/>
226+
<rect x="10" y="20" width="180" height="180" fill="none" stroke="#1B3B36" stroke-width="2"/>
227+
<g fill="#EDEFE6"><circle cx="100" cy="90" r="34"/><path d="M40 180l45-58 36 40 26-28 43 46z"/></g>
228+
</g>
229+
<g>
230+
<rect x="210" y="20" width="180" height="180" fill="#3E7C8C"/>
231+
<rect x="210" y="20" width="180" height="180" fill="none" stroke="#1B3B36" stroke-width="2"/>
232+
<g fill="#EDEFE6"><rect x="272" y="60" width="60" height="60"/><path d="M240 180l45-56 36 40 26-28 43 44z"/></g>
233+
</g>
234+
<g>
235+
<rect x="410" y="20" width="180" height="180" fill="#3E7C8C"/>
236+
<rect x="410" y="20" width="180" height="180" fill="none" stroke="#1B3B36" stroke-width="2"/>
237+
<g fill="#EDEFE6"><rect x="470" y="60" width="60" height="60"/><rect x="440" y="140" width="120" height="40"/></g>
238+
</g>
239+
<g>
240+
<rect x="610" y="20" width="180" height="180" fill="#3E7C8C"/>
241+
<rect x="610" y="20" width="180" height="180" fill="none" stroke="#1B3B36" stroke-width="2"/>
242+
<g fill="#EDEFE6"><rect x="640" y="50" width="120" height="120"/></g>
243+
</g>
244+
<g font-family="var(--mono)" font-size="14" fill="#4B6660" text-anchor="middle">
245+
<text x="100" y="232">FP16</text><text x="300" y="232">INT8</text>
246+
<text x="500" y="232">INT4</text><text x="700" y="232">再压就废了</text>
247+
</g>
248+
</svg>
249+
</div>
250+
</section>
251+
252+
<section class="plate wrap rise">
253+
<p class="plate__eyebrow">05 / 你会看到的名字</p>
254+
<h2 class="plate__title">下载模型时的那串后缀。</h2>
255+
<p class="plate__note">看到 Q4、INT4、4bit,就是"压过的版本"。</p>
256+
<div class="tags">
257+
<span class="tag"><b>Q4_K_M</b> · 最常用,够小够稳</span>
258+
<span class="tag"><b>Q8_0</b> · 几乎无损,但大一倍</span>
259+
<span class="tag"><b>GGUF</b> · 文件格式,配 llama.cpp / Ollama</span>
260+
<span class="tag"><b>GPTQ / AWQ</b> · 显卡上跑的压缩方案</span>
261+
</div>
262+
</section>
263+
264+
</main>
265+
266+
<footer class="end wrap">
267+
<p class="end__line">量化,<br><em>一点点准确度</em>,换<em>跑得起来</em></p>
268+
<p class="end__foot">END</p>
269+
</footer>
270+
271+
<script>
272+
(function(){
273+
/* --- 02 数字被削短 --- */
274+
var full = "3.14159265", box = document.getElementById("digits");
275+
var keep = 4;
276+
function paintDigits(k){
277+
box.innerHTML = full.split("").map(function(c,i){
278+
return i < k ? '<span class="keep">'+c+'</span>' : '<span class="cut">'+c+'</span>';
279+
}).join("");
280+
}
281+
paintDigits(full.length);
282+
var reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
283+
284+
/* --- 03 控制台 --- */
285+
var levels = [
286+
{name:"FP32", bits:"32 位", gb:28, fit:0, q:100, keepDigits:10},
287+
{name:"FP16", bits:"16 位", gb:14, fit:0, q:100, keepDigits:7},
288+
{name:"INT8", bits:"8 位", gb:7, fit:2, q:99, keepDigits:4},
289+
{name:"INT4", bits:"4 位", gb:3.8, fit:1, q:96, keepDigits:3}
290+
];
291+
var verdictText = {0:"装不下", 2:"刚好卡边", 1:"跑得动"};
292+
293+
var bar=document.getElementById("bar"), barLabel=document.getElementById("barLabel");
294+
var bits=document.getElementById("bits"), size=document.getElementById("size");
295+
var verdict=document.getElementById("verdict"), q=document.getElementById("q"), qfill=document.getElementById("qfill");
296+
var range=document.getElementById("range"), ticks=document.getElementById("ticks");
297+
var SCALE = 320/30; // 320px 高 = 30GB
298+
299+
function apply(i){
300+
var L = levels[i];
301+
bar.style.height = Math.max(L.gb*SCALE, 14) + "px";
302+
bar.dataset.fit = (L.fit===0) ? "0" : "1";
303+
barLabel.textContent = L.gb + " GB";
304+
bits.textContent = L.bits;
305+
size.textContent = L.gb + " GB";
306+
verdict.textContent = verdictText[L.fit];
307+
verdict.dataset.fit = L.fit;
308+
q.textContent = L.q + "%";
309+
qfill.style.width = ((L.q-90)/10*100) + "%";
310+
paintDigits(L.keepDigits);
311+
Array.prototype.forEach.call(ticks.children, function(s,k){
312+
s.dataset.on = (k===i) ? "1" : "0";
313+
});
314+
}
315+
range.addEventListener("input", function(){ apply(+range.value); });
316+
Array.prototype.forEach.call(ticks.children, function(s){
317+
s.addEventListener("click", function(){ range.value = s.dataset.i; apply(+s.dataset.i); });
318+
});
319+
apply(0);
320+
321+
/* --- reveal --- */
322+
var items = document.querySelectorAll(".rise");
323+
if(!("IntersectionObserver" in window) || reduce){
324+
items.forEach(function(el){ el.classList.add("on"); });
325+
} else {
326+
var io = new IntersectionObserver(function(en){
327+
en.forEach(function(e){ if(e.isIntersecting){ e.target.classList.add("on"); io.unobserve(e.target); } });
328+
},{threshold:.15});
329+
items.forEach(function(el){ io.observe(el); });
330+
}
331+
})();
332+
</script>
333+
</body>
334+
</html>

0 commit comments

Comments
 (0)