secryst 0.1.0 → 1.0.0
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.
- checksums.yaml +4 -4
- data/README.adoc +407 -15
- data/bin/secryst +42 -0
- data/lib/secryst/byt5_onnx.rb +103 -0
- data/lib/secryst/imf.rb +155 -0
- data/lib/secryst/model.rb +52 -0
- data/lib/secryst/multi_head_attention_forward.rb +2 -2
- data/lib/secryst/provisioning.rb +189 -0
- data/lib/secryst/translator.rb +21 -35
- data/lib/secryst/version.rb +2 -2
- data/lib/secryst/vocab.rb +14 -54
- data/lib/secryst.rb +20 -9
- data/spec/fixtures/tiny-imf.zip +0 -0
- data/spec/parity_spec.rb +33 -0
- data/spec/secryst/byt5_onnx_spec.rb +17 -0
- data/spec/secryst/imf_spec.rb +180 -0
- data/spec/spec_helper.rb +3 -0
- metadata +72 -17
- data/lib/secryst/clip_grad_norm.rb +0 -25
- data/lib/secryst/multihead_attention.rb +0 -156
- data/lib/secryst/trainer.rb +0 -235
- data/lib/secryst/transformer.rb +0 -382
- data/lib/secryst-trainer.rb +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 185de54f6ce981b7739770b422262452752460c5559e029e4b5039f67f6483ef
|
|
4
|
+
data.tar.gz: df7df11ff4fc5a476074e8155448a3b1ab0577ab693c37e796a98c0cf7934af8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c51c600385d5a05e9f9298925f2ba7fb1c32a49b26f0345d877c8a11da04459a50d4adb5a23aedac480212e5b83af621a917db472382194ddeb716bd2ae5c4f
|
|
7
|
+
data.tar.gz: 558249e6dcf7ec5cb927512f6487ee8edd911cb06f9f1685bd4ab1b00ec651e230940880cc2c0f3cd61a095a37c3dc0cad046ea4299b25acdd5ee2669ba35df4
|
data/README.adoc
CHANGED
|
@@ -1,3 +1,53 @@
|
|
|
1
|
+
== secryst 1.0 — modernized
|
|
2
|
+
|
|
3
|
+
*Secryst* is coined from *scrying* + *crystal*: gazing into an opaque
|
|
4
|
+
script to reveal its hidden reading — the vowels, phonemes, and word
|
|
5
|
+
boundaries that live in the reader's mind but not on the page. This gem
|
|
6
|
+
is the Ruby crystal of the family (Python: `pip install secryst` at
|
|
7
|
+
https://github.com/secryst/secryst-py[secryst/secryst-py]; TypeScript:
|
|
8
|
+
`npm i secryst`). All crystals implement the *interscript-ml* contract —
|
|
9
|
+
the `models.yaml` index, IMF v1 model zips, and shared golden sets
|
|
10
|
+
(https://github.com/interscript/interscript-ml[interscript/interscript-ml]) — and
|
|
11
|
+
are diffed against each other in CI. Overrides: `SECRYST_INDEX`,
|
|
12
|
+
`SECRYST_CACHE` (default `~/.cache/secryst`).
|
|
13
|
+
|
|
14
|
+
Secryst transliterates text using neural sequence-to-sequence models,
|
|
15
|
+
with training in Python (PyTorch + HuggingFace) and inference in pure
|
|
16
|
+
Ruby via https://github.com/secryst/onnxruntime[secryst/onnxruntime] —
|
|
17
|
+
no libtorch, no native compilation.
|
|
18
|
+
|
|
19
|
+
=== New in 1.0
|
|
20
|
+
|
|
21
|
+
- *Byte-level models (ByT5 family)*: model zips now contain
|
|
22
|
+
`encoder.onnx` + `decoder.onnx` + `metadata.yaml` (`name: byt5`).
|
|
23
|
+
The tokenizer is UTF-8 bytes themselves (pad=0, EOS=1) — no vocab
|
|
24
|
+
files.
|
|
25
|
+
- *Modern training stack*: ByT5/umt5 fine-tuning, Muon optimizer
|
|
26
|
+
variants, CTC/curriculum ablations, Modal pipelines (see `src/`,
|
|
27
|
+
`train_*.py`, `docs/RESULTS.md`).
|
|
28
|
+
- *Legacy single-file ONNX zips* (vocabs.yaml based) still load.
|
|
29
|
+
- The old Ruby trainer and libtorch/TorchScript path are removed —
|
|
30
|
+
training lives in Python now.
|
|
31
|
+
|
|
32
|
+
=== Export a trained checkpoint
|
|
33
|
+
|
|
34
|
+
....
|
|
35
|
+
python scripts/export_onnx_byt5.py <hf_checkpoint_dir> khm-latn-byt5.zip
|
|
36
|
+
....
|
|
37
|
+
|
|
38
|
+
=== Use from Ruby
|
|
39
|
+
|
|
40
|
+
....
|
|
41
|
+
translator = Secryst::Translator.new(model_file: 'khm-latn-byt5.zip')
|
|
42
|
+
translator.translate('ភាសាខ្មែរ')
|
|
43
|
+
....
|
|
44
|
+
|
|
45
|
+
=== CLI
|
|
46
|
+
|
|
47
|
+
....
|
|
48
|
+
secryst translate -f khm-latn-byt5.zip -i 'ភាសាខ្មែរ'
|
|
49
|
+
secryst install <model-name> # via Secrystfile remotes
|
|
50
|
+
....
|
|
1
51
|
= Secryst
|
|
2
52
|
|
|
3
53
|
image:https://github.com/secryst/secryst/workflows/test/badge.svg["Build status", link="https://github.com/secryst/secryst/actions?workflow=test"]
|
|
@@ -17,22 +67,143 @@ The goal is to allow:
|
|
|
17
67
|
* Users of the library in Ruby who only want to "use" the trained models should not require special bindings to run.
|
|
18
68
|
|
|
19
69
|
|
|
20
|
-
==
|
|
70
|
+
== Introduction
|
|
21
71
|
|
|
22
|
-
|
|
72
|
+
Secryst works with a number of Romanization and transliteration systems.
|
|
73
|
+
|
|
74
|
+
It is composed of two separate pieces of software (Ruby gems):
|
|
75
|
+
|
|
76
|
+
* Secryst Translator: `secryst`, for users of trained models
|
|
77
|
+
* Secryst Trainer: `secryst-trainer` for users who wish to train models
|
|
78
|
+
|
|
79
|
+
Secryst models are platform-independent, interoperable
|
|
80
|
+
and transferrable across installations.
|
|
81
|
+
|
|
82
|
+
There are two types of Secryst models:
|
|
83
|
+
|
|
84
|
+
Secryst ONNX model (recommended)::
|
|
85
|
+
These can be run on any platform without `libtorch`
|
|
86
|
+
(which requires installation of development tools).
|
|
87
|
+
|
|
88
|
+
Secryst Torch model::
|
|
89
|
+
These require `libtorch` to run. Secryst Torch models are trained in Ruby.
|
|
90
|
+
|
|
91
|
+
Secryst Torch models can be converted to Secryst ONNX models
|
|
92
|
+
via Python using instructions below.
|
|
93
|
+
|
|
94
|
+
Secryst Trainer also supports checkpoint resumption.
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
== Examples
|
|
98
|
+
|
|
99
|
+
Under the `examples/` directory the following systems are provided
|
|
100
|
+
|
|
101
|
+
* `examples/khm-latn`: Khmer Romanization
|
|
102
|
+
* `examples/arm-latn`: Armenian Romanization based on Wikipedia data
|
|
23
103
|
|
|
24
104
|
|
|
25
105
|
== Prerequisites
|
|
26
106
|
|
|
27
|
-
* Ruby 2.7 (*MUST* - 2.6 does not work with the latest torch-rb)
|
|
28
107
|
|
|
29
|
-
|
|
108
|
+
=== Secryst Translator
|
|
109
|
+
|
|
110
|
+
==== Basic (usage of Secryst ONNX models only)
|
|
111
|
+
|
|
112
|
+
If you only need to use Secryst ONNX models, there is no need
|
|
113
|
+
to install `libtorch` and development tools.
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
macOS and Ubuntu:
|
|
117
|
+
|
|
118
|
+
[source,sh]
|
|
119
|
+
----
|
|
120
|
+
$ gem install bundler
|
|
121
|
+
$ bundle install
|
|
122
|
+
----
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
On macOS:
|
|
126
|
+
|
|
127
|
+
[source,sh]
|
|
128
|
+
----
|
|
129
|
+
$ gem install bundler
|
|
130
|
+
$ bundle install
|
|
131
|
+
----
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
==== To use Secryst Torch models
|
|
135
|
+
|
|
136
|
+
Usage of Secryst Torch models require `libtorch`, which
|
|
137
|
+
requires installation of the following packages:
|
|
138
|
+
|
|
139
|
+
* `libtorch` (1.8.1)
|
|
30
140
|
* `fftw`
|
|
31
141
|
* `gsl`
|
|
142
|
+
|
|
143
|
+
And also you must explicitly add `torch` gem to your Gemfile:
|
|
144
|
+
|
|
145
|
+
[source,ruby]
|
|
146
|
+
----
|
|
147
|
+
gem 'torch-rb', '~> 0.6'
|
|
148
|
+
----
|
|
149
|
+
|
|
150
|
+
On Ubuntu:
|
|
151
|
+
|
|
152
|
+
[source,sh]
|
|
153
|
+
----
|
|
154
|
+
$ sudo apt-get -y install libfftw3-dev libgsl-dev unzip automake \
|
|
155
|
+
make gcc g++ libtorch libtorch-dev
|
|
156
|
+
$ wget https://download.pytorch.org/libtorch/cpu/libtorch-cxx11-abi-shared-with-deps-1.9.0%2Bcpu.zip
|
|
157
|
+
$ unzip libtorch-cxx11-abi-shared-with-deps-1.8.1%2Bcpu.zip
|
|
158
|
+
|
|
159
|
+
$ gem install bundler
|
|
160
|
+
$ bundle config build.torch-rb \
|
|
161
|
+
--with-torch-dir=$(pwd)/libtorch
|
|
162
|
+
|
|
163
|
+
$ bundle install
|
|
164
|
+
----
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
On Fedora:
|
|
168
|
+
|
|
169
|
+
[source,sh]
|
|
170
|
+
----
|
|
171
|
+
$ sudo dnf -y install ruby-devel gsl-devel fftw-devel rubygem-bundler unzip \
|
|
172
|
+
automake make gcc gcc-c++
|
|
173
|
+
$ git clone --recurse-submodules --depth=5 -b release/1.7 https://github.com/pytorch/pytorch.git
|
|
174
|
+
$ pushd pytorch
|
|
175
|
+
$ python setup.py install
|
|
176
|
+
$ popd
|
|
177
|
+
$ TORCH_DIR=$HOME/.local/lib/python3.9/site-packages/torch # Ensure this dir exists
|
|
178
|
+
$ ln -s lib $TORCH_DIR/lib64
|
|
179
|
+
|
|
180
|
+
$ bundle config build.torch-rb \
|
|
181
|
+
--with-torch-dir=$TORCH_DIR
|
|
182
|
+
|
|
183
|
+
$ bundle install
|
|
184
|
+
----
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
On macOS:
|
|
188
|
+
|
|
189
|
+
[source,sh]
|
|
190
|
+
----
|
|
191
|
+
$ brew install libtorch gsl fftw automake gcc
|
|
192
|
+
$ gem install bundler
|
|
193
|
+
$ bundle config build.torch-rb \
|
|
194
|
+
--with-torch-dir=$(brew --prefix libtorch)
|
|
195
|
+
$ bundle install
|
|
196
|
+
----
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
=== Secryst Trainer
|
|
201
|
+
|
|
202
|
+
In order to use Secryst Trainer two additional components are necessary:
|
|
203
|
+
|
|
32
204
|
* `lapack`
|
|
33
205
|
* `openblas`
|
|
34
206
|
|
|
35
|
-
|
|
36
207
|
On Ubuntu:
|
|
37
208
|
|
|
38
209
|
[source,sh]
|
|
@@ -40,14 +211,17 @@ On Ubuntu:
|
|
|
40
211
|
$ sudo apt-get -y install libfftw3-dev libgsl-dev libopenblas-dev \
|
|
41
212
|
liblapack-dev liblapacke-dev unzip automake make gcc g++ \
|
|
42
213
|
libtorch libtorch-dev
|
|
43
|
-
$ wget https://download.pytorch.org/libtorch/
|
|
44
|
-
$ unzip libtorch-cxx11-abi-shared-with-deps-1.
|
|
214
|
+
$ wget https://download.pytorch.org/libtorch/cu111/libtorch-cxx11-abi-shared-with-deps-1.8.1%2Bcu111.zip
|
|
215
|
+
$ unzip libtorch-cxx11-abi-shared-with-deps-1.8.1%2Bcu111.zip
|
|
45
216
|
|
|
46
|
-
$ gem install bundler
|
|
217
|
+
$ gem install bundler
|
|
47
218
|
$ bundle config build.torch-rb \
|
|
48
219
|
--with-torch-dir=$(pwd)/libtorch
|
|
49
220
|
|
|
50
221
|
$ bundle install
|
|
222
|
+
|
|
223
|
+
# To enable ONNX training, you must also install the Python portions
|
|
224
|
+
$ pip3 install -r requirements.txt
|
|
51
225
|
----
|
|
52
226
|
|
|
53
227
|
|
|
@@ -57,12 +231,17 @@ On macOS:
|
|
|
57
231
|
----
|
|
58
232
|
$ brew install libtorch gsl lapack openblas fftw automake gcc
|
|
59
233
|
|
|
60
|
-
$ gem install bundler
|
|
234
|
+
$ gem install bundler
|
|
61
235
|
$ bundle config build.numo-linalg \
|
|
62
|
-
--with-openblas-dir
|
|
63
|
-
--with-lapack-lib
|
|
236
|
+
--with-openblas-dir=$(brew --prefix openblas) \
|
|
237
|
+
--with-lapack-lib=$(brew --prefix lapack)
|
|
238
|
+
$ bundle config build.torch-rb \
|
|
239
|
+
--with-torch-dir=$(brew --prefix libtorch)
|
|
64
240
|
|
|
65
241
|
$ bundle install
|
|
242
|
+
|
|
243
|
+
# To enable ONNX training, you must also install the Python portions
|
|
244
|
+
$ pip3 install -r requirements.txt
|
|
66
245
|
----
|
|
67
246
|
|
|
68
247
|
|
|
@@ -77,6 +256,223 @@ $ bundle exec gem uninstall numo-linalg
|
|
|
77
256
|
----
|
|
78
257
|
|
|
79
258
|
|
|
259
|
+
== Usage
|
|
260
|
+
|
|
261
|
+
Secryst provides a CLI for training models and re-using trained models.
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
=== Using trained models
|
|
265
|
+
|
|
266
|
+
You will need to install the `secryst` gem (prerequisites must be fulfilled):
|
|
267
|
+
|
|
268
|
+
[source,sh]
|
|
269
|
+
----
|
|
270
|
+
$ gem install secryst
|
|
271
|
+
----
|
|
272
|
+
|
|
273
|
+
To utilize a trained model:
|
|
274
|
+
|
|
275
|
+
[source,sh]
|
|
276
|
+
----
|
|
277
|
+
# Transform all individual lines of `--input_text_file`.
|
|
278
|
+
# Specifying:
|
|
279
|
+
# - trained model zip archive at `--model-file`.
|
|
280
|
+
# Must include `metadata.yaml`, `vocabs.yaml` and
|
|
281
|
+
# an `.pth` or `.onnx` model file.
|
|
282
|
+
|
|
283
|
+
secryst translate \
|
|
284
|
+
--input_text_file=examples/to-translate.txt \
|
|
285
|
+
--model-file=examples/checkpoints/checkpoint-500.zip
|
|
286
|
+
----
|
|
287
|
+
|
|
288
|
+
Both Secryst ONNX models and Secryst Torch models can
|
|
289
|
+
be used with this command.
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
=== Training models in Ruby (output: Secryst Torch model)
|
|
293
|
+
|
|
294
|
+
Secryst supports training models in Ruby into the Secryst Torch model format.
|
|
295
|
+
These created models can then be used by other users through the `secryst` gem.
|
|
296
|
+
|
|
297
|
+
NOTE: To make a trained Secryst model available for all platforms,
|
|
298
|
+
you should convert the Secryst Torch model into a Secryst ONNX model.
|
|
299
|
+
|
|
300
|
+
You will need to install the `secryst-trainer` gem (prerequisites must be fulfilled):
|
|
301
|
+
|
|
302
|
+
[source,sh]
|
|
303
|
+
----
|
|
304
|
+
$ gem install secryst-trainer
|
|
305
|
+
----
|
|
306
|
+
|
|
307
|
+
NOTE: The `secryst` gem will be automatically installed alongside `secryst-trainer`.
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
Training a typical model:
|
|
311
|
+
|
|
312
|
+
[source,sh]
|
|
313
|
+
----
|
|
314
|
+
# Train all individual lines of the file specified in `-i` to the
|
|
315
|
+
# corresponding line in target `-t`.
|
|
316
|
+
#
|
|
317
|
+
# Specifying:
|
|
318
|
+
# - `max-epochs` specifies how many epochs training will be run
|
|
319
|
+
# - `log-interval` specifies how often should Secryst report on
|
|
320
|
+
# learning parameters.
|
|
321
|
+
# - `checkpoint-every` indicates how often Secryst saves a checkpoint
|
|
322
|
+
# file to `checkpoint_dir`, in the format `checkpoint-{epoch}.zip`.
|
|
323
|
+
# - `checkpoint_dir` specifies the directory to store checkpoint files. If some checkpoints are already in the directory, the training will continue from the latest
|
|
324
|
+
|
|
325
|
+
secryst-trainer train \
|
|
326
|
+
-i 'data/khm-latn-small/input.csv' \
|
|
327
|
+
-t 'data/khm-latn-small/target.csv' \
|
|
328
|
+
--max-epochs=500 \
|
|
329
|
+
--log-interval=1 \
|
|
330
|
+
--checkpoint-every=50 \
|
|
331
|
+
--checkpoint_dir=examples/checkpoints
|
|
332
|
+
----
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
Training with all options:
|
|
336
|
+
|
|
337
|
+
[source,sh]
|
|
338
|
+
----
|
|
339
|
+
# Train all individual lines of the file specified in `-i` to the
|
|
340
|
+
# corresponding line in target `-t`.
|
|
341
|
+
#
|
|
342
|
+
# Specifying:
|
|
343
|
+
# - `batch-size` specifies the batch size for training
|
|
344
|
+
# - `max-epochs` specifies how many epochs training will be run
|
|
345
|
+
# - `log-interval` specifies how often should Secryst report on
|
|
346
|
+
# learning parameters.
|
|
347
|
+
# - `checkpoint-every` indicates how often Secryst saves a checkpoint
|
|
348
|
+
# file to `checkpoint_dir`, in the format `checkpoint-{epoch}.zip`.
|
|
349
|
+
# - `checkpoint_dir` specifies the directory to store checkpoint. If some checkpoints are already in the directory, the training will continue from the latest
|
|
350
|
+
# - `gamma` specifies the gamma value used
|
|
351
|
+
# - hyperparameters in a key-value pair format
|
|
352
|
+
|
|
353
|
+
secryst-trainer train --model=transformer \
|
|
354
|
+
-i 'data/khm-latn-small/input.csv' \
|
|
355
|
+
-t 'data/khm-latn-small/target.csv' \
|
|
356
|
+
--batch-size=32 \
|
|
357
|
+
--max-epochs=500 \
|
|
358
|
+
--log-interval=1 \
|
|
359
|
+
--checkpoint-every=50 \
|
|
360
|
+
--checkpoint_dir=checkpoints \
|
|
361
|
+
--gamma=0.2 \
|
|
362
|
+
-h d_model:64 nhead:8 num_encoder_layers:4 num_decoder_layers:4 \
|
|
363
|
+
dim_feedforward:256 dropout:0.05 activation:relu
|
|
364
|
+
----
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
=== Convert Secryst Torch models to Secryst ONNX models
|
|
368
|
+
|
|
369
|
+
Due to a limitation of https://pytorch.org/cppdocs/[libtorch's C++ interface]
|
|
370
|
+
not being able to encode trained models in ONNX, we have to use PyTorch to
|
|
371
|
+
convert Secryst Torch models into Secryst ONNX models.
|
|
372
|
+
|
|
373
|
+
Secryst supports generation of Secryst ONNX models using PyTorch.
|
|
374
|
+
|
|
375
|
+
First, clone this current repository.
|
|
376
|
+
|
|
377
|
+
To convert a Secryst Torch model to a Secryst ONNX model, run:
|
|
378
|
+
|
|
379
|
+
[source,sh]
|
|
380
|
+
----
|
|
381
|
+
python3 python/pth_to_onnx.py checkpoint.zip output.zip
|
|
382
|
+
----
|
|
383
|
+
|
|
384
|
+
The trained Secryst ONNX model can be used as usual:
|
|
385
|
+
|
|
386
|
+
[source,sh]
|
|
387
|
+
----
|
|
388
|
+
bundle exec secryst translate --model-file output.zip -t texts.txt
|
|
389
|
+
----
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
=== Resuming training
|
|
394
|
+
|
|
395
|
+
Secryst Trainer supports checkpoint resumption.
|
|
396
|
+
|
|
397
|
+
It will detect whether you already have checkpoint model files in the model output
|
|
398
|
+
directory (in the filename of `checkpoint-nnn.zip`), and attempt to
|
|
399
|
+
resume training from there. Notice that in resuming training, you
|
|
400
|
+
must use identical parameters and the identical training dataset,
|
|
401
|
+
otherwise the process will throw out an error.
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
=== Importing non-Secryst ONNX models
|
|
405
|
+
|
|
406
|
+
You can easily utilize non-Secryst trained ONNX models in Secryst as well.
|
|
407
|
+
|
|
408
|
+
You need to prepare a zip file with:
|
|
409
|
+
|
|
410
|
+
* An `.onnx` model file
|
|
411
|
+
* The `vocabs.yaml` file
|
|
412
|
+
|
|
413
|
+
The `vocabs.yaml` file has to contain two keys - `input` and `target` (it's okay if they are the same), which contain all tokens from vocabulary placed in original order.
|
|
414
|
+
|
|
415
|
+
Like this:
|
|
416
|
+
|
|
417
|
+
[source,yaml]
|
|
418
|
+
----
|
|
419
|
+
input:
|
|
420
|
+
- [UNK]
|
|
421
|
+
- ...
|
|
422
|
+
target:
|
|
423
|
+
- [UNK]
|
|
424
|
+
- ...
|
|
425
|
+
----
|
|
426
|
+
|
|
427
|
+
Then just utilize model usually as described above. You can find an example of this in the examples folder (`onnx_import.rb`).
|
|
428
|
+
|
|
429
|
+
=== Training on GPU
|
|
430
|
+
|
|
431
|
+
To allow training on CUDA drivers Secryst ships Python trainer.
|
|
432
|
+
|
|
433
|
+
Install the python version 3.8, and required packages:
|
|
434
|
+
|
|
435
|
+
[source,sh]
|
|
436
|
+
----
|
|
437
|
+
pip3 install -r requirements.txt
|
|
438
|
+
----
|
|
439
|
+
|
|
440
|
+
And start the training (all the options are the same as Ruby trainer):
|
|
441
|
+
|
|
442
|
+
[source,sh]
|
|
443
|
+
----
|
|
444
|
+
python3 python/train.py -i 'data/khm-latn-small/input.csv' \
|
|
445
|
+
-t 'data/khm-latn-small/target.csv' \
|
|
446
|
+
--max-epochs=500 \
|
|
447
|
+
--log-interval=1 \
|
|
448
|
+
--checkpoint-every=50 \
|
|
449
|
+
--checkpoint-dir=examples/checkpoints
|
|
450
|
+
----
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
== Examples
|
|
454
|
+
|
|
455
|
+
The Khmer transliteration system is implemented as an example.
|
|
456
|
+
|
|
457
|
+
To run the training:
|
|
458
|
+
|
|
459
|
+
[source,sh]
|
|
460
|
+
----
|
|
461
|
+
$ bundle exec examples/training.rb
|
|
462
|
+
----
|
|
463
|
+
|
|
464
|
+
To run translations through the transformer:
|
|
465
|
+
|
|
466
|
+
[source,sh]
|
|
467
|
+
----
|
|
468
|
+
$ bundle exec examples/translating.rb
|
|
469
|
+
----
|
|
470
|
+
|
|
471
|
+
* Checkpoint files are generated as `examples/checkpoints/*.zip`
|
|
472
|
+
* It includes `metadata.yaml`, `model.pth` and `vocabs.yaml` files
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
|
|
80
476
|
|
|
81
477
|
== References
|
|
82
478
|
|
|
@@ -88,10 +484,6 @@ based on:
|
|
|
88
484
|
Attention is all you need. 2017. In:
|
|
89
485
|
_Advances in Neural Information Processing Systems_, pages 6000-6010.
|
|
90
486
|
|
|
91
|
-
The sample transliteration system implemented is the Khmer system:
|
|
92
|
-
|
|
93
|
-
* https://viblo.asia/p/nlp-khmer-word-segmentation-YWOZrgNNlQ0
|
|
94
|
-
* https://viblo.asia/p/nlp-khmer-romanization-using-seq2seq-m68Z07OQKkG
|
|
95
487
|
|
|
96
488
|
|
|
97
489
|
== Origin of name
|
data/bin/secryst
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
require "secryst"
|
|
5
|
+
|
|
6
|
+
class SecrystCli < Thor
|
|
7
|
+
def self.exit_on_failure?
|
|
8
|
+
true
|
|
9
|
+
end
|
|
10
|
+
desc "translate", "Translate input sequence using specified model"
|
|
11
|
+
method_option :source, :aliases => "-s", :desc => "Add a model index to provision the model"
|
|
12
|
+
method_option :input_text, :aliases => "-i", :desc => "String of text to translate"
|
|
13
|
+
method_option :input_text_file, :aliases => "-t", :desc => "Path to file with text to translate"
|
|
14
|
+
method_option :model_file, :aliases => "-f", :desc => "Specify model name or a checkpoint .zip file", :required => true
|
|
15
|
+
|
|
16
|
+
def translate
|
|
17
|
+
Secryst::Provisioning.add_remote(options[:source]) if options[:source]
|
|
18
|
+
|
|
19
|
+
translator = Secryst::Translator.new(
|
|
20
|
+
model_file: options[:model_file],
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if options[:input_text]
|
|
24
|
+
puts translator.translate(options[:input_text])
|
|
25
|
+
elsif options[:input_text_file]
|
|
26
|
+
File.readlines(options[:input_text_file]).each do |line|
|
|
27
|
+
puts translator.translate(line)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
desc "install <name>", "Installs a model locally"
|
|
33
|
+
method_option :source, :aliases => "-s", :desc => "Add a model index to provision the model"
|
|
34
|
+
|
|
35
|
+
def install(name)
|
|
36
|
+
Secryst::Provisioning.add_remote(options[:source]) if options[:source]
|
|
37
|
+
|
|
38
|
+
Secryst::Provisioning.locate(name)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
SecrystCli.start
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require 'zip'
|
|
2
|
+
require 'onnxruntime'
|
|
3
|
+
require 'tmpdir'
|
|
4
|
+
|
|
5
|
+
module Secryst
|
|
6
|
+
# Byte-level seq2seq (ByT5 family) inference over ONNX sessions from an
|
|
7
|
+
# IMF v1 model zip: sha256-verified graphs, greedy KV-cache decode when
|
|
8
|
+
# the zip ships decoder-kv.onnx (default), plain full-recompute
|
|
9
|
+
# fallback otherwise. Tokenization is the canonical ByT5 table
|
|
10
|
+
# (byte b -> id b+3, trailing EOS) — see Secryst::IMF.
|
|
11
|
+
class Byt5Onnx
|
|
12
|
+
def initialize(zip_path)
|
|
13
|
+
@manifest = IMF.manifest(zip_path)
|
|
14
|
+
graphs = IMF.verify_and_read(zip_path)
|
|
15
|
+
@tmpdir = Dir.mktmpdir('secryst-imf')
|
|
16
|
+
@encoder = build_session(graphs, 'encoder.onnx')
|
|
17
|
+
@kv = @manifest['decoder'] == 'kv' && graphs.key?('decoder-kv.onnx')
|
|
18
|
+
@decoder = build_session(graphs, @kv ? 'decoder-kv.onnx' : 'decoder.onnx')
|
|
19
|
+
@pasts = @kv ? zero_pasts : {}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def translate(text, max_seq_length: 256)
|
|
23
|
+
ids = IMF.encode(text)
|
|
24
|
+
return '' if ids.length == 1 # only the trailing EOS: empty input
|
|
25
|
+
|
|
26
|
+
hidden = @encoder.predict({ input_ids: [ids] })['last_hidden_state']
|
|
27
|
+
tokens = @kv ? greedy_kv(hidden, max_seq_length) : greedy_plain(hidden, max_seq_length)
|
|
28
|
+
IMF.decode(tokens)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def id
|
|
32
|
+
@manifest['id']
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# The onnxruntime gem loads from paths, not bytes: verified bytes go
|
|
38
|
+
# to a private tmpdir.
|
|
39
|
+
def build_session(graphs, name)
|
|
40
|
+
path = File.join(@tmpdir, name)
|
|
41
|
+
File.binwrite(path, graphs.fetch(name))
|
|
42
|
+
OnnxRuntime::Model.new(path)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def zero_pasts
|
|
46
|
+
@decoder.inputs.each_with_object({}) do |meta, pasts|
|
|
47
|
+
next unless meta[:name].start_with?('past_')
|
|
48
|
+
shape = meta[:shape] # [batch, heads, past_seq, d_kv]; dynamic dims are Strings
|
|
49
|
+
heads = shape[1].is_a?(Integer) ? shape[1] : 4
|
|
50
|
+
d_kv = shape[3].is_a?(Integer) ? shape[3] : 8
|
|
51
|
+
# Numo carries the explicit [1, heads, 0, d_kv] shape through;
|
|
52
|
+
# nested Ruby arrays cannot express a zero-length dim.
|
|
53
|
+
pasts[meta[:name]] = Numo::DFloat.zeros(1, heads, 0, d_kv)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def argmax(row)
|
|
58
|
+
best = 0
|
|
59
|
+
best_val = -Float::INFINITY
|
|
60
|
+
row.each_with_index do |value, index|
|
|
61
|
+
if value > best_val
|
|
62
|
+
best_val = value
|
|
63
|
+
best = index
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
best
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def greedy_kv(hidden, max_seq_length)
|
|
70
|
+
pasts = @pasts.dup
|
|
71
|
+
current = [IMF::PAD_ID]
|
|
72
|
+
generated = []
|
|
73
|
+
max_seq_length.times do
|
|
74
|
+
results = @decoder.predict(
|
|
75
|
+
{ input_ids: [current], encoder_hidden_states: hidden }.merge(pasts)
|
|
76
|
+
)
|
|
77
|
+
token = argmax(results['logits'].first.last)
|
|
78
|
+
break if token == IMF::EOS_ID
|
|
79
|
+
|
|
80
|
+
generated << token
|
|
81
|
+
pasts = pasts.keys.to_h { |name| [name, results[name.sub('past_', 'present_')]] }
|
|
82
|
+
current = [token]
|
|
83
|
+
end
|
|
84
|
+
generated
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def greedy_plain(hidden, max_seq_length)
|
|
88
|
+
decoder_ids = [IMF::PAD_ID]
|
|
89
|
+
generated = []
|
|
90
|
+
max_seq_length.times do
|
|
91
|
+
logits = @decoder.predict(
|
|
92
|
+
{ input_ids: [decoder_ids], encoder_hidden_states: hidden }
|
|
93
|
+
)['logits']
|
|
94
|
+
token = argmax(logits.first.last)
|
|
95
|
+
break if token == IMF::EOS_ID
|
|
96
|
+
|
|
97
|
+
generated << token
|
|
98
|
+
decoder_ids = decoder_ids + [token]
|
|
99
|
+
end
|
|
100
|
+
generated
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|