8 美元芯片跑通 28.9M 模型,端侧卡的从来不是算力
一块 8 美元的单片机,跑起了一个 28.9M 参数的语言模型,端到端 9.5 tok/s(tokens per second,每秒吐几个词的碎片),不联网,不连服务器,全在芯片里算完。
然后它张嘴说的第一句话是,从前有一只小兔子。
这个叫 esp32-ai 的项目今天凌晨冲上了 Hacker News 热榜,仓库九百多颗星。我刷到的时候第一反应跟大部分人一样,好家伙,八块钱都能跑大模型了。第二反应是去翻它的 RESULTS.md,翻完之后那个「好家伙」的位置换了一个地方。
它不会答题,不会跟指令,不会写代码,脑子里没有任何关于这个世界的事实。作者自己在 README 里写得明明白白。
但它把 25M 个参数塞进了 flash 里,每生成一个 token 只从 flash 读 450 字节。
这两句话,第二句才是这个项目的价值。今天想聊的就是这个落差,以及为什么 HN 评论区那批骂它「没用」的人,其实也没全说错。
先把传播里的水挤掉
X 上有个大 V 转这个项目,标题是「这是一台完整的 8 美元 AI 电脑」,还顺手做了个对比,说初版 ChatGPT 是 1.17 亿参数,所以这玩意大概有四分之一的智商。XDA 的报道标题更直接,有人把 28.9M 的 LLM 塞进了 ESP32-S3,你也可以。
我看到「四分之一的智商」这句的时候是真的没绷住。
参数量跟能力之间那个关系,从来就不是这么线性的。更要命的是,这个模型的训练数据只有 TinyStories,一个合成的儿童短故事数据集,几千个常用词,语法简单,情节就是小动物找妈妈那种。用这个数据训出来的模型,它学会的是「怎么把一个短故事讲得前后不打架」,不是「世界是怎么运转的」。
作者本人的表述比任何一篇转载都诚实。他在仓库里老老实实致谢了 TinyStories 的原始研究和 Karpathy 的 llama2.c,然后写道,架构上的进步并没有解决小计算核心在推理能力上的天然限制。他还特意在仓库里保留了开发过程中的历史记录,包括一个自己在参数统计上犯的 bug,就为了让人看到这是个实验,不是个产品。
一个作者自己都在拼命标注边界的项目,传到中间层就变成了「8 美元的 AI 大脑」。这个转化率有点子牛逼。
我是真的觉得,现在 AI 圈最累的不是跟上技术,是每天要花一半精力把营销话术从技术事实上刮下来。
真正值钱的那手,是把参数分成了两类
好,水挤完了,说正事。
作者在 README 里有一句话,我觉得是整个项目的题眼。大意是,一个语言模型的大部分参数其实待在 embedding 表里,模型是去读它,而不是去算它。
这句话听着像废话,但你顺着往下想一层就不一样了。
先解释一下 embedding 表是什么。模型不认识汉字也不认识英文单词,它只认向量。所以每个 token 进模型之前,得先去一张巨大的表里查一行出来,把「兔子」这两个字换成一串数字。这张查找表,在小模型里往往占了参数总量的一大半。
关键在于,这张表的使用方式是查,不是算。矩阵乘法要把整块权重搬进高速内存反复碾,查表只需要把你要的那一行拎出来。这两种访问模式对硬件的要求,差着一个数量级。
传统的做法是不分家的,模型多大就得有多大的 RAM。ESP32-S3 内部 SRAM 只有 512KB,你要按这个思路做,能跑的模型撑死几十万参数。此前同类芯片上的公开记录是 DaveBben 的 esp32-llm,26 万参数。
作者干的事是把参数按「算不算」拆开,然后按访问速度分三层住。
| 存储层 | 容量 | 住什么 | 实测带宽 |
|---|---|---|---|
| SRAM | 512KB | 每个 token 都要参与计算的 dense core,55.9 万参数,4-bit 下 273KB | 顺序读 240 MB/s |
| PSRAM | 8MB | 输出头 3.1M 参数 + 工作内存,共占 1.64MB | 顺序读 60.7 MB/s |
| Flash | 16MB | 25M 参数的查找表,内存映射直接执行 | 512 字节行随机读 20.3 微秒 |
每个 token 只从 flash 里捞 6 行,加起来约 450 字节,代价 0.12 毫秒。
0.12 毫秒。一个 token 从头到尾要 102.9 毫秒,这 25M 参数只占掉了千分之一的时间。
这套东西的学名叫 Per-Layer Embeddings,出处是 Google 在 Gemma 3n 上用的技巧,本来就是为端侧设计的。作者把它从手机搬到了单片机上,参数量做到前人的 110 倍,速度还差不多。
厉害了。这不是把模型压小了,这是把模型摊平了。
它证明的不只是「能塞进去」,还有「塞进去不亏」
我一开始担心的是,这种拆法会不会拿精度换体积。毕竟把参数扔进 flash 听起来就像把常用工具放到地下室。
作者的消融实验把这个担心堵死了。
同样的词表规模下,不用 PLE 的基线困惑度是 12.58,用了之后 11.41。困惑度这个指标是越低越好,简单说就是模型看到下一个词时有多「意外」,数值低说明它猜得准。这一手不但没掉,还涨了 9.3%。
更有意思的是量化之后的表现。4-bit 量化(把权重从 16 位浮点压成 4 位整数,体积砍到四分之一,代价是精度有损)之后,基线掉了 0.079 到 0.088 nats,PLE 版本只掉 0.055 到 0.061。也就是说压缩对基线的伤害更大,PLE 的相对优势反而从 100% 涨到了 124% 到 128%。
出货用的那套格式(group 128,fp16 缩放系数)也是一样的规律,优势保留 121% 到 126%。
然后是我最喜欢的那组数据,小词表消融。作者想搞清楚,性能提升到底是因为那张大表本身,还是因为 PLE 这套连线方式。他做了四个对照,基线 8.21,PLE 8.00,单纯把 embedding 加厚 8.26,保留 PLE 的走线但把表拿掉 8.35。
结论一句话,干活的是那张表,不是走线。

我看到这行的时候在心里给他鼓了个掌。这年头肯做消融、肯把「我这套设计其实有一半是没用的」写进结果文件的人,不多了。做过实验的都知道,消融跑出来发现自己的巧思贡献是负数,那种感觉挺酸的。
瓶颈在一个大家都没盯着的地方
再说一个我觉得最有工程价值的发现。
作者把每个 token 的耗时按模块拆开了。你猜哪块最慢。
输出头,57.6 毫秒,占了 56% 的墙上时间。注意力机制 25.6 毫秒占 25%,PLE 8.5 毫秒占 8%,前馈网络 6.9 毫秒占 7%,输入 4.4 毫秒占 4%。

平时聊模型优化,大家的注意力(一语双关了,不好意思)几乎全在 attention 上,什么 flash attention 什么 KV cache(把已经算过的中间结果缓存下来,避免每生成一个词都从头重算)。结果在这台设备上,attention 只吃掉四分之一,真正的大头是最后那层把隐藏状态映射回词表的输出头。
原因也很朴素。输出头 3.1M 参数住在 PSRAM 里,每个 token 都要把 1.5MB 数据完整过一遍,PSRAM 顺序读只有 60.7 MB/s,算下来 17.3 毫秒是硬成本。作者的原话是,头现在卡在 PSRAM 带宽上了。
这就带出一个我觉得对所有做端侧的人都成立的判断。在小设备上,你的性能账本不是按 FLOPs 记的,是按「每个 token 要搬多少字节、从多慢的地方搬」记的。
作者算过这台机器的带宽天花板,大概 58 tok/s。他还说 SIMD 指令完全没用上,现在全是标量运算,但下一步 15% 的提速已经贴着带宽天花板了。意思是就算把向量指令都堆上,也拉不开多少。算力早就不是瓶颈了,数据搬运才是。
我平时的活是给模型搭那层让它真正能干活的工程脚手架,agent 的工具链、上下文预算、超时重试这些。跟这个项目八竿子打不着,但看到这个结论的时候我有点恍惚,因为在云端做上下文管理踩的坑,形状居然是一样的。你以为瓶颈是模型不够聪明,实际上大部分时候是你在反复搬同一堆字节。缓存没命中、上下文重新编码、工具返回值原样塞回去。算力在那儿等着,管道堵着。
尺度差了六个数量级,病因是一个。
那批说「没用」的人,也没全错
HN 上 130 分 28 条评论,褒贬基本对半。
夸的那批说得挺实在。有个叫 rao-v 的说,这个 per-layer embedding 技巧用得非常漂亮,你可以让一个没有网络的 ESP32 把东西读出来。还有人说 9.7 tok/s 居然还挺快,挺好玩的。
骂的那批更狠。有人说,我不知道为什么要在这上面跑模型,这个环境受限到对这个任务来说毫无用处。最刻薄的一条把它比作一块布谷鸟腕表,但里面没地方装机芯。
我第一反应是想反驳的。但坐下来想了几分钟,我觉得得承认,从产品角度看,他们说的是事实。
你把这块芯片装进一个智能音箱,它不能回答任何问题。装进一个传感器,它不能理解任何指令。它唯一能干的事是生成语法通顺的儿童故事,而这件事你用一个几百字节的模板加随机数就能做到,效果还更可控。
评论区还岔出去比了一圈硬件。有个叫 titzer 的说,现在 5 美元能在单片机上买到的东西太疯狂了,某款板子最多 256MB 内存,还带 1TOPS 的 INT8 算力。言下之意是,你花这么大力气在 ESP32 上腾挪,不如换块板子。这个账算得也没错。
还有个叫 skippyfish 的说了句很实际的,你能买到 5 毛钱以下的单片机,我可不想再来一次 MCU 缺货。经历过 2021 年那波芯片荒的硬件工程师看到这条估计会心一笑。
所以骂声成立在哪。成立在「这个 demo 本身没有产品价值」。
不成立在哪。不成立在把「demo 没用」等同于「这事没意义」。
有个叫 dannyw 的评论我觉得说到点子上了,好玩和学到东西本身就是很好的理由,我大部分嵌入式项目其实都没什么实用性。还有 NooneAtAll3 说,在小设备上跑 LLM 很酷,但我更佩服的是能训出这些权重的训练过程。
这个项目的输出不是那个会讲故事的芯片,是那份 RESULTS.md。是「参数可以按访问模式分层存放」这个被完整验证过一遍的工程结论,是「输出头才是端侧瓶颈」这个反常规的剖面数据,是「4-bit 量化后 PLE 优势不减反增」这条可以直接拿去用的经验。
这些东西谁能用上。任何一个正在往手机、手表、耳机、车机里塞模型的人。他们手上的设备都比 ESP32 强得多,但面对的是同一个问题结构,快内存永远不够装下全部参数。
最后
再往回想一层,这套「按访问频率把数据分层」的思路,其实一点都不新。
存储金字塔,寄存器到 L1 到 L2 到内存到磁盘,是每本操作系统教材第三章的东西。虚拟内存、页面置换、内存映射文件,UNIX 那帮人五十年前就想明白了。
大模型这几年跑得太快,快到大家默认「模型必须整个装进显存」是一条物理定律。然后一个人拿着 8 美元的芯片和一份计算机体系结构的常识,跑过来提醒你,那不是定律,那只是你没分层。
我不知道这个项目三个月后还会不会有人记得。九百颗星在 GitHub 上不算什么,热度散得比想象中快。但那份 RESULTS.md 会留在那儿,写着输出头 57.6 毫秒,写着干活的是那张表不是走线,写着 SIMD 还没用上。
一个人,一块八块钱的板子,把一件所有人都觉得没必要做的事做完了,还老老实实把自己算错参数的过程留在提交历史里。
从前有一只小兔子。它讲的第一个故事很难听,但它证明了参数可以住在很慢的地方。
这就够了。