Skip to main content
Acorn Framework Logo

🌰 Acorn Framework

Big models. Tiny footprint. —— 芥子纳须弥,把大模型装进口袋。

PyPI Python Deps License Tests

以小搏大的 AI 运行与训练框架:INT4 压缩态常驻 + 原生 AVX2 内核 + 自研 CUDA int4 内核 + KV int8 + 层流式 + 投机解码 + LoRA + AutoBit + 能效治理 + 本地 OpenAI 兼容 API,零依赖核心,全部自研。


⚡ 60 秒看到它跑真模型

pip install acorn-llm          # 零依赖;有 gcc 会自动编译 AVX2 内核
pip install 'acorn-llm[fast]'  # 真实模型(Qwen/LLaMA)需要 NumPy
acorn chat                     # ← 打开本地 AI 聊天 TUI
============================================================
  Acorn 本地聊天室 | Qwen3-8B | eco=whisper | CPU
  /exit 退出 · /new 新对话 · /eco 切档 · /spec 投机 · /raw 渲染开关
============================================================

你> 1+1等于几?

AI> thinking...
    嗯,用户问的是 1+1 等于几。这是最基础的算术……

    think done

    1+1 等于 2。这是整数加法的基本运算……

实测数据(本机:16 核 CPU / 16.5GB RAM / 无 GPU / 一块 P620)

模型 原始体积 Acorn INT4 常驻内存 decode 备注
Qwen3-8B 16.38 GB (bf16) 4.10 GB (7.99×) 4.10 GB 4.9 tok/s(turbo 16 线程) 36 层/32 头/8 KV,纯 CPU 跑通中文思维链;v0.10.0 提速 +41%(修 scales 转换开销 + eco 线程贯通);v0.10.1 长上下文再 +49%(GQA 免展开)
Qwen2.5-0.5B 0.99 GB 0.25 GB (4×) 0.25 GB 10–15.7 tok/s 静音档 2 线程仍同速
7B 规格(合成) 18.5 GB (fp32) 2.91 GB 154 MB(层流式) 4.7 tok/s 省内存 19×
int4 GEMV (4096×11008) 12.3 ms (fp32 BLAS) 1.95 ms 0.5 B/参数 18.6 GB/s 自研 AVX2,机器带宽上限的 72%
GPU int4 GEMV (4096×11008) — 1.68 ms 0.5 B/参数 13.4 GB/s 自研 CUDA 内核(NVRTC),0.9.10 起全形状快于 CPU 内核

🧠 这个框架到底做了什么(不是 PPT,全部实测)

能力 实现 实测/验证
INT4 压缩态常驻 权重以 int4 留在内存,推理时在线解包参与 GEMM,绝不物化 fp 矩阵 7.99× 压缩;数值与参考实现 rel=1.5e-7
原生 AVX2 内核 C 手写:nibble 在线解包 + 256-bit FMA 点积 + 常驻线程池,64 值/迭代 + 软件预取 比 fp32 BLAS 快 6.3×;turbo 24.0 GB/s(带宽上限 92%)
真实模型接入 零依赖 safetensors 读写(单文件/多分片)、LLaMA/Qwen2/Qwen3 架构、BPE 分词器 Qwen3-8B 实跑,中文思维链正确
AutoBit 混合位宽 逐层 KL 敏感度实测 → 字节预算下贪心分配 int4/int8/fp32 同预算质量 KL ↓87%
投机解码 n-gram 草稿 + 批量验证 + cache 回退 ×1.3–1.7,且与常规贪心输出逐字一致(金标准)
KV Cache int8 逐 token absmax 量化 内存 28%,输出偏差 0.4%
层流式加载 按层惰性读取 + LRU 驱逐 7B 常驻 154 MB(4GB 内存设备可跑)
结构化剪枝 + 稀疏内核 FFN 神经元幅度剪枝 + 位图跳零 + 紧凑存储 50% 剪枝:字节 50%、加速 ×1.6、loss 仅 ×1.08
LoRA 轻训练 冻结基座 + 低秩适配器 + 合并回稠密 25.3% 参数学新语料
能效治理 (eco) whisper/balanced/turbo + race-to-idle + 进程优先级;v0.10.0 起真实模型路径也真正生效(此前线程设置被忽略,一直满核) Qwen3-8B:2 线程 1.6 tok/s / 4 线程 2.6 / 16 线程 4.9(同功耗包络下按需取舍)
CUDA Graph 后端 自研 GPU 解码图(捕获+回放) 数值正确;P620 上仍不及 CPU int4 内核(诚实数据)
自研 CUDA int4 内核 NVRTC 运行时编译 + CUDA 驱动 API,8 路在途加载,无需 CUDA Toolkit 数值 rel≤1.3e-7;P620 上 8.8–15.3 GB/s,全形状快于 CPU 内核(0.9.10)
int4 权重常驻显存 LLaMA/Qwen 全模型 int4 权重直接住显存,0.5 B/参数 0.5B 模型 249 MB 显存;与 CPU 模型 prefill+decode rel≤1.4e-5
本地 API(Ollama 风格) OpenAI 兼容端点 + SSE 流式 /v1/chat/completions /v1/models /health /api/show 实测通过

📦 安装

pip install acorn-llm              # 零依赖核心(训练栈 / TinyGPT)
pip install acorn-llm[fast]        # + NumPy(真实模型 LLaMA/Qwen 必需,约 50× 提速)
pip install acorn-llm[gpu]         # + PyTorch(GPU int4 内核 / CUDA Graph 后端)

要求 Python ≥ 3.9。有 gcc/cl 时首次运行自动编译原生内核(缓存到 ~/.cache/acorn);没有编译器也不影响使用(自动回退 NumPy 路径,功能一致、速度较低)。

依赖边界要说清楚:

  • 零依赖核心 = autograd / 训练引擎 / TinyGPT / 打包 / 量化 / 内核编译 —— pip install acorn-llm 即可跑通
  • 真实模型路径(LLaMA / Qwen 推理、转换、API 服务)需要 NumPy —— 装 [fast] 或 [gpu](GPU extra 会带上 NumPy 依赖链)
  • 缺 NumPy 时会直接给出明确提示(pip install 'acorn-llm[fast]'),而不是在深处报类型错误

💬 TUI 聊天(装完就能用)

acorn chat                                  # 自动搜索本地模型
acorn chat --model-dir ./models/Qwen3-8B    # 指定模型目录
acorn chat --model-dir ./qwen --auto-convert  # 目录里是 safetensors 就地转换
acorn chat --system "你是一个简洁的技术助手"   # 设定人设
acorn chat --gpu                            # 用 CUDA 后端(若有)
acorn chat --eco turbo                      # 全速(风扇起飞)

模型从哪来?(三种方式)

① 直接下载已转换的模型? —— 没有官方分发,但转换只需一次(10 分钟内完成)。

② 从 ModelScope / HuggingFace 下载原始权重 → 自动转换(推荐)

# ModelScope(国内直连,无需梯子)
mkdir -p models/Qwen2.5-0.5B
curl -L -o models/Qwen2.5-0.5B/model.safetensors \
  "https://modelscope.cn/api/v1/models/Qwen/Qwen2.5-0.5B-Instruct/repo?FilePath=model.safetensors"
curl -L -o models/Qwen2.5-0.5B/tokenizer.json \
  "https://modelscope.cn/api/v1/models/Qwen/Qwen2.5-0.5B-Instruct/repo?FilePath=tokenizer.json"
curl -L -o models/Qwen2.5-0.5B/config.json \
  "https://modelscope.cn/api/v1/models/Qwen/Qwen2.5-0.5B-Instruct/repo?FilePath=config.json"

# 然后二选一:
acorn chat --model-dir models/Qwen2.5-0.5B --auto-convert   # 自动转 + 直接聊

手动转换(可复用、参数自动读 config.json,rope_theta 等超参不会手填错):

python -m acorn convert --src models/Qwen2.5-0.5B/model.safetensors \
                        --out models/Qwen2.5-0.5B/qwen.acorn

自动从 config.json 读取:层数 / 维度 / 头数 / KV 头 / FFN / 词表 / rope_theta(Qwen2/3 是 1e6!)/ tie_word_embeddings / qk_norm(Qwen3)。 转换是流式的:RAM 峰值 ≈ 单个张量,16GB 内存机器可转 16GB 模型(多分片自动走流式路径)。

HuggingFace 同理,把 URL 换成 https://huggingface.co/Qwen/Qwen2.5-0.5B-Instruct/resolve/main/...。

③ 已有的第三方 .acorn / GGUF? —— GGUF 暂不支持,请先用 acorn convert(支持 safetensors 系)。

模型目录长什么样

models/Qwen2.5-0.5B/
├── model.safetensors     # 原始权重(可选,--auto-convert 用)
├── config.json           # HF 配置(--auto-convert / convert 自动读它)
├── tokenizer.json        # 必需
└── qwen.acorn            # 转换产物(必需,或由 --auto-convert 生成)

该下载哪个模型?(已验证可跑)

模型 规格 下载量 Acorn INT4 后 速度(实测) 备注
Qwen2.5-0.5B-Instruct 0.49B · 24L · d896 · 14Q/2KV 0.99 GB 0.25 GB 14–16 tok/s 首选,秒级响应
Qwen2.5-1.5B-Instruct 1.54B · 28L · d1536 3.1 GB ~0.8 GB ~7 tok/s 中文更好
Qwen3-8B 8.2B · 36L · d4096 · 32Q/8KV · QK-Norm 16.4 GB 4.10 GB 2.4–3.1 tok/s 思维链最强,本机 16GB 可跑
Qwen3-1.7B 1.7B · 28L · d2048 3.4 GB ~0.9 GB ~6 tok/s 质量/体积最佳平衡

Qwen3 用 QK-Norm 架构,Acorn 已支持(数值 vs 独立参考实现 rel=3.8e-7); Qwen2/3 的 rope_theta=1e6 会在转换时自动从 config.json 读取(手填 10000 会得到乱码)。

TUI 里的命令

命令 作用
/new 开新对话(清空上下文与 KV)
/eco whisper|balanced|turbo 能效档位:静音 / 甜点 / 全速
/spec 投机解码开关(重复文本/代码可提速)
/think 思考内容显隐开关(隐藏时只留 think done · N 字 行)
/len [N] 查看/调整单轮回答上限(默认 4096;触顶会有 ⏹ 已达上限 提示)
/raw 关闭 Markdown 渲染(看原始输出)
/info 当前档位、线程数、轮次、上下文长度
Ctrl+C 中断本轮生成——上下文自动回滚,会话可继续(不重算历史)
/exit 退出

界面说明

  • 思考流:模型的 thinking 内容以深灰逐字显示,头部 thinking...,结束切 think done · N 字
  • 正文:完整流式 Markdown——标题/列表/引用/代码块结构着色,行内 **粗体** / *斜体* / `代码` / [链接](url) 同样渲染(标记延迟闭合缓冲:纯文本逐字即出,标记闭合即整段着色,未闭合原样保留不吞内容)
  • 跨轮复用 KV:每轮只前向增量,历史不重算
  • 回答上限:默认 4096 token(--max-tokens / /len N 可调);自动钳制到上下文剩余额度,不会写越界
  • 颜色:自动探测终端能力——不支持 ANSI 的终端(老 conhost / 输出重定向)一个转义都不发,只输出干净文本(markdown 标记会被剥离,不会看到 ** 残留);强制开色用 ACORN_FORCE_COLOR=1,强制关色用 NO_COLOR=1

think done 的判定规则(不是靠猜)

思维链的结束必须可靠,否则正文会被误吞或被提前吐出。Acorn 用三级判定,从精确到兜底:

优先级 规则 为什么
① 显式标签(主路径) 解码器把 </think> special token 透传为文本,状态机在标签处精确切换;词表含 </think> 时空行启发式整体关闭 Qwen3 的 </think> 是 special token 不在普通 vocab——曾因被流式解码器丢弃而只能靠空行猜边界(v0.9.8 位置 bug 根因);且启发式若先切,随后到达的真标签会泄漏成正文文字(v0.9.9 实测)
② 空行启发式(兜底) 仅当词表没有 </think> 标签时启用;单/双空行均要求思考已 > 600 字符 思考内部出现空行(哪怕 \n\n\n 整块 token)很常见——实测 Qwen3-int4 在 ~240 字符处吐过 \n\n\n
③ 生成结束 finish() 兜底切换 保证 think done 一定出现

设计取向:早切(思考尾部当正文,不可挽回)比晚切(正文短暂灰色,随流纠正)危害更大。 标签容错:thinking 态重复 <think> 静默吞掉;answer 态游离 </think> 吞掉不泄漏;answer 态新 <think> 重新进入思考。


🌐 本地 API(Ollama 风格,OpenAI 兼容)

一条命令把你的模型变成局域网服务,任何 OpenAI 客户端(LangChain / LlamaIndex / Cursor / Continue / 自写脚本)都能直接接:

acorn serve --model-dir ./models/Qwen2.5-0.5B --port 11435
# 或从 safetensors 自动转换后直接起服务
acorn serve --model-dir ./models/Qwen3-8B --auto-convert
端点 方法 说明
/v1/chat/completions POST 对话;"stream": true 返回 SSE 增量(data: 帧,末帧 data: [DONE]);支持多模态 image_url
/v1/completions POST 文本补全
/v1/models GET 模型列表(OpenAI 格式)
/health GET 健康检查
/api/show POST Acorn 扩展:常驻内存 / 位宽 / 内核状态 / 档位

端口:默认 11435(Ollama 占用 11434,互不冲突);端口被占时会自动顺延并打印实际地址。

⚡ 会话级 KV 复用(v0.10.1)

多轮对话不再重算历史——按 token 前缀匹配,复用上一轮的 KV cache(llama.cpp / ollama 同类机制):

curl http://127.0.0.1:11435/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "X-Acorn-Session: my-chat" \        # 会话标识(缺省为单会话)
  -d '{"model":"qwen","messages":[...]}'
  • 命中前缀 → 只前向新增部分;响应的 acorn.reused_tokens 显示复用了多少 token
  • 逐位精确:复用的位置 token id 完全相同 → KV 必然相同(实测 diff=0)
  • 最多保留 4 个会话(LRU 淘汰,每个占一份 KV 内存);多模态请求不参与复用(图像 embedding 与位置绑定)

🖥️ 内置 Web UI(v0.11,浏览器即产品)

acorn serve 后打开 http://127.0.0.1:11435/ 就是一个完整的本地 AI 界面:

能力 说明
流式聊天 SSE 增量渲染,前端自带 Markdown(标题/列表/代码/引用)
多模态 贴图 / 拖图 → 走 image_url 通道(data URI,离线可用)
能效档位 界面直接切 静音 / 均衡 / 全速
生成参数 温度 / token 上限实时可调
本地模型库 自动列出本机 .acorn 文件与体积
零外链 纯 HTML + 原生 JS,无 CDN / 无构建工具 → 断网可用

终端用户不需要懂命令行——这是与 ollama 正面竞争的第一道门槛。

用 curl 直接聊(零依赖验证):

curl http://127.0.0.1:11435/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen","messages":[{"role":"user","content":"1+1等于几?"}],"stream":true}'

服务端默认 --eco whisper(静音档,风扇不转);要吞吐就 --eco turbo。 纯标准库 http.server 实现,零额外依赖。


🎯 int4 量化质量:分组量化(g=128),不是"逐通道凑数"

这是 v0.11.4 最重要的一次修正:我们原来的 int4 是"逐输出通道一个 scale"的经典 RTN, 实测把模型打残(Qwen2.5-0.5B-Instruct,困惑度 37 → 135,模型开始胡言乱语)。 现在改成分组量化(沿输入维每 128 个值一个 scale,行业标准做法,文件格式不变):

量化方案 体积 困惑度(400 token 内置语料) 0.5B 解码速度
fp32(参照) — 37.4 —
逐通道 RTN(旧,v0.11.3) 249.1 MB 134.9 25.5 tok/s
分组 g=128(现在) 262.7 MB 83.6 13.1 tok/s
分组 g=256 255.9 MB 93.2 16.6 tok/s
int8 逐通道(对照) ~494 MB 37.0 —

同一体积下质量差 2 倍;代价是解码慢约一半(分组内核每 64 值换一次 scale)。 想换回速度:ACORN_INT4_GROUP=0(退回逐通道,质量会明显变差,不建议)。

配套实现与验证:

  • C 内核新增分组变体(acorn_gemm_i4_f32_group):每组一次 scale 加权,整条通道只做一次水平归约 (逐组归约会慢 4~5 倍);复用常驻线程池,不额外占内存;
  • 容器格式不变(scales 变多而已,header 多一个 group 字段,老读取器忽略);
  • 回归 tests/test_v28.py:round-trip / 回退规则 / 内核对拍 / 嵌入 gather / 容器往返 / 端到端 logits;
  • 推理引擎本身经独立参考实现对拍确认无误(fp32 权重下 max diff 0.0001、top1 100%) —— 所以之前的胡言乱语是量化问题,不是引擎问题。

一句话:"更小"必须同时"不烂"。否则省下的那点磁盘,换来的是一台会说胡话的机器。


🔥 自研 CUDA int4 内核(不装 CUDA Toolkit 也能跑)

acorn/gpu_int4.py 是自己写的 CUDA int4 GEMV/GEMM/gather 内核,不是调 torch eager:

  • 编译方式:用 PyTorch wheel 自带的 nvrtc64_*.dll 运行时编译 PTX,无需安装 CUDA Toolkit(不用 nvcc)
  • 驱动接入:直接调 nvcuda.dll 驱动 API(cuModuleLoadData / cuLaunchKernel),不依赖 numba
  • 内核内容:nibble 在线解包 → __dp4a/FMA 点积 → 共享内存分块 → 多行 GEMM(prefill 友好)
  • 数值正确:GEMV/GEMM 相对误差 ≤ 1.3e-7;int4 GPU 模型 vs CPU 模型 prefill+decode rel ≤ 1.4e-5
from acorn import gpu_int4 as GI
print(GI.info())
# {'available': True, 'device': 'Quadro P620', 'cc': (6, 1),
#  'toolchain': 'NVRTC (torch 自带) + CUDA 驱动 API, 无需 nvcc'}

诚实的速度对比(本机 Quadro P620 / CC 6.1 / 4GB VRAM)

0.9.10 内核升级:GEMV/GEMM 改用 8 路在途加载(该内核是访存延迟受限,提高内存级并行才有效)。 变体实测(4096×11008):逐字节 4 路 ILP 12.0 GB/s → 8 路 ILP 13.9 GB/s;而 uint4 向量化 5.0、x 驻留 shared memory 7.2——在 Pascal 上反而劣化(教训如实记录)。

R × C GPU int4(自研 CUDA) CPU int4(自研 AVX2) torch fp32 GPU/CPU
1024×1024 0.06 ms · 8.8 GB/s 0.13 ms · 3.9 GB/s 0.06 ms · 72 GB/s ×2.22
2048×2048 0.16 ms · 13.4 GB/s 0.34 ms · 6.2 GB/s 0.22 ms · 77 GB/s ×2.16
4096×4096 0.55 ms · 15.3 GB/s 0.86 ms · 9.8 GB/s 0.88 ms · 76 GB/s ×1.57
4096×11008 1.68 ms · 13.4 GB/s 2.10 ms · 10.7 GB/s 2.50 ms · 72 GB/s ×1.25
4096×14336 2.55 ms · 11.5 GB/s 2.69 ms · 10.9 GB/s 3.29 ms · 71 GB/s ×1.06
8192×8192 2.35 ms · 14.3 GB/s 2.54 ms · 13.2 GB/s 5.19 ms · 52 GB/s ×1.08

测量方法:每格 3 轮 × 20 次迭代取最小值(examples/bench_gpu_kernel.py 可复现)。 0.9.10 起 GPU 内核在全部形状反超 CPU 内核(×1.06–×2.22;0.9.9 及之前为 0.7×)。 本机同时有其他程序(含另一个 LLM 运行时)占用 CPU/GPU,实测方差可达 ±40%。

结论必须说清楚:

  • 在这张 P620(2016 年 Pascal,1.9 TFLOPS FP32、186 GB/s 显存带宽) 上,自研 GPU int4 内核达 8.8–15.3 GB/s,已全面快于 CPU AVX2 int4 内核,但仍远低于显存带宽理论值——Pascal 无 int4 tensor core,这是架构上限
  • torch fp32 因为能用 tensor core(TF32 通路)跑到 52–77 GB/s,仍快于我们的 int4 内核
  • GPU int4 内核的核心价值是把 int4 权重常驻显存:0.5B 模型仅占 249 MB 显存(0.5 B/参数),CPU 侧则可把系统内存留给别的任务
  • 在现代 GPU(Ampere/Hopper,带 int4 tensor core)上预期会大幅提升 —— 但这一点我们还没有硬件验证,不作为已证实的结论

CPU AVX2 内核(0.9.10 小幅提效 + 一个缓存 bug 修复)

  • 点积主循环 32 → 64 值/迭代(8 路累加器)+ 软件预取。严格 A/B(新旧 DLL 同进程交替测量,消除时间漂移):
    • whisper 2 线程:+1.5 ~ +3.3%;balanced 4 线程:+2.0 ~ +3.8%;turbo 16 线程:±0(已逼近带宽上限)
    • 提升发生在能效档位——同线程数、同功耗包络下的纯每字节效率提升("温度不提高的提速")
  • 修复内核缓存 bug:编译缓存原本只按 平台_CPU数 命名、不含源码哈希——升级 acorn 后仍加载旧内核 DLL(静默性能回退)。现在缓存名含源码 SHA1,源码升级自动重编

另一组诚实数字(turbo 16 线程,交替 A/B):4096×14336 达 24.0 GB/s(机器带宽上限 26 GB/s 的 92%); 而 whisper 2 线程即达 10 GB/s——每线程效率是 16 线程的 3.6 倍,这是 eco 能效治理的数据支撑。

int4 权重常驻显存(gpu_model.py)

from acorn.runtime import load
m = load("models/Qwen2.5-0.5B/qwen.acorn", device="cuda")   # 自动走 GPU int4 路径
  • 所有 int4 权重直接住显存(不物化 fp32),0.5B 模型 249 MB
  • 完整 LLaMA/Qwen 前向:RMSNorm / RoPE / GQA / SwiGLU / QK-Norm / KV Cache 全部在 GPU
  • 与 CPU int4 路径逐 token 数值一致(top-1 相同,logits rel ≤ 1.4e-5)
  • ⚠️ P620 只有 4GB 显存:8B 模型 int4 需 4.10 GB,放不下

🏁 为什么还比 llama.cpp 慢 20–30%?(诚实的差距分析)

llama.cpp 是 MIT 开源 的,我们已按其核心思路实现(int4 权重 + int8 激活 + 整数点积,代码在 acorn/_kernels/acorn_int4.c 的 dot_i4_i8_avx2)。差距来自三处:

# llama.cpp 做到的 我们现状 能否补
1 权重预重排(repack):把 int4 存成"免洗牌"的布局,运行时不做 unpack 我们运行时现解包(每 32 值约 6 条指令) 可,需新增存储布局(v3 格式)
2 int8 激活 + vpmaddubsw 整数点积 已实现同等技术(dot_i4_i8_avx2),但本机编译器把它编错 见下
3 十年手写汇编(AVX2/AVX512 多版本 + 手调调度) 纯 C 内建函数 难,短期不追

关于第 2 条(本轮实测的重要发现):

本机: AMD Ryzen 7 4800H (Zen 2) + TDM-GCC 10.3
  -O0/-O1: vpmaddubsw/vpmaddwd 序列正确  -> 22.8 GB/s (fp32 路径的 5 倍)
  -O2/-O3: 该指令序列被 GCC 误编译        -> 结果错误(已实测)
  同一段代码在独立函数里 -O0/-O1 正确, 在内核里 -O0 也错 -> 编译器 codegen 问题

我们的处理:构建期自检——编译后用已知数据验证该路径,不通过就自动禁用(绝不输出错误结果)。 在编译器正确的机器(MSVC / clang)上,这项技术会自动生效,GEMM 提速约 5 倍。

启用方式(仅在自检通过时有效):

ACORN_ACTQ=1 acorn chat     # 整数点积路径(激活 int8 量化, logits 相对偏差 ~1e-3)

诚实结论:在 GCC 编译环境下我们追不上 llama.cpp 的汇编级优化; 但我们的 int4 压缩率(0.5 B/参数)、零依赖、单文件完整模型、训推一体、多模态、 能效治理是它没有的。若要 CPU 推理绝对速度,最经济的路是直接用 llama.cpp 的 后端(我们可把它作为可选 backend 接入)。


📦 .acorn v2 —— 一个文件 = 一个完整大模型

.acorn 不只是权重容器,而是完整模型包:权重(int4) + tokenizer + 多模态视觉塔 + 训练状态 + 校验 + 元信息,全在一个文件里。拷走一个文件就能在另一台机器上跑。

# 把 tokenizer(和多模态)合并进 .acorn —— 不重写权重, 4GB 模型也只要几秒
acorn pack --src model.acorn --out model-single.acorn \
           --tokenizer tokenizer.json --vision model.mm --crc

# 查看模型内部结构(给客户/审核看)
acorn inspect model-single.acorn --verify

# 之后加载【不需要】外部 tokenizer.json
acorn chat --model-dir .        # 或 acorn.load("model-single.acorn")
段 内容 作用
tensors int4/int8/fp32 张量(块布局,0.5 B/参数) 权重本体,mmap 惰性加载
tokenizer 内嵌 tokenizer.json 原始字节 单文件即完整模型
multimodal 内嵌视觉塔 + projector(.mm) 多模态随模型走
training 训练状态(优化器动量等) 训推同格式,可续训
model_info 参数数/来源/许可/时间戳 分发与授权元数据
crc32 每张量校验和 损坏检测(inspect --verify)

向后兼容:v1 文件照常读(无内嵌段即旧行为);v1 读取器读 v2 时会忽略新字段。


🐿️ 越小越好 —— 带质量报告的模型瘦身

行业默认是"模型越大越好"。Acorn 反着做:在可接受的质量损失内把模型变小, 并给出可复现的质量报告——不吹"无损",每个数字都能在你机器上复跑。

# FFN 神经元剪枝(激活感知打分, 默认) + 质量报告
acorn shrink --model qwen.acorn --out small.acorn --prune-ffn 0.2 --report

# 按目标体积自动搜方案(预算内质量损失最小)
acorn shrink --model qwen.acorn --out small.acorn --target-mb 200

实测曲线(Qwen2.5-0.5B int4,249.1 MB 基线;下表全部由上面的 CLI 命令直接打印, 一条命令即可复现;内置校准语料 16 句、困惑度基线 154.0):

剪枝 体积 省 困惑度 质量损失 KL top1 一致率*
10% 233.3 MB 6% 194.4 +26% 0.61 50%
20%(推荐档) 217.6 MB 13% 230.7 +50% 1.17 62%
25%(激进档) 209.7 MB 16% 280.6 +82% 1.34 38%
30% 201.7 MB 19% 340.6 +121% 1.60 62%
50%(不建议) 170.2 MB 32% 925.0 +501% 1.81 62%

* top1 一致率只测 8 条提示,噪声大,困惑度是主指标。

校准量会影响结果:上表用 16 句校准;把校准语料加到 25 句时,20% 档的 困惑度损失从 +50% 降到 +37%。排序更准 = 同样剪枝比例质量更好。 用 --calib 32 可以加大校准集。

诚实清单:

  • 纯权重幅度打分(--score weight)在真实分布下是灾难(剪 10% 困惑度爆到 14 万), 所以默认激活感知;这是 Wanda/LLM-Pruner 一脉的零训练近似,不做恢复训练。
  • 丢层(--drop-layers)对小模型很贵:丢 1 层省 3% 但困惑度 +27%,丢 2 层 +82%。 大模型的深度冗余更明显,0.5B 上不推荐。
  • 剪枝直接作用在 int4 块布局上(不物化 fp32),块级实现与"解量化后剪再量化" 逐位等价(回归测试 tests/test_v26.py,rel < 1e-6)。
  • 校准前向与推理引擎逐位对齐(含 Qwen QKV 偏置 / QK-Norm / GeGLU / 滑窗), 对拍 logits rel ≈ 2e-06。

🖼️ 多模态(视觉塔 + 投影器)

Acorn 支持 LLaVA / Gemma 3 一类"视觉塔 + projector"架构的图像输入:PNG 零依赖解码 → 视觉塔 → projector → 图像 embedding 注入 LLM 的输入序列。

# 1) 转换(LLM 走常规 .acorn;视觉塔走 .mm 旁挂,不重写主文件)
acorn convert --src model.safetensors --out model.acorn \
              --vision-src ./model_dir --vision-preset llava

# 2) 聊天时带图
acorn chat --model-dir ./model_dir --image cat.png
#    会话内也可: /image cat.png

# 3) API(OpenAI 多模态格式)
curl http://127.0.0.1:11435/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"m","messages":[{"role":"user","content":[
        {"type":"text","text":"这是什么?"},
        {"type":"image_url","image_url":{"url":"data:image/png;base64,..."}}]}]}'
组件 实现 说明
PNG 解码 acorn/vision.py 零依赖(zlib + 反滤波) 8bit 灰度/RGB/调色板/RGBA,非隔行;JPEG 需 Pillow(可选)
预处理 双线性缩放 + 归一化 → (3, S, S) float32
视觉塔 patch embed + pre-LN transformer 块 + 末端 LN ViT/SigLIP 通用形态,QKV 融合,权重 int4 可用
投影器 linear / 2 层 MLP 输出维度与 LLM hidden_size 强校验(不一致直接报错)
注入 图像占位 token 展开成 N 个位置 + embedding 覆盖 占位展开与注入严格对齐

诚实限制:

  • 只实现通用 ViT/SigLIP 形态的视觉塔;Gemma 3 的 pan-and-scan 多裁剪与 SigLIP resampler 未实现(Gemma 3 需额外适配)
  • Gemma 3 的 LLM 侧部分兼容(v0.10.2):activation="geglu" / rms_norm_one_plus_w=True / final_logit_softcapping 已实现(合成测试验证生效);滑窗/全局混合注意力仍未实现,且未经真实 Gemma checkpoint 验证 —— Gemma 权重仍不能直接跑
  • 转换器只搬运张量,不做数值等价验证(需真 checkpoint 对照)
  • 视觉塔默认 fp32(0.4B → 1.6 GB),可 --vision-quant 走 int4

🧪 30 秒训一个自己的模型

import acorn

model = acorn.train(corpus=open("my_novel.txt", encoding="utf-8").read(),
                    steps=300, batch_size=12, ctx=64)
acorn.pack(model, "mymodel.acorn")     # INT4 打包
m = acorn.load("mymodel.acorn")
print(m.generate("第一章", 200, temperature=0.8))

训练栈全是自研:矩阵级 autograd(13 个可微节点)· AdamW · warmup + 余弦调度 · 全局范数梯度裁剪 · 数值对拍测试(44+9 位置有限差分)。

LoRA 轻量微调

import acorn
from acorn import lora

model = acorn.train(steps=100)
lora.inject_lora(model, rank=8)          # 冻结基座 + 注入低秩适配器
acorn.train(model=model, corpus="新领域文本…", steps=60)
lora.merge_lora(model)                   # 合并回稠密权重
acorn.pack(model, "tuned.acorn")

真实模型微调(诚实说明)

  • 0.5B 级:本机可行(acorn.to_trainable(acorn.load(...)) → LoRA 流程同上)
  • 8B 级:fp32 反量化需 32GB 内存,本机不可行;且 LLaMA 路径目前是数值推理路径,尚未接入 autograd 图——这是路线图 M2 项

🖥️ CLI 全表

acorn chat                             # ★ 交互式 TUI 聊天
acorn serve --model-dir ./models/Qwen2.5-0.5B   # ★ 本地 OpenAI 兼容 API
acorn pull Qwen2.5-0.5B-Instruct       # ★ 一键下载模型 + 转成 .acorn
acorn say "用一句话介绍杭州"            # ★ 一行命令直接出答案
acorn train  --steps 200 --out m.acorn # 训练 + INT4 打包
acorn run    --model m.acorn --stream  # TinyGPT 推理
acorn convert --src X.safetensors --out X.acorn   # 真实模型转换(自动读 config.json)
acorn shrink --model X.acorn --out small.acorn --prune-ffn 0.2 --report  # 瘦身+质量报告
acorn run-llama --model X.acorn --tokenizer tok.json --prompt "你好" --stream
acorn bench  --model m.acorn           # 基准(吞吐/带宽/规模外推/内核状态)
acorn lora   --model m.acorn --corpus my.txt --steps 150   # LoRA 微调

🧰 Python API 速查

API 用途
acorn.train(steps=, corpus=, lr=, batch_size=, ctx=) 训练
acorn.pack(model, path) INT4 量化打包(投影融合 + 嵌入表 gather 布局)
acorn.load(path, tokenizer_path=, kv_cache_kind="int8", ctx=, eco=, device=) 加载(自动按架构分派)
m.generate_chat(prompt, …, repetition=, speculative=) 对话式生成(自动模板 + 重复惩罚 + 停止序列)
m.generate_stream(...) / m.generate(...) 生成(token 级流式)
ChatSession(m).send(text) 多轮对话(跨轮 KV 复用)
m.set_eco("whisper") 能效档位切换
acorn.load_streaming(path, max_resident_layers=2) 层流式加载(内存省 19×)
acorn.bench(m) 性能基准
acorn.shrink.shrink(tensors, cfg, prune_ffn=, act_scores=) 模型瘦身(激活感知剪枝 + 丢层)
acorn.pull.pull("Qwen2.5-0.5B-Instruct") 一键下载 + 转换(ModelScope 直连)
acorn.autobit / acorn.prune 混合位宽量化 / 结构化剪枝
acorn.llama.convert_safetensors(...) / acorn.shard.convert_sharded(...) 真实模型转换器
acorn._kernels.info() 内核状态(AVX2/线程/编译器)
acorn.gpu_int4.info() GPU int4 内核状态(设备/CC/工具链)
acorn.server.main(["--model-dir", ...]) 起本地 OpenAI 兼容 API 服务

🎛️ 调优菜单(按需叠加,互不冲突)

手段 效果 怎么用
INT4 量化 权重 ÷8 默认开启
KV int8 长上下文 KV ÷4 load(..., kv_cache_kind="int8")
能效静音 同速、省电、风扇不转 load(..., eco="whisper") 或 m.set_eco()
层流式 常驻 ÷19(吞吐换空间) acorn.load_streaming(path)
AutoBit 同体积质量 ↑87% convert(..., autobit={...}, model=...)
剪枝 + 稀疏 字节 ÷2、加速 ×1.6 acorn.prune.prune_ffn(...)
投机解码 有效 tok/s ×1.3–1.7 generate_chat(..., speculative=True)
CUDA Graph GPU 解码零发射开销 load(..., device="cuda")

🏗️ 架构

L5 用户 API      acorn chat / serve / train / pack / load / generate_chat / bench
L4 编排层       AutoBit · LoRA · 剪枝 · 转换器 · 能效治理
L3 运行时        AcornPack 容器 · 层流式调度 · 分页/int8 KV Cache · 投机解码
L2 计算层        微型 autograd(13 节点) · RoPE · SwiGLU · GQA
L1 内核后端      AVX2 int4 GEMM(在线解包+FMA) · 常驻线程池 · CUDA Graph
                 CUDA int4 GEMV/GEMM (NVRTC 运行时编译) · int4 权重常驻显存
L0 算子层        NumPy 加速 + 纯 Python 回退(零依赖)

📁 项目结构

acorn-framework/
├── PLAN.md                 # 总体项目计划(里程碑/风险/验收标准)
├── RUNTIME_PLAN.md         # 运行模型详细计划(格式/内存/量化/API)
├── USAGE.md                # 使用手册
├── assets/                 # Logo(主/深色/favicon/横版字标)
├── acorn/
│   ├── _kernels/           # 原生内核:acorn_int4.c + 自动编译/ctypes 绑定
│   ├── _ops.py             # 张量算子(NumPy 加速 + 纯 Python 回退)
│   ├── _autograd.py        # 微型自动微分(requires_grad 冻结机制)
│   ├── model.py            # TinyGPT(RoPE/因果注意力/MLP)
│   ├── llama.py            # LLaMA/Qwen 架构 + 转换器
│   ├── shard.py            # 多分片流式转换(RAM 峰值 < 1GB)
│   ├── safetensors.py      # 零依赖 safetensors 读写
│   ├── tokenizer.py        # BPE 分词器(字节级 + 增量解码)
│   ├── pack.py             # INT4 量化 + .acorn 容器(mmap 加载)
│   ├── stream.py           # 层流式加载
│   ├── kvcache.py          # fp32 / int8 KV 缓存
│   ├── runtime.py          # 运行时主循环
│   ├── session.py          # 多轮对话(跨轮 KV 复用)
│   ├── chat.py             # 模板/惩罚/停止序列
│   ├── chat_cli.py         # TUI 入口(acorn chat)
│   ├── tui.py              # 终端 UI(思考流 + 流式 Markdown)
│   ├── speculative.py      # 投机解码(Prompt-Lookup)
│   ├── autobit.py          # 动态混合位宽
│   ├── prune.py            # 结构化剪枝
│   ├── lora.py             # LoRA
│   ├── train.py            # 训练引擎(AdamW/裁剪/调度)
│   ├── bench.py            # 性能基准
│   ├── eco.py              # 能效治理
│   ├── gpu.py              # CUDA Graph 后端
│   ├── gpu_int4.py         # 自研 CUDA int4 内核(NVRTC 运行时编译)
│   ├── gpu_model.py        # int4 权重常驻显存的 LLaMA/Qwen GPU 模型
│   ├── vision.py           # 多模态:PNG 解码 / 视觉塔 / 投影器 / 图像注入
│   ├── multimodal.py       # 多模态 checkpoint 转换器(llava/gemma3/clip 预设)
│   ├── server.py           # 本地 OpenAI 兼容 API(零依赖 http.server)
│   └── __main__.py         # CLI
├── examples/               # 演示脚本(hello_acorn / run_qwen3 / bench_* /
│                          #   bench_gpu_kernel / bench_gpu_model / api_test)
└── tests/                  # 129 项自测

🧪 测试与质量保证

python tests/test_seed.py     # 基础(autograd 数值对拍 / 量化 round-trip)
python tests/test_v02.py      # 框架(RoPE/KV Cache/LoRA/CLI)
python tests/test_v03.py      # 内核(AVX2 数值 / 压缩态 / 性能)
python tests/test_v04.py      # 真实模型(LLaMA 数值 / Qwen bias / BPE)
python tests/test_v04b.py     # 端到端流水线
python tests/test_v06.py      # AutoBit
python tests/test_v07.py      # 层流式
python tests/test_v08.py      # KV int8
python tests/test_v09.py      # 真实模型回归(Qwen2.5)
python tests/test_v10.py      # 剪枝 / 稀疏内核
python tests/test_v11.py      # Chat 层 / 投机解码金标准
python tests/test_v12.py      # Qwen3 QK-Norm / 分片转换
python tests/test_v13.py      # 能效治理
python tests/test_v14.py      # GPU int4 内核(数值/速度/模型一致性,无 CUDA 自动跳过)
python tests/test_v15.py      # API 服务健壮性(500 JSON / 依赖边界)
python tests/test_v16.py      # think done 位置(</think> 透传 / 空行兜底门槛)
python tests/test_v17.py      # 流式 Markdown 完整渲染 / 回答上限提示
python tests/test_v18.py      # 颜色能力探测 / 4096 上限 / 上下文钳制
python tests/test_v19.py      # 多模态(PNG 解码/视觉塔/投影器/图像注入)
python tests/test_v20.py      # GQA 免展开 + 服务端会话 KV 复用
python tests/test_v21.py      # Gemma 兼容开关(GeGLU / RMSNorm(1+w) / softcapping)
python tests/test_v22.py      # 滑窗注意力(只读窗口内 KV)
python tests/test_v23.py      # .acorn v2 单文件模型(内嵌 tokenizer / crc32)
python tests/test_v24.py      # 内置 Web UI / SSE 关闭 / 模型库 / eco 端点

质量红线(均有测试守护):

  • LLaMA/Qwen 前向 vs 独立参考实现:rel ≤ 3.8e-7
  • 投机解码 vs 常规贪心:逐字一致
  • 稀疏内核 vs 稠密:diff = 0
  • AutoBit 同预算:质量 ↓87% 误差

🗺️ 路线图

阶段 内容 状态
M0 种子内核:autograd / INT4 / 内核 / 训练 / 容器 ✅
M1.0 原生 AVX2 内核 + 常驻线程池 + 压缩态常驻 ✅
M1.1 真实模型接入(safetensors / LLaMA / BPE / 转换器) ✅
M1.2 Qwen3-8B 上机 + QK-Norm + 多分片流式转换 ✅
M2.0 投机解码 + Chat 层 + 能效治理 + 剪枝稀疏 + KV int8 ✅
M2.1 GPU int4 内核(NVRTC 运行时编译,无需 CUDA Toolkit)+ int4 权重常驻显存 ✅
M2.2 LLaMA 路径接入 autograd → 真实模型 LoRA 训练 🔜
M2.3 连续批处理 / 多用户服务 / Hub 分发 🔜
M2.4 本地 OpenAI 兼容 API(acorn serve) ✅
M2.5 多模态:PNG 解码 / 视觉塔 / 投影器 / 图像注入(.mm 旁挂) ✅

⚠️ 已知限制(诚实清单)

  • 分组 int4(g=128)解码比逐通道慢约一半(0.5B 实测 13.1 vs 25.5 tok/s): 每 64 个值换一次 scale,内核要多做加权与分组边界处理。想换回速度用 ACORN_INT4_GROUP=0(退回逐通道,质量会明显变差,不建议)。 分组内核还在继续优化(目标是与逐通道同速)。
  • int4 量化质量只对 Qwen 系做过完整验收:其他架构的分组收益未逐个实测, 换模型请用 acorn convert 后自己测困惑度,别默认"质量没问题"
  • int3 量化已实现但很慢(3-bit 无法 SIMD 化,实测比 int4 慢 4 个数量级),默认不用
  • 多模态只覆盖通用 ViT/SigLIP 形态:Gemma 3 的 pan-and-scan 多裁剪、SigLIP resampler 未实现
  • 多模态转换器不做数值等价验证(只搬运张量),需真 checkpoint 对照
  • GQA 免展开(v0.10.1):长上下文提速 +49%(ctx=128 实测 330→222 ms/token),数学等价(float64 下注意力输出差 5.6e-17);但 float32 求和顺序不同 → 第 0 层即有 ~1e-8 相对差,经 24 层放大后 logits 可差 ~1.5%,极少数情况会翻转贪心 token(实测 3 个 prompt 中 1 个翻转)。可用 acorn.llama.set_gqa_no_expand(False) 退回旧路径
  • spin 等待屏障(未采用):Windows 事件唤醒每次派发有 5–50 μs 延迟(145 次 GEMM/token);改成"自旋快路径 + 事件慢路径"后实测死锁(两种实现都试过),收益/风险比不划算 → 保留事件实现(稳定)。每次 GEMM 固定开销实测 ~0.22 ms,是后续可攻的点
  • int8 激活整数点积(llama.cpp 同款):已实现,但本机编译器(TDM-GCC 10.3 @ -O2/+O3 on Zen 2)会误编译该指令序列 → 已加构建期自检,不通过自动禁用(绝不输出错误结果)。编译器正确的机器上自动生效,GEMM 约 5×(22.8 GB/s);本机需 ACORN_ACTQ=1 且自检通过才启用
  • CUDA Graph 后端在 P620 上仍不及 CPU int4 内核(Pascal 算力弱 + eager 开销)
  • 自研 GPU int4 内核在 P620 上 8.8–15.3 GB/s,0.9.10 起全形状快于 CPU AVX2;但仍远低于 186 GB/s 显存理论带宽(Pascal 无 int4 tensor core);torch fp32 借 tensor core 仍最快(52–77 GB/s)。GPU 路径的核心价值是 int4 权重常驻显存(0.5B 仅 249 MB)
  • 8B 模型放不进 4GB 显存的 P620(int4 需 4.10 GB);且 LLaMA 路径尚未接入 autograd,8B 训练在本机不可行(fp32 反量化 32GB)
  • 不支持 GGUF / AWQ / GPTQ 权重,只支持 safetensors 系
  • 单机单卡,无分布式训练
  • TensorFlow 2 对比:在算子库/编译器/分布式/生态上 Acorn 落后几个数量级;Acorn 的主张限定在「小内存 CPU 推理 + 训推一体 + 零依赖 + 能效治理」这条窄道

🤝 参与

git clone <your-fork> && cd acorn-framework
pip install -e .[fast,dev]
python tests/test_seed.py && python tests/test_v04.py

欢迎提交 PR:新模型架构适配、新内核、基准数据、文档。

📄 License

MIT


🌍 English summary (for PyPI visitors)

Acorn is a from-scratch AI runtime & training framework built on one idea: big models should not require big machines.

  • INT4 compressed-resident inference — weights stay int4 in memory and are unpacked on the fly inside a hand-written AVX2 SIMD kernel (never materialized as fp32). 7.99× smaller; numerically identical to an independent reference implementation (rel ≤ 3.8e-7).
  • Real models, zero dependencies — ships its own safetensors reader (single-file & sharded, streaming), BPE tokenizer, LLaMA/Qwen2/Qwen3 architectures (RoPE-rotate_half, GQA, SwiGLU, QK-Norm), and converters. Runs Qwen3-8B on a 16 GB, GPU-less laptop.
  • Memory & speed arsenal — KV cache int8 (28% memory), layer streaming (19× less resident memory), speculative decoding (1.3–1.7×, bit-identical to greedy), AutoBit mixed-precision (87% better quality at equal bytes), structured pruning + sparse kernels, and a performance-per-watt governor that keeps the fan quiet while matching full-core speed.
  • Training too — own autodiff (13 differentiable ops, numerically checked by finite differences), AdamW, warmup+cosine, gradient clipping, LoRA.
  • Self-written CUDA int4 kernel — compiles PTX at runtime with the NVRTC shipped inside the PyTorch wheel (no CUDA Toolkit needed) and drives the GPU through the raw driver API. Numerically exact (rel ≤ 1.3e-7); int4 weights stay resident in VRAM (0.5 B/param, 249 MB for a 0.5B model).
  • Local OpenAI-compatible API — acorn serve exposes /v1/chat/completions (with SSE streaming), /v1/completions, /v1/models, /health, using nothing but the standard library.
pip install acorn-llm
acorn chat            # interactive TUI with a local model
acorn serve           # local OpenAI-compatible HTTP API

Big models. Tiny footprint.

一颗橡子,长成参天大树。 🌰

Metadata

Release files for acorn-llm 0.11.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for acorn-llm 0.11.4
File Size Uploaded Provenance Actions
acorn_llm-0.11.4.tar.gz 264.1 kB Without provenance Details for acorn_llm-0.11.4.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for acorn-llm 0.11.4
File Interpreter ABI Platform Actions
acorn_llm-0.11.4-py3-none-any.whl Python 3 none any Details for acorn_llm-0.11.4-py3-none-any.whl

Total release size: 456.4 kB

Release files / acorn_llm-0.11.4.tar.gz

Download URL acorn_llm-0.11.4.tar.gz
Size 264.1 kB
Tags Source
SHA-256 checksum
How to use checksums
dba68700782dbead12696c99e0a0984dc7098179e14bff9f22072a7dc40bb986
BLAKE2b-256 checksum
How to use checksums
9ee83a9ea8b0e4838e509b5a35d53a1e19c9f4b79ebc6c17893caa3456406c32
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.6

Release files / acorn_llm-0.11.4-py3-none-any.whl

Download URL acorn_llm-0.11.4-py3-none-any.whl
Size 192.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e762a99ced9781590a3f65079f3e390512f1684f3204b43dfa3b884d402e8360
BLAKE2b-256 checksum
How to use checksums
43536c0d901693f671f31881387628453d3de2054cc57292c9040fb1cff33520
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.6

Security for acorn-llm 0.11.4

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page