Skip to content

Commit f95ee89

Browse files
test+docs: 分词器测试迁入(tokenizer/nnvocab)+ dataset_integration_test + README + LICENSE + CI
- tests/tokenizer_test + tokenizer_consistency_test 迁自 neuralnet.cpp 主仓 (pre_tokenize ↔ regex 对拍 / 标记往返 / 并行 encode 确定性;剥离门面 test_common, 改用本仓最小 CHECK 头) - tests/nnvocab_test 迁自主仓(json→nnvocab 语义一致 / save-load 往返 / 旧 json 拒绝) - tests/dataset_integration_test = 主仓 dataset_test 的分词器耦合段: 真实 BPE 训练 → encode_docs_parallel 保序 → assistant 掩码与全局扫描逐位一致 → 写读 .nndataset 逐位 round-trip → .nnvocab 独立加载 - CMakeLists: NN_BUILD_TESTS 门 + ctest 注册 - README: API 速览(训练/编解码/对话标记/持久化)+ 构建与测试 - LICENSE(LGPL-3.0)+ CI workflow
1 parent 6ee337a commit f95ee89

9 files changed

Lines changed: 1979 additions & 14 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# tokenizer.neuralnet.cpp — 构建 + ctest(Linux clang++ / g++,Windows MSVC)
2+
name: ci
3+
4+
on:
5+
push:
6+
branches: [main]
7+
pull_request:
8+
9+
jobs:
10+
linux:
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
compiler: [clang++, g++]
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
with:
19+
submodules: recursive
20+
- name: Configure
21+
run: >
22+
cmake -B build -G Ninja
23+
-DCMAKE_BUILD_TYPE=Release
24+
-DCMAKE_CXX_COMPILER=${{matrix.compiler}}
25+
- name: Build
26+
run: cmake --build build
27+
- name: Test
28+
run: ctest --test-dir build --output-on-failure
29+
30+
windows:
31+
runs-on: windows-latest
32+
steps:
33+
- uses: actions/checkout@v4
34+
with:
35+
submodules: recursive
36+
- name: Configure
37+
run: cmake -B build -DCMAKE_BUILD_TYPE=Release
38+
- name: Build
39+
run: cmake --build build --config Release
40+
- name: Test
41+
run: ctest --test-dir build -C Release --output-on-failure

‎CMakeLists.txt‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,21 @@ target_include_directories(neuralnet_tokenizer INTERFACE
3737
target_link_libraries(neuralnet_tokenizer INTERFACE
3838
neuralnet::base neuralnet::threadpool neuralnet::kvrecord neuralnet::dataset)
3939
target_compile_features(neuralnet_tokenizer INTERFACE cxx_std_23)
40+
41+
# ── 测试(NN_BUILD_TESTS;顶层工程默认 ON,被 add_subdirectory 消费时默认 OFF)──
42+
# 独立开发/CI:cmake -B build && cmake --build build && ctest --test-dir build
43+
option(NN_BUILD_TESTS "Build tokenizer.neuralnet.cpp tests" ${PROJECT_IS_TOP_LEVEL})
44+
if(NN_BUILD_TESTS)
45+
enable_testing()
46+
if(MSVC)
47+
add_compile_options(/W4 /permissive- /Zc:preprocessor /utf-8)
48+
else()
49+
add_compile_options(-fno-exceptions -Wall -Wextra -Wpedantic -Werror)
50+
endif()
51+
52+
foreach(_t tokenizer nnvocab dataset_integration)
53+
add_executable(tokenizer_${_t}_test tests/${_t}_test.cpp)
54+
target_link_libraries(tokenizer_${_t}_test PRIVATE neuralnet::tokenizer)
55+
add_test(NAME tokenizer_${_t}_test COMMAND tokenizer_${_t}_test)
56+
endforeach()
57+
endif()

‎LICENSE‎

Lines changed: 843 additions & 0 deletions
Large diffs are not rendered by default.

‎README.md‎

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,37 +4,88 @@ neuralnet.cpp 生态的 **BPE / CharBPE 分词器**库(header-only)。
44

55
| 头文件 | 内容 |
66
|---|---|
7-
| `domain_tokenizer.hpp` | 聚合入口 |
8-
| `domain_tokenizer_base.hpp` | `Tokenizer` 基类 + 并行预分词 + 词表持久化(`load`/`save`) |
9-
| `domain_tokenizer_bpe.hpp` | `BPETokenizer` |
10-
| `domain_tokenizer_charbpe.hpp` | `CharBPETokenizer` + `make_tokenizer` 工厂 |
7+
| `domain_tokenizer.hpp` | 聚合入口(include 它即可) |
8+
| `domain_tokenizer_base.hpp` | `Tokenizer` 基类 + 并行预分词(`pre_tokenize`)+ 词表持久化(`to_kvrec`/`load_from_kvrec`/`save`/`load`)+ 多轮对话 prompt 构建(`chat_prompt`)+ `encode_docs_parallel` |
9+
| `domain_tokenizer_bpe.hpp` | `BPETokenizer`(GPT 风格字节对编码) |
10+
| `domain_tokenizer_charbpe.hpp` | `CharBPETokenizer` + `make_tokenizer` 工厂 + `load_tokenizer_from_{string,bytes,file}` |
1111

12-
## 用法
12+
## API 速览
1313

1414
```cpp
1515
#include <neuralnet.cpp/domain_tokenizer.hpp>
1616

17-
auto tok = nn::make_tokenizer(...); // 工厂
18-
tok->train(...);
19-
tok->save("vocab.nnvocab");
17+
// ── 训练 ──────────────────────────────────────────────────────
18+
nn::BPETokenizer tok;
19+
nn::BPETokenizer::Config cfg;
20+
cfg.vocab_size = 320;
21+
cfg.min_freq = 1;
22+
cfg.threads = 0; // 0 = 自动(线程池全部核心)
23+
cfg.log = [](std::string_view) {}; // 训练日志回调
24+
auto tr = tok.train(corpus, cfg); // Result<void>
25+
26+
// ── 编解码 ────────────────────────────────────────────────────
27+
std::vector<std::size_t> ids = tok.encode("hello world");
28+
std::string text = tok.decode(ids); // encode∘decode 字节级往返一致
29+
std::size_t n = tok.vocab_size();
30+
31+
// 批量文档并行编码(保序:任意并行度与顺序 encode 逐字节一致)
32+
auto per_doc = nn::encode_docs_parallel(tok, docs);
33+
34+
// ── 对话标记 ──────────────────────────────────────────────────
35+
tok.has_dialogue_markers();
36+
tok.assistant_marker_id(); tok.end_assistant_marker_id(); // 及 system/user 等
37+
std::vector<std::size_t> prompt = tok.chat_prompt(
38+
{{/*user=*/true, "hi", /*tokens=*/{}}, {false, "hello", {}}},
39+
/*system_prompt=*/"", /*max_tokens=*/0);
40+
41+
// ── 词表持久化(.nnvocab,kvrec v2)────────────────────────────
42+
tok.save("vocab.nnvocab"); // Result<void>
43+
nn::KeyValueRecord rec = tok.to_kvrec(); // 模型内嵌词表字节来源
44+
auto loaded = nn::load_tokenizer_from_file("vocab.nnvocab"); // Result<unique_ptr<Tokenizer>>
45+
auto made = nn::make_tokenizer("bpe"); // 按类型字符串造工厂实例
46+
47+
// ── CharBPE(字符级 BPE)───────────────────────────────────────
48+
nn::CharBPETokenizer cbpe;
49+
cbpe.train(corpus, cbpe.Config{/*...*/});
50+
```
51+
52+
## 用法 / CMake
53+
54+
```cmake
55+
add_subdirectory(tokenizer) # 自动拉起 extern/{base,threadpool,kvrecord,dataset}
56+
target_link_libraries(app PRIVATE neuralnet::tokenizer) # 提供 include + C++23
2057
```
2158

2259
## 依赖
2360

2461
均以 submodule 位于 `extern/`:
2562

2663
- [`base.neuralnet.cpp`](https://github.com/neuralnet-cpp-dev/base.neuralnet.cpp) — `Result<T>` / 断言 / 精度 / 常量
27-
- [`threadpool.neuralnet.cpp`](https://github.com/neuralnet-cpp-dev/threadpool.neuralnet.cpp) — 并行预分词
64+
- [`threadpool.neuralnet.cpp`](https://github.com/neuralnet-cpp-dev/threadpool.neuralnet.cpp) — 并行预分词(`parallel_for_samples`)
2865
- [`kvrecord.neuralnet.cpp`](https://github.com/neuralnet-cpp-dev/kvrecord.neuralnet.cpp) — 词表二进制容器
29-
- [`dataset.neuralnet.cpp`](https://github.com/neuralnet-cpp-dev/dataset.neuralnet.cpp) — 词表文件格式(`parse_nnvocab`)
66+
- [`dataset.neuralnet.cpp`](https://github.com/neuralnet-cpp-dev/dataset.neuralnet.cpp) — 词表文件格式(`parse_nnvocab`/`write_nnvocab`)+ `.nndataset` 集成
3067

3168
```bash
3269
git clone --recurse-submodules .../tokenizer.neuralnet.cpp
70+
# 或已 clone 后:git submodule update --init --recursive
3371
```
3472

35-
CMake:
73+
## 构建与测试
3674

37-
```cmake
38-
add_subdirectory(tokenizer) # 自动拉起 extern/{base,threadpool,kvrecord,dataset}
39-
target_link_libraries(app PRIVATE neuralnet::tokenizer)
75+
```bash
76+
git submodule update --init --recursive
77+
cmake -B build
78+
cmake --build build
79+
ctest --test-dir build --output-on-failure
4080
```
81+
82+
| 测试 | 覆盖 |
83+
|---|---|
84+
| `tokenizer_tokenizer_test` | `pre_tokenize` 手写状态机 ↔ `std::regex` 参考逐字节对拍(缩写/数字/空白/标点/非 ASCII/标记边界 + 随机字节);标记边界训练后仍按单 token 识别;encode∘decode 字节级往返;并行 encode 确定性(任意并行度逐字节一致) |
85+
| `tokenizer_nnvocab_test` | 旧 JSON 词表 → `.nnvocab` → 加载语义一致(vocab/merges/markers/编解码);训练态 save/load 往返逐位一致(BPE + CharBPE);旧 `.json` 词表明确拒绝;词表镜像字节直载(v5 JSON / v6 kvrec) |
86+
| `tokenizer_dataset_integration_test` | 真实 BPE 训练 → `encode_docs_parallel` 保序 → `mark_assistant_span` 掩码与全局扫描逐位一致 → 写读 `.nndataset` 逐位 round-trip(all/assistant 双模式、空 doc、train/test 双子集)→ `.nnvocab` 独立加载 |
87+
88+
被父工程 `add_subdirectory` 消费时测试**默认不构建**(`NN_BUILD_TESTS` 默认值 =
89+
是否顶层工程);显式 `-DNN_BUILD_TESTS=ON` 可在嵌入时强制打开。
90+
91+
许可证:LGPL-3.0(见 [LICENSE](LICENSE))。

0 commit comments

Comments
 (0)