11. Datasets¶
Runnable companion:
examples/08_workset_to_dataset.py
Everything so far read through a live workset, which is what exploring wants. It is also what training does not want: a source that can answer differently tomorrow cannot be reproduced.
A dataset is a named, frozen workset. Converting resolves the membership once, at that instant, and freezes the result under a name.
Convert¶
dataset = client.convert_workset_to_dataset(
workset.workset_id,
"voice_train_v1",
shard_plan=ShardPlan(shard_size=1000),
)
ready = client.wait_for_dataset("voice_train_v1", timeout_seconds=1800)
ready.lifecycle_state # "ready"
for sample in SampleStream.from_dataset(
"voice_train_v1", keys=["audio.wav", "transcript.txt"], client=client
):
...
Sources, filters and key groups come from the workset, so the dataset is that
workset frozen — you are keeping it, not redefining it. Override key_groups=
only for a different download grouping, and scalars= to record constants
alongside the data ({"sample_rate": 24000}).
Conversion is worker-deferred: it returns a planning dataset and a worker
materializes membership, order and shard plan. wait_for_dataset polls to
ready. The workset stays live and usable afterwards; nothing consumed it.
While it plans, the dataset's planning field says where it is: the phase
(queued, waiting_for_store_lock, writing_members, …), done/total for the
sample-scale phases, and attempt — above 1 when an earlier attempt's worker was
stopped or killed. The dataset page and datavo dataset get show the same.
What freezing buys¶
| Workset | Dataset | |
|---|---|---|
| Membership | re-resolved per read | fixed at conversion |
| A store that grows | shows up | does not |
| Name | none | required, unique |
| Reproducible | no | yes |
| Splits | not possible | yes (chapter 12) |
| Reading | SampleStream.from_workset |
SampleStream.from_dataset |
Import 1000 more samples into the store, and the workset yields them on the next read while the dataset does not. That is the guarantee, not staleness: a dataset opened a year from now yields the same samples in the same order.
from_dataset also has less to do than from_workset: a dataset's shard plan is
already materialized, so opening one is a couple of indexed reads and the first
network cost lands at the first sample, not at open. keys= is optional here —
omit it to read every key the dataset defines, and
client.get_dataset(name).key_groups lists them.
Which producer each key reads¶
A dataset reads one revision of each key per member, fixed when it froze. For a derived key that a store holds from several producers (chapter 9), that is:
- a key pinned with
producer_pinsreads its producer; - an unpinned key reads, per member, whichever producer attached it most recently before the freeze. A newer producer that covered only some members leaves the dataset reading a mix.
The dataset tells you which:
provenance = client.get_dataset_key_provenance("voice_train_v1")
for group in provenance.key_groups:
for key in group.keys:
for producer in key.producers:
print(key.key, producer.producer_id or "imported", producer.member_count)
print(" pinned:", key.pinned_producer_ids, "unpinned:", key.reads_unpinned)
Producers come most members first, each with the range of times its revisions
were attached. An imported key is one imported entry that counts its source
imports. A producer that attaches after the freeze never appears, because the
dataset does not read it. datavo dataset get prints the same table, and the
dataset page shows it on each key in the Keys panel and in Segmentation.
Naming a workset is converting it¶
There is one freeze verb and one way to keep a workset. There is no "save this workset", no pinning, no TTL to extend: worksets are unnamed and ephemeral by construction, and the moment you want a name, you want a dataset.
The working order is therefore: select freely, read, refine, convert the one you keep.
create_workset ──read──▶ refine ──read──▶ convert_workset_to_dataset ──▶ train
(free) (free) (materialized, named)
Retention¶
A dataset holds a tracked dependency on the stores it reads, which is what keeps its bytes alive. That dictates teardown order: delete the dataset before the store it reads. Deleting the store first returns 409 while a protecting dataset depends on it. Likewise a dataset another dataset is built from — a dataset converted from a union with it as a part — cannot be deleted while that dataset exists: delete the one built from it first.
Downloading it¶
Datasets can be pulled to local tars — for an offline machine, an archive, or a non-Python consumer:
datavo dataset get voice_train_v1
datavo dataset pull voice_train_v1 --output-dir ./downloaded/train
That writes one tar per shard, one directory per key group; add --key-group to
pull only some. For normal training you do not need it: SampleStream.from_dataset streams and caches the
same shards on demand (shard cache).
Pitfalls¶
- Opening a
planningdataset. Conversion is worker-deferred; wait forready. - Reusing a name. Dataset names are unique and datasets are frozen. A changed
workset is a new dataset name — version them (
voice_train_v1,_v2). - Converting to escape a slow read. Both paths stream the same cached shards; convert for reproducibility, not for speed.
- Deleting the store first. Archive and delete the dataset, then the store.
- Deleting a dataset another dataset is built from. It returns 409 naming the dependents; delete those first.
- Assuming an unpinned derived key reads one producer. Check the dataset's key provenance, or pin the producer before converting.
- Converting too early. A dataset is permanent and named. While you are still changing your mind, stay on the workset — it costs nothing.
Next: 12. Split sets — cut a dataset into train/val/test with rules and deterministic assignment.