tensorless 0.2.0__tar.gz → 0.3.0__tar.gz
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.
- tensorless-0.3.0/PKG-INFO +77 -0
- tensorless-0.3.0/README.md +63 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/automatic_mode.md +10 -1
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/checkpointing.md +4 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/configuration.md +1 -1
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/roadmap.md +0 -13
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/training.md +10 -3
- {tensorless-0.2.0 → tensorless-0.3.0}/pyproject.toml +4 -1
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/__init__.py +2 -1
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/api.py +17 -1
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/auto/config.py +27 -10
- tensorless-0.3.0/tensorless/data/english_grammar.txt +262 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/data/tabular.py +48 -14
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/serialization/tl_format.py +31 -1
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/tokenization/bpe_tokenizer.py +11 -6
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/training/data_prep.py +44 -28
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/training/early_stopping.py +1 -1
- tensorless-0.3.0/tensorless.egg-info/PKG-INFO +77 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless.egg-info/SOURCES.txt +2 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tests/test_checkpoint_resume.py +18 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tests/test_serialization.py +42 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tests/test_train_tabular.py +39 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tests/test_train_text_generation.py +29 -0
- tensorless-0.2.0/PKG-INFO +0 -13
- tensorless-0.2.0/tensorless.egg-info/PKG-INFO +0 -13
- {tensorless-0.2.0 → tensorless-0.3.0}/LICENSE +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/MANIFEST.in +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/api_reference.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/architecture.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/cli.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/contributing.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/examples.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/inference.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/installation.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/limitations.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/quickstart.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/tl_format.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/troubleshooting.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/docs/tutorial.md +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/examples/tabular_classification_example.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/examples/tabular_regression_example.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/examples/text_classification_example.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/examples/text_generation_example.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/setup.cfg +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/_version.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/auto/__init__.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/auto/detector.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/checkpoint/__init__.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/checkpoint/manager.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/cli/__init__.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/cli/main.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/config.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/data/__init__.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/data/fingerprint.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/data/inspector.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/data/loader.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/devices/__init__.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/devices/device.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/errors.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/models/__init__.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/models/mlp.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/models/registry.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/models/transformer.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/runtime.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/serialization/__init__.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/tokenization/__init__.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/tokenization/char_tokenizer.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/training/__init__.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless/training/trainer.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless.egg-info/dependency_links.txt +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless.egg-info/entry_points.txt +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless.egg-info/requires.txt +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tensorless.egg-info/top_level.txt +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tests/test_auto_detection.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tests/test_cli.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tests/test_data_loading.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tests/test_end_to_end.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tests/test_fingerprint.py +0 -0
- {tensorless-0.2.0 → tensorless-0.3.0}/tests/test_train_text_classification.py +0 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tensorless
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: ML with maximum automation and minimum setup.
|
|
5
|
+
Author: Tensorless Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: torch>=2.0
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# Tensorless
|
|
16
|
+
|
|
17
|
+
Tensorless trains small PyTorch models with sensible defaults. It supports
|
|
18
|
+
text generation, text classification, tabular classification, and regression.
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install -e .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Train on your data
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import tensorless as tl
|
|
30
|
+
|
|
31
|
+
model = tl.train("./corpus.txt", task="text-generation")
|
|
32
|
+
print(model.generate("The", max_new_tokens=40))
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Text files are trained as next-token language models. BPE is the default
|
|
36
|
+
tokenizer; use `tokenizer="char"` for a character-level model. Tensorless
|
|
37
|
+
derives model size, batch size, epochs, validation, device, and BPE vocabulary
|
|
38
|
+
size from the data, while every setting can be overridden.
|
|
39
|
+
|
|
40
|
+
Long text is tokenized lazily and fed through PyTorch in fixed-size batches.
|
|
41
|
+
The automatic batch size uses a token budget; reduce `batch_size` if your
|
|
42
|
+
available memory is limited.
|
|
43
|
+
|
|
44
|
+
## English starter pretraining
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import tensorless as tl
|
|
48
|
+
|
|
49
|
+
model = tl.pretrain(out="english.tl", epochs=20, max_seq_len=128)
|
|
50
|
+
print(model.generate("A complete sentence", max_new_tokens=30))
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This offline starter corpus contains English prose and grammar examples. It is
|
|
54
|
+
for demos and smoke tests, not a replacement for a large language dataset. For
|
|
55
|
+
real pretraining, pass your own `.txt` corpus to `tl.train()` and increase the
|
|
56
|
+
training settings as your hardware allows.
|
|
57
|
+
|
|
58
|
+
## Other tasks
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
tl.train("reviews/", task="text-classification")
|
|
62
|
+
tl.train("housing.csv", task="regression")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Tabular preprocessing automatically handles numeric values, ISO dates, and
|
|
66
|
+
high-cardinality categories. Missing and rare values are handled using the
|
|
67
|
+
fitted training data, and the same preprocessing is stored in the `.tl` file.
|
|
68
|
+
|
|
69
|
+
Models are saved as `.tl` files and can be loaded later:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
model = tl.load("model.tl")
|
|
73
|
+
print(model.info())
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
See the [documentation](docs/quickstart.md) for data formats, configuration,
|
|
77
|
+
checkpointing, and the command-line interface.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Tensorless
|
|
2
|
+
|
|
3
|
+
Tensorless trains small PyTorch models with sensible defaults. It supports
|
|
4
|
+
text generation, text classification, tabular classification, and regression.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install -e .
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Train on your data
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
import tensorless as tl
|
|
16
|
+
|
|
17
|
+
model = tl.train("./corpus.txt", task="text-generation")
|
|
18
|
+
print(model.generate("The", max_new_tokens=40))
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Text files are trained as next-token language models. BPE is the default
|
|
22
|
+
tokenizer; use `tokenizer="char"` for a character-level model. Tensorless
|
|
23
|
+
derives model size, batch size, epochs, validation, device, and BPE vocabulary
|
|
24
|
+
size from the data, while every setting can be overridden.
|
|
25
|
+
|
|
26
|
+
Long text is tokenized lazily and fed through PyTorch in fixed-size batches.
|
|
27
|
+
The automatic batch size uses a token budget; reduce `batch_size` if your
|
|
28
|
+
available memory is limited.
|
|
29
|
+
|
|
30
|
+
## English starter pretraining
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import tensorless as tl
|
|
34
|
+
|
|
35
|
+
model = tl.pretrain(out="english.tl", epochs=20, max_seq_len=128)
|
|
36
|
+
print(model.generate("A complete sentence", max_new_tokens=30))
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This offline starter corpus contains English prose and grammar examples. It is
|
|
40
|
+
for demos and smoke tests, not a replacement for a large language dataset. For
|
|
41
|
+
real pretraining, pass your own `.txt` corpus to `tl.train()` and increase the
|
|
42
|
+
training settings as your hardware allows.
|
|
43
|
+
|
|
44
|
+
## Other tasks
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
tl.train("reviews/", task="text-classification")
|
|
48
|
+
tl.train("housing.csv", task="regression")
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Tabular preprocessing automatically handles numeric values, ISO dates, and
|
|
52
|
+
high-cardinality categories. Missing and rare values are handled using the
|
|
53
|
+
fitted training data, and the same preprocessing is stored in the `.tl` file.
|
|
54
|
+
|
|
55
|
+
Models are saved as `.tl` files and can be loaded later:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
model = tl.load("model.tl")
|
|
59
|
+
print(model.info())
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
See the [documentation](docs/quickstart.md) for data formats, configuration,
|
|
63
|
+
checkpointing, and the command-line interface.
|
|
@@ -31,14 +31,23 @@ Once the task is known, `tensorless/auto/config.py` picks:
|
|
|
31
31
|
|
|
32
32
|
- **model type**: `transformer` for text tasks, `mlp` for tabular tasks
|
|
33
33
|
- **size** (`d_model`, `layers`, `heads`): scaled to dataset size, from
|
|
34
|
-
a tiny 2-layer/64-dim model for a few hundred examples up to an
|
|
34
|
+
a tiny 2-layer/64-dim model for a few hundred effective text examples up to an
|
|
35
35
|
8-layer/384-dim model for 50,000+ examples
|
|
36
36
|
|
|
37
|
+
For text corpora, effective examples include corpus character count, so a
|
|
38
|
+
single large `.txt` file is not treated like one training example. BPE
|
|
39
|
+
vocabulary size is also bounded from corpus character diversity rather than
|
|
40
|
+
always using a fixed oversized vocabulary.
|
|
41
|
+
|
|
37
42
|
This is a heuristic, not a search — the goal is "a model that trains
|
|
38
43
|
quickly and doesn't wildly overfit or underfit for typical dataset
|
|
39
44
|
sizes," not the best possible architecture. Override any of it:
|
|
40
45
|
`tl.train("./data", d_model=512, layers=6)`.
|
|
41
46
|
|
|
47
|
+
For a packaged English grammar starter corpus, use
|
|
48
|
+
`tl.pretrain(out="english.tl")`. It is intended for demos and smoke tests;
|
|
49
|
+
larger local corpora should be passed to `tl.train()`.
|
|
50
|
+
|
|
42
51
|
## 3. Hyperparameter selection
|
|
43
52
|
|
|
44
53
|
Batch size, epoch count, learning rate, warmup steps, and the
|
|
@@ -40,6 +40,10 @@ Writes are atomic: Tensorless writes to a temporary file in the same
|
|
|
40
40
|
directory and renames it into place, so a crash mid-write never leaves a
|
|
41
41
|
corrupt checkpoint that would block resumption.
|
|
42
42
|
|
|
43
|
+
When loading `.tl` files, Tensorless fills compatible fields introduced by
|
|
44
|
+
older versions with safe defaults. Files created by a newer unsupported format
|
|
45
|
+
version are rejected with an upgrade message instead of being partially read.
|
|
46
|
+
|
|
43
47
|
## How resumption works
|
|
44
48
|
|
|
45
49
|
When `tl.train()` finds a checkpoint whose `dataset_fingerprint` matches
|
|
@@ -47,7 +47,7 @@ defaults.
|
|
|
47
47
|
| Field | Default | Description |
|
|
48
48
|
|---|---|---|
|
|
49
49
|
| `val_split` | `0.1` (if ≥50 examples, else `0`) | Fraction of data held out for validation |
|
|
50
|
-
| `patience` | `
|
|
50
|
+
| `patience` | `3` | Epochs without improvement before stopping early |
|
|
51
51
|
| `min_delta` | `1e-4` | Minimum improvement to count as "improved" |
|
|
52
52
|
|
|
53
53
|
## Hardware
|
|
@@ -5,18 +5,7 @@ a commitment or timeline.
|
|
|
5
5
|
|
|
6
6
|
## Near-term
|
|
7
7
|
|
|
8
|
-
- **Subword/BPE tokenization** as an alternative to the default
|
|
9
|
-
character-level tokenizer, for better efficiency on larger text
|
|
10
|
-
corpora
|
|
11
|
-
- **`.tl` format migration** — forward-compatible loading of files
|
|
12
|
-
written by older Tensorless versions
|
|
13
|
-
- **Better tabular preprocessing** — handling of date/datetime columns,
|
|
14
|
-
high-cardinality categorical columns, and more robust outlier handling
|
|
15
|
-
for regression targets
|
|
16
8
|
- **Progress bars** for training (currently plain print-based logging)
|
|
17
|
-
- **`tl.train(..., resume=False)`** enforcement — currently `resume` is
|
|
18
|
-
accepted in `TrainConfig` but the automatic resume decision doesn't
|
|
19
|
-
yet fully respect an explicit `False` override in every code path
|
|
20
9
|
- **Multi-GPU / distributed training** for larger datasets
|
|
21
10
|
|
|
22
11
|
## Medium-term
|
|
@@ -38,8 +27,6 @@ a commitment or timeline.
|
|
|
38
27
|
|
|
39
28
|
- **Alternate backends** (JAX, a lightweight NumPy-only backend) behind
|
|
40
29
|
the same `tl.train()`/`tl.load()` API
|
|
41
|
-
- **Streaming/out-of-core training** for datasets too large to fit in
|
|
42
|
-
memory
|
|
43
30
|
- **Export to other formats** (ONNX, TorchScript) from a `.tl` file for
|
|
44
31
|
deployment outside Python
|
|
45
32
|
|
|
@@ -38,6 +38,11 @@ A directory can mix multiple files of the *same* format (e.g. several
|
|
|
38
38
|
plain text files with structured (JSON/CSV) files in the same directory
|
|
39
39
|
raises a `DataError` asking you to separate them.
|
|
40
40
|
|
|
41
|
+
For tabular data, numeric columns are robustly scaled and missing values use
|
|
42
|
+
the training median. ISO-8601 date and datetime columns are converted to
|
|
43
|
+
numeric timestamps. Categorical columns are frequency-ranked and capped at
|
|
44
|
+
1,000 learned values; rare or unseen values use the `<unk>` category.
|
|
45
|
+
|
|
41
46
|
Tensorless never modifies, moves, or deletes files in your dataset
|
|
42
47
|
directory. It only ever reads from `path`; all output goes to the `out`
|
|
43
48
|
file and `checkpoint_dir`.
|
|
@@ -68,7 +73,9 @@ By default, Tensorless holds out `val_split` of the data (10% for
|
|
|
68
73
|
datasets with 50+ examples, 0% for smaller ones where a held-out split
|
|
69
74
|
wouldn't be meaningful) and tracks validation loss after each epoch. If
|
|
70
75
|
validation loss doesn't improve by at least `min_delta` for `patience`
|
|
71
|
-
consecutive epochs, training stops early.
|
|
76
|
+
consecutive epochs, training stops early. The automatic default is 3
|
|
77
|
+
consecutive epochs, and the completed model is still written to the `.tl`
|
|
78
|
+
output file.
|
|
72
79
|
|
|
73
80
|
## Checkpointing during training
|
|
74
81
|
|
|
@@ -81,8 +88,8 @@ for resumption.
|
|
|
81
88
|
|
|
82
89
|
| Task | Model | What's learned |
|
|
83
90
|
|---|---|---|
|
|
84
|
-
| `text-generation` | Small GPT-style decoder transformer,
|
|
85
|
-
| `text-classification` | Same transformer backbone, classification head on the final token | Text → one of your labeled classes |
|
|
91
|
+
| `text-generation` | Small GPT-style decoder transformer, BPE tokenizer by default | Next-token prediction over your text |
|
|
92
|
+
| `text-classification` | Same transformer backbone, BPE tokenizer by default, classification head on the final token | Text → one of your labeled classes |
|
|
86
93
|
| `classification` | MLP with per-column categorical embeddings | Row of features → one of your labeled classes |
|
|
87
94
|
| `regression` | Same MLP, single continuous output | Row of features → a number |
|
|
88
95
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tensorless"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "ML with maximum automation and minimum setup."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -22,3 +22,6 @@ tensorless = "tensorless.cli.main:main"
|
|
|
22
22
|
|
|
23
23
|
[tool.setuptools.packages.find]
|
|
24
24
|
include = ["tensorless*"]
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.package-data]
|
|
27
|
+
tensorless = ["data/*.txt"]
|
|
@@ -12,7 +12,7 @@ ML with maximum automation and minimum setup.
|
|
|
12
12
|
See https://github.com/tensorless/tensorless for full documentation.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
from .api import train, run, load, inspect
|
|
15
|
+
from .api import train, pretrain, run, load, inspect
|
|
16
16
|
from .config import TrainConfig
|
|
17
17
|
from .errors import (
|
|
18
18
|
TensorlessError,
|
|
@@ -26,6 +26,7 @@ from ._version import __version__
|
|
|
26
26
|
|
|
27
27
|
__all__ = [
|
|
28
28
|
"train",
|
|
29
|
+
"pretrain",
|
|
29
30
|
"run",
|
|
30
31
|
"load",
|
|
31
32
|
"inspect",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
14
|
import dataclasses
|
|
15
|
+
import importlib.resources
|
|
15
16
|
import os
|
|
16
17
|
from typing import Any, Optional
|
|
17
18
|
|
|
@@ -92,7 +93,7 @@ def train(path: str, **kwargs: Any) -> LoadedModel:
|
|
|
92
93
|
return LoadedModel(existing)
|
|
93
94
|
|
|
94
95
|
# 2. Is there an interrupted / matching checkpoint to resume from?
|
|
95
|
-
if checkpoint_mgr.exists():
|
|
96
|
+
if checkpoint_mgr.exists() and user_cfg.resume is not False:
|
|
96
97
|
ckpt = checkpoint_mgr.load()
|
|
97
98
|
if ckpt.get("dataset_fingerprint") == fingerprint:
|
|
98
99
|
if ckpt.get("training_complete"):
|
|
@@ -119,6 +120,10 @@ def train(path: str, **kwargs: Any) -> LoadedModel:
|
|
|
119
120
|
"checkpoint -- retraining from scratch."
|
|
120
121
|
)
|
|
121
122
|
checkpoint_mgr.clear()
|
|
123
|
+
elif checkpoint_mgr.exists():
|
|
124
|
+
if user_cfg.verbose:
|
|
125
|
+
print("[tensorless] resume=False -- ignoring the existing checkpoint.")
|
|
126
|
+
checkpoint_mgr.clear()
|
|
122
127
|
else:
|
|
123
128
|
checkpoint_mgr.clear()
|
|
124
129
|
|
|
@@ -162,6 +167,17 @@ def train(path: str, **kwargs: Any) -> LoadedModel:
|
|
|
162
167
|
return LoadedModel(payload)
|
|
163
168
|
|
|
164
169
|
|
|
170
|
+
def pretrain(
|
|
171
|
+
out: str = "english_pretrained.tl", language: str = "english", **kwargs: Any
|
|
172
|
+
) -> LoadedModel:
|
|
173
|
+
"""Pretrain a small language model on the built-in starter corpus."""
|
|
174
|
+
if language.lower() != "english":
|
|
175
|
+
raise ValueError("The built-in pretraining corpus currently supports only 'english'.")
|
|
176
|
+
corpus = importlib.resources.files("tensorless.data").joinpath("english_grammar.txt")
|
|
177
|
+
options = {"task": "text-generation", "out": out, **kwargs}
|
|
178
|
+
return train(str(corpus), **options)
|
|
179
|
+
|
|
180
|
+
|
|
165
181
|
def _finalize_from_checkpoint(ckpt: dict, out: str, verbose: bool) -> LoadedModel:
|
|
166
182
|
payload = {
|
|
167
183
|
"tl_format_version": _tl_format_version,
|
|
@@ -43,15 +43,17 @@ def _auto_model_size(n_examples: int, kind: str):
|
|
|
43
43
|
return 128, 4, 1, 2
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
def _auto_batch_size(n_examples: int) -> int:
|
|
46
|
+
def _auto_batch_size(n_examples: int, max_seq_len: int) -> int:
|
|
47
|
+
token_budget = 8192
|
|
48
|
+
sequence_batch = max(1, token_budget // max_seq_len)
|
|
47
49
|
if n_examples < 200:
|
|
48
|
-
return 8
|
|
50
|
+
return min(8, sequence_batch)
|
|
49
51
|
elif n_examples < 2000:
|
|
50
|
-
return 16
|
|
52
|
+
return min(16, sequence_batch)
|
|
51
53
|
elif n_examples < 20000:
|
|
52
|
-
return 32
|
|
54
|
+
return min(32, sequence_batch)
|
|
53
55
|
else:
|
|
54
|
-
return 64
|
|
56
|
+
return min(64, sequence_batch)
|
|
55
57
|
|
|
56
58
|
|
|
57
59
|
def _auto_epochs(n_examples: int) -> int:
|
|
@@ -65,8 +67,22 @@ def _auto_epochs(n_examples: int) -> int:
|
|
|
65
67
|
return 5
|
|
66
68
|
|
|
67
69
|
|
|
70
|
+
def _effective_text_size(ds: Dataset) -> int:
|
|
71
|
+
"""Estimate useful training examples for raw corpora."""
|
|
72
|
+
if ds.kind in ("text", "text_labeled"):
|
|
73
|
+
return max(len(ds), sum(len(text) for text in ds.texts) // 200)
|
|
74
|
+
return len(ds)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _auto_vocab_size(ds: Dataset) -> int:
|
|
78
|
+
if ds.kind not in ("text", "text_labeled"):
|
|
79
|
+
return 1000
|
|
80
|
+
unique_chars = len(set("".join(ds.texts)))
|
|
81
|
+
return min(4096, max(64, unique_chars * 8))
|
|
82
|
+
|
|
83
|
+
|
|
68
84
|
def resolve_config(ds: Dataset, user: TrainConfig) -> ResolvedConfig:
|
|
69
|
-
n =
|
|
85
|
+
n = _effective_text_size(ds)
|
|
70
86
|
task = user.task or detect_task(ds)
|
|
71
87
|
model_type = user.model_type or (
|
|
72
88
|
"transformer" if task in ("text-generation", "text-classification") else "mlp"
|
|
@@ -82,6 +98,7 @@ def resolve_config(ds: Dataset, user: TrainConfig) -> ResolvedConfig:
|
|
|
82
98
|
out = user.out or "model.tl"
|
|
83
99
|
checkpoint_dir = user.checkpoint_dir or (out + ".ckpt")
|
|
84
100
|
|
|
101
|
+
max_seq_len = user.max_seq_len or (256 if ds.kind in ("text", "text_labeled") else 1)
|
|
85
102
|
resolved = ResolvedConfig(
|
|
86
103
|
out=out,
|
|
87
104
|
force=bool(user.force),
|
|
@@ -94,19 +111,19 @@ def resolve_config(ds: Dataset, user: TrainConfig) -> ResolvedConfig:
|
|
|
94
111
|
heads=user.heads or heads,
|
|
95
112
|
ff_mult=user.ff_mult or ff_mult,
|
|
96
113
|
dropout=user.dropout if user.dropout is not None else 0.1,
|
|
97
|
-
max_seq_len=
|
|
114
|
+
max_seq_len=max_seq_len,
|
|
98
115
|
tokenizer=tokenizer,
|
|
99
|
-
bpe_vocab_size=user.bpe_vocab_size
|
|
116
|
+
bpe_vocab_size=user.bpe_vocab_size if user.bpe_vocab_size is not None else _auto_vocab_size(ds),
|
|
100
117
|
optimizer=user.optimizer or "adamw",
|
|
101
118
|
learning_rate=user.learning_rate or (3e-4 if model_type == "transformer" else 1e-3),
|
|
102
119
|
weight_decay=user.weight_decay if user.weight_decay is not None else 0.01,
|
|
103
|
-
batch_size=user.batch_size
|
|
120
|
+
batch_size=user.batch_size if user.batch_size is not None else _auto_batch_size(n, max_seq_len),
|
|
104
121
|
epochs=user.epochs or _auto_epochs(n),
|
|
105
122
|
max_steps=user.max_steps,
|
|
106
123
|
grad_clip=user.grad_clip if user.grad_clip is not None else 1.0,
|
|
107
124
|
warmup_steps=user.warmup_steps if user.warmup_steps is not None else min(100, max(1, n // 10)),
|
|
108
125
|
val_split=user.val_split if user.val_split is not None else (0.1 if n >= 50 else 0.0),
|
|
109
|
-
patience=user.patience
|
|
126
|
+
patience=user.patience if user.patience is not None else 3,
|
|
110
127
|
min_delta=user.min_delta if user.min_delta is not None else 1e-4,
|
|
111
128
|
device=device,
|
|
112
129
|
precision=precision,
|