Simpler, clearer model card: what it is, how it was built, how to use it, how well it does
Browse files
README.md
CHANGED
|
@@ -15,12 +15,14 @@ tags:
|
|
| 15 |
- ctc
|
| 16 |
- gru
|
| 17 |
- swipe-keyboard
|
|
|
|
|
|
|
| 18 |
model-index:
|
| 19 |
-
- name: swipe-typing
|
| 20 |
results:
|
| 21 |
- task:
|
| 22 |
type: other
|
| 23 |
-
name: Swipe decoding, English, first guess right (all swipes)
|
| 24 |
dataset:
|
| 25 |
name: bench-2000 (2,000 real English swipes, FUTO MIT test split)
|
| 26 |
type: custom
|
|
@@ -30,7 +32,7 @@ model-index:
|
|
| 30 |
name: first guess right, all swipes
|
| 31 |
- task:
|
| 32 |
type: other
|
| 33 |
-
name: Swipe decoding, English, right word in top 3 (all swipes)
|
| 34 |
dataset:
|
| 35 |
name: bench-2000 (2,000 real English swipes, FUTO MIT test split)
|
| 36 |
type: custom
|
|
@@ -40,179 +42,100 @@ model-index:
|
|
| 40 |
name: right word in top 3, all swipes
|
| 41 |
---
|
| 42 |
|
| 43 |
-
#
|
| 44 |
|
| 45 |
-
|
| 46 |
|
| 47 |
-
|
| 48 |
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
Code, scripts and tests: https://github.com/itsdestin/swipe-keyboard/tree/feature/179-engine-in-keyboard (development branch; it moves to the main branch when merged). How every number below was measured: [methods page](METHODS.md) (published copy: METHODS.md).
|
| 52 |
-
|
| 53 |
-
**Status: an opt-in alternative engine.** In the keyboard it is used only when the user creates an `own-engine` file; FUTO's engine remains the default. On English it is **not yet as accurate** as FUTO's engine (see the numbers below).
|
| 54 |
-
|
| 55 |
-
## What it is, and what it is not
|
| 56 |
-
|
| 57 |
-
- A 1-D convolution stack followed by a 2-layer bidirectional GRU, trained with the CTC loss (Graves et al., 2006) on the sequence of keys the finger passes over. Read from the shipped file's header: 2 convolution layers of width 96 (kernel 5), a 2-layer bidirectional GRU with hidden size 128, a pointer head of width 32, 64 time steps of 8 features, float32 weights. That is about **529,000 parameters** and a **2.1 MB** file (2,115,260 bytes).
|
| 58 |
-
- Layout independent: the network does not have a fixed output per key. For every time step it scores each key from the finger's offset to that key (in key widths) plus the key's size, with one small shared scoring network ("pointer head"). A QWERTY, AZERTY, QWERTZ or Spanish keyboard with an n-tilde key therefore use the same weights.
|
| 59 |
-
- It outputs probabilities over keys, not words. Words come from a **lexicon beam search** over a word list (a trie of key sequences), so every answer is a word in the list, scored as network evidence + word frequency + the n-gram sentence prior when previous words are known + a **geometric cross-check** (a separate, classical shape-matching decoder, blended in with weight 40).
|
| 60 |
-
- **Fallback:** with no model file, a damaged one, or an empty result, the geometric decoder alone answers; swiping never depends on the network.
|
| 61 |
-
- It is **not** a handwriting or general gesture recogniser, and it cannot output a word that is not in the word list (words missing from the list are always misses; see limitations).
|
| 62 |
-
|
| 63 |
-
## Intended use
|
| 64 |
-
|
| 65 |
-
On-device swipe typing. The keyboard supplies: the finger path (x, y, time), the live rectangles of the keys, a verified word list for the language, and, when available, the previous words of the sentence. Settings used for all reported results: frequency weight 0.06, geometric cross-check weight 40, beam width 48.
|
| 66 |
-
|
| 67 |
-
Speed on one CPU thread: about 3.5 ms per swipe (measured on a loaded desktop, plain `-O3` build).
|
| 68 |
-
|
| 69 |
-
Not intended for: security-sensitive input, identifying people, or any use that stores or sends a user's swipes. The keyboard does not record swipes unless a user switches on a separate opt-in recorder.
|
| 70 |
-
|
| 71 |
-
## File format
|
| 72 |
-
|
| 73 |
-
File `swipe-model.bin`, read by our own plain C++ runner (`engine/src/swipe_net.cpp`, no machine-learning library). Magic `SKNN`, version 1, little-endian.
|
| 74 |
-
|
| 75 |
-
| Part | Content |
|
| 76 |
|---|---|
|
| 77 |
-
|
|
| 78 |
-
|
|
| 79 |
-
|
|
| 80 |
-
|
|
| 81 |
-
|
| 82 |
-
Because the layer sizes are stored in the file, a bigger or smaller network needs no code change. The loader validates every header field against fixed ranges and refuses a file that is too short for the tensors it declares. **There is no checksum inside this file**, unlike the next-word models; integrity comes from the SHA-256 the keyboard pins (`941272a7017229a73e07d1c0bd766757e739174db3a42fb622c26256b8c46110`), checked before the engine loads it. A mismatch means geometry-only mode.
|
| 83 |
-
|
| 84 |
-
Input per step (64 steps resampled by arc length): x, y, dx, dy, log elapsed time, fraction of the path done, step length, log path length.
|
| 85 |
|
| 86 |
-
|
| 87 |
|
| 88 |
-
|
| 89 |
-
|---|---|---|
|
| 90 |
-
| FUTO's public swipe collections (swipe.futo.org: `swipe-1` training split, `swipe-2` to `swipe-5`) | real swipes, English QWERTY; `swipe-5` also holds real German, French and Spanish swipes on their own layouts | MIT (Copyright (c) 2025 FUTO; text kept in `FUTO-SWIPE-DATA-LICENSE.txt`) |
|
| 91 |
-
| Our own synthetic swipes | 100,000 each for Spanish, French and German, generated by `scripts/swipe/synth_swipes.py` on approximate layouts; no synthetic English in T1 | ours; MIT |
|
| 92 |
-
| Word lists (used at run time, not part of the model file) | the benchmarks used 260,000-word lists counted from FineWeb / FineWeb-2 and Tatoeba; the keyboard plugin loads the language profile's verified `.combined` list instead (top 100,000 words for es/fr/de) | ODC-By 1.0 and CC BY 2.0 FR, see the language-pack notices |
|
| 93 |
|
| 94 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
-
|
| 97 |
-
- `bench-2000`, `test-extra-10000` and `test.jsonl` (FUTO's MIT test split) and the validation sessions: an overlap check dropped any swipe whose id or session appeared in them; no overlapping swipe or session was found in any collection (56,005 swipe keys and 768 sessions checked).
|
| 98 |
-
- The HeliBoard gesture test sets (CC BY-SA): used for **testing only**. No HeliBoard data is in the training set, which is why the model's licence is not share-alike.
|
| 99 |
-
- No FUTO model weights were used or fine-tuned; the architecture and code are our own, written from published literature (CTC, lexicon-constrained gesture decoding, SHARK2, dynamic time warping).
|
| 100 |
|
| 101 |
-
|
| 102 |
|
| 103 |
-
|
| 104 |
-
- Hardware: one desktop NVIDIA RTX 4070 Super. **Training time for T1: 16 minutes** for 30 epochs.
|
| 105 |
-
- Model selection: search settings (frequency weight, cross-check weight, beam) were fixed on the clean validation split before scoring. `bench-2000` and `test-extra-10000` were scored once for the earlier model F1 and a second time for T1, the final model only; T1 was chosen using other data (trv8k, `swipe-5` slices, clean3000), see below.
|
| 106 |
-
- Not recorded in the repository: the exact command line of the T1 run and the random seed (the script default is 1).
|
| 107 |
|
| 108 |
-
|
| 109 |
|
| 110 |
-
|
| 111 |
|
| 112 |
-
|
|
|
|
|
|
|
| 113 |
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
| Set | FUTO engine, first / top 3 | This model, first / top 3 | Denominator |
|
| 123 |
-
|---|---|---|---|
|
| 124 |
-
| bench-2000 | 93.32 / 96.56 | 90.40 / 94.95 | all 2,000 swipes (FUTO figure: all 2,000 through our recogniser harness) |
|
| 125 |
-
| bench-2000 | 93.32 / 96.56 | 92.34 / 96.99 | ours: in-dictionary swipes only; the FUTO figure is not restricted this way, so this row is not like-for-like |
|
| 126 |
-
| clean3000 | 92.5 / 96.3 (2,909 swipes FUTO's tool can score) | 93.13 / 97.37 | ours: in-dictionary only (97.5 % of the 3,000) |
|
| 127 |
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
-
|
| 131 |
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
|
| 134 |
-
|
| 135 |
-
|---|---|---|
|
| 136 |
-
| English | 82.1 / 91.5 | 84.5 / 93.4 |
|
| 137 |
-
| Spanish | 81.1 / 89.8 | 77.7 / 85.1 |
|
| 138 |
-
| French | 71.7 / 85.9 | 64.6 / 76.9 |
|
| 139 |
-
| German | 73.3 / 82.3 | 69.7 / 76.4 |
|
| 140 |
|
| 141 |
-
|
| 142 |
|
| 143 |
-
|
| 144 |
|
| 145 |
-
|
| 146 |
|
| 147 |
-
|
|
| 148 |
|---|---|---|---|
|
| 149 |
-
|
|
| 150 |
-
|
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
### Synthetic Spanish, French and German (not human)
|
| 155 |
-
|
| 156 |
-
The synthetic noisy sets (`bench/languages/synthetic-noisy-v1-*.json`, 400 words each) were scored with the **older FUTO-engine keyboard**, not with this model: Spanish 89.00 / 98.25, French 83.25 / 93.00, German 87.75 / 97.00 (first / top 3). We have **no synthetic-set score for T1 in the sources**. Synthetic English calibration: real 92.8 / 96.4 against synthetic 92.3 / 96.0. These sets only cover dictionary words and assume people swipe other languages like English.
|
| 157 |
-
|
| 158 |
-
### Sentence-context prior (experiment)
|
| 159 |
-
|
| 160 |
-
When the keyboard knows the previous words, the English n-gram prior lifts T1 on a 7,198-swipe set (trv8k) from 92.19 / 95.79 to 93.10 / 95.92 (first / top 3, all swipes; in-dictionary 95.28 / 98.17). That is +0.9 first guess. The set is a held-back 5 % slice of FUTO's `train.jsonl` that still has sentence text. Single-word swipes (bench-2000) carry no sentence, so the headline numbers above do not include this gain.
|
| 161 |
-
|
| 162 |
-
### Gboard
|
| 163 |
-
|
| 164 |
-
Gboard's swipe typing was **not measured** (any version, learning on or off).
|
| 165 |
-
|
| 166 |
-
## Known limitations
|
| 167 |
|
| 168 |
-
|
| 169 |
-
- **French and German are lower than English** on HeliBoard (71.7 and 73.3 first guess vs 82.1). The sources do not isolate the cause. One measured factor is dictionary coverage of the targets (German 91.9 % even with the 260,000-word list; compounds are the likely reason, not verified). The spread between swipers on the same layout is also large (5 to 7 points).
|
| 170 |
-
- **Accent twins** (Spanish que / qué, como / cómo; French ou / où) share one swipe path. The shape cannot decide between them; the sentence prior has to, and without previous words the choice is frequency only.
|
| 171 |
-
- **Short words (3 letters or fewer)** were the other common miss when the older FUTO-engine keyboard was scored on synthetic Spanish, French and German swipes; we have not repeated that breakdown for T1.
|
| 172 |
-
- **Out-of-dictionary words are always misses.** About 2.5 % of English targets are not in the word list (bench-2000: 42 of 2,000).
|
| 173 |
-
- **Spanish, French and German training is partly synthetic** (100,000 each) plus the real `swipe-5` slices. Only English has large real recordings. Real volunteers' swipes per language are still missing, so non-English numbers are less certain than English ones.
|
| 174 |
-
- **Layout handling is approximate where we had no real key rectangles.** The AZERTY/QWERTZ/Spanish layouts used for synthetic training are approximations (`make_layouts.py`); the real keyboard supplies live rectangles at run time. Key pitch, not drawn key width, is the unit, because boards with gaps between keys were 20 % off otherwise.
|
| 175 |
-
- **An earlier validation error was found and corrected** (see the incident below). Every number on this page comes from the corrected, clean sets.
|
| 176 |
-
- No bias or fairness evaluation was done. The training swipers are volunteers on FUTO's collection; their handedness, device and language mix are not documented here.
|
| 177 |
|
| 178 |
-
|
| 179 |
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
```
|
| 187 |
-
# 1. Collect the evaluation keys so training data can be checked against them
|
| 188 |
-
python -I scripts/swipe/holdout_ids.py holdout-final.json bench-2000.jsonl test-extra-10000.jsonl test.jsonl
|
| 189 |
-
python -I scripts/swipe/holdout_ids.py holdout-val.json <validation sessions>.jsonl
|
| 190 |
-
# 2. Filter FUTO's collections (drops two-finger and invalid swipes and anything overlapping the evaluation files)
|
| 191 |
-
python -I scripts/swipe/assemble_futo.py DATA_DIR OUT_DIR holdout-final.json holdout-val.json
|
| 192 |
-
# 3. Synthetic swipes for es / fr / de (own generator, different from the test generator)
|
| 193 |
-
python -I scripts/swipe/synth_swipes.py --words words.tsv --layout scripts/swipe/layouts/azerty.tsv --n 100000 --out synth-fr.jsonl
|
| 194 |
-
# 4. Convert and train (see the script header for all options; recipe R2 flags: --dropout 0.2 --geo-aug ... --time-aug ...)
|
| 195 |
-
python -I scripts/swipe/prep_data.py ...
|
| 196 |
-
python -I scripts/swipe/train.py --train real.npz --train synth-fr.npz:scripts/swipe/layouts/azerty.tsv --val val.npz \
|
| 197 |
-
--out run --epochs 30 --batch 512 --conv 96,96 --hidden 128 --layers 2 --dropout 0.2 --device cuda
|
| 198 |
-
# 5. Score (first guess / top 3, ms per swipe)
|
| 199 |
-
build/tools/swipekb-swipenet-bench --model run/model.bin --words words.tsv --layout scripts/swipe/layouts/qwerty.tsv bench-2000.jsonl
|
| 200 |
-
```
|
| 201 |
-
|
| 202 |
-
The exact flag values of the shipped T1 run were not recorded; the sizes above follow the run table in `docs/active/specs/2026-10-23-own-swipe-engine.md`, and the geometry and speed augmentation strengths should be taken from the script's help text. Expect to need a few attempts to match the published numbers. More commands, and the Gboard and FUTO measurements, are on the [methods page](METHODS.md).
|
| 203 |
-
|
| 204 |
-
## Licence of the model file
|
| 205 |
|
| 206 |
-
|
| 207 |
|
| 208 |
-
|
| 209 |
|
| 210 |
-
|
| 211 |
-
- The MIT licence text of FUTO's swipe data (`FUTO-SWIPE-DATA-LICENSE.txt`, shipped with the model).
|
| 212 |
-
- The release notice `MODELS-NOTICE.md` (release `models-2026-10-23`).
|
| 213 |
|
| 214 |
-
|
| 215 |
|
| 216 |
-
|
| 217 |
|
| 218 |
-
|
|
|
|
| 15 |
- ctc
|
| 16 |
- gru
|
| 17 |
- swipe-keyboard
|
| 18 |
+
datasets:
|
| 19 |
+
- futo-org/swipe.futo.org
|
| 20 |
model-index:
|
| 21 |
+
- name: swipe-typing
|
| 22 |
results:
|
| 23 |
- task:
|
| 24 |
type: other
|
| 25 |
+
name: Swipe decoding, English, first guess right (all swipes, model alone)
|
| 26 |
dataset:
|
| 27 |
name: bench-2000 (2,000 real English swipes, FUTO MIT test split)
|
| 28 |
type: custom
|
|
|
|
| 32 |
name: first guess right, all swipes
|
| 33 |
- task:
|
| 34 |
type: other
|
| 35 |
+
name: Swipe decoding, English, right word in top 3 (all swipes, model alone)
|
| 36 |
dataset:
|
| 37 |
name: bench-2000 (2,000 real English swipes, FUTO MIT test split)
|
| 38 |
type: custom
|
|
|
|
| 42 |
name: right word in top 3, all swipes
|
| 43 |
---
|
| 44 |
|
| 45 |
+
# swipe-typing
|
| 46 |
|
| 47 |
+
A small offline neural network that reads the path of a finger across an on-screen keyboard and says which keys it passed over. With a word list and a search step it turns a swipe into ranked word guesses. One model serves English, Spanish, French and German on any key layout. It is built for the [Swipe Keyboard](https://github.com/itsdestin/swipe-keyboard), an open-source swipe-typing and word-prediction add-on for KDE Plasma's on-screen keyboard, which uses it as its only swipe engine from version 0.5.0 (earlier versions used FUTO's engine).
|
| 48 |
|
| 49 |
+
Models trained and released by Destin Moss via youcoded.ai, 2026
|
| 50 |
|
| 51 |
+
| | |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|---|---|
|
| 53 |
+
| File | `swipe-model.bin`, 2,115,260 bytes (2.1 MB) |
|
| 54 |
+
| Languages | English, Spanish, French, German (any layout) |
|
| 55 |
+
| Shape | 2 convolution layers (96 wide, kernel 5), 2-layer bidirectional GRU (128 hidden units), small shared "pointer" scorer; about 529,000 numbers, stored as float32 |
|
| 56 |
+
| Licence | MIT |
|
| 57 |
+
| SHA-256 | `941272a7017229a73e07d1c0bd766757e739174db3a42fb622c26256b8c46110` |
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
+
It outputs probabilities over keys, not words, so it is not a word decoder on its own and cannot be used for handwriting or other gestures.
|
| 60 |
|
| 61 |
+
## How it was built
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
+
- **Data:** real swipes from FUTO's public [swipe.futo.org](https://huggingface.co/datasets/futo-org/swipe.futo.org) collections (MIT licence, Copyright (c) 2025 FUTO): about 317,000 swipes from 35% of the recording sessions of collections 1 to 4, FUTO's `dev` set (about 47,600 English swipes after holding out validation sessions), and all of collection 5, which includes real French, German and Spanish swipes on their own layouts (repeated three times). Plus 100,000 synthetic swipes each for Spanish, French and German from our own generator (`scripts/swipe/synth_swipes.py`). No synthetic English.
|
| 64 |
+
- **Held out:** FUTO's test split (including the 2,000-swipe `bench-2000` and 10,000-swipe `test-extra-10000` slices) and the validation sessions were never trained on. A check dropped any swipe whose id or session appeared in them, and found no overlap in any collection (56,005 swipe keys and 768 sessions compared). The HeliBoard gesture sets (CC BY-SA) were used for testing only.
|
| 65 |
+
- **No FUTO code or model weights** were used; the network and code are our own, written from published methods (CTC, lexicon-constrained gesture decoding, SHARK2).
|
| 66 |
+
- **Training:** `scripts/swipe/train.py` (PyTorch): CTC loss on the sequence of keys the finger passes, AdamW, one-cycle learning rate, 30 epochs, batch 512, dropout 0.2, and random changes to key geometry, speed and position of the trace during training. About 16 minutes on one NVIDIA RTX 4070 Super. The exact command line and augmentation strengths of the shipped run were not recorded (the script's random seed default is 1).
|
| 67 |
+
- **How it handles any layout:** the network has no fixed output per key. At each of 64 time steps it scores every key from the finger's offset to that key (in key widths) and the key's size, with one small shared scorer, so QWERTY, AZERTY, QWERTZ and Spanish keyboards use the same weights.
|
| 68 |
|
| 69 |
+
## How to use it
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
+
**Who it is for:** developers of an on-screen keyboard who want offline swipe recognition (about 3.5 ms per swipe on one CPU thread, no machine-learning library) in these four languages.
|
| 72 |
|
| 73 |
+
**Pieces you need besides this file:** a word list for the language (every answer is a word from it), and the search step. The project's code does all of this, in plain C++20 (needs only Qt 6 Core 6.8+ for Unicode): the network is `engine/src/swipe_net.cpp`, the word search `engine/src/swipe_beam.cpp`. The search is a beam search over a trie of the word list; its scores add the network's key probabilities, word frequency, a sentence prior when previous words are known, and a classical shape-matching cross-check. With no model file, or a damaged one, the engine falls back to the shape-matching decoder alone.
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
+
**Input:** the path (x, y, time) is resampled by arc length to 64 steps with 8 features each (x, y, dx, dy, log elapsed time, fraction of path done, step length, log path length), plus the live key rectangles. `scripts/swipe/swipelib.py` (`trace_to_input`) shows the same preparation in Python.
|
| 76 |
|
| 77 |
+
**Minimal example** (C++, from the project's `engine/README.md`; word lists are in GitHub release `language-files-2026-10-23-apostrophes`):
|
| 78 |
|
| 79 |
+
```cpp
|
| 80 |
+
#include "swipekb/language_engine.hpp"
|
| 81 |
+
using namespace swipekb;
|
| 82 |
|
| 83 |
+
LanguageEngine engine;
|
| 84 |
+
LanguageEngineSetup setup;
|
| 85 |
+
setup.language = "en"; // "en", "es", "fr" or "de"
|
| 86 |
+
setup.dictionary = "en_swipe.combined"; // required word list
|
| 87 |
+
setup.ngram = "en.ngram"; // optional sentence prior
|
| 88 |
+
setup.swipeModel = "swipe-model.bin"; // this file
|
| 89 |
+
if (!engine.init(setup)) { /* word list unreadable */ }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
|
| 91 |
+
// trace: finger samples {x, y, t_ms}; keys: one {letter, x, y, w, h} per letter key, same pixel space
|
| 92 |
+
auto guesses = engine.recognize(trace, keys, contextWords("I think we"), 3);
|
| 93 |
+
// guesses[0].word is the best guess; engine.hasModel() is false if the model was not loaded
|
| 94 |
+
```
|
| 95 |
|
| 96 |
+
**Limits**
|
| 97 |
|
| 98 |
+
- English trails FUTO's engine by 1 to 2 points on first guess (table below). Spanish and German also trail on the HeliBoard swipes; French is ahead.
|
| 99 |
+
- A word missing from the word list is always a miss (42 of 2,000 English test targets, 2.1%). German is hit hardest: the shipped German list contains only 80% of the HeliBoard targets.
|
| 100 |
+
- Only English has large real recordings. Spanish, French and German training is partly synthetic (plus a small amount of real data), so their results are less certain.
|
| 101 |
+
- Accent twins (Spanish que / qué, French ou / où) have the same swipe path; the sentence prior or word frequency has to choose.
|
| 102 |
+
- The training swipers are FUTO volunteers; their handedness, devices and language mix are not documented. No bias evaluation was done.
|
| 103 |
+
- The file has no internal checksum; the keyboard checks its SHA-256 before loading it.
|
| 104 |
|
| 105 |
+
**File layout** (little-endian, `engine/src/swipe_net.cpp`): the 4 bytes `SKNN`; ten 32-bit integers (version 1, data type 0 = float32 or 1 = float16, time steps 64, features per step 8, key features 4, number of convolution layers 2, kernel size 5, GRU hidden size 128, GRU layers 2, pointer width 32); one integer per convolution layer (its width); then the weights as plain arrays: each convolution layer (weights [out][in][kernel], bias), each GRU layer and direction (input weights, recurrent weights, two biases, PyTorch gate order r, z, n), the pointer scorer and the "no key" (blank) scorer. Layer sizes come from the header, so a different size needs no code change.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
|
| 107 |
+
## How well it does
|
| 108 |
|
| 109 |
+
All figures are percent. **First guess** = the top suggestion is the intended word; **top 3** = it is among the first three. Real swipes are from volunteers, never trained on: `bench-2000` = 2,000 English swipes spread evenly over FUTO's MIT test split. "FUTO's scorer counts" = the 1,946 of them that FUTO's tool can score (target of 2+ letters, more than 2 trace points).
|
| 110 |
|
| 111 |
+
**English, bench-2000**
|
| 112 |
|
| 113 |
+
| What was measured | Swipes counted | First guess | Top 3 |
|
| 114 |
|---|---|---|---|
|
| 115 |
+
| This model alone (frequency prior only) | all 2,000 | 90.40 | 94.95 |
|
| 116 |
+
| Whole keyboard (this model + word list + sentence prior, previous words given), build 0.5.0-0.5 | all 2,000 | 90.10 | 93.10 |
|
| 117 |
+
| Whole keyboard, same build | 1,946 FUTO's scorer counts | 91.47 | 94.55 |
|
| 118 |
+
| FUTO's engine in the previous keyboard (0.3.x), previous words given | 1,946 FUTO's scorer counts | 93.32 | 96.56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
+
So on the same 1,946 swipes the keyboard trails FUTO's engine by 1.9 points on first guess and 2.0 on top 3. FUTO's own command-line tool with its stock dictionary scored 92.50 / 95.43 on these swipes, which makes the gap 1.0 and 0.9; the 93.32 / 96.56 figure comes from running FUTO's models in the project's test harness with the project's word list. (The keyboard also hides stretched and rare spellings from guesses 2 and 3, so they are real words more often; that costs a little top-3 accuracy against the model alone.)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
+
**Other languages, first guess, real swipes from HeliBoard** (test only; no sentence given; lower-case, accents kept, apostrophes ignored; "Swipes" is how many of the 5,000 per language were scored)
|
| 123 |
|
| 124 |
+
| Language | Swipes | Whole keyboard (this model) | FUTO's engine in keyboard 0.3.1 |
|
| 125 |
+
|---|---|---|---|
|
| 126 |
+
| English | 4,576 | 80.5 | 84.5 |
|
| 127 |
+
| Spanish | 4,790 | 75.9 | 77.7 |
|
| 128 |
+
| French | 4,999 | 68.2 | 64.6 |
|
| 129 |
+
| German | 4,980 | 64.7 | 69.7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
|
| 131 |
+
Each engine used the word list it shipped with, and FUTO's was older and smaller, so this is a comparison of shipped keyboards, not of models alone. Two common English boards with nearly the same geometry differ by 5 to 7 points for every decoder tested, so some of the spread between rows is the swipers, not the decoder.
|
| 132 |
|
| 133 |
+
No comparison with Gboard's swipe typing was made. Test-set details and how each number was produced: [METHODS.md](METHODS.md).
|
| 134 |
|
| 135 |
+
## Licence and credit
|
|
|
|
|
|
|
| 136 |
|
| 137 |
+
`swipe-model.bin` is **MIT**. The real training swipes come from FUTO's swipe.futo.org (MIT, Copyright (c) 2025 FUTO); keep the licence text `FUTO-SWIPE-DATA-LICENSE.txt` from this repository when you redistribute. Also keep:
|
| 138 |
|
| 139 |
+
- the credit line above (Models trained and released by Destin Moss via youcoded.ai, 2026) and `NOTICE.md`.
|
| 140 |
|
| 141 |
+
The word lists used with the model carry their own attribution terms (FineWeb / FineWeb-2: ODC-By 1.0; Tatoeba: CC BY 2.0 FR; English also Apache-2.0); they are separate files.
|