Checkpoint API
Item embeddings
See Using item embeddings for named feature spaces and missingness masks, whether the vectors are imported or computed by the user.
- compresso_recsys.embeddings.save_item_embeddings(root, name, *, item_ids, embeddings, available=None, metadata=None)[source]
Save finite float32 features, explicit IDs, a presence mask, and provenance.
Call inside
update_checkpoint. Names such astext/minilmare independent feature spaces. Missing rows are stored as zeros, never inferred from values.
- compresso_recsys.embeddings.load_item_embeddings(root, name, *, item_ids=None)[source]
Load one feature space, optionally reindexing to a requested item catalog.
Unknown IDs become zero rows with
available=False. Old checkpoints have no registered feature spaces; an absent name raisesKeyError.
Checkpoint Contexts
- compresso_recsys.checkpoint.update_checkpoint(path)[source]
Extract a zip checkpoint to a temp dir, let caller edit it, then rewrite it.
- Return type:
Iterator[Path]- Parameters:
path (str | Path)
- compresso_recsys.checkpoint.read_checkpoint(path)[source]
Extract a zip checkpoint to a read-only temp workspace.
- Return type:
Iterator[Path]- Parameters:
path (str | Path)
Manifest and JSON Helpers
- compresso_recsys.checkpoint.load_manifest(root)[source]
- Return type:
dict[str,Any]- Parameters:
root (str | Path)
- compresso_recsys.checkpoint.save_manifest(root, manifest)[source]
- Return type:
None- Parameters:
root (str | Path)
manifest (dict[str, Any])
- compresso_recsys.checkpoint.update_stage_manifest(root, stage, metadata)[source]
- Return type:
None- Parameters:
root (str | Path)
stage (str)
metadata (dict[str, Any])
- compresso_recsys.checkpoint.save_json(root, relpath, data)[source]
- Return type:
Path- Parameters:
root (str | Path)
relpath (str)
data (dict[str, Any])
- compresso_recsys.checkpoint.load_json(root, relpath)[source]
- Return type:
dict[str,Any]- Parameters:
root (str | Path)
relpath (str)
Split and Cluster Stages
Item partitions
warm_item_indices, val_cold_item_indices and test_cold_item_indices are
positions into item_ids naming the items each phase introduces, not the
items it may score:
|
Train partition |
Val / test partitions |
|---|---|---|
|
Full catalog range |
Empty; no items are held out |
|
Warm items |
The disjoint cold items held out of training |
|
Every item; nothing is withheld from the catalog |
Only items whose every occurrence falls in a held-out tail |
|
Items in the first window |
Items first seen in each later window |
An empty partition means this phase introduces no new items, which is not the
same as this phase has no candidates. A user split scores the whole catalog in
every phase; it simply adds nothing new. The candidate space of a phase is
{phase}_item_ids, which equals item_ids unless the split gives each
phase its own item space (only temporal does, flagged by
has_stage_item_spaces in the split metadata).
So to select feature or metadata rows for a phase, index with that phase’s
*_item_ids, not by mirroring warm_item_indices: for splits that hold no
items out, the latter yields an empty selection that fails much later and far
from its cause. has_item_partitions in the split metadata tells you whether
a split partitions items at all.
Chronological split modes additionally store sequence views —
x_train_sequences and {stage}_source_sequences — holding the same events
as the matrices in order, with duplicates preserved. They load as None for
user_split and item_split, and for any checkpoint built before sequences
existed.
- compresso_recsys.checkpoint.save_recsys_split(root, *, item_ids, x_train, train_item_ids=None, val_item_ids=None, test_item_ids=None, val_source_indices, val_target_indices, test_source_indices, test_target_indices, train_source_matrix=None, train_target_matrix=None, val_source_matrix=None, val_target_matrix=None, test_source_matrix=None, test_target_matrix=None, train_user_ids=None, val_user_ids=None, test_user_ids=None, val_eval_user_ids=None, test_eval_user_ids=None, warm_item_indices=None, val_cold_item_indices=None, test_cold_item_indices=None, x_train_sequences=None, train_source_sequences=None, val_source_sequences=None, test_source_sequences=None, entity_tag_matrix=None, tag_names=None, entity_metadata=None, metadata=None)[source]
Write the split stage of a checkpoint.
- Return type:
None- Parameters:
root (str | Path)
item_ids (ndarray)
x_train (csr_matrix)
train_item_ids (ndarray | list[str] | None)
val_item_ids (ndarray | list[str] | None)
test_item_ids (ndarray | list[str] | None)
val_source_indices (list[ndarray])
val_target_indices (list[ndarray])
test_source_indices (list[ndarray])
test_target_indices (list[ndarray])
train_source_matrix (csr_matrix | None)
train_target_matrix (csr_matrix | None)
val_source_matrix (csr_matrix | None)
val_target_matrix (csr_matrix | None)
test_source_matrix (csr_matrix | None)
test_target_matrix (csr_matrix | None)
train_user_ids (ndarray | list[str] | None)
val_user_ids (ndarray | list[str] | None)
test_user_ids (ndarray | list[str] | None)
val_eval_user_ids (ndarray | list[str] | None)
test_eval_user_ids (ndarray | list[str] | None)
warm_item_indices (ndarray | None)
val_cold_item_indices (ndarray | None)
test_cold_item_indices (ndarray | None)
x_train_sequences (ItemSequences | None)
train_source_sequences (ItemSequences | None)
val_source_sequences (ItemSequences | None)
test_source_sequences (ItemSequences | None)
entity_tag_matrix (csr_matrix | None)
tag_names (ndarray | list[str] | None)
entity_metadata (DataFrame | None)
metadata (dict[str, Any] | None)
Training matrices
Three keys describe the same training data, and the relationship between them is fixed:
x_train = train_source_matrix ∪ train_target_matrix
x_trainis what a symmetric model trains on — an autoencoder reconstructs the whole window. The pair is what an asymmetric model trains on, mapping source to target. They must agree, and this function refuses a checkpoint where they do not.How the training data is partitioned follows each split mode’s protocol, and only the chronological modes have one to follow:
temporal: by time. Source is everything before the first target window, target is the events inside it.leave_last_out: by position. Target is the last interaction of the training window, source is everything earlier.user_splitanditem_split: no partition. Both keys equalx_train, and the invariant holds trivially.
The last case is deliberate rather than a gap. A non-chronological split has no boundary to divide on, so any per-user division would be an arbitrary choice invented here rather than a property of the protocol. A model wanting asymmetric training on those modes can partition
x_trainitself, under its own seed, and own that choice. The same absence of an ordering is why sequences exist only for the chronological modes.Sequence views
x_train_sequencesand{stage}_source_sequencescarry the same events as their matrix counterparts, in chronological order and with duplicates kept. A matrix row is a set; a sequence row is a history. Targets have no sequence view because a ranking target is a set — order is irrelevant to every metric — so{stage}_target_matrixserves both model families.They are written only when the split mode produced them, which means the chronological modes.
user_splitanditem_splithave no ordering to preserve, and the same absence that makes their training partition arbitrary (above) makes a sequence meaningless.Loading a checkpoint without them yields
Nonerather than an error. A checkpoint that predates sequences, or comes from a non-chronological mode, is still complete for every matrix model, so refusing it would break working setups over a field they never touch. A sequential model fails later, where the message can name the split mode that would have produced them.A sequence whose
n_itemsdisagrees with its own stage’s item IDs is refused: the two views must share a column space or a model scores one item and is credited for another. Per stage rather than globally, because temporal windows each have their own catalog — it grows window by window — which is the same allowance the matrix check above makes.Item partitions
warm_item_indices,val_cold_item_indicesandtest_cold_item_indicesare positions intoitem_idsnaming the items that phase introduces, not the items it may score. Together they partition the catalog by first appearance: the warm partition is exactly the columns present inx_train, and each cold partition holds the items that become observable only at that stage.They are named for what they hold rather than for their phase because the older
{phase}_item_indicesspelling promised a relationship to{phase}_item_idsthat does not exist. The two answer different questions:*_item_idsis the column space a phase lives in, while these are a partition by first appearance. The two agree only by coincidence, and only undertemporalanduser_split, where the catalogs already encode the partition; underleave_last_outanditem_splitall three phases share one catalog and the partition is observed, so it cannot be recovered from the catalogs at all.user_split: training spans every item and the later phases introduce none, so the train partition is the full range and val/test are empty.item_split: three disjoint partitions, the val/test ones being the cold items held out of training.leave_last_out: nothing is held out of the catalog. An item lands in the val or test partition only when every one of its occurrences falls in a held-out tail, so on dense data both partitions are empty and on sparse data they hold the genuinely new items.temporal: each phase introduces the items first seen in its window, so the partitions are consecutive ranges of the growing catalog.
An empty partition therefore means “this phase introduces no new items”, which is not the same as “this phase has no candidates”. The candidate space of a phase is
{phase}_item_ids, which defaults toitem_idswhen not given. Callers that select feature or metadata rows for a phase should index with that phase’s*_item_ids(or the union of partitions up to it), because mirroringwarm_item_indicesinto a later phase silently yields an empty selection for splits that hold no items out.Passing
Nonefor a partition omits its file, andload_recsys_split()then falls back to the whole catalog for the warm partition and to an empty array for the cold ones. Prefer writing all three explicitly, since those defaults turn an omission into a confident wrong answer rather than an error.
- compresso_recsys.checkpoint.load_recsys_split(root)[source]
Read the split stage of a checkpoint.
See
save_recsys_split()for what*_item_indicesmean: they are the items each phase introduces, so they are empty for phases that hold no items out, while*_item_idsgive the candidate space and default toitem_ids.For checkpoints written before every partition was stored explicitly, a missing
warm_item_indices.npyloads as the full catalog range and missing cold files load as empty arrays. Checkpoints written before the rename are read under their old names first, because those defaults would otherwise turn a missing file into a confident wrong answer.- Return type:
dict[str,Any]- Parameters:
root (str | Path)
- compresso_recsys.checkpoint.save_cluster_graph_stage(root, graph, *, stage_dir='clustering', metadata=None)[source]
- Return type:
Path- Parameters:
root (str | Path)
graph (SparseClusterSet)
stage_dir (str)
metadata (dict[str, Any] | None)
- compresso_recsys.checkpoint.load_cluster_graph_stage(root, *, stage_dir='clustering')[source]
- Return type:
SparseClusterSet- Parameters:
root (str | Path)
stage_dir (str)