Born 集成架构分析
1. 完整文件清单
直接依赖 github.com/born-ml/born(4 个源文件)
| 文件 | 角色 |
|---|---|
c:\GitHub\leaves\tree\born_engine.go | Born 推理引擎入口,borncpu.New() + 调用 tree/born |
c:\GitHub\leaves\tree\born\tensors.go | Backend 类型别名 + tensor.FromSlice 辅助 |
c:\GitHub\leaves\tree\born\forest.go | GBDT 张量算法:WalkTreeBatch、ForestMarginsDense 等 |
c:\GitHub\leaves\treebuilder\hist_accel_born.go | 训练直方图增益扫描(//go:build born_train),绕过 tree/born |
tree/born 子包(3 个源文件,无 born-ml import 的 IR 层)
| 文件 | 角色 |
|---|---|
c:\GitHub\leaves\tree\born\ir.go | TreeData / ForestData / DecisionFunc |
c:\GitHub\leaves\tree\born\forest.go | 见上 |
c:\GitHub\leaves\tree\born\tensors.go | 见上 |
Born 桥接 / 转换 / 选型(无 born-ml import)
| 文件 | 角色 |
|---|---|
c:\GitHub\leaves\tree\born_convert.go | ForestIR → born.ForestData 深拷贝 + MakeDecisionFn |
c:\GitHub\leaves\tree\decision.go | TreeDecision — Born 标量回退路径的 categorical 逻辑 |
c:\GitHub\leaves\tree\backend_select.go | Backend* 常量、SelectBackend、BornSupports |
c:\GitHub\leaves\tree\engine.go | Engine 接口、Backend 枚举 |
c:\GitHub\leaves\tree\forest_predict.go | Native 路径 margin 聚合(与 born/forest.go 逻辑平行) |
引擎工厂 / API 层
| 文件 | 角色 |
|---|---|
c:\GitHub\leaves\model\build.go | BackendBornCPU/GPU → NewBornEngine,否则 NewNativeEngine |
c:\GitHub\leaves\model\backend.go | ModelIR → ModelCaps → tree.ResolveBackend |
c:\GitHub\leaves\bridge.go | EngineOptions{Backend, Workload} → model.NewEngineWithHint |
c:\GitHub\leaves\io\io.go / load.go | 重导出 Backend* 常量 |
c:\GitHub\leaves\predict\engine.go | 统一 predict.Engine 接口 |
Native 对照路径(golden,非 Born 但架构对称)
| 文件 | 角色 |
|---|---|
c:\GitHub\leaves\tree\native.go | NativeEngine,调用 ForestMargins |
c:\GitHub\leaves\tree\ir.go | ForestIR / TreeIR 领域 IR |
训练 Born 桩 / 测试
| 文件 | 角色 |
|---|---|
c:\GitHub\leaves\treebuilder\hist_accel_stub.go | !born_train 时回退 CPU |
c:\GitHub\leaves\treebuilder\hist.go | 调用 scanHistGains |
c:\GitHub\leaves\tree\born_test.go | Born vs Native smoke/parity |
c:\GitHub\leaves\born_parity_test.go | 全库 parity 门禁 |
c:\GitHub\leaves\tree\backend_select_test.go | 后端选型测试 |
c:\GitHub\leaves\treebuilder\t4_born_test.go | hist Born 增益测试 |
c:\GitHub\leaves\backend_bench_test.go | Native/BornCPU/BornGPU benchmark |
c:\GitHub\leaves\delegation_test.go | Native vs BornCPU 委托对比 |
c:\GitHub\leaves\io\backend_test.go | BackendAuto 加载测试 |
配置 / 文档
| 文件 | 角色 |
|---|---|
c:\GitHub\leaves\go.mod | replace github.com/born-ml/born => C:/GitHub/born |
c:\GitHub\leaves\AGENTS.md, NOTES.md, README.md, 演进计划.md | 架构决策文档 |
未接线 / 遗留
BornConfig.UseGPU在born_engine.go中被_ = cfg.UseGPU丢弃,始终borncpu.New()—BackendBornGPU选型存在但 GPU 未实现tree/born/forest.go中HasNaN无调用方(死代码)- glob 显示
tree/gomlx_engine.go存在但当前环境读不到(可能未跟踪);AGENTS 已声明 GoMLX 废弃
2. Import 依赖图
github.com/born-ml/born
├── backend/cpu ─────────────────────────────────────────┐
└── tensor ─────────────────────────────────────────┤
│
tree/born/tensors.go ◄───────────────────────────────┤
tree/born/forest.go ◄───────────────────────────────┤
tree/born_engine.go ◄───────────────────────────────┤
treebuilder/hist_accel_born.go (born_train tag) ◄────┘
github.com/dmitryikh/leaves/tree/born
tree/born/ir.go (无 born-ml import)
tree/born_convert.go ◄── tree/born
tree/born_engine.go ◄── tree/born + born-ml
github.com/dmitryikh/leaves/tree (领域 IR + 双引擎)
ir.go, decision.go, forest_predict.go, native.go
born_convert.go, born_engine.go, backend_select.go, engine.go
│
├── model/build.go ──► predict.Engine
├── bridge.go (EngineOptions)
└── io/load.go (Backend 重导出)
treebuilder/hist_accel_born.go ──► born-ml 直接(不经 tree/born)
treebuilder/hist.go ──► scanHistGains() 抽象
关键边界:tree/ 不依赖 train/;tree/born/ 不 import tree(避免循环),因此需要 TreeData/ForestData 快照 + DecisionFunc 回调注入 categorical 逻辑。
---
3. 各层抽象:leaves ↔ born-ml/born
| 层 | 抽象 | 作用 |
|---|---|---|
| L0 领域 IR | ForestIR / TreeIR (tree/ir.go) | 框架无关 GBDT 中间表示(LGB/XGB/sklearn 加载目标) |
| L1 快照 IR | born.TreeData / born.ForestData (tree/born/ir.go) | 与 TreeIR/ForestIR 字段镜像,解 import cycle |
| L2 转换 | ForestToBorn / TreeToBorn (tree/born_convert.go) | 引擎构造时 深拷贝 全部 slice |
| L3 决策注入 | DecisionFunc + MakeDecisionFn → TreeDecision | CatSmall / 大 bitset 分类分裂回退到 tree/decision.go |
| L4 GBDT 算法 | tree/born/forest.go | 在 Born 张量上做 batch 树遍历、margin 聚合(非 born-ml 内置能力) |
| L5 张量辅助 | tree/born/tensors.go | Backend = borncpu.Backend 别名 + FromSlice/Gather 封装 |
| L6 引擎 | BornEngine vs NativeEngine | 同构 tree.Engine 接口;构造时二选一,非运行时互调 |
| L7 选型 | Backend + SelectBackend + BornSupports | workload 驱动的引擎选择 |
| L8 训练 | hist_accel_born.go | 独立使用 born-ml 做 hist 增益向量化,与推理 tree/born 无关 |
tensor.Tensor、Gather、Mul、Div 等),无 GBDT / 树遍历抽象。leaves 在 tree/born/ 自行实现 GBDT 所需原语。---
4. tree/born/ 子包:是 facade 吗?
不完全是。 三层混合:
1. 薄 facade(tensors.go):type Backend = borncpu.Backend,mustFromF64 等 — 可合并进 born_engine.go。
2. 适配器 IR(ir.go):TreeData/ForestData 是 TreeIR/ForestIR 的平行 struct — 为解循环,不是 born-ml API 的 facade。
3. 算法层(forest.go,~487 行):WalkTreeBatch、ForestMarginsDense、叶值 gather — 核心业务逻辑,不应称为 facade。
设计意图(见 ir.go 注释):*「与 tree.TreeIR 解耦,避免 import cycle」*。
---
5. BornEngine vs NativeEngine 委托模式
引擎级:互斥,非委托
model/build.go 在构造时按 Backend 选择 一个 引擎:
``34:38:c:\GitHub\leaves\model\build.go
case tree.BackendBornCPU, tree.BackendBornGPU:
cfg := &tree.BornConfig{UseGPU: backend == tree.BackendBornGPU}
return tree.NewBornEngine(ir.Forest, transform, outputType, ir.NOutputGroups, cfg)
default:
return tree.NewNativeEngine(ir.Forest, transform, outputType, ir.NOutputGroups), nil
ForestIR (保留)
↓ 无拷贝指针
Born GBDT kernels (合并进 tree 或保留 tree/born)
↓
born-ml tensor (Gather/Mul/…)
+
NativeEngine (golden + categorical fallback,长期可仅测试用)
两者平行实现同一 `tree.Engine` / `predict.Engine` 接口;`NativeEngine` 是 golden,`BornEngine` 是加速路径。
### Born 路径内部:张量 + Native 混合
`ForestMarginsDense` 对每棵树分支:
- **张量 batch**:数值分裂 + CatOneHot(`TreeNeedsNativeWalk == false`)→ `WalkTreeBatch` + Born `Gather`
- **标量回退**:CatSmall / 大 bitset → 逐行 `WalkTreeScalar(t, fvals, decide)`,`decide` 来自 `TreeDecision(t.TreeIR, ...)`
Leaf index 预测(`TransformLeafIndex`)在 `BornEngine` 中逐树、逐样本混合上述两路。
CSR 输入:`BornEngine.PredictCSR` 先 **densify** 再 `PredictDense`(无 Born 稀疏路径)。
### 选型级:`BornSupports` 整森林 gate
含 `CatSmall` 时 `BackendBornCPU` 自动选型返回 false;但 **显式** 指定 `BackendBornCPU` 仍可创建 `BornEngine`(per-tree 标量回退),见 `born_parity_test.go` KDDCup99 注释。
---
## 6. ForestData/TreeData vs ForestIR/TreeIR
| 维度 | `ForestIR` / `TreeIR` | `born.ForestData` / `born.TreeData` |
|------|----------------------|-------------------------------------|
| 位置 | `tree/ir.go` | `tree/born/ir.go` |
| 额外字段 | `Name`, `NumParallelTree`, `SplitGain`, `SumHess`, `NumLeaves` | 无(仅 Born 推理所需子集) |
| 生命周期 | 加载/训练全程 | `NewBornEngine` 时深拷贝,引擎内只读 |
| 用途 | 领域 IR、Native、explain、io | Born 张量图输入 |
转换在 `born_convert.go` 逐字段 `append([]T(nil), ...)` — **O(n) 内存复制**,引擎持有双份树数据(`BornEngine.forest` + `BornEngine.bornForest`)。
---
## 7. 冗余转换 / 包装类型
### 明确冗余
| 冗余 | 位置 A | 位置 B | 说明 |
|------|--------|--------|------|
| **Margin 聚合** | `tree/forest_predict.go` `ForestMargins` | `tree/born/forest.go` `ForestMarginsDense` | `adjustNEstimators`、`classBaseScore`、`forestTreeClassIndex`、`scaleExceptBase` 几乎同构 |
| **树遍历** | `forest_predict.go` `walkTree` | `born/forest.go` `WalkTreeScalar` | 相同 DFS 逻辑 |
| **叶值读取** | `treeLeafScalar/Vector` (两处) | 同上 | 重复 |
| **零值判定** | `decision.go` `isZeroFval` | `born/forest.go` `isZeroFval` | 第三份在 `explain/treeutil.go` |
| **IR 快照** | `TreeIR`/`ForestIR` | `TreeData`/`ForestData` + `ForestToBorn` | 字段镜像 + 深拷贝 |
| **引擎状态** | `BornEngine.forest *ForestIR` | `BornEngine.bornForest ForestData` | 双份持有;leaf index 路径仍读 `forest.Trees` |
| **Decision 闭包** | `[]born.DecisionFunc` 每棵树一个 | 闭包捕获 `*TreeIR` | 与 `bornForest` 中 `TreeData` 并行存在 |
| **Backend 包装** | `born.Backend = borncpu.Backend` | 可直接用 borncpu | 一行别名 |
| **GPU 占位** | `BackendBornGPU` / `BornConfig.UseGPU` | 实际只用 CPU | 虚假抽象层 |
### 非冗余(应保留)
- **`ForestIR` / `TreeIR`**:框架无关领域 IR,加载、explain、训练、Native golden 共用
- **`tree.Engine` / `predict.Engine`**:可插拔后端契约
- **`NativeEngine`**:正确性基准与 categorical 完整实现
- **`DecisionFunc` 注入模式**:在 Born 未实现 CatSmall/bitset 张量化前必要
- **`backend_select.go`**:部署/workload 策略
- **`tree/born/forest.go` 算法本身**:born-ml 不提供 GBDT,此层不可删,只能合并位置
---
## 8. 「完全拥抱 Born、去掉中间层」建议
### 可移除 / 合并
1. **`TreeData` / `ForestData` + `born_convert.go`**
- 方案:把 `tree/born/*.go` 移入 `tree` 包(如 `tree/born_forest.go`),直接读 `*ForestIR`/`*TreeIR`
- 前提:接受 `tree` 包 import `born-ml`(已有 `born_engine.go` 先例)
- 收益:消除深拷贝、双份 IR、转换维护成本
2. **`ForestMargins` vs `ForestMarginsDense` 重复**
- 方案:提取共享 `forestMarginsCore`(接受 interface 或函数参数),Native 单样本 / Born batch 共用迭代逻辑
- 或:Native 小 batch 也走 Born(仅保留 Native 作 golden 测试对照)
3. **`born.Backend` 类型别名 + `tensors.go` 小 helper**
- 可内联到 `born_engine.go` / `forest.go`,减文件数
4. **`BornEngine.forest` 冗余引用**
- leaf index 改为只用 `bornForest` + `decisions`,或统一 IR 后只保留一份
5. **`BackendBornGPU` / `UseGPU` 占位**
- 未实现前应删除或明确 stub,避免误导(当前 `backend_test` 期望 GPU 选型但引擎不接线)
6. **`HasNaN`(born/forest.go)**
- 未使用,可删
7. **训练路径统一**
- `hist_accel_born.go` 直接 import born-ml,与 `tree/born` 分裂;可共用 `tree/born/tensors.go` 的 helper(若保留子包)
### 应保留
| 组件 | 理由 |
|------|------|
| **`ForestIR` / `TreeIR`** | 领域 IR;Born 只是执行后端 |
| **`NativeEngine`** | Golden、完整 categorical、零 Born 依赖部署 |
| **`BornEngine` 作为 `Engine` 实现** | 接口清晰,benchmark/parity 可切换 |
| **`tree/born/` 算法层(或合并后的等价代码)** | born-ml 无 GBDT;必须在 leaves 实现 batch 树遍历 |
| **`DecisionFunc` + `TreeDecision`(至 CatSmall 张量化完成)** | 分类特征正确性 |
| **`backend_select.go`** | Auto/WASM/GPU batch 策略 |
| **`born_train` build tag** | 可选训练加速,默认零依赖 |
| **`model/build.go` 工厂** | 统一入口 |
### 「完全拥抱」的 realistic 终点
不是删掉所有 wrapper 直接用 born-ml API(born 没有树模型),而是:
mermaid
flowchart TB
subgraph domain ["领域层 (保留)"]
IR["ForestIR / TreeIR"]
Native["NativeEngine\nforest_predict.go"]
end---
## 9. 架构示意
subgraph born_layer ["Born 集成层 (可瘦身)"] Conv["born_convert.go\nForestToBorn 深拷贝"] Snap["TreeData / ForestData"] Algo["tree/born/forest.go\nWalkTreeBatch / ForestMarginsDense"] Eng["BornEngine"] end
subgraph external ["github.com/born-ml/born"] CPU["backend/cpu"] T["tensor"] end
subgraph train ["训练 (独立)"] Hist["hist_accel_born.go\nborn_train tag"] end
IR --> Native
IR --> Conv --> Snap --> Algo
IR --> Eng
Snap --> Eng
Eng --> Algo
Algo --> T
Algo --> CPU
Eng --> CPU
Hist --> T
Hist --> CPU
decision["tree/decision.go\nTreeDecision"] -.->|DecisionFunc 回调| Algo
`
---
10. 要点总结
- Born 集成是 四层:born-ml 张量 → tree/born
GBDT 算法 →born_convert/born_engine→model/io选型。 - tree/born/
不是 born-ml 的 facade,而是 GBDT-on-tensor 算法包 + 解循环 IR 快照;仅tensors.go偏 facade。 - BornEngine
与NativeEngine构造期互斥;Born 内部对复杂 categorical per-tree/per-row 回退 Native 决策。 - 最大冗余:双份 IR(ForestIR + ForestData) 和 双份 margin 逻辑(forest_predict vs born/forest)。
- BackendBornGPU
已选型但未实现;BornConfig.UseGPU被忽略。 - 训练加速 (hist_accel_born.go
) 与推理 (tree/born`) 共享 born-ml 依赖但代码路径分离。