"""【估算 · 嵌入式设备可行性】给定内存预算,函数化 + 量化能装下多大模型。
一句话结论:
本库能把「大模型上嵌入式」的体积/内存这堵墙撬动,但它只做「压缩 + 生成物」,
不含推理运行时——产物要能在嵌入式跑,必须有一个 C/Rust 推理 kernel 去消费它。
本例按「只算权重存储」的口径,把函数化 + 量化(f32→i8→i4)能压出的体积,
对照三档设备(MCU / 边缘 SoC / 64MB 中间档)的内存预算逐行判「装得下 / 装不下」。
对应 README「嵌入式设备可行性」节的估算表。口径与 README 一致:
- f32 体积 = 整个模型参数量 × 4 字节(本库处理对象是「一层权重」,但本节
估的是「整个模型」的上嵌入式体积,故按模型总参数算);
- 函数化压缩比:约 7~17×(取决于 K/n),本节统一取保守中值 12×;
- 再叠加量化位宽:i8=÷4、i4=÷8(相对 f32),f32 函数化=÷12。
这是【估算】,不是【实测】:体积是确定性算术,此处给「装得下」口径;
「跑得动」与「不掉点」是另外两堵更硬的墙,本例只在结尾如实标注边界。
诚实边界(必须记住):
① 上表只算权重、不含激活缓冲(嵌入式运行时激活常比权重更吃内存);
② 掉点口径:跨规模 MLP 扫描的实测(scale_scan.py)+ 真实 ResNet50 函数化验证;
真实大 CNN/ResNet50 层函数化掉点可压到 ±0.5pt,但小层(h=64)叠加掉点仍会到
4.16pt——「装得下」与「不掉点」都取决于层冗余度,本例不代测掉点;
③ 「装得下」≠「跑得动」,推理框架与算力是另一堵更硬的墙。
"""
import _common
MODELS = [
('小 MLP(0.5M)', 0.5e6),
('中 CNN(5M)', 5e6),
('较大 CNN(25M)', 25e6),
('大 ViT(100M)', 100e6),
]
FUNC_COMPRESS = 12.0
BUDGETS_MB = [512, 64, 8]
def _mb(params):
"""参数量 → f32 字节 → MB。"""
return params * 4 / (1024 ** 2)
def _region_mb(f32_mb, bits):
"""函数化后体积:f32 体积 ÷ 函数化压缩比 ÷ 量化压缩比。
bits=32 → 与 f32 同口径(仅函数化 ÷12);8 → ÷4;4 → ÷8。
"""
quant = 32 // bits
return f32_mb / FUNC_COMPRESS / quant
def estimate_embedded():
print('=' * 88)
print('嵌入式设备可行性估算(只算权重存储,不含激活缓冲)')
print('=' * 88)
print('口径:')
print(' · f32 体积 = 整个模型参数量 × 4 字节;')
print(' · 函数化压缩比取 12×(README「约 7~17 倍」保守中值,统一口径);')
print(' · 再叠加量化:i8=÷4、i4=÷8(相对 f32)。')
print()
print(f"{'模型':<18}{'f32体积':>12}" + ''.join(
f"{f'预算{b}MB':>14}" for b in BUDGETS_MB))
print('-' * (18 + 12 + 14 * len(BUDGETS_MB)))
for name, params in MODELS:
f32_mb = _mb(params)
i4_mb = _region_mb(f32_mb, 4)
marks = ''.join(
f"{('✓' if i4_mb <= b else '✗'):>14}" for b in BUDGETS_MB)
print(f'{name:<18}{f32_mb:>11.1f} MB{marks}')
print('-' * (18 + 12 + 14 * len(BUDGETS_MB)))
print()
print(f"{'模型':<18}{'f32':>12}{'f32函数化':>12}{'i8函数化':>12}{'i4函数化':>12}")
print('-' * 68)
for name, params in MODELS:
f32 = _mb(params)
print(f'{name:<18}{f32:>11.2f} MB{_region_mb(f32, 32):>11.2f} MB'
f'{_region_mb(f32, 8):>11.2f} MB{_region_mb(f32, 4):>11.2f} MB')
print('-' * 68)
print()
print('逐行结论(「装得下」判定,取最省 i4 口径):')
for name, params in MODELS:
f32 = _mb(params)
i4 = _region_mb(f32, 4)
i8 = _region_mb(f32, 8)
if f32 <= 8:
note = '轻松(f32 就装得下)'
elif i8 <= 8:
note = f'轻松(i8 函数化 {i8:.2f}MB 即装下)'
elif i4 <= 8:
note = f'仅 i4 能过(i4 函数化 {i4:.2f}MB)'
else:
note = '装不下'
print(f' · {name}:f32 {f32:.1f}MB → i4 函数化 {i4:.2f}MB,{note}')
print()
_print_honesty()
def _print_honesty():
print('=' * 88)
print('三档设备分水岭(统一 12× 函数化压缩比口径):')
print(' · MCU 级(STM32/ESP32,KB~MB RAM):基本不可行——即便 i4 压进体积,')
print(' MCU 也没有算力跑它,本库只能当「存储端压缩」。')
print(' · 边缘 SoC(RK3588/树莓派,GB 级):最佳落点——中模型轻松装下、')
print(' 且「又省又稳」。')
print(' · 中间档(64MB / 8MB 级):25M 参数 i8 即过 64MB,100M 需 i4 才稳妥。')
print()
print('诚实边界(必须一起读,别只看「装得下」的 ✓):')
print(' ① 上表只算权重、不含激活缓冲(嵌入式运行时激活常比权重更吃内存)。')
print(' ② 掉点口径:跨规模 MLP 扫描的实测(scale_scan.py)+ 真实 ResNet50 函数化验证;真实大')
print(' CNN/ResNet50 层函数化掉点可压到 ±0.5pt,但小层(h=64)叠加掉点仍会到 4.16pt——')
print(' 「装得下」与「不掉点」都取决于层冗余度,本例不代测掉点。')
print(' ③ 「装得下」≠「跑得动」,推理框架与算力是另一堵更硬的墙。')
print(' ④ 本库只做「压缩 + 生成物」,不含推理运行时——产物要能在嵌入式跑,')
print(' 必须有一个 C/Rust 推理 kernel 去消费它。')
print('=' * 88)
def main():
estimate_embedded()
if __name__ == '__main__':
main()