tpt_tokenizer_core/
wordpiece.rs1use alloc::collections::BTreeMap;
4use alloc::{
5 format,
6 string::{String, ToString},
7 vec,
8 vec::Vec,
9};
10
11use crate::error::TokenizerError;
12use crate::pretokenize::bert_basic;
13use crate::tokenizer::{TokenId, Tokenizer};
14
15const CONTINUATION: &str = "##";
17
18#[derive(Debug, Clone)]
27pub struct WordPieceTokenizer {
28 vocab: BTreeMap<String, TokenId>,
29 id_to_token: BTreeMap<TokenId, String>,
30 unk_id: TokenId,
31 max_input_chars_per_word: usize,
32 lowercase: bool,
33 #[cfg(feature = "normalization")]
34 normalize: Option<crate::normalize::NormalizationForm>,
35}
36
37impl WordPieceTokenizer {
38 pub fn from_vocab(
45 vocab: BTreeMap<String, TokenId>,
46 unk_token: &str,
47 ) -> Result<Self, TokenizerError> {
48 let unk_id = *vocab
49 .get(unk_token)
50 .ok_or_else(|| TokenizerError::UnknownToken(unk_token.to_string()))?;
51 let id_to_token = vocab
52 .iter()
53 .map(|(token, &id)| (id, token.clone()))
54 .collect();
55 Ok(Self {
56 vocab,
57 id_to_token,
58 unk_id,
59 max_input_chars_per_word: 100,
60 lowercase: false,
61 #[cfg(feature = "normalization")]
62 normalize: None,
63 })
64 }
65
66 #[must_use]
69 pub fn with_lowercase(mut self) -> Self {
70 self.lowercase = true;
71 self
72 }
73
74 #[cfg(feature = "normalization")]
78 #[must_use]
79 pub fn with_normalization(mut self, form: crate::normalize::NormalizationForm) -> Self {
80 self.normalize = Some(form);
81 self
82 }
83
84 #[must_use]
86 pub fn vocab_size(&self) -> usize {
87 self.vocab.len()
88 }
89
90 fn tokenize_word(&self, word: &str) -> Option<Vec<String>> {
96 if self.vocab.contains_key(word) {
97 return Some(vec![word.to_string()]);
98 }
99 let chars: Vec<char> = word.chars().collect();
100 if chars.len() > self.max_input_chars_per_word {
101 return None;
102 }
103
104 let mut sub_tokens = Vec::new();
105 let mut start = 0usize;
106 while start < chars.len() {
107 let mut end = chars.len();
108 let mut cur_substr: Option<String> = None;
109 while start < end {
110 let substr: String = chars[start..end].iter().collect();
111 let candidate = if start > 0 {
112 format!("{CONTINUATION}{substr}")
113 } else {
114 substr.clone()
115 };
116 if self.vocab.contains_key(&candidate) {
117 cur_substr = Some(candidate);
118 break;
119 }
120 end -= 1;
121 }
122 let sub = cur_substr?;
123 sub_tokens.push(sub);
124 start = end;
125 }
126
127 Some(sub_tokens)
128 }
129
130 #[cfg(feature = "std")]
137 pub fn from_file(vocab_path: &str, unk_token: &str) -> Result<Self, TokenizerError> {
138 let text = std::fs::read_to_string(vocab_path)?;
139 let vocab = crate::tokenizer::parse_vocab_lines(&text);
140 Self::from_vocab(vocab, unk_token)
141 }
142}
143
144impl Tokenizer for WordPieceTokenizer {
145 fn encode(&self, text: &str) -> Result<Vec<TokenId>, TokenizerError> {
146 #[cfg(feature = "normalization")]
147 let normalized;
148 #[cfg(feature = "normalization")]
149 let text = if let Some(form) = self.normalize {
150 normalized = crate::normalize::normalize(text, form);
151 normalized.as_str()
152 } else {
153 text
154 };
155
156 let mut ids = Vec::new();
157 for word in bert_basic(text, self.lowercase) {
158 match self.tokenize_word(&word) {
159 Some(subs) => {
160 for sub in subs {
161 ids.push(
162 *self
163 .vocab
164 .get(&sub)
165 .ok_or(TokenizerError::UnknownToken(sub))?,
166 );
167 }
168 }
169 None => ids.push(self.unk_id),
170 }
171 }
172 Ok(ids)
173 }
174
175 fn decode(&self, ids: &[TokenId]) -> Result<String, TokenizerError> {
184 let mut out = String::new();
185 for (i, &id) in ids.iter().enumerate() {
186 let token = self
187 .id_to_token
188 .get(&id)
189 .ok_or_else(|| TokenizerError::UnknownToken(id.to_string()))?;
190 if let Some(stripped) = token.strip_prefix(CONTINUATION) {
191 out.push_str(stripped);
192 } else {
193 if i > 0 {
194 out.push(' ');
195 }
196 out.push_str(token);
197 }
198 }
199 Ok(out)
200 }
201}