Core: Models¶
The live model surface consumes PyG TemporalData event batches.
Current temporal model families:
TemporalEventClassifier: stateless MLP baseline over event messages plus ID embeddings.TemporalRNNClassifier: supervised GRU classifier that carries hidden state across adjacentTemporalDataLoaderbatches and resets atreset_afterboundaries.TemporalGAT: causal event-attention classifier.TemporalVGAE: recurrent variational event autoencoder for event-level surprise scoring.TemporalHybridModel: modular temporal hybrid for supervised, anomaly, or joint learning. It composes an event encoder, optional TGN-style arbitration ID memory, a stream backbone (none,gru,ssm_lite, or optionalmamba), and independently enabled classifier / self-supervised anomaly heads. Its memory can include per-ID elapsed-time encodings, and optional rhythm and motif contexts add causal CAN schedule features before the stream backbone.id_encoding/: categorical-ID encoders with reservedUNKat index0.
TemporalHybridModel is configured through the temporal_hybrid primitive.
Use objective: supervised for classifier-only training, objective: anomaly
for self-supervised attack-free anomaly scoring, and objective: joint when a
classifier and anomaly heads should train together. Runtime dimensions
(num_ids, in_channels, and num_classes) are still injected from the
TemporalDataModule after setup(), so YAML configs do not hard-code dataset
vocabulary sizes.
Anomaly heads default to the original regression/error scoring mode. Set
anomaly.mode: nll to score IAT and payload-delta heads with Gaussian negative
log-likelihood while keeping next-ID categorical NLL.
graphids.core.models¶
models ¶
Core model families.
base ¶
Shared model infrastructure for temporal modules.
safe_load_checkpoint ¶
Load a checkpoint using the class path stored in the checkpoint.
Source code in graphids/core/models/base.py
strip_orig_mod_prefix ¶
Drop _orig_mod. prefixes injected by torch.compile.
id_encoding ¶
Pluggable identity-encoding strategies for event/node IDs.
An IdEncoder maps a node_id LongTensor to per-node embedding
vectors. Subclasses implement different strategies (lookup table,
k-probe hash, ...) behind a uniform interface so temporal models do not
know which strategy is in use.
Research basis: ~/plans/oov-embedding-handling.md.
HashIdEncoder ¶
Bases: IdEncoder
Source code in graphids/core/models/id_encoding/hash_embedding.py
from_vocab_size
classmethod
¶
from_vocab_size(num_ids: int, *, embedding_dim: int, k: int = 2, seed: int = 42, num_buckets_factor: int = 4, num_buckets: int | None = None) -> HashIdEncoder
Build from a datamodule-injected num_ids.
Default bucket count: next_pow2(num_buckets_factor · num_ids),
minimum 8. Per plan: Yan 2021 / Coleman 2023 use 2–4× vocab size
as a sweet spot between collision rate and parameter count.
num_buckets can be passed explicitly to override.
Source code in graphids/core/models/id_encoding/hash_embedding.py
IdEncoder ¶
Bases: Module
Maps per-node identities to per-node embedding vectors.
Planned subclasses:
- LookupIdEncoder — dense nn.Embedding over a shared vocab,
with optional stochastic UNK-drop (Stage 3 ablation).
- HashIdEncoder (Stage 2 primary, not yet implemented) — k-probe
hash embedding per Yan et al. 2021 (CIKM).
build_encoder ¶
Resolve a dotted class_path and call from_vocab_size.
num_ids is data-dependent (populated by datamodule.setup), so
encoder construction stays at model-build time.
Source code in graphids/core/models/id_encoding/base.py
base ¶
Base class for pluggable identity encoders.
Contract (duck-typed, matching the rest of the codebase):
forward(node_id: LongTensor) -> Tensorof shape(N, out_dim).out_dim: intattribute set in__init__.- All stateful policy (vocab size, hash seeds, UNK-drop rate) lives on
the encoder instance —
InputEncoderholds one and does not branch on its type.
IdEncoder ¶
Bases: Module
Maps per-node identities to per-node embedding vectors.
Planned subclasses:
- LookupIdEncoder — dense nn.Embedding over a shared vocab,
with optional stochastic UNK-drop (Stage 3 ablation).
- HashIdEncoder (Stage 2 primary, not yet implemented) — k-probe
hash embedding per Yan et al. 2021 (CIKM).
build_encoder ¶
Resolve a dotted class_path and call from_vocab_size.
num_ids is data-dependent (populated by datamodule.setup), so
encoder construction stays at model-build time.
Source code in graphids/core/models/id_encoding/base.py
config ¶
Explicit ID-encoding configs and factories.
hash_embedding ¶
k-probe hash embedding — primary Stage-2 treatment.
Every id (seen or unseen) deterministically maps to k rows of a
bucketed embedding table by k decorrelated hash functions; the
per-probe vectors are summed. Because any id hits trained buckets by
construction, no special OOV slot is needed.
Shape follows Coleman et al. 2023 Unified Embedding (NeurIPS
Spotlight): one shared table, k probes, sum combiner — minimum
parameters, clean theoretical analysis. Yan et al. 2021 Binary Code
Hash Embedding (CIKM) uses the same k-probe idea with separate tables
per hash; at CAN scale (~100 ids, B=512) the shared table has the
same expressive power at half the parameters.
Hash: bucket_i(id) = (id * KNUTH + offset_i) mod num_buckets, where
KNUTH = 2654435761 (golden-ratio-derived Knuth multiplier) and the
k offsets are deterministic functions of the seed constructor
arg. The multiplier is coprime to any num_buckets >= 2 that isn't
a specific pathological case, and Knuth's value is well-studied for
integer-id hashing at tiny scale.
Research basis: ~/plans/oov-embedding-handling.md (Stage 2).
HashIdEncoder ¶
Bases: IdEncoder
Source code in graphids/core/models/id_encoding/hash_embedding.py
from_vocab_size
classmethod
¶
from_vocab_size(num_ids: int, *, embedding_dim: int, k: int = 2, seed: int = 42, num_buckets_factor: int = 4, num_buckets: int | None = None) -> HashIdEncoder
Build from a datamodule-injected num_ids.
Default bucket count: next_pow2(num_buckets_factor · num_ids),
minimum 8. Per plan: Yan 2021 / Coleman 2023 use 2–4× vocab size
as a sweet spot between collision rate and parameter count.
num_buckets can be passed explicitly to override.
Source code in graphids/core/models/id_encoding/hash_embedding.py
lookup ¶
Dense lookup embedding with optional stochastic UNK-drop.
Default (p_unk_drop=0.0) reproduces the pre-refactor nn.Embedding
behavior byte-for-byte so existing single-vocab runs are a no-op change.
p_unk_drop > 0.0 implements the Stage 3 ablation arm from
~/plans/oov-embedding-handling.md: during training, each node_id is
remapped to UNK_INDEX with probability p, so the OOV row
receives gradient and attack-introduced IDs at inference land in a
trained slot instead of init noise.
temporal ¶
Temporal event model family exports.
TemporalEventClassifier ¶
TemporalEventClassifier(*, loss_fn: Module | None = None, hidden: int | None = None, layers: int | None = None, embedding_dim: int | None = None, dropout: float = 0.2, lr: float = 0.001, weight_decay: float = 0.0001, scale: str = 'small', model_type: str = 'temporal_event_classifier', dataset: str = '', seed: int = 42, num_ids: int = 0, in_channels: int = 0, num_classes: int = 2)
Bases: TemporalModuleBase
MLP baseline over TemporalData event messages plus ID embeddings.
Source code in graphids/core/models/temporal/event_classifier.py
TemporalGAT ¶
TemporalGAT(*, loss_fn: Module | None = None, hidden: int | None = None, layers: int | None = None, heads: int | None = None, embedding_dim: int | None = None, dropout: float = 0.2, lr: float = 0.001, weight_decay: float = 0.0001, scale: str = 'small', model_type: str = 'temporal_gat', dataset: str = '', seed: int = 42, num_ids: int = 0, in_channels: int = 0, num_classes: int = 2)
Bases: TemporalModuleBase
Causal event-attention classifier over PyG TemporalData batches.
Source code in graphids/core/models/temporal/gat.py
TemporalHybridModel ¶
TemporalHybridModel(*, loss_fn: Module | None = None, scale: str = 'small', objective: Objective = 'supervised', input: dict[str, Any] | None = None, memory: dict[str, Any] | None = None, backbone: dict[str, Any] | None = None, heads: dict[str, bool] | None = None, anomaly: dict[str, Any] | None = None, rhythm: dict[str, Any] | None = None, motif: dict[str, Any] | None = None, loss_weights: dict[str, float] | None = None, anomaly_score_weights: dict[str, float] | None = None, lr: float = 0.001, weight_decay: float = 0.0001, model_type: str = 'temporal_hybrid', dataset: str = '', seed: int = 42, num_ids: int = 0, in_channels: int = 0, num_classes: int = 2)
Bases: TemporalModuleBase
Hybrid classifier/anomaly detector with ID memory and stream backbones.
Source code in graphids/core/models/temporal/_hybrid/model.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | |
TemporalRNNClassifier ¶
TemporalRNNClassifier(*, loss_fn: Module | None = None, hidden: int | None = None, layers: int | None = None, embedding_dim: int | None = None, dropout: float = 0.2, lr: float = 0.001, weight_decay: float = 0.0001, scale: str = 'small', model_type: str = 'temporal_rnn_classifier', dataset: str = '', seed: int = 42, num_ids: int = 0, in_channels: int = 0, num_classes: int = 2)
Bases: TemporalModuleBase
GRU classifier that carries hidden state across temporal event batches.
Source code in graphids/core/models/temporal/rnn_classifier.py
TemporalVGAE ¶
TemporalVGAE(*, hidden: int | None = None, layers: int | None = None, embedding_dim: int | None = None, latent_dim: int | None = None, dropout: float = 0.1, kl_weight: float = 0.01, lr: float = 0.001, weight_decay: float = 0.0001, scale: str = 'small', model_type: str = 'temporal_vgae', dataset: str = '', seed: int = 42, num_ids: int = 0, in_channels: int = 0, num_classes: int = 2)
Bases: TemporalModuleBase
Recurrent variational autoencoder that scores event-level surprise.
Source code in graphids/core/models/temporal/vgae.py
base ¶
Shared infrastructure for temporal event models.
TemporalModuleBase ¶
Bases: _ModelBase
Base class for models that consume PyG TemporalData batches.
event_classifier ¶
Stateless supervised classifier for temporal CAN events.
TemporalEventClassifier ¶
TemporalEventClassifier(*, loss_fn: Module | None = None, hidden: int | None = None, layers: int | None = None, embedding_dim: int | None = None, dropout: float = 0.2, lr: float = 0.001, weight_decay: float = 0.0001, scale: str = 'small', model_type: str = 'temporal_event_classifier', dataset: str = '', seed: int = 42, num_ids: int = 0, in_channels: int = 0, num_classes: int = 2)
Bases: TemporalModuleBase
MLP baseline over TemporalData event messages plus ID embeddings.
Source code in graphids/core/models/temporal/event_classifier.py
gat ¶
Temporal supervised attention model for CAN event streams.
TemporalGAT ¶
TemporalGAT(*, loss_fn: Module | None = None, hidden: int | None = None, layers: int | None = None, heads: int | None = None, embedding_dim: int | None = None, dropout: float = 0.2, lr: float = 0.001, weight_decay: float = 0.0001, scale: str = 'small', model_type: str = 'temporal_gat', dataset: str = '', seed: int = 42, num_ids: int = 0, in_channels: int = 0, num_classes: int = 2)
Bases: TemporalModuleBase
Causal event-attention classifier over PyG TemporalData batches.
Source code in graphids/core/models/temporal/gat.py
hybrid ¶
Compatibility exports for the temporal hybrid model.
Implementation lives under :mod:graphids.core.models.temporal._hybrid; this
module remains the public import and checkpoint class path.
TemporalHybridModel ¶
TemporalHybridModel(*, loss_fn: Module | None = None, scale: str = 'small', objective: Objective = 'supervised', input: dict[str, Any] | None = None, memory: dict[str, Any] | None = None, backbone: dict[str, Any] | None = None, heads: dict[str, bool] | None = None, anomaly: dict[str, Any] | None = None, rhythm: dict[str, Any] | None = None, motif: dict[str, Any] | None = None, loss_weights: dict[str, float] | None = None, anomaly_score_weights: dict[str, float] | None = None, lr: float = 0.001, weight_decay: float = 0.0001, model_type: str = 'temporal_hybrid', dataset: str = '', seed: int = 42, num_ids: int = 0, in_channels: int = 0, num_classes: int = 2)
Bases: TemporalModuleBase
Hybrid classifier/anomaly detector with ID memory and stream backbones.
Source code in graphids/core/models/temporal/_hybrid/model.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | |
TemporalIdMemory ¶
TemporalIdMemory(*, num_ids: int, hidden: int, use_source: bool = True, use_destination: bool = True, time_encoding_dim: int = 0)
Bases: Module
TGN-style per-ID memory updated causally after each event.
Source code in graphids/core/models/temporal/_hybrid/contexts.py
TemporalInputEncoder ¶
TemporalInputEncoder(*, num_ids: int, in_channels: int, embedding_dim: int, hidden: int, dropout: float)
Bases: Module
Encode event bytes/features plus source and destination ID embeddings.
Source code in graphids/core/models/temporal/_hybrid/encoders.py
TemporalMotifContext ¶
Bases: Module
Encode recent destination-ID and IAT motifs from the current stream.
Source code in graphids/core/models/temporal/_hybrid/contexts.py
TemporalRhythmContext ¶
Bases: Module
Causal rolling IAT summaries for source and destination IDs.
Source code in graphids/core/models/temporal/_hybrid/contexts.py
TemporalStreamBackbone ¶
Bases: Module
Configurable causal stream backbone.
Source code in graphids/core/models/temporal/_hybrid/backbone.py
TemporalTimeEncoder ¶
Bases: Module
Sinusoidal encoding for positive elapsed times.
Source code in graphids/core/models/temporal/_hybrid/encoders.py
rnn_classifier ¶
Stateful supervised recurrent classifier for temporal CAN events.
TemporalRNNClassifier ¶
TemporalRNNClassifier(*, loss_fn: Module | None = None, hidden: int | None = None, layers: int | None = None, embedding_dim: int | None = None, dropout: float = 0.2, lr: float = 0.001, weight_decay: float = 0.0001, scale: str = 'small', model_type: str = 'temporal_rnn_classifier', dataset: str = '', seed: int = 42, num_ids: int = 0, in_channels: int = 0, num_classes: int = 2)
Bases: TemporalModuleBase
GRU classifier that carries hidden state across temporal event batches.
Source code in graphids/core/models/temporal/rnn_classifier.py
vgae ¶
Temporal variational event autoencoder for CAN streams.
TemporalVGAE ¶
TemporalVGAE(*, hidden: int | None = None, layers: int | None = None, embedding_dim: int | None = None, latent_dim: int | None = None, dropout: float = 0.1, kl_weight: float = 0.01, lr: float = 0.001, weight_decay: float = 0.0001, scale: str = 'small', model_type: str = 'temporal_vgae', dataset: str = '', seed: int = 42, num_ids: int = 0, in_channels: int = 0, num_classes: int = 2)
Bases: TemporalModuleBase
Recurrent variational autoencoder that scores event-level surprise.