bge-m3-lite 0.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bge_m3_lite-0.0.2/PKG-INFO +103 -0
- bge_m3_lite-0.0.2/README.md +77 -0
- bge_m3_lite-0.0.2/bge_m3_lite/__init__.py +6 -0
- bge_m3_lite-0.0.2/bge_m3_lite/_grapheme.py +128 -0
- bge_m3_lite-0.0.2/bge_m3_lite/_grapheme_data.py +5616 -0
- bge_m3_lite-0.0.2/bge_m3_lite/_proto.py +89 -0
- bge_m3_lite-0.0.2/bge_m3_lite/_torch_pickle.py +87 -0
- bge_m3_lite-0.0.2/bge_m3_lite/cli.py +138 -0
- bge_m3_lite-0.0.2/bge_m3_lite/embedder.py +197 -0
- bge_m3_lite-0.0.2/bge_m3_lite/hub.py +252 -0
- bge_m3_lite-0.0.2/bge_m3_lite/model.py +62 -0
- bge_m3_lite-0.0.2/bge_m3_lite/quantize.py +82 -0
- bge_m3_lite-0.0.2/bge_m3_lite/tokenizer.py +335 -0
- bge_m3_lite-0.0.2/pyproject.toml +105 -0
- bge_m3_lite-0.0.2/pyproject.toml.orig +59 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bge-m3-lite
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Lightweight CPU inference for BAAI/bge-m3 (dense + sparse + ColBERT) with onnxruntime as the only dependency.
|
|
5
|
+
Keywords: bge-m3,colbert,embedding,onnxruntime,retrieval,sparse
|
|
6
|
+
Author: Allen Chou
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Operating System :: MacOS
|
|
11
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Requires-Dist: onnxruntime>=1.17
|
|
19
|
+
Requires-Dist: onnx>=1.16 ; extra == 'quant'
|
|
20
|
+
Requires-Dist: onnx-ir ; extra == 'quant'
|
|
21
|
+
Requires-Dist: sympy ; extra == 'quant'
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Project-URL: Homepage, https://github.com/allen2c/bge-m3-lite
|
|
24
|
+
Provides-Extra: quant
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# bge-m3-lite
|
|
28
|
+
|
|
29
|
+
CPU inference for [BAAI/bge-m3](https://huggingface.co/BAAI/bge-m3) with
|
|
30
|
+
**`onnxruntime` as the only dependency**. All three BGE-M3 outputs are supported
|
|
31
|
+
and match the official PyTorch implementation (FlagEmbedding) to fp32 precision:
|
|
32
|
+
|
|
33
|
+
| output | shape | notes |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `dense_vecs` | `(n, 1024)` | CLS pooling, L2-normalised |
|
|
36
|
+
| `lexical_weights` | `list[dict[str, float]]` | token-id → weight, max-pooled, specials removed |
|
|
37
|
+
| `colbert_vecs` | `list[(len-1, 1024)]` | per-token vectors without `<s>`, L2-normalised |
|
|
38
|
+
|
|
39
|
+
Everything except the transformer forward pass is implemented in this package
|
|
40
|
+
from scratch: the XLM-RoBERTa tokenizer (SentencePiece unigram model, the
|
|
41
|
+
`nmt_nfkc` precompiled charsmap, Unicode grapheme segmentation), the torch-free
|
|
42
|
+
loader for the sparse / ColBERT heads, the model downloader and the pooling.
|
|
43
|
+
|
|
44
|
+
Platforms: Apple Silicon, Linux ARM64, Linux x86_64 (Python 3.11+).
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uv add bge-m3-lite # or: pip install bge-m3-lite
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Use
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from bge_m3_lite import BGEM3Embedder
|
|
56
|
+
|
|
57
|
+
embedder = BGEM3Embedder() # first call downloads ~2.3 GB into ~/.cache/bge-m3-lite
|
|
58
|
+
out = embedder.encode(
|
|
59
|
+
["What is BGE M3?", "BGE M3 是一個多語言嵌入模型。"],
|
|
60
|
+
return_dense=True,
|
|
61
|
+
return_sparse=True,
|
|
62
|
+
return_colbert_vecs=True,
|
|
63
|
+
)
|
|
64
|
+
out["dense_vecs"].shape # (2, 1024)
|
|
65
|
+
out["lexical_weights"][0] # {'4865': 0.08, '83': 0.08, ...}
|
|
66
|
+
out["colbert_vecs"][0].shape # (7, 1024)
|
|
67
|
+
|
|
68
|
+
embedder.convert_id_to_token(out["lexical_weights"][0])
|
|
69
|
+
embedder.compute_lexical_matching_score(lw_query, lw_passage)
|
|
70
|
+
embedder.colbert_score(q_vecs, p_vecs)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Passing a single string returns unwrapped values, like FlagEmbedding.
|
|
74
|
+
`BGEM3Embedder(precision="int8")` loads a 4× smaller quantised backbone
|
|
75
|
+
(see `docs/quantization.md` for the accuracy trade-off).
|
|
76
|
+
|
|
77
|
+
### CLI
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
bge-m3-lite download # pre-fetch the model files
|
|
81
|
+
bge-m3-lite info # cache state
|
|
82
|
+
echo "hello" | bge-m3-lite encode --sparse --colbert --tokens
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Environment variables
|
|
86
|
+
|
|
87
|
+
| variable | effect |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `BGE_M3_LITE_CACHE` | cache directory (default `~/.cache/bge-m3-lite/BAAI--bge-m3`) |
|
|
90
|
+
| `HF_ENDPOINT` | Hugging Face mirror, e.g. `https://hf-mirror.com` |
|
|
91
|
+
| `BGE_M3_LITE_OFFLINE=1` | never download, fail if files are missing |
|
|
92
|
+
| `BGE_M3_LITE_THREADS` | onnxruntime intra-op threads (default: physical cores) |
|
|
93
|
+
|
|
94
|
+
Model files are pinned to a specific Hugging Face revision and verified by
|
|
95
|
+
SHA-256 after download.
|
|
96
|
+
|
|
97
|
+
## Development
|
|
98
|
+
|
|
99
|
+
See `AGENTS.md` and `docs/` (architecture, tokenizer, verification, development).
|
|
100
|
+
|
|
101
|
+
## Status
|
|
102
|
+
|
|
103
|
+
v0.0.2: fp32 with exact parity with FlagEmbedding, plus an opt-in int8 backbone.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# bge-m3-lite
|
|
2
|
+
|
|
3
|
+
CPU inference for [BAAI/bge-m3](https://huggingface.co/BAAI/bge-m3) with
|
|
4
|
+
**`onnxruntime` as the only dependency**. All three BGE-M3 outputs are supported
|
|
5
|
+
and match the official PyTorch implementation (FlagEmbedding) to fp32 precision:
|
|
6
|
+
|
|
7
|
+
| output | shape | notes |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `dense_vecs` | `(n, 1024)` | CLS pooling, L2-normalised |
|
|
10
|
+
| `lexical_weights` | `list[dict[str, float]]` | token-id → weight, max-pooled, specials removed |
|
|
11
|
+
| `colbert_vecs` | `list[(len-1, 1024)]` | per-token vectors without `<s>`, L2-normalised |
|
|
12
|
+
|
|
13
|
+
Everything except the transformer forward pass is implemented in this package
|
|
14
|
+
from scratch: the XLM-RoBERTa tokenizer (SentencePiece unigram model, the
|
|
15
|
+
`nmt_nfkc` precompiled charsmap, Unicode grapheme segmentation), the torch-free
|
|
16
|
+
loader for the sparse / ColBERT heads, the model downloader and the pooling.
|
|
17
|
+
|
|
18
|
+
Platforms: Apple Silicon, Linux ARM64, Linux x86_64 (Python 3.11+).
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv add bge-m3-lite # or: pip install bge-m3-lite
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Use
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from bge_m3_lite import BGEM3Embedder
|
|
30
|
+
|
|
31
|
+
embedder = BGEM3Embedder() # first call downloads ~2.3 GB into ~/.cache/bge-m3-lite
|
|
32
|
+
out = embedder.encode(
|
|
33
|
+
["What is BGE M3?", "BGE M3 是一個多語言嵌入模型。"],
|
|
34
|
+
return_dense=True,
|
|
35
|
+
return_sparse=True,
|
|
36
|
+
return_colbert_vecs=True,
|
|
37
|
+
)
|
|
38
|
+
out["dense_vecs"].shape # (2, 1024)
|
|
39
|
+
out["lexical_weights"][0] # {'4865': 0.08, '83': 0.08, ...}
|
|
40
|
+
out["colbert_vecs"][0].shape # (7, 1024)
|
|
41
|
+
|
|
42
|
+
embedder.convert_id_to_token(out["lexical_weights"][0])
|
|
43
|
+
embedder.compute_lexical_matching_score(lw_query, lw_passage)
|
|
44
|
+
embedder.colbert_score(q_vecs, p_vecs)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Passing a single string returns unwrapped values, like FlagEmbedding.
|
|
48
|
+
`BGEM3Embedder(precision="int8")` loads a 4× smaller quantised backbone
|
|
49
|
+
(see `docs/quantization.md` for the accuracy trade-off).
|
|
50
|
+
|
|
51
|
+
### CLI
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
bge-m3-lite download # pre-fetch the model files
|
|
55
|
+
bge-m3-lite info # cache state
|
|
56
|
+
echo "hello" | bge-m3-lite encode --sparse --colbert --tokens
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Environment variables
|
|
60
|
+
|
|
61
|
+
| variable | effect |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `BGE_M3_LITE_CACHE` | cache directory (default `~/.cache/bge-m3-lite/BAAI--bge-m3`) |
|
|
64
|
+
| `HF_ENDPOINT` | Hugging Face mirror, e.g. `https://hf-mirror.com` |
|
|
65
|
+
| `BGE_M3_LITE_OFFLINE=1` | never download, fail if files are missing |
|
|
66
|
+
| `BGE_M3_LITE_THREADS` | onnxruntime intra-op threads (default: physical cores) |
|
|
67
|
+
|
|
68
|
+
Model files are pinned to a specific Hugging Face revision and verified by
|
|
69
|
+
SHA-256 after download.
|
|
70
|
+
|
|
71
|
+
## Development
|
|
72
|
+
|
|
73
|
+
See `AGENTS.md` and `docs/` (architecture, tokenizer, verification, development).
|
|
74
|
+
|
|
75
|
+
## Status
|
|
76
|
+
|
|
77
|
+
v0.0.2: fp32 with exact parity with FlagEmbedding, plus an opt-in int8 backbone.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Extended grapheme cluster segmentation (UAX #29), pure Python.
|
|
2
|
+
|
|
3
|
+
Needed because Hugging Face's ``Precompiled`` normalizer applies the
|
|
4
|
+
SentencePiece charsmap per grapheme cluster rather than by longest prefix.
|
|
5
|
+
Tables come from ``_grapheme_data`` (generated from the Unicode database).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from bisect import bisect_right
|
|
11
|
+
from collections.abc import Iterator
|
|
12
|
+
|
|
13
|
+
from bge_m3_lite import _grapheme_data as _d
|
|
14
|
+
|
|
15
|
+
# Grapheme_Cluster_Break values (indices into _d.GCB_NAMES)
|
|
16
|
+
_OTHER, _CR, _LF, _CONTROL, _EXTEND, _ZWJ, _RI, _PREPEND, _SPACINGMARK = range(9)
|
|
17
|
+
_L, _V, _T, _LV, _LVT = range(9, 14)
|
|
18
|
+
# Indic_Conjunct_Break values
|
|
19
|
+
_INCB_NONE, _INCB_CONSONANT, _INCB_EXTEND, _INCB_LINKER = range(4)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _lookup(cp: int, starts, ends, values) -> int:
|
|
23
|
+
i = bisect_right(starts, cp) - 1
|
|
24
|
+
if i >= 0 and cp <= ends[i]:
|
|
25
|
+
return values[i]
|
|
26
|
+
return 0
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_gcb_cache: dict[int, int] = {}
|
|
30
|
+
_incb_cache: dict[int, int] = {}
|
|
31
|
+
_ext_cache: dict[int, bool] = {}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def gcb(cp: int) -> int:
|
|
35
|
+
v = _gcb_cache.get(cp)
|
|
36
|
+
if v is None:
|
|
37
|
+
v = _lookup(cp, _d.GCB_STARTS, _d.GCB_ENDS, _d.GCB_VALUES)
|
|
38
|
+
if len(_gcb_cache) < 65536:
|
|
39
|
+
_gcb_cache[cp] = v
|
|
40
|
+
return v
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def incb(cp: int) -> int:
|
|
44
|
+
v = _incb_cache.get(cp)
|
|
45
|
+
if v is None:
|
|
46
|
+
v = _lookup(cp, _d.INCB_STARTS, _d.INCB_ENDS, _d.INCB_VALUES)
|
|
47
|
+
if len(_incb_cache) < 65536:
|
|
48
|
+
_incb_cache[cp] = v
|
|
49
|
+
return v
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def is_extended_pictographic(cp: int) -> bool:
|
|
53
|
+
v = _ext_cache.get(cp)
|
|
54
|
+
if v is None:
|
|
55
|
+
v = bool(_lookup(cp, _d.EXTPICT_STARTS, _d.EXTPICT_ENDS, _d.EXTPICT_VALUES))
|
|
56
|
+
if len(_ext_cache) < 65536:
|
|
57
|
+
_ext_cache[cp] = v
|
|
58
|
+
return v
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def grapheme_boundaries(text: str) -> Iterator[int]:
|
|
62
|
+
"""Yield every index ``i`` (0 < i < len) where a grapheme cluster boundary falls."""
|
|
63
|
+
n = len(text)
|
|
64
|
+
if n < 2:
|
|
65
|
+
return
|
|
66
|
+
cps = [ord(c) for c in text]
|
|
67
|
+
props = [gcb(cp) for cp in cps]
|
|
68
|
+
ri_run = 0 # number of consecutive Regional_Indicator chars ending at i-1
|
|
69
|
+
for i in range(1, n):
|
|
70
|
+
prev, cur = props[i - 1], props[i]
|
|
71
|
+
if prev == _RI:
|
|
72
|
+
ri_run += 1
|
|
73
|
+
else:
|
|
74
|
+
ri_run = 0
|
|
75
|
+
# GB3
|
|
76
|
+
if prev == _CR and cur == _LF:
|
|
77
|
+
continue
|
|
78
|
+
# GB4 / GB5
|
|
79
|
+
if prev in (_CONTROL, _CR, _LF) or cur in (_CONTROL, _CR, _LF):
|
|
80
|
+
yield i
|
|
81
|
+
continue
|
|
82
|
+
# GB6 / GB7 / GB8 (Hangul)
|
|
83
|
+
if prev == _L and cur in (_L, _V, _LV, _LVT):
|
|
84
|
+
continue
|
|
85
|
+
if prev in (_LV, _V) and cur in (_V, _T):
|
|
86
|
+
continue
|
|
87
|
+
if prev in (_LVT, _T) and cur == _T:
|
|
88
|
+
continue
|
|
89
|
+
# GB9 / GB9a / GB9b
|
|
90
|
+
if cur in (_EXTEND, _ZWJ, _SPACINGMARK) or prev == _PREPEND:
|
|
91
|
+
continue
|
|
92
|
+
# GB9c: Consonant [Extend Linker]* Linker [Extend Linker]* x Consonant
|
|
93
|
+
if incb(cps[i]) == _INCB_CONSONANT:
|
|
94
|
+
j = i - 1
|
|
95
|
+
seen_linker = False
|
|
96
|
+
while j >= 0:
|
|
97
|
+
v = incb(cps[j])
|
|
98
|
+
if v == _INCB_LINKER:
|
|
99
|
+
seen_linker = True
|
|
100
|
+
elif v != _INCB_EXTEND:
|
|
101
|
+
break
|
|
102
|
+
j -= 1
|
|
103
|
+
if seen_linker and j >= 0 and incb(cps[j]) == _INCB_CONSONANT:
|
|
104
|
+
continue
|
|
105
|
+
# GB11: ExtPict Extend* ZWJ x ExtPict
|
|
106
|
+
if prev == _ZWJ and is_extended_pictographic(cps[i]):
|
|
107
|
+
j = i - 2
|
|
108
|
+
while j >= 0 and props[j] == _EXTEND:
|
|
109
|
+
j -= 1
|
|
110
|
+
if j >= 0 and is_extended_pictographic(cps[j]):
|
|
111
|
+
continue
|
|
112
|
+
# GB12 / GB13: keep RI pairs together
|
|
113
|
+
if prev == _RI and cur == _RI and ri_run % 2 == 1:
|
|
114
|
+
continue
|
|
115
|
+
# GB999
|
|
116
|
+
yield i
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def graphemes(text: str) -> list[str]:
|
|
120
|
+
"""Split ``text`` into extended grapheme clusters."""
|
|
121
|
+
out: list[str] = []
|
|
122
|
+
start = 0
|
|
123
|
+
for b in grapheme_boundaries(text):
|
|
124
|
+
out.append(text[start:b])
|
|
125
|
+
start = b
|
|
126
|
+
if text:
|
|
127
|
+
out.append(text[start:])
|
|
128
|
+
return out
|