Durch die Verwendung je eines Finite State Transducers für Wortanfangs- und Folgetoken kann unser Tokenizer prefixes Byte-weise ermitteln, und bei jedem Byte entscheiden, ob dies ein komplettes Token ist, und ob das Vokabular noch längere Token mit demselben Präfix enthält. Das so minimierte Datenaufkommen ist ideal für moderne Prozessoren, bei denen jeder Speicherzugriff, der nicht in den Cache passt, zeitaufwändig ist.



In einem Benchmark zwischen wordpiece und aleph-alpha-tokenizer über einen kleinen Korpus deutscher Sätze fanden wir, dass der verbesserte Algorithmus Sätze mit langen Worten, die in viele Tokens zerlegt werden, am meisten beschleunigt (bis zu etwa einem Faktor Sechs). Sogar bei kurzen Sätzen mit ein-Token-Wörtern, der nachvollziehbar beste Fall für wordpiece, ist aleph-alpha-tokenizer um mindestens 20% schneller (wenn man ihn als Model benutzt, die selbe Schnittstelle, die auch wordpiece implementiert).



Abgesehen von der algorithmischen Verbesserung bemerkte llogiq, dass die internen Schnittstellen bei tokenizers für die Verwendung aus python oder javascript optimiert sind. Das ist sinnvoll, weil die Mehrzahl der Nutzer diese Sprachen verwenden. Allerdings bezahlt tokenizers diese Vereinfachung mit zusätzlichen Speicheranforderungen und Datenkopieen. Diese Kosten kann man vermeiden, wenn man direkt in Rust arbeitet.



Also baute er seinen Prototypen in eine komplette Bibliothek aus, die entweder alleine oder zusammen mit tokenizers verwendet werden kann. Die unabhängige Version ist weniger flexibel, was die Eingabetokens angeht, dafür einfacher zu nutzen. Dies wird sich allerdings in Zukunft ändern, wenn wir weitere Nutzungsfälle mit der Bibliothek abdecken. Außerdem erlaubt die Rust-Schnittstelle dem Nutzer, den Token ID Typ vorzugeben, was für uns die Umsetzung der IDs in andere Typen eliminiert, wenn die weitere Verarbeitung andere Datentypen als &[u32] verwendet.



Die Bibliothek hat noch keine Anbindung zur Verwendung aus anderen Sprachen. Wir planen, einen weiteren pull request an huggingface zu senden, um den aleph-alpha-tokenizer als optionales feature einzubinden. By using one finite state transducer for word start and follower tokens, the tokenizer can look up prefixes byte-wise, at each byte deciding if this is a complete token and if there are longer tokens with the same prefix. The minimal data required for this task makes it very suitable for modern processors where memory access that does not fit in caches are at a premium.



Benchmarking both wordpiece and aleph-alpha-tokenizer on a small corpus of German sentences, we find that the algorithmic improvement speeds up sentences with long words that tokenize into many tokens the most (up to roughly a factor of six). Even on short sentences with one-token words, which is arguably the best case for wordpiece, aleph-alpha-tokenizer is at least 20% faster (when used as a `Model`, the same trait wordpiece implements).



Apart from the algorithmic improvements, llogiq noticed that the internal interfaces of tokenizers were optimized for use from dynamic languages to simplify writing the python and javascript bindings. This makes sense as the majority of users will access tokenizers from one the bindings. However, this trade-off involves some cost in memory allocation and copying that can be avoided when working directly in Rust.



Thus he fleshed out the initial proof of concept to a library crate that can be used either standalone or in conjunction with tokenizers. The standalone version is much less flexible regarding tokens, but easier to setup, however, this is likely to change in the future as we move more use cases to the new interface. Also the standalone version allows the user to specify the token ID type, which removes the need to transform the token IDs if the model does not take `u32`s.



The crate lacks bindings to other languages for now. We plan to send yet another PR to huggingface to include it in tokenizers as an optional feature.