tensorless 0.1.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.1.0 → tensorless-0.3.0}/docs/architecture.md +2 -1
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/automatic_mode.md +10 -1
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/checkpointing.md +4 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/configuration.md +3 -1
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/installation.md +0 -20
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/limitations.md +4 -6
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/roadmap.md +0 -13
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/training.md +10 -3
- {tensorless-0.1.0 → tensorless-0.3.0}/examples/text_generation_example.py +6 -6
- {tensorless-0.1.0 → tensorless-0.3.0}/pyproject.toml +4 -1
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/__init__.py +2 -1
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/api.py +17 -1
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/auto/config.py +31 -9
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/config.py +4 -0
- tensorless-0.3.0/tensorless/data/english_grammar.txt +262 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/data/tabular.py +48 -14
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/runtime.py +2 -2
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/serialization/tl_format.py +31 -1
- tensorless-0.3.0/tensorless/tokenization/__init__.py +11 -0
- tensorless-0.3.0/tensorless/tokenization/bpe_tokenizer.py +128 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/tokenization/char_tokenizer.py +1 -1
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/training/data_prep.py +55 -32
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/training/early_stopping.py +1 -1
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/training/trainer.py +2 -2
- tensorless-0.3.0/tensorless.egg-info/PKG-INFO +77 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless.egg-info/SOURCES.txt +2 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tests/test_checkpoint_resume.py +18 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tests/test_serialization.py +42 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tests/test_train_tabular.py +39 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tests/test_train_text_generation.py +47 -0
- tensorless-0.1.0/PKG-INFO +0 -111
- tensorless-0.1.0/README.md +0 -97
- tensorless-0.1.0/tensorless/tokenization/__init__.py +0 -3
- tensorless-0.1.0/tensorless.egg-info/PKG-INFO +0 -111
- {tensorless-0.1.0 → tensorless-0.3.0}/LICENSE +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/MANIFEST.in +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/api_reference.md +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/cli.md +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/contributing.md +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/examples.md +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/inference.md +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/quickstart.md +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/tl_format.md +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/troubleshooting.md +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/docs/tutorial.md +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/examples/tabular_classification_example.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/examples/tabular_regression_example.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/examples/text_classification_example.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/setup.cfg +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/_version.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/auto/__init__.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/auto/detector.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/checkpoint/__init__.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/checkpoint/manager.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/cli/__init__.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/cli/main.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/data/__init__.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/data/fingerprint.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/data/inspector.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/data/loader.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/devices/__init__.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/devices/device.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/errors.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/models/__init__.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/models/mlp.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/models/registry.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/models/transformer.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/serialization/__init__.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless/training/__init__.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless.egg-info/dependency_links.txt +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless.egg-info/entry_points.txt +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless.egg-info/requires.txt +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tensorless.egg-info/top_level.txt +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tests/test_auto_detection.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tests/test_cli.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tests/test_data_loading.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tests/test_end_to_end.py +0 -0
- {tensorless-0.1.0 → tensorless-0.3.0}/tests/test_fingerprint.py +0 -0
- {tensorless-0.1.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.
|
|
@@ -22,7 +22,8 @@ tensorless/
|
|
|
22
22
|
│ ├── detector.py Dataset -> task string
|
|
23
23
|
│ └── config.py Dataset + TrainConfig -> ResolvedConfig
|
|
24
24
|
├── tokenization/
|
|
25
|
-
│
|
|
25
|
+
│ ├── char_tokenizer.py CharTokenizer: vocab build/encode/decode/save/load
|
|
26
|
+
│ └── bpe_tokenizer.py BPETokenizer: corpus-trained subword encoding
|
|
26
27
|
├── models/
|
|
27
28
|
│ ├── transformer.py TinyTransformer (GPT-style decoder)
|
|
28
29
|
│ ├── mlp.py TabularMLP
|
|
@@ -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
|
|
@@ -26,6 +26,8 @@ defaults.
|
|
|
26
26
|
| `ff_mult` | auto | Feed-forward expansion multiplier (transformer only) |
|
|
27
27
|
| `dropout` | `0.1` | Dropout probability |
|
|
28
28
|
| `max_seq_len` | `256` (text) / `1` (tabular) | Max sequence length in tokens (text tasks) |
|
|
29
|
+
| `tokenizer` | `"bpe"` | Text tokenizer: `"bpe"` or `"char"` |
|
|
30
|
+
| `bpe_vocab_size` | `1000` | Maximum vocabulary size when `tokenizer="bpe"` |
|
|
29
31
|
|
|
30
32
|
## Optimization
|
|
31
33
|
|
|
@@ -45,7 +47,7 @@ defaults.
|
|
|
45
47
|
| Field | Default | Description |
|
|
46
48
|
|---|---|---|
|
|
47
49
|
| `val_split` | `0.1` (if ≥50 examples, else `0`) | Fraction of data held out for validation |
|
|
48
|
-
| `patience` | `
|
|
50
|
+
| `patience` | `3` | Epochs without improvement before stopping early |
|
|
49
51
|
| `min_delta` | `1e-4` | Minimum improvement to count as "improved" |
|
|
50
52
|
|
|
51
53
|
## Hardware
|
|
@@ -7,26 +7,6 @@
|
|
|
7
7
|
- Optional: a CUDA-capable GPU, or a TPU with `torch_xla` installed, for
|
|
8
8
|
faster training. Tensorless works fine on CPU-only machines too.
|
|
9
9
|
|
|
10
|
-
## Install from source
|
|
11
|
-
|
|
12
|
-
Tensorless isn't published to PyPI yet. Install it directly from a
|
|
13
|
-
checkout of this repository:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
git clone https://github.com/tensorless/tensorless.git
|
|
17
|
-
cd tensorless
|
|
18
|
-
pip install -e .
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
The `-e` (editable) flag means changes to the source are picked up
|
|
22
|
-
immediately without reinstalling — useful if you're also contributing.
|
|
23
|
-
|
|
24
|
-
If you plan to run the test suite, install the dev extras too:
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
pip install -e ".[dev]"
|
|
28
|
-
```
|
|
29
|
-
|
|
30
10
|
## Verify your installation
|
|
31
11
|
|
|
32
12
|
```bash
|
|
@@ -6,12 +6,10 @@ current limits:
|
|
|
6
6
|
|
|
7
7
|
## Models
|
|
8
8
|
|
|
9
|
-
- The default text tokenizer is
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
tokenizers
|
|
13
|
-
more sequence positions to represent, and generation quality per
|
|
14
|
-
parameter is lower than a comparably-sized BPE-tokenized model.
|
|
9
|
+
- The default text tokenizer is the dependency-free **BPE** tokenizer, which
|
|
10
|
+
learns merges from the training corpus. It is more token-efficient than the
|
|
11
|
+
optional `tokenizer="char"` alternative, but still simpler than production
|
|
12
|
+
tokenizers such as GPT-style BPE or SentencePiece.
|
|
15
13
|
- Models are trained **from scratch** every time — there's no
|
|
16
14
|
fine-tuning of pretrained checkpoints. This keeps things dependency-
|
|
17
15
|
free and fast to set up, but means text-generation quality on small
|
|
@@ -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
|
|
|
@@ -32,12 +32,12 @@ def main():
|
|
|
32
32
|
model = tl.train(
|
|
33
33
|
CORPUS_PATH,
|
|
34
34
|
out=os.path.join(DATA_DIR, "text_gen_example.tl"),
|
|
35
|
-
epochs=5,
|
|
36
|
-
d_model=64,
|
|
37
|
-
layers=2,
|
|
38
|
-
heads=4,
|
|
39
|
-
batch_size=16,
|
|
40
|
-
max_seq_len=64,
|
|
35
|
+
# epochs=5,
|
|
36
|
+
# d_model=64,
|
|
37
|
+
# layers=2,
|
|
38
|
+
# heads=4,
|
|
39
|
+
# batch_size=16,
|
|
40
|
+
# max_seq_len=64,
|
|
41
41
|
)
|
|
42
42
|
|
|
43
43
|
print("\nGenerated text:")
|
|
@@ -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,12 +67,29 @@ 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"
|
|
73
89
|
)
|
|
90
|
+
tokenizer = user.tokenizer or "bpe"
|
|
91
|
+
if tokenizer not in ("char", "bpe"):
|
|
92
|
+
raise ValueError("tokenizer must be 'char' or 'bpe'")
|
|
74
93
|
|
|
75
94
|
d_model, layers, heads, ff_mult = _auto_model_size(n, ds.kind)
|
|
76
95
|
|
|
@@ -79,6 +98,7 @@ def resolve_config(ds: Dataset, user: TrainConfig) -> ResolvedConfig:
|
|
|
79
98
|
out = user.out or "model.tl"
|
|
80
99
|
checkpoint_dir = user.checkpoint_dir or (out + ".ckpt")
|
|
81
100
|
|
|
101
|
+
max_seq_len = user.max_seq_len or (256 if ds.kind in ("text", "text_labeled") else 1)
|
|
82
102
|
resolved = ResolvedConfig(
|
|
83
103
|
out=out,
|
|
84
104
|
force=bool(user.force),
|
|
@@ -91,17 +111,19 @@ def resolve_config(ds: Dataset, user: TrainConfig) -> ResolvedConfig:
|
|
|
91
111
|
heads=user.heads or heads,
|
|
92
112
|
ff_mult=user.ff_mult or ff_mult,
|
|
93
113
|
dropout=user.dropout if user.dropout is not None else 0.1,
|
|
94
|
-
max_seq_len=
|
|
114
|
+
max_seq_len=max_seq_len,
|
|
115
|
+
tokenizer=tokenizer,
|
|
116
|
+
bpe_vocab_size=user.bpe_vocab_size if user.bpe_vocab_size is not None else _auto_vocab_size(ds),
|
|
95
117
|
optimizer=user.optimizer or "adamw",
|
|
96
118
|
learning_rate=user.learning_rate or (3e-4 if model_type == "transformer" else 1e-3),
|
|
97
119
|
weight_decay=user.weight_decay if user.weight_decay is not None else 0.01,
|
|
98
|
-
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),
|
|
99
121
|
epochs=user.epochs or _auto_epochs(n),
|
|
100
122
|
max_steps=user.max_steps,
|
|
101
123
|
grad_clip=user.grad_clip if user.grad_clip is not None else 1.0,
|
|
102
124
|
warmup_steps=user.warmup_steps if user.warmup_steps is not None else min(100, max(1, n // 10)),
|
|
103
125
|
val_split=user.val_split if user.val_split is not None else (0.1 if n >= 50 else 0.0),
|
|
104
|
-
patience=user.patience
|
|
126
|
+
patience=user.patience if user.patience is not None else 3,
|
|
105
127
|
min_delta=user.min_delta if user.min_delta is not None else 1e-4,
|
|
106
128
|
device=device,
|
|
107
129
|
precision=precision,
|
|
@@ -33,6 +33,8 @@ class TrainConfig:
|
|
|
33
33
|
ff_mult: Optional[int] = None
|
|
34
34
|
dropout: Optional[float] = None
|
|
35
35
|
max_seq_len: Optional[int] = None
|
|
36
|
+
tokenizer: Optional[str] = None # "char" or "bpe"
|
|
37
|
+
bpe_vocab_size: Optional[int] = None
|
|
36
38
|
|
|
37
39
|
# --- optimization ---
|
|
38
40
|
optimizer: Optional[str] = None # "adamw", "adam", "sgd"
|
|
@@ -89,6 +91,8 @@ class ResolvedConfig:
|
|
|
89
91
|
ff_mult: int
|
|
90
92
|
dropout: float
|
|
91
93
|
max_seq_len: int
|
|
94
|
+
tokenizer: str
|
|
95
|
+
bpe_vocab_size: int
|
|
92
96
|
|
|
93
97
|
optimizer: str
|
|
94
98
|
learning_rate: float
|