Metadata-Version: 2.4
Name: acorn-llm
Version: 0.10.4
Summary: Acorn — Big models. Tiny footprint. 以小搏大的 AI 运行与训练框架（INT4 压缩态推理 / 自研 AVX2+CUDA 内核 / 本地 OpenAI 兼容 API / KV int8 / 层流式 / 投机解码 / LoRA / 能效治理 / 零依赖核心）
Author: Acorn Contributors
License: MIT
Project-URL: Homepage, https://github.com/yourname/acorn
Keywords: llm,inference,quantization,int4,transformers,qwen,llama,cpu
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Provides-Extra: fast
Requires-Dist: numpy>=1.24; extra == "fast"
Provides-Extra: gpu
Requires-Dist: torch>=2.0; extra == "gpu"
Provides-Extra: dev
Requires-Dist: numpy>=1.24; extra == "dev"

<div align="center">

<img src="assets/logo.svg" width="150" alt="Acorn Framework Logo"/>

# 🌰 Acorn Framework

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

[![PyPI](https://img.shields.io/pypi/v/acorn-llm.svg)](https://pypi.org/project/acorn-llm/)
[![Python](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/)
[![Deps](https://img.shields.io/badge/core%20deps-0-brightgreen.svg)](https://pypi.org/project/acorn-llm/)
[![License](https://img.shields.io/badge/license-MIT-black.svg)](LICENSE)
[![Tests](https://img.shields.io/badge/tests-124%20green.svg)](tests/)

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

</div>

---

## ⚡ 60 秒看到它跑真模型

```bash
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` 实测通过 |

---

## 📦 安装

```bash
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 聊天（装完就能用）

```bash
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 下载原始权重 → 自动转换（推荐）**

```bash
# 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 等超参不会手填错**）：

```bash
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 / 自写脚本）都能直接接：

```bash
acorn serve --model-dir ./models/Qwen2.5-0.5B --port 11434
# 或从 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 扩展：常驻内存 / 位宽 / 内核状态 / 档位 |

### ⚡ 会话级 KV 复用（v0.10.1）

多轮对话**不再重算历史**——按 token 前缀匹配，复用上一轮的 KV cache（llama.cpp / ollama 同类机制）：

```bash
curl http://127.0.0.1:11434/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 与位置绑定）

用 curl 直接聊（零依赖验证）：

```bash
curl http://127.0.0.1:11434/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` 实现，**零额外依赖**。

---

## 🔥 自研 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

```python
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`）

```python
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 倍。

启用方式（仅在自检通过时有效）：
```bash
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 + 多模态视觉塔 +
训练状态 + 校验 + 元信息，全在一个文件里。**拷走一个文件就能在另一台机器上跑。**

```bash
# 把 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 支持 LLaVA / Gemma 3 一类"**视觉塔 + projector**"架构的图像输入：PNG 零依赖解码 → 视觉塔 → projector → 图像 embedding 注入 LLM 的输入序列。

```bash
# 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:11434/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 秒训一个自己的模型

```python
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 轻量微调

```python
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 全表

```bash
acorn chat                             # ★ 交互式 TUI 聊天
acorn serve --model-dir ./models/Qwen2.5-0.5B   # ★ 本地 OpenAI 兼容 API
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 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.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/                  # 124 项自测
```

---

## 🧪 测试与质量保证

```bash
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）
```

质量红线（均有测试守护）：
- 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 旁挂） | ✅ |

---

## ⚠️ 已知限制（诚实清单）

- **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 推理 + 训推一体 + 零依赖 + 能效治理」这条窄道

---

## 🤝 参与

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

---

<details>
<summary><b>🌍 English summary (for PyPI visitors)</b></summary>

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

```bash
pip install acorn-llm
acorn chat            # interactive TUI with a local model
acorn serve           # local OpenAI-compatible HTTP API
```

</details>

---

<div align="center">

**Big models. Tiny footprint.**

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

</div>
