|
| 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