ember.cpp
0.1.0
C++ inference engine for Llama-family and paperkiln models (CPU, CUDA, WebAssembly)
Loading...
Searching...
No Matches
spm_tokenizer.h
Go to the documentation of this file.
1
#pragma once
2
// SentencePiece vocabularies (tokenizer.ggml.model "llama") as llama.cpp b6000 encodes them
3
// (llama-vocab.cpp: tokenizer_st_partition, then llm_tokenizer_spm_session on each text
4
// fragment), for the chat mode of Gemma 3 GGUFs (chat_tokenizer.h). ember's older SentencePiece
5
// path (tokenizer.cpp) approximates merges from scores and stays with the TinyLlama models.
6
//
7
// - Special tokens: every CONTROL, USER_DEFINED and UNKNOWN token (GGUF token types 3, 4, 2) is
8
// cut out of the text before encoding, longest token text first, each occurrence left to right
9
// (llama.cpp's parse_special = true: chat prompts carry <bos> and <start_of_turn> as text).
10
// - Each remaining fragment: " " -> U+2581 (and a leading U+2581 when
11
// tokenizer.ggml.add_space_prefix is true and the fragment starts the text or follows a special
12
// token), split into UTF-8 characters, then the adjacent pair whose concatenation is a token with
13
// the highest score is merged, ties to the leftmost, until no pair is a token. A final symbol that
14
// is not a token is written as its bytes (<0xXX>).
15
// - No BOS is added: a chat template writes its own.
16
// Decoding: U+2581 -> " ", byte tokens -> their byte, every other token -> its text.
17
#include <algorithm>
18
#include <cstdint>
19
#include <cstdio>
20
#include <functional>
21
#include <queue>
22
#include <stdexcept>
23
#include <string>
24
#include <unordered_map>
25
#include <utility>
26
#include <vector>
27
28
namespace
ember
{
29
30
class
SpmTokenizer
{
31
public
:
32
SpmTokenizer
(std::vector<std::string> tokens, std::vector<float> scores, std::vector<int32_t>
types
,
33
bool
add_space_prefix)
34
:
tokens_
(std::
move
(tokens)),
scores_
(std::
move
(scores)),
types_
(std::
move
(
types
)),
35
add_space_prefix_
(add_space_prefix) {
36
if
(
scores_
.size() !=
tokens_
.size())
throw
std::runtime_error(
"SentencePiece GGUF: scores and tokens differ in length"
);
37
if
(
types_
.size() !=
tokens_
.size())
throw
std::runtime_error(
"SentencePiece GGUF: token types and tokens differ in length"
);
38
id_
.reserve(
tokens_
.size());
39
for
(
size_t
i
= 0;
i
<
tokens_
.size(); ++
i
)
id_
[
tokens_
[
i
]] =
static_cast<
int
>
(
i
);
// the last one wins
40
for
(
size_t
i
= 0;
i
<
tokens_
.size(); ++
i
)
41
if
((
types_
[
i
] ==
kControl
||
types_
[
i
] ==
kUserDefined
||
types_
[
i
] ==
kUnknown
) && !
tokens_
[
i
].
empty
())
42
specials_
.push_back(
static_cast<
int
>
(
i
));
43
// Longest text first (llama.cpp sorts by length with std::sort; among equal lengths the
44
// order only matters for overlapping specials, which the gate against HF would show).
45
std::stable_sort(
specials_
.begin(),
specials_
.end(),
46
[&](
int
a
,
int
b
) { return tokens_[a].size() > tokens_[b].size(); });
47
for
(
int
b
= 0;
b
< 256; ++
b
) {
48
char
buf
[8];
49
std::snprintf(
buf
,
sizeof
buf
,
"<0x%02X>"
,
b
);
50
auto
it
=
id_
.find(
buf
);
51
if
(
it
==
id_
.end())
it
=
id_
.find(std::string(1,
static_cast<
char
>
(
b
)));
52
byte_id_
[
b
] =
it
==
id_
.end() ? -1 :
it
->second;
53
}
54
}
55
56
std::vector<int>
encode
(
const
std::string&
text
)
const
{
57
// Fragments: (special token id, or -1 with [offset, offset + length) of text).
58
struct
Frag
{
59
int
id
;
60
size_t
off
, len;
61
};
62
std::vector<Frag>
frags
= {{-1, 0,
text
.size()}};
63
for
(
int
sid
:
specials_
) {
64
const
std::string&
s
=
tokens_
[
sid
];
65
std::vector<Frag> next;
66
next.reserve(
frags
.size());
67
for
(
const
Frag
&
f
:
frags
) {
68
if
(
f
.id >= 0) {
69
next.push_back(
f
);
70
continue
;
71
}
72
size_t
at
=
f
.off;
73
const
size_t
end
=
f
.off +
f
.len;
74
while
(
at
<
end
) {
75
const
size_t
hit
=
text
.find(
s
,
at
);
76
if
(
hit
== std::string::npos ||
hit
+
s
.size() >
end
)
break
;
77
if
(
hit
>
at
) next.push_back({-1,
at
,
hit
-
at
});
78
next.push_back({
sid
,
hit
,
s
.size()});
79
at
=
hit
+
s
.size();
80
}
81
if
(
at
<
end
) next.push_back({-1,
at
,
end
-
at
});
82
}
83
frags
.swap(next);
84
}
85
std::vector<int>
out
;
86
bool
prev_special
=
true
;
87
for
(
const
Frag
&
f
:
frags
) {
88
if
(
f
.id >= 0) {
89
out
.push_back(
f
.id);
90
prev_special
=
true
;
91
continue
;
92
}
93
if
(
f
.len == 0)
continue
;
94
std::string
piece
=
add_space_prefix_
&&
prev_special
? std::string(
" "
) : std::string();
95
piece
+=
text
.substr(
f
.off,
f
.len);
96
encode_fragment
(
escape
(
piece
),
out
);
97
prev_special
=
false
;
98
}
99
return
out
;
100
}
101
102
std::string
decode
(
const
std::vector<int>& ids)
const
{
103
std::string
s
;
104
for
(
int
id
: ids) {
105
if
(
id
< 0 ||
static_cast<
size_t
>
(
id
) >=
tokens_
.size())
continue
;
106
const
std::string&
t
=
tokens_
[
id
];
107
if
(
types_
[
id
] ==
kByte
&&
t
.size() == 6 &&
t
.compare(0, 3,
"<0x"
) == 0) {
108
s
+=
static_cast<
char
>
(std::stoi(
t
.substr(3, 2),
nullptr
, 16));
109
continue
;
110
}
111
for
(
size_t
i
= 0;
i
<
t
.size();) {
112
if
(
t
.compare(
i
, 3,
kSpace
) == 0) {
113
s
+=
' '
;
114
i
+= 3;
115
}
else
{
116
s
+=
t
[
i
++];
117
}
118
}
119
}
120
return
s
;
121
}
122
123
size_t
size
()
const
{
return
tokens_
.size(); }
124
125
private
:
126
static
constexpr
int32_t
kUnknown
= 2,
kControl
= 3,
kUserDefined
= 4,
kByte
= 6;
127
static
constexpr
const
char
*
kSpace
=
"\xe2\x96\x81"
;
// U+2581
128
129
static
std::string
escape
(
const
std::string&
s
) {
130
std::string
o
;
131
o
.reserve(
s
.size() +
s
.size() / 2);
132
for
(
char
c
:
s
) {
133
if
(
c
==
' '
)
o
+=
kSpace
;
134
else
o
+=
c
;
135
}
136
return
o
;
137
}
138
139
struct
Symbol
{
140
int
prev
,
next
;
141
size_t
off
,
n
;
142
};
143
struct
Bigram
{
144
int
left
,
right
;
145
float
score
;
146
size_t
size
;
147
};
148
struct
Lower
{
// llama.cpp's comparator: the highest score on top, ties to the leftmost
149
bool
operator()
(
const
Bigram
&
l
,
const
Bigram
&
r
)
const
{
150
return
l
.score <
r
.score || (
l
.score ==
r
.score &&
l
.left >
r
.left);
151
}
152
};
153
154
int
lookup
(
const
std::string&
s
)
const
{
155
auto
it
=
id_
.find(
s
);
156
return
it
==
id_
.end() ? -1 :
it
->second;
157
}
158
159
void
encode_fragment
(
const
std::string&
text
, std::vector<int>&
out
)
const
{
160
static
const
uint8_t
utf8_len[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 4};
161
std::vector<Symbol>
sym
;
162
for
(
size_t
off
= 0;
off
<
text
.size();) {
163
const
size_t
len = std::min<size_t>(utf8_len[
static_cast<
uint8_t
>
(
text
[
off
]) >> 4],
text
.size() -
off
);
164
const
int
index
=
static_cast<
int
>
(
sym
.size());
165
sym
.push_back({
index
- 1,
off
+ len ==
text
.size() ? -1 :
index
+ 1,
off
, len});
166
off
+= len;
167
}
168
std::priority_queue<Bigram, std::vector<Bigram>,
Lower
> queue;
169
std::unordered_map<std::string, std::pair<int, int>>
rev_merge
;
170
auto
try_add
= [&](
int
left,
int
right) {
171
if
(left == -1 || right == -1)
return
;
172
const
std::string
s
=
text
.substr(
sym
[left].
off
,
sym
[left].
n
+
sym
[right].
n
);
173
const
int
id
=
lookup
(
s
);
174
if
(
id
< 0)
return
;
175
queue.push({left, right,
scores_
[
id
],
s
.size()});
176
rev_merge
[
s
] = {left, right};
177
};
178
for
(
int
i
= 1;
i < static_cast<int>
(
sym
.size()); ++
i
)
try_add
(
i
- 1,
i
);
179
while
(!queue.empty()) {
180
const
Bigram
b
= queue.top();
181
queue.pop();
182
Symbol
&
l
=
sym
[
b
.left];
183
Symbol
&
r
=
sym
[
b
.right];
184
if
(
l
.n == 0 ||
r
.n == 0 ||
l
.n +
r
.n !=
b
.size)
continue
;
185
l
.
n
+=
r
.n;
186
r
.n = 0;
187
l
.next =
r
.next;
188
if
(
r
.next >= 0)
sym
[
r
.next].prev =
b
.left;
189
try_add
(
l
.prev,
b
.left);
190
try_add
(
b
.left,
l
.next);
191
}
192
std::function<
void
(
const
Symbol
&)>
resegment
= [&](
const
Symbol
&
s
) {
193
const
std::string
t
=
text
.substr(
s
.off,
s
.n);
194
const
int
id
=
lookup
(
t
);
195
if
(
id
>= 0) {
196
out
.push_back(
id
);
197
return
;
198
}
199
const
auto
p
=
rev_merge
.find(
t
);
200
if
(
p
==
rev_merge
.end()) {
201
for
(
size_t
j
= 0;
j
<
s
.n; ++
j
) {
202
const
int
bid
=
byte_id_
[
static_cast<
uint8_t
>
(
text
[
s
.off +
j
])];
203
if
(
bid
< 0)
throw
std::runtime_error(
"SentencePiece GGUF has no byte token for a byte of the text"
);
204
out
.push_back(
bid
);
205
}
206
return
;
207
}
208
resegment
(
sym
[
p
->second.first]);
209
resegment
(
sym
[
p
->second.second]);
210
};
211
for
(
int
i
=
sym
.empty() ? -1 : 0;
i
!= -1;
i
=
sym
[
i
].next)
resegment
(
sym
[
i
]);
212
}
213
214
std::vector<std::string>
tokens_
;
215
std::vector<float>
scores_
;
216
std::vector<int32_t>
types_
;
217
bool
add_space_prefix_
;
218
std::unordered_map<std::string, int>
id_
;
219
std::vector<int>
specials_
;
220
int
byte_id_
[256];
221
};
222
223
}
// namespace ember
ember::SpmTokenizer
Definition
spm_tokenizer.h:30
ember::SpmTokenizer::add_space_prefix_
bool add_space_prefix_
Definition
spm_tokenizer.h:217
ember::SpmTokenizer::decode
std::string decode(const std::vector< int > &ids) const
Definition
spm_tokenizer.h:102
ember::SpmTokenizer::specials_
std::vector< int > specials_
Definition
spm_tokenizer.h:219
ember::SpmTokenizer::id_
std::unordered_map< std::string, int > id_
Definition
spm_tokenizer.h:218
ember::SpmTokenizer::lookup
int lookup(const std::string &s) const
Definition
spm_tokenizer.h:154
ember::SpmTokenizer::kControl
static constexpr int32_t kControl
Definition
spm_tokenizer.h:126
ember::SpmTokenizer::tokens_
std::vector< std::string > tokens_
Definition
spm_tokenizer.h:214
ember::SpmTokenizer::scores_
std::vector< float > scores_
Definition
spm_tokenizer.h:215
ember::SpmTokenizer::SpmTokenizer
SpmTokenizer(std::vector< std::string > tokens, std::vector< float > scores, std::vector< int32_t > types, bool add_space_prefix)
Definition
spm_tokenizer.h:32
ember::SpmTokenizer::encode_fragment
void encode_fragment(const std::string &text, std::vector< int > &out) const
Definition
spm_tokenizer.h:159
ember::SpmTokenizer::encode
std::vector< int > encode(const std::string &text) const
Definition
spm_tokenizer.h:56
ember::SpmTokenizer::kUnknown
static constexpr int32_t kUnknown
Definition
spm_tokenizer.h:126
ember::SpmTokenizer::byte_id_
int byte_id_[256]
Definition
spm_tokenizer.h:220
ember::SpmTokenizer::size
size_t size() const
Definition
spm_tokenizer.h:123
ember::SpmTokenizer::types_
std::vector< int32_t > types_
Definition
spm_tokenizer.h:216
ember::SpmTokenizer::kSpace
static constexpr const char * kSpace
Definition
spm_tokenizer.h:127
ember::SpmTokenizer::kUserDefined
static constexpr int32_t kUserDefined
Definition
spm_tokenizer.h:126
ember::SpmTokenizer::kByte
static constexpr int32_t kByte
Definition
spm_tokenizer.h:126
ember::SpmTokenizer::escape
static std::string escape(const std::string &s)
Definition
spm_tokenizer.h:129
p
const float * p[kIrDepthSourcesMax]
Definition
graph_ir_cuda.cu:118
n
int n
Definition
graph_ir_cuda.cu:326
text
std::string text
Definition
graph_ir_expr.cpp:37
c
float c
Definition
graph_ir_expr.cpp:256
off
long off
Definition
graph_ir_recurrence.cpp:120
id
int id
Definition
graph_ir_recurrence.cpp:126
index
int index
Definition
graph_ir_recurrence.cpp:119
a
std::vector< E > a
Definition
graph_ir_recurrence.cpp:128
out
node out
Definition
graph_ir_schema.h:9
j
size_t j
Definition
graph_ir_validate.cpp:222
ember
Definition
chat_gguf.h:11
ember::SpmTokenizer::Bigram
Definition
spm_tokenizer.h:143
ember::SpmTokenizer::Bigram::score
float score
Definition
spm_tokenizer.h:145
ember::SpmTokenizer::Bigram::size
size_t size
Definition
spm_tokenizer.h:146
ember::SpmTokenizer::Bigram::left
int left
Definition
spm_tokenizer.h:144
ember::SpmTokenizer::Bigram::right
int right
Definition
spm_tokenizer.h:144
ember::SpmTokenizer::Lower
Definition
spm_tokenizer.h:148
ember::SpmTokenizer::Lower::operator()
bool operator()(const Bigram &l, const Bigram &r) const
Definition
spm_tokenizer.h:149
ember::SpmTokenizer::Symbol
Definition
spm_tokenizer.h:139
ember::SpmTokenizer::Symbol::n
size_t n
Definition
spm_tokenizer.h:141
ember::SpmTokenizer::Symbol::prev
int prev
Definition
spm_tokenizer.h:140
ember::SpmTokenizer::Symbol::next
int next
Definition
spm_tokenizer.h:140
ember::SpmTokenizer::Symbol::off
size_t off
Definition
spm_tokenizer.h:141
spm_tokenizer.h
Generated by
1.9.8