From 0f445fa833370049d9a774b0da0ac236c73d966e Mon Sep 17 00:00:00 2001 From: ifountalis Date: Mon, 30 Mar 2026 15:51:01 -0400 Subject: [PATCH 1/5] predictive management skills --- skills/rai-predictive-management/SKILL.md | 194 +++++++++++ .../examples/register_and_load.py | 51 +++ skills/rai-predictive-modeling/SKILL.md | 309 ++++++++++++++++++ .../examples/link_prediction_snowflake.py | 86 +++++ .../examples/node_classification_snowflake.py | 82 +++++ .../references/property-transformer-types.md | 66 ++++ skills/rai-predictive-training/SKILL.md | 262 +++++++++++++++ .../examples/train_link_prediction.py | 49 +++ .../examples/train_node_classification.py | 46 +++ .../references/hyperparameters.md | 79 +++++ .../references/prediction-attributes.md | 67 ++++ .../references/task-types-and-metrics.md | 68 ++++ 12 files changed, 1359 insertions(+) create mode 100644 skills/rai-predictive-management/SKILL.md create mode 100644 skills/rai-predictive-management/examples/register_and_load.py create mode 100644 skills/rai-predictive-modeling/SKILL.md create mode 100644 skills/rai-predictive-modeling/examples/link_prediction_snowflake.py create mode 100644 skills/rai-predictive-modeling/examples/node_classification_snowflake.py create mode 100644 skills/rai-predictive-modeling/references/property-transformer-types.md create mode 100644 skills/rai-predictive-training/SKILL.md create mode 100644 skills/rai-predictive-training/examples/train_link_prediction.py create mode 100644 skills/rai-predictive-training/examples/train_node_classification.py create mode 100644 skills/rai-predictive-training/references/hyperparameters.md create mode 100644 skills/rai-predictive-training/references/prediction-attributes.md create mode 100644 skills/rai-predictive-training/references/task-types-and-metrics.md diff --git a/skills/rai-predictive-management/SKILL.md b/skills/rai-predictive-management/SKILL.md new file mode 100644 index 0000000..d671ada --- /dev/null +++ b/skills/rai-predictive-management/SKILL.md @@ -0,0 +1,194 @@ +--- +name: rai-predictive-management +description: Register trained GNN models to Snowflake Model Registry and load previously trained models for inference. Use after training with rai-predictive-training, or when loading a saved model by registry key or run ID. +--- + +# Predictive Management + + +## Summary + +**What:** Encodes the model lifecycle workflow — registering trained GNN models and loading them for later inference. + +**When to use:** +- Saving a trained model to Snowflake Model Registry +- Loading a previously registered model by name and version +- Loading a model by run ID +- Understanding the train-register-load multi-session workflow + +**When NOT to use:** +- Defining concepts, building graphs, or configuring features — see `rai-predictive-modeling` +- Training a model or generating predictions — see `rai-predictive-training` + +**Overview:** +1. Register a trained model with `gnn.register_model()` +2. Load a model by registry key or run ID with `GNN(...).load()` +3. Generate predictions with the loaded model + +--- + +## Quick Reference + +**Register:** +```python +gnn.register_model( + model_database="MY_DB", + model_schema="MODEL_REGISTRY", + model_name="fraud_detector", + version_name="v1", +) +``` + +**Load by registry key:** +```python +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, pt=pt, + model_database="MY_DB", model_schema="MODEL_REGISTRY", + model_name="fraud_detector", version_name="v1", +) +gnn.load() +``` + +**Load by run ID:** +```python +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, pt=pt, + model_run_id="01c2d9a0-0711-c54d-000a-1dc707f7a1e6", +) +gnn.load() +``` + +**What to include vs. omit when loading:** + +| Include | Omit | +|---------|------| +| `database`, `schema` | `train`, `validation` | +| `exp_database`, `exp_schema` | `task_type`, `eval_metric` | +| `graph`, `pt` | hyperparameters (`device`, `n_epochs`, etc.) | +| model identifier (registry key or run ID) | | + +--- + +## Register a Model + +After `gnn.fit()` completes, register the model to the Snowflake Model Registry: + +```python +gnn.register_model( + model_database="MY_DB", + model_schema="MODEL_REGISTRY", + model_name="fraud_detector", + version_name="v1", + comment="Initial training run", # optional +) +``` + +The combination of `(model_database, model_schema, model_name, version_name)` uniquely identifies a registered model. + +--- + +## Load a Model + +### By Registry Key + +```python +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, + pt=pt, + model_database="MY_DB", + model_schema="MODEL_REGISTRY", + model_name="fraud_detector", + version_name="v1", +) +gnn.load() + +User.predictions = gnn.predictions(domain=Test) +``` + +### By Run ID + +```python +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, + pt=pt, + model_run_id="01c2d9a0-0711-c54d-000a-1dc707f7a1e6", +) +gnn.load() + +User.predictions = gnn.predictions(domain=Test) +``` + +After `gnn.load()`, use `gnn.predictions(domain=Test)` exactly as after `gnn.fit()` — the prediction workflow is the same (see `rai-predictive-training`). + +--- + +## Train-Register-Load Workflow + +A typical multi-session workflow: + +### Session 1: Train and Register + +```python +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, pt=pt, + train=Train, validation=Val, + task_type="binary_classification", eval_metric="roc_auc", + has_time_column=True, + device="cuda", n_epochs=5, +) +gnn.fit() +gnn.register_model( + model_database="MY_DB", model_schema="MODEL_REGISTRY", + model_name="fraud_detector", version_name="v1", +) +``` + +### Session 2: Load and Predict + +Rebuild the graph and PropertyTransformer with the same structure as training, then load: + +```python +# Rebuild graph and pt (same as training session) +gnn_graph = Graph(model, directed=True, weighted=False, aggregator="sum") +# ... define edges ... +pt = PropertyTransformer(...) + +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, pt=pt, + model_database="MY_DB", model_schema="MODEL_REGISTRY", + model_name="fraud_detector", version_name="v1", +) +gnn.load() +User.predictions = gnn.predictions(domain=Test) +``` + +--- + +## Common Pitfalls + +| Mistake | Cause | Fix | +|---------|-------|-----| +| Calling `register_model()` before `fit()` | Model must be trained first | Always call `gnn.fit()` before `gnn.register_model()` | +| Omitting `graph` or `pt` when loading | Loaded models still need the graph structure | Provide the same `graph` and `pt` used during training | +| Passing `train`, `validation`, or hyperparameters when loading | These are training-only parameters | Omit `train`, `validation`, `task_type`, `eval_metric`, and all hyperparameters | +| Reusing the same `(name, version)` tuple | Registry keys must be unique | Use a new `version_name` for each registration | + +--- + +## Examples + +| Pattern | Description | File | +|---------|-------------|------| +| Register and load | Complete train-register-load workflow across sessions | [examples/register_and_load.py](examples/register_and_load.py) | diff --git a/skills/rai-predictive-management/examples/register_and_load.py b/skills/rai-predictive-management/examples/register_and_load.py new file mode 100644 index 0000000..5db1f6c --- /dev/null +++ b/skills/rai-predictive-management/examples/register_and_load.py @@ -0,0 +1,51 @@ +""" +GNN Model Management — Register and Load Workflow +================================================== +Demonstrates the train-register-load pattern across sessions. + +Session 1: Train a model and register it to Snowflake Model Registry. +Session 2: Load the registered model and generate predictions. +""" + +# ── Session 1: Train and Register ─────────────────────────────────────────── +# Assumes data model from `rai-predictive-modeling`: +# gnn_graph, pt, Train, Val, Test, User + +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, pt=pt, + train=Train, validation=Val, + task_type="binary_classification", eval_metric="roc_auc", + has_time_column=True, + device="cuda", n_epochs=5, +) +gnn.fit() + +gnn.register_model( + model_database="MY_DB", + model_schema="MODEL_REGISTRY", + model_name="fraud_detector", + version_name="v1", + comment="Initial training run", +) + + +# ── Session 2: Load and Predict ───────────────────────────────────────────── +# Rebuild graph and PropertyTransformer (same structure as training session) +# gnn_graph = Graph(model, directed=True, weighted=False, aggregator="sum") +# ... define edges ... +# pt = PropertyTransformer(...) + +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, pt=pt, + model_database="MY_DB", + model_schema="MODEL_REGISTRY", + model_name="fraud_detector", + version_name="v1", +) +gnn.load() + +User.predictions = gnn.predictions(domain=Test) diff --git a/skills/rai-predictive-modeling/SKILL.md b/skills/rai-predictive-modeling/SKILL.md new file mode 100644 index 0000000..3c04b26 --- /dev/null +++ b/skills/rai-predictive-modeling/SKILL.md @@ -0,0 +1,309 @@ +--- +name: rai-predictive-modeling +description: Build GNN data models with concepts, Snowflake population, task relationships, graph edges, and feature transformation. Use when defining entity types, loading data, configuring graph structure, or setting up PropertyTransformer for a predictive GNN pipeline. +--- + +# Predictive Modeling + + +## Summary + +**What:** Encodes the data modeling workflow for GNN pipelines — from imports through graph construction and feature configuration. + +**When to use:** +- Defining domain concepts (entity types) and their primary keys +- Loading data from Snowflake tables into concepts +- Setting up train/validation/test task relationships +- Building a graph with edges between concepts +- Configuring PropertyTransformer feature annotations + +**When NOT to use:** +- Training a GNN model or generating predictions — see `rai-predictive-training` +- Registering or loading saved models — see `rai-predictive-management` + +**Overview:** +1. Imports and Model setup +2. Define concepts (graph entities + task tables) +3. Populate concepts from Snowflake +4. Define task relationships (train/val/test splits) +5. Build graph and define edges +6. Configure PropertyTransformer (optional) + +--- + +## Quick Reference + +**Imports:** +```python +from relationalai.semantics import Model, select, define, Integer, String, Any +from relationalai.semantics.reasoners.graph import Graph +from relationalai.semantics.reasoners.predictive import GNN, PropertyTransformer +``` + +Additional type imports as needed: `Date`, `DateTime`, `Float`. + +**Model setup:** +```python +model = Model("") +Concept, Table, Relationship = model.Concept, model.Table, model.Relationship +``` + +**Concept patterns:** + +| Pattern | Code | +|---------|------| +| Single PK | `User = Concept("User", identify_by={"user_id": Integer})` | +| Composite PK | `Class = Concept("Class", identify_by={"courseid": Integer, "year": Integer})` | +| No PK | `Transaction = Concept("Transaction")` | +| Task table | `train_table_concept = Concept("TrainTable")` | + +**Relationship arity rules:** + +| Task Type | Train/Val template | Test template | +|-----------|-------------------|---------------| +| classification (no time) | `f"{Source} has {Any:label}"` | `f"{Source}"` | +| classification (with time) | `f"{Source} at {Any:ts} has {Any:label}"` | `f"{Source} at {Any:ts}"` | +| regression (no time) | `f"{Source} has {Any:value}"` | `f"{Source}"` | +| regression (with time) | `f"{Source} at {Any:ts} has {Any:value}"` | `f"{Source} at {Any:ts}"` | +| link_prediction | `f"{Source} has {Target}"` | `f"{Source}"` | +| repeated_link_prediction | `f"{Source} at {Any:ts} has {Target}"` | `f"{Source} at {Any:ts}"` | + +**Graph init:** +```python +gnn_graph = Graph(model, directed=True, weighted=False, aggregator="sum") +Edge = gnn_graph.Edge +``` + +--- + +## Imports and Model Setup + +Every GNN pipeline starts with these imports: + +```python +from relationalai.semantics import Model, select, define, Integer, String, Any +from relationalai.semantics.reasoners.graph import Graph +from relationalai.semantics.reasoners.predictive import GNN, PropertyTransformer +``` + +Add type imports based on your concept primary keys and data: +- `Integer` — integer primary keys +- `String` — string primary keys +- `Any` — flexible types in Relationship templates +- `Date`, `DateTime` — temporal fields +- `Float` — float values + +Unpack the DSL primitives from the Model: + +```python +model = Model("") +Concept, Table, Relationship = model.Concept, model.Table, model.Relationship +``` + +--- + +## Define and Populate Concepts + +### Graph Concepts + +Graph concepts represent domain entities. Define with `identify_by` for primary keys: + +```python +Customer = Concept("Customer", identify_by={"customer_id": Integer}) +Article = Concept("Article", identify_by={"article_id": Integer}) +Transaction = Concept("Transaction") # no PK — identity from data source +``` + +Populate from Snowflake using fully qualified table names: + +```python +define(Customer.new(Table("DB.SCHEMA.CUSTOMERS").to_schema())) +define(Article.new(Table("DB.SCHEMA.ARTICLES").to_schema())) +define(Transaction.new(Table("DB.SCHEMA.TRANSACTIONS").to_schema())) +``` + +### Task Table Concepts + +Task table concepts hold train/validation/test split data. They have no `identify_by`: + +```python +train_table_concept = Concept("TrainTable") +val_table_concept = Concept("ValidationTable") +test_table_concept = Concept("TestTable") + +define(train_table_concept.new(Table("DB.SCHEMA.TRAIN").to_schema())) +define(val_table_concept.new(Table("DB.SCHEMA.VAL").to_schema())) +define(test_table_concept.new(Table("DB.SCHEMA.TEST").to_schema())) +``` + +--- + +## Task Relationships + +Relationships encode the task structure using a template string. The template has three parts: +- **Head** = source concept (the concept being predicted on) +- **"at" clause** = optional timestamp field +- **"has" clause** = label (classification/regression) or target concept (link prediction) + +### Node Classification (with time) + +```python +Train = Relationship(f"{User} at {Any:timestamp} has {Any:target}") +define(Train(User, train_table_concept.timestamp, train_table_concept.target)).where( + User.user_id == train_table_concept.user +) + +Val = Relationship(f"{User} at {Any:timestamp} has {Any:target}") +define(Val(User, val_table_concept.timestamp, val_table_concept.target)).where( + User.user_id == val_table_concept.user +) + +Test = Relationship(f"{User} at {Any:timestamp}") +define(Test(User, test_table_concept.timestamp)).where( + User.user_id == test_table_concept.user +) +``` + +### Node Classification (no time) + +```python +Train = Relationship(f"{User} has {Any:target}") +Val = Relationship(f"{User} has {Any:target}") +Test = Relationship(f"{User}") +``` + +### Link Prediction (with time / repeated_link_prediction) + +```python +Train = Relationship(f"{Customer} at {Any:timestamp} has {Article}") +define(Train(Customer, train_table_concept.timestamp, Article)).where( + Customer.customer_id == train_table_concept.customer_id, + Article.article_id == train_table_concept.article_id, +) + +Val = Relationship(f"{Customer} at {Any:timestamp} has {Article}") +define(Val(Customer, val_table_concept.timestamp, Article)).where( + Customer.customer_id == val_table_concept.customer_id, + Article.article_id == val_table_concept.article_id, +) + +Test = Relationship(f"{Customer} at {Any:timestamp}") +define(Test(Customer, test_table_concept.timestamp)).where( + Customer.customer_id == test_table_concept.customer_id, +) +``` + +### Link Prediction (no time) + +```python +Train = Relationship(f"{Customer} has {Article}") +Val = Relationship(f"{Customer} has {Article}") +Test = Relationship(f"{Customer}") +``` + +--- + +## Graph and Edges + +Create the graph with standard defaults and define edges via field equality: + +```python +gnn_graph = Graph(model, directed=True, weighted=False, aggregator="sum") +Edge = gnn_graph.Edge + +define(Edge.new(src=Transaction, dst=Customer)).where( + Transaction.customer_id == Customer.customer_id) +define(Edge.new(src=Transaction, dst=Article)).where( + Transaction.article_id == Article.article_id) +``` + +### Self-Referential Edges + +When both sides of an edge are the same concept, use `.ref()`: + +```python +PostRef = Post.ref() +define(Edge.new(src=Post, dst=PostRef)).where( + PostRef.parent_id == Post.id) +``` + +### Multiple Typed Edges Between Same Pair + +```python +BB1Edge = Concept("BB1Edge", extends=[Edge]) +BB2Edge = Concept("BB2Edge", extends=[Edge]) + +Bref = B.ref() +define(BB1Edge.new(src=B, dst=Bref)).where(B.field1 == Bref.id) +define(BB2Edge.new(src=B, dst=Bref)).where(B.field2 == Bref.id) +``` + +--- + +## Feature Configuration + +`PropertyTransformer` annotates concept fields with their semantic types. Organize fields by concept, then combine: + +```python +# User features +category_user = [User.locale, User.gender] +datetime_user = [User.joinedAt] +continuous_user = [User.birthyear] + +# Event features +category_event = [Event.city, Event.state, Event.country] +datetime_event = [Event.start_time] + +pt = PropertyTransformer( + category=[*category_user, *category_event], + datetime=[*datetime_user, *datetime_event], + continuous=[*continuous_user], + time_col=[Event.start_time], +) +``` + +### Feature Type Guidelines + +| Data type | Annotation | +|-----------|-----------| +| Boolean flags, enum/status codes | `category` | +| Ages, prices, ratings | `continuous` | +| Free-form text, names, descriptions | `text` | +| Dates, timestamps | `datetime` | + +PropertyTransformer is optional — omitting it auto-infers all field types. For production, explicit annotation is recommended. Use `drop` to exclude fields or entire concepts: `drop=[Customer, Article.COLOUR_GROUP_CODE]`. + +For the full feature type reference including drop patterns, see [references/property-transformer-types.md](references/property-transformer-types.md). + +--- + +## Common Pitfalls + +| Mistake | Cause | Fix | +|---------|-------|-----| +| Concept name is plural (e.g. "Customers") | Naming convention | Use singular names: `Concept("Customer")` | +| Task table concept has `identify_by` | Task tables don't need primary keys | Use plain `Concept("TrainTable")` with no `identify_by` | +| Snowflake table name not fully qualified | Missing database or schema prefix | Use `"DATABASE.SCHEMA.TABLE"` format | +| Test Relationship includes label/target | Test data should not contain the answer | Omit the "has" clause: `f"{Source}"` or `f"{Source} at {Any:ts}"` | +| Positional args in `define(Train(...))` don't match template | Template and population call must align | Match the order: source, [timestamp], [label/target] | +| Self-referential edge without `.ref()` | Same concept on both sides creates ambiguity | Use `PostRef = Post.ref()` for the destination | +| `time_col` fields not in `datetime` list | Both lists must include the field | Add time columns to both `datetime=[...]` and `time_col=[...]` | +| Task table concept used in edge definition | Only graph concepts participate in edges | Edges connect domain entities, not task tables | +| Missing type import | e.g. using `Date` without importing it | Add missing types to the import line | + +--- + +## Examples + +| Pattern | Description | File | +|---------|-------------|------| +| Node classification | Binary classification with temporal features (User/Event/EventAttendee) | [examples/node_classification_snowflake.py](examples/node_classification_snowflake.py) | +| Link prediction | Repeated link prediction on H&M data (Customer/Article/Transaction) | [examples/link_prediction_snowflake.py](examples/link_prediction_snowflake.py) | + +--- + +## Reference files + +| Reference | Description | File | +|-----------|-------------|------| +| PropertyTransformer types | Full feature type reference, drop patterns, and guidelines | [references/property-transformer-types.md](references/property-transformer-types.md) | diff --git a/skills/rai-predictive-modeling/examples/link_prediction_snowflake.py b/skills/rai-predictive-modeling/examples/link_prediction_snowflake.py new file mode 100644 index 0000000..ca705c7 --- /dev/null +++ b/skills/rai-predictive-modeling/examples/link_prediction_snowflake.py @@ -0,0 +1,86 @@ +""" +GNN Link Prediction — Data Modeling (Phases 1-6) +================================================= +Repeated link prediction on H&M customer-article data from Snowflake. +Demonstrates: concepts, population, task relationships, graph, and features. + +For training and prediction, see `rai-predictive-training`. +""" + +# ── Phase 1: Imports & Model Setup ────────────────────────────────────────── +from relationalai.semantics import Model, select, define, Integer, Date, Any +from relationalai.semantics.reasoners.graph import Graph +from relationalai.semantics.reasoners.predictive import GNN, PropertyTransformer + +model = Model("gnn_link_prediction_example") +Concept, Table, Relationship = model.Concept, model.Table, model.Relationship + +# ── Phase 2: Define Concepts ──────────────────────────────────────────────── +# graph concepts +Customer = Concept("Customer", identify_by={"C_customer_id": Integer}) +Article = Concept("Article", identify_by={"A_article_id": Integer}) +Transaction = Concept("Transaction") + +# task table concepts +train_table_concept = Concept("TrainTable") +val_table_concept = Concept("ValidationTable") +test_table_concept = Concept("TestTable") + +# ── Phase 3: Populate Concepts (from Snowflake) ───────────────────────────── +define(Customer.new(Table("HM_MINI.PUBLIC.CUSTOMERS").to_schema())) +define(Article.new(Table("HM_MINI.PUBLIC.ARTICLES").to_schema())) +define(Transaction.new(Table("HM_MINI.PUBLIC.TRANSACTIONS_DEDUP").to_schema())) + +define(train_table_concept.new(Table("HM_MINI.PUBLIC.TRAIN_LINK").to_schema())) +define(val_table_concept.new(Table("HM_MINI.PUBLIC.VALIDATION_LINK").to_schema())) +define(test_table_concept.new(Table("HM_MINI.PUBLIC.TEST_LINK").to_schema())) + +# ── Phase 4: Setup Task Relationships ───────────────────────────────────────── +Train = Relationship(f"{Customer} at {Any:timestamp} has {Article}") +define(Train(Customer, train_table_concept.timestamp, Article)).where( + Customer.c_customer_id == train_table_concept.customer_id, + Article.a_article_id == train_table_concept.article_id, +) + +Val = Relationship(f"{Customer} at {Any:timestamp} has {Article}") +define(Val(Customer, val_table_concept.timestamp, Article)).where( + Customer.c_customer_id == val_table_concept.customer_id, + Article.a_article_id == val_table_concept.article_id, +) + +Test = Relationship(f"{Customer} at {Any:timestamp}") +define(Test(Customer, test_table_concept.timestamp)).where( + Customer.c_customer_id == test_table_concept.customer_id, +) + +# ── Phase 5: Build Graph & Edges ──────────────────────────────────────────── +gnn_graph = Graph(model, directed=True, weighted=False, aggregator="sum") +Edge = gnn_graph.Edge + +define(Edge.new(src=Transaction, dst=Customer)).where( + Transaction.t_customer_id == Customer.C_customer_id) +define(Edge.new(src=Transaction, dst=Article)).where( + Transaction.t_article_id == Article.a_article_id) + +# ── Phase 6: Configure PropertyTransformer ────────────────────────────────── +# Customer features +category_customer = [Customer.FN, Customer.ACTIVE, Customer.POSTAL_CODE, + Customer.CLUB_MEMBER_STATUS, Customer.FASHION_NEWS_FREQUENCY] +continuous_customer = [Customer.AGE] + +# Article features +category_article = [Article.PRODUCT_CODE] +text_article = [Article.PROD_NAME] + +# Transaction features +category_transaction = [Transaction.SALES_CHANNEL_ID] +continuous_transaction = [Transaction.PRICE] +datetime_transaction = [Transaction.T_DAT] + +pt = PropertyTransformer( + category=[*category_customer, *category_article, *category_transaction], + continuous=[*continuous_customer, *continuous_transaction], + datetime=[*datetime_transaction], + text=[*text_article], + time_col=[Transaction.T_DAT], +) diff --git a/skills/rai-predictive-modeling/examples/node_classification_snowflake.py b/skills/rai-predictive-modeling/examples/node_classification_snowflake.py new file mode 100644 index 0000000..0b03d45 --- /dev/null +++ b/skills/rai-predictive-modeling/examples/node_classification_snowflake.py @@ -0,0 +1,82 @@ +""" +GNN Node Classification — Data Modeling (Phases 1-6) +===================================================== +Binary classification on user data from Snowflake with temporal features. +Demonstrates: concepts, population, task relationships, graph, and features. + +For training and prediction, see `rai-predictive-training`. +""" + +# ── Phase 1: Imports & Model Setup ────────────────────────────────────────── +from relationalai.semantics import Model, select, define, Integer, String, Any +from relationalai.semantics.reasoners.graph import Graph +from relationalai.semantics.reasoners.predictive import GNN, PropertyTransformer + +model = Model("gnn_node_classification_example") +Concept, Table, Relationship = model.Concept, model.Table, model.Relationship + +# ── Phase 2: Define Concepts ──────────────────────────────────────────────── +# graph concepts +User = Concept("User", identify_by={"user_id": Integer}) +Event = Concept("Event", identify_by={"event_id": Integer}) +EventAttendee = Concept("EventAttendee") + +# task table concepts +train_table_concept = Concept("TrainTable") +val_table_concept = Concept("ValidationTable") +test_table_concept = Concept("TestTable") + +# ── Phase 3: Populate Concepts (from Snowflake) ───────────────────────────── +define(User.new(Table("DB.SCHEMA.USERS").to_schema())) +define(Event.new(Table("DB.SCHEMA.EVENTS").to_schema())) +define(EventAttendee.new(Table("DB.SCHEMA.EVENT_ATTENDEES").to_schema())) + +define(train_table_concept.new(Table("DB.SCHEMA.TRAIN").to_schema())) +define(val_table_concept.new(Table("DB.SCHEMA.VAL").to_schema())) +define(test_table_concept.new(Table("DB.SCHEMA.TEST").to_schema())) + +# ── Phase 4: Setup Task Relationships ───────────────────────────────────────── +Train = Relationship(f"{User} at {Any:timestamp} has {Any:target}") +define(Train(User, train_table_concept.timestamp, train_table_concept.target)).where( + User.user_id == train_table_concept.user +) + +Val = Relationship(f"{User} at {Any:timestamp} has {Any:target}") +define(Val(User, val_table_concept.timestamp, val_table_concept.target)).where( + User.user_id == val_table_concept.user +) + +Test = Relationship(f"{User} at {Any:timestamp}") +define(Test(User, test_table_concept.timestamp)).where( + User.user_id == test_table_concept.user +) + +# ── Phase 5: Build Graph & Edges ──────────────────────────────────────────── +gnn_graph = Graph(model, directed=True, weighted=False, aggregator="sum") +Edge = gnn_graph.Edge + +define(Edge.new(src=Event, dst=User)).where( + Event.user_id == User.user_id) +define(Edge.new(src=EventAttendee, dst=Event)).where( + EventAttendee.event == Event.event_id) +define(Edge.new(src=EventAttendee, dst=User)).where( + EventAttendee.user_id == User.user_id) + +# ── Phase 6: Configure PropertyTransformer ────────────────────────────────── +category_user = [User.locale, User.gender] +datetime_user = [User.joinedAt] +continuous_user = [User.birthyear] + +category_event = [Event.city, Event.state, Event.zip, Event.country] +datetime_event = [Event.start_time] +continuous_event = [Event.lat, Event.lng] + +category_event_attendee = [EventAttendee.status] +datetime_event_attendee = [EventAttendee.start_time] + +pt = PropertyTransformer( + category=[*category_user, *category_event, *category_event_attendee], + datetime=[*datetime_user, *datetime_event, *datetime_event_attendee], + continuous=[*continuous_user, *continuous_event], + time_col=[Event.start_time, EventAttendee.start_time], +) diff --git a/skills/rai-predictive-modeling/references/property-transformer-types.md b/skills/rai-predictive-modeling/references/property-transformer-types.md new file mode 100644 index 0000000..f902105 --- /dev/null +++ b/skills/rai-predictive-modeling/references/property-transformer-types.md @@ -0,0 +1,66 @@ +# PropertyTransformer Feature Types + +The `PropertyTransformer` class specifies how concept fields are transformed into GNN-compatible features. + +## Feature Types + +| Type | PropertyTransformer kwarg | Description | Example fields | +|------|--------------------------|-------------|----------------| +| Category | `category=[...]` | Discrete categorical values (int or string) | Gender, product code, membership status | +| Continuous | `continuous=[...]` | Numeric continuous values (float) | Age, price, rating | +| Text | `text=[...]` | Text strings (embedded via language model) | Product name, description, comment | +| Datetime | `datetime=[...]` | Timestamps or dates | Transaction date, creation date | +| Integer | `integer=[...]` | Integer values (kept as integers, not categorical) | Explicit integer IDs | +| Drop | `drop=[...]` | Exclude field from model entirely | Foreign keys, sensitive data, redundant IDs | + +## Special Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `time_col` | list or single | Time column(s) for temporal models. Must NOT appear in `drop`. | + +## Usage + +```python +from relationalai.semantics.reasoners.predictive import PropertyTransformer + +pt = PropertyTransformer( + category=[Customer.gender, Article.PRODUCT_CODE, Transaction.SALES_CHANNEL_ID], + continuous=[Customer.age, Transaction.PRICE], + text=[Article.PROD_NAME], + datetime=[Transaction.T_DAT], + drop=[Customer, Article.GRAPHICAL_APPEARANCE_NAME], + time_col=[Transaction.T_DAT], +) +``` + +## Drop Patterns + +### Drop specific fields +```python +drop=[Article.GRAPHICAL_APPEARANCE_NAME, Article.COLOUR_GROUP_CODE] +``` + +### Drop all fields of a concept (identifier columns) +```python +drop=[Customer] # drops all Customer fields (including primary key) +``` + +### Mixed: drop entire concept + specific fields from another +```python +drop=[Customer, Article.GRAPHICAL_APPEARANCE_NAME, Article.COLOUR_GROUP_CODE] +``` + +## Default Behavior + +Fields not mentioned in any category are auto-inferred by the GNN engine (equivalent to the `Infer` embedding type). This is usually fine for most fields, but explicitly annotating them improves reproducibility. + +## Guidelines + +- **Primary key / identifier fields**: Usually `drop` (they don't carry predictive signal) +- **Foreign key join columns**: Usually `drop` (the graph structure captures the relationship) +- **Numeric IDs that encode meaning** (e.g. product_code): Use `category` +- **Free-form text**: Use `text` +- **Dates/timestamps**: Use `datetime`. If it's the temporal ordering column, also add to `time_col` +- **Boolean flags**: Use `category` +- **Continuous measurements**: Use `continuous` diff --git a/skills/rai-predictive-training/SKILL.md b/skills/rai-predictive-training/SKILL.md new file mode 100644 index 0000000..390ec1a --- /dev/null +++ b/skills/rai-predictive-training/SKILL.md @@ -0,0 +1,262 @@ +--- +name: rai-predictive-training +description: Configure and train GNN models with hyperparameters, generate predictions, and inspect results. Use after building the data model with rai-predictive-modeling, when ready to run training or evaluate predictions. +--- + +# Predictive Training + + +## Summary + +**What:** Encodes the training and evaluation workflow for GNN pipelines — configuring the GNN estimator, running training, and generating predictions. + +**When to use:** +- Configuring a GNN constructor with task type, metric, and hyperparameters +- Running `gnn.fit()` to train a model +- Generating predictions with `gnn.predictions(domain=Test)` +- Inspecting or exporting prediction results + +**When NOT to use:** +- Defining concepts, loading data, or building the graph — see `rai-predictive-modeling` +- Registering or loading saved models — see `rai-predictive-management` + +**Overview:** +1. Configure the GNN estimator (database, graph, task type, metric, hyperparameters) +2. Train with `gnn.fit()` +3. Generate predictions with `gnn.predictions(domain=Test)` +4. Inspect or export results + +--- + +## Quick Reference + +**GNN constructor (node classification):** +```python +gnn = GNN( + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, pt=pt, + train=Train, validation=Val, + task_type="binary_classification", + eval_metric="roc_auc", + has_time_column=True, + device="cuda", n_epochs=5, +) +gnn.fit() +``` + +**Default metrics per task type:** + +| Task Type | Suggested Metric | +|-----------|-----------------| +| binary_classification | `roc_auc` | +| multiclass_classification | `accuracy` | +| multilabel_classification | `multilabel_auprc_macro` | +| regression | `rmse` | +| link_prediction | `link_prediction_precision@5` | +| repeated_link_prediction | `link_prediction_precision@5` | + +**Prediction attributes:** + +| Task Type | Attributes | +|-----------|-----------| +| classification | `.probs`, `.predicted_labels` | +| regression | `.predicted_value` | +| link prediction | `.rank`, `.scores`, `.predicted_` | + +--- + +## GNN Constructor + +The `GNN` constructor takes data locations, graph structure, task configuration, and hyperparameters. + +### Required Parameters + +| Parameter | Description | +|-----------|-------------| +| `database`, `schema` | Snowflake location of source data tables | +| `exp_database`, `exp_schema` | Snowflake location for experiment artifacts | +| `graph` | Graph object with edges defined (from `rai-predictive-modeling`) | +| `train`, `validation` | Relationship objects for train and validation splits | +| `task_type` | One of: `binary_classification`, `multiclass_classification`, `multilabel_classification`, `regression`, `link_prediction`, `repeated_link_prediction` | +| `eval_metric` | Evaluation metric compatible with the task type | + +### Optional Parameters + +| Parameter | Default | Description | +|-----------|---------|-------------| +| `pt` | None | PropertyTransformer (omit for auto-inference) | +| `has_time_column` | False | Set `True` when Relationships use the "at" keyword | +| `export_csv` | True | Export tables as CSV instead of Snowflake staging | +| `skip_cdc` | True | Skip CDC for faster data loading | +| `stream_logs` | True | Stream training logs to console | + +### Node Classification Example + +```python +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, + pt=pt, + train=Train, + validation=Val, + task_type="binary_classification", + eval_metric="roc_auc", + device="cuda", + n_epochs=5, + lr=0.005, +) +gnn.fit() +``` + +### Link Prediction Example (temporal) + +```python +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, + pt=pt, + train=Train, + validation=Val, + task_type="repeated_link_prediction", + eval_metric="link_prediction_precision@5", + has_time_column=True, + export_csv=True, + skip_cdc=True, + device="cuda", + n_epochs=5, + lr=0.005, + head_layers=2, + num_negative=20, + label_smoothing=True, +) +gnn.fit() +``` + +### Common Hyperparameters + +| Parameter | Default | Description | +|-----------|---------|-------------| +| `device` | `"cuda"` | `"cuda"` (GPU) or `"cpu"` | +| `n_epochs` | 5 | Number of training epochs | +| `lr` | 0.005 | Learning rate | +| `train_batch_size` | 256 | Training batch size | + +For link prediction, also consider: `head_layers=2`, `num_negative=20`, `label_smoothing=True`. + +For all hyperparameters, see [references/hyperparameters.md](references/hyperparameters.md). + +--- + +## Training + +Call `gnn.fit()` to start training. This executes the following stages: +1. Prepare dataset (load data from Snowflake) +2. Configure trainer (set up model architecture) +3. Submit training job (run on compute) + +### Detecting `has_time_column` + +If the Train Relationship template contains the "at" keyword (e.g. `f"{User} at {Any:timestamp} ..."`), set `has_time_column=True` in the GNN constructor. + +--- + +## Predictions + +After training, generate predictions on the test set: + +```python +Source.predictions = gnn.predictions(domain=Test) +``` + +### Classification (binary, multiclass, multilabel) + +```python +User.predictions = gnn.predictions(domain=Test) + +select( + User.user_id, + User.predictions.probs, + User.predictions.predicted_labels, +).where(User.predictions).inspect() +``` + +### Regression + +```python +Unit.predictions = gnn.predictions(domain=Test) + +select( + Unit.unit_id, + Unit.predictions.predicted_value, +).where(Unit.predictions).inspect() +``` + +### Link Prediction + +```python +Customer.predictions = gnn.predictions(domain=Test) + +select( + Customer.customer_id, + Article.article_id, + Customer.predictions.rank, + Customer.predictions.scores, +).where( + Customer.predictions.predicted_article == Article, +).inspect() +``` + +The `predicted_` attribute name is derived from the target concept name, always lowercase: +- Target `Article` -> `.predicted_article` +- Target `Product` -> `.predicted_product` + +### As DataFrame + +Replace `.inspect()` with `.to_df()` to get a pandas DataFrame: + +```python +df = select( + User.user_id, + User.predictions.probs, + User.predictions.predicted_labels, +).where(User.predictions).to_df() +``` + +For the full prediction attributes reference, see [references/prediction-attributes.md](references/prediction-attributes.md). + +--- + +## Common Pitfalls + +| Mistake | Cause | Fix | +|---------|-------|-----| +| Passing `select(...)` fragments to `train=` or `validation=` | GNN expects Relationship objects | Use `train=Train` with the Relationship object directly | +| Missing `has_time_column=True` | Relationships use "at" keyword but flag not set | Set `has_time_column=True` when templates contain "at" | +| Treating hyperparameters as named GNN parameters | Hyperparameters are `**kwargs` | Pass them directly: `GNN(..., device="cuda", n_epochs=5)` | +| Expecting `gnn.predictions()` to return results | Predictions are assigned to a concept attribute | Use `Source.predictions = gnn.predictions(domain=Test)` | +| Using `.predicted_Article` (uppercase) | Attribute name is always lowercase | Use `.predicted_article` regardless of concept casing | +| Passing wrong object to `domain=` | Must be the Test Relationship | Use `domain=Test` with the Relationship object | +| Omitting `pt=None` when no PropertyTransformer | Not a real issue | `pt` defaults to `None`; omitting it is fine | +| Invalid task_type/metric combination | Not all metrics work with all task types | Check the valid pairs in [references/task-types-and-metrics.md](references/task-types-and-metrics.md) | + +--- + +## Examples + +| Pattern | Description | File | +|---------|-------------|------| +| Node classification | Binary classification training + prediction (User/Event) | [examples/train_node_classification.py](examples/train_node_classification.py) | +| Link prediction | Repeated link prediction training + prediction (Customer/Article) | [examples/train_link_prediction.py](examples/train_link_prediction.py) | + +--- + +## Reference files + +| Reference | Description | File | +|-----------|-------------|------| +| Task types and metrics | All valid (task_type, eval_metric) combinations | [references/task-types-and-metrics.md](references/task-types-and-metrics.md) | +| Hyperparameters | Full hyperparameter table with types, defaults, and descriptions | [references/hyperparameters.md](references/hyperparameters.md) | +| Prediction attributes | Prediction attributes by task type with usage examples | [references/prediction-attributes.md](references/prediction-attributes.md) | diff --git a/skills/rai-predictive-training/examples/train_link_prediction.py b/skills/rai-predictive-training/examples/train_link_prediction.py new file mode 100644 index 0000000..6732342 --- /dev/null +++ b/skills/rai-predictive-training/examples/train_link_prediction.py @@ -0,0 +1,49 @@ +""" +GNN Link Prediction — Training & Prediction (Phases 7-8) +========================================================= +Repeated link prediction training and prediction on H&M data. + +Assumes data model from `rai-predictive-modeling`: + - gnn_graph: Graph with edges defined + - pt: PropertyTransformer configured + - Train, Val, Test: Relationship objects + - Customer: source concept, Article: target concept +""" + +# ── Phase 7: Train GNN ────────────────────────────────────────────────────── +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, + pt=pt, + train=Train, + validation=Val, + task_type="repeated_link_prediction", + eval_metric="link_prediction_precision@5", + has_time_column=True, + export_csv=True, + skip_cdc=True, + device="cuda", + n_epochs=5, + train_batch_size=256, + lr=0.005, + head_layers=2, + num_negative=20, + label_smoothing=True, +) +gnn.fit() + +# ── Phase 8: Predict & Inspect ────────────────────────────────────────────── +Customer.predictions = gnn.predictions(domain=Test) + +select( + Customer.c_customer_id, + Customer.age, + Article.a_article_id, + Customer.predictions.rank, + Customer.predictions.scores, +).where( + Customer.predictions.predicted_article == Article, + Customer.age < 50, + Customer.age > 20, +).inspect() diff --git a/skills/rai-predictive-training/examples/train_node_classification.py b/skills/rai-predictive-training/examples/train_node_classification.py new file mode 100644 index 0000000..a7d89f6 --- /dev/null +++ b/skills/rai-predictive-training/examples/train_node_classification.py @@ -0,0 +1,46 @@ +""" +GNN Node Classification — Training & Prediction (Phases 7-8) +============================================================= +Binary classification training and prediction on user data. + +Assumes data model from `rai-predictive-modeling`: + - gnn_graph: Graph with edges defined + - pt: PropertyTransformer configured + - Train, Val, Test: Relationship objects + - User: source concept (head of Relationship template) +""" + +# ── Phase 7: Train GNN ────────────────────────────────────────────────────── +gnn = GNN( + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, + pt=pt, + train=Train, + validation=Val, + task_type="binary_classification", + eval_metric="roc_auc", + has_time_column=True, + export_csv=True, + skip_cdc=True, + device="cuda", + n_epochs=5, +) +gnn.fit() + +# ── Phase 8: Predict & Inspect ────────────────────────────────────────────── +User.predictions = gnn.predictions(domain=Test) + +select( + User.user_id, + User.predictions.probs, + User.predictions.predicted_labels, +).where(User.predictions).inspect() + +df = select( + User.user_id, + User.predictions.probs, + User.predictions.predicted_labels, +).where(User.predictions).to_df() + +print(f"Predictions: {len(df)} rows, {len(df.dropna())} after dropping NaNs") diff --git a/skills/rai-predictive-training/references/hyperparameters.md b/skills/rai-predictive-training/references/hyperparameters.md new file mode 100644 index 0000000..3f5dae6 --- /dev/null +++ b/skills/rai-predictive-training/references/hyperparameters.md @@ -0,0 +1,79 @@ +# GNN Hyperparameters + +Hyperparameters are passed as `**train_params` kwargs to the `GNN(...)` constructor. + +## Common Hyperparameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `device` | str | `"cuda"` | Compute device: `"cuda"` (GPU) or `"cpu"` | +| `n_epochs` | int | 5 | Number of training epochs | +| `lr` | float | 0.005 | Learning rate | +| `train_batch_size` | int | 256 | Training batch size | +| `head_layers` | int | 2 | Number of prediction head layers | +| `seed` | int | - | Random seed for reproducibility | +| `channels` | int | 64 | Hidden channel dimension | + +## Link Prediction Hyperparameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `num_negative` | int | 20 | Number of negative samples per positive | +| `label_smoothing` | bool | True | Apply label smoothing during training | + +## Advanced Hyperparameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `temporal_strategy` | str | - | Temporal modeling strategy (e.g. `"last"`) | +| `text_embedder` | str | - | Text embedding model (e.g. `"model2vec-potion-base-4M"`) | +| `max_iters` | int | - | Maximum training iterations | + +## GNN Constructor Operational Flags + +These are named parameters on `GNN(...)`, not train_params: + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `test_batch_size` | int | None | Batch size for prediction/inference | +| `export_csv` | bool | True | Export tables as CSV instead of Snowflake staging | +| `stream_logs` | bool | True | Stream training logs to console | +| `skip_cdc` | bool | True | Skip CDC for faster data loading | +| `extract_embeddings` | bool | False | Extract node embeddings during prediction | +| `use_current_time` | bool | True | Use current time for temporal models | + +## Example Configurations + +### Node Classification (small dataset) +```python +train_params = {"device": "cpu", "n_epochs": 10, "seed": 42} +``` + +### Node Classification (large dataset) +```python +train_params = {"device": "cuda", "n_epochs": 5, "lr": 0.005, "train_batch_size": 256} +``` + +### Link Prediction +```python +train_params = { + "device": "cuda", + "n_epochs": 5, + "train_batch_size": 256, + "lr": 0.005, + "head_layers": 2, + "num_negative": 20, + "label_smoothing": True, +} +``` + +### Regression with Temporal Data +```python +train_params = { + "device": "cuda", + "n_epochs": 5, + "channels": 64, + "head_layers": 2, + "temporal_strategy": "last", +} +``` diff --git a/skills/rai-predictive-training/references/prediction-attributes.md b/skills/rai-predictive-training/references/prediction-attributes.md new file mode 100644 index 0000000..92491a8 --- /dev/null +++ b/skills/rai-predictive-training/references/prediction-attributes.md @@ -0,0 +1,67 @@ +# Prediction Attributes by Task Type + +After calling `gnn.predictions(domain=Test)`, the prediction results are attached to the source concept (head of the Relationship) and accessed via `select(...)`. + +## Classification (binary, multiclass, multilabel) + +| Attribute | Type | Description | +|-----------|------|-------------| +| `Source.predictions.probs` | float/array | Probability distribution over classes | +| `Source.predictions.predicted_labels` | int/str | Predicted class label (argmax of probs) | + +```python +Source.predictions = gnn.predictions(domain=Test) +select( + Source.id, + Source.predictions.probs, + Source.predictions.predicted_labels, +).where(Source.predictions).inspect() +``` + +## Regression + +| Attribute | Type | Description | +|-----------|------|-------------| +| `Source.predictions.predicted_value` | float | Predicted continuous value | + +```python +Source.predictions = gnn.predictions(domain=Test) +select( + Source.id, + Source.predictions.predicted_value, +).where(Source.predictions).inspect() +``` + +## Link Prediction (link_prediction, repeated_link_prediction) + +| Attribute | Type | Description | +|-----------|------|-------------| +| `Source.predictions.rank` | int | Ranking position (1, 2, 3, ...) | +| `Source.predictions.scores` | float | Relevance/similarity score | +| `Source.predictions.predicted_` | reference | Predicted target concept instance | + +The `predicted_` attribute name is derived from the target concept in the Relationship template. For example, if the Relationship tail is `Article`, the attribute is `predicted_article`. + +```python +Source.predictions = gnn.predictions(domain=Test) +select( + Source.source_id, + Target.target_id, + Source.predictions.rank, + Source.predictions.scores, +).where( + Source.predictions.predicted_article == Target, +).inspect() +``` + +## Using `.to_df()` Instead of `.inspect()` + +Replace `.inspect()` with `.to_df()` to get a pandas DataFrame: + +```python +df = select( + Source.id, + Source.predictions.probs, + Source.predictions.predicted_labels, +).where(Source.predictions).to_df() +``` diff --git a/skills/rai-predictive-training/references/task-types-and-metrics.md b/skills/rai-predictive-training/references/task-types-and-metrics.md new file mode 100644 index 0000000..ab3ed05 --- /dev/null +++ b/skills/rai-predictive-training/references/task-types-and-metrics.md @@ -0,0 +1,68 @@ +# Task Types and Evaluation Metrics + +Valid `(task_type, eval_metric)` combinations for the GNN constructor. + +## Binary Classification + +| task_type | eval_metric | +|-----------|-------------| +| `"binary_classification"` | `"accuracy"` | +| `"binary_classification"` | `"f1"` | +| `"binary_classification"` | `"roc_auc"` | +| `"binary_classification"` | `"average_precision"` | + +## Multiclass Classification + +| task_type | eval_metric | +|-----------|-------------| +| `"multiclass_classification"` | `"accuracy"` | +| `"multiclass_classification"` | `"macro_f1"` | +| `"multiclass_classification"` | `"micro_f1"` | + +## Multilabel Classification + +| task_type | eval_metric | +|-----------|-------------| +| `"multilabel_classification"` | `"multilabel_auprc_micro"` | +| `"multilabel_classification"` | `"multilabel_auroc_micro"` | +| `"multilabel_classification"` | `"multilabel_precision_micro"` | +| `"multilabel_classification"` | `"multilabel_auprc_macro"` | +| `"multilabel_classification"` | `"multilabel_auroc_macro"` | +| `"multilabel_classification"` | `"multilabel_precision_macro"` | + +## Regression + +| task_type | eval_metric | +|-----------|-------------| +| `"regression"` | `"r2"` | +| `"regression"` | `"mae"` | +| `"regression"` | `"rmse"` | + +## Link Prediction + +| task_type | eval_metric | +|-----------|-------------| +| `"link_prediction"` | `"link_prediction_precision@k"` | +| `"link_prediction"` | `"link_prediction_recall@k"` | +| `"link_prediction"` | `"link_prediction_map@k"` | + +## Repeated Link Prediction (temporal) + +| task_type | eval_metric | +|-----------|-------------| +| `"repeated_link_prediction"` | `"link_prediction_precision@k"` | +| `"repeated_link_prediction"` | `"link_prediction_recall@k"` | +| `"repeated_link_prediction"` | `"link_prediction_map@k"` | + +Replace `@k` with the desired top-k value, e.g. `"link_prediction_precision@5"`. + +## Task Type Summary + +| Task Type | has_time_column | Train Relationship template | Test Relationship template | +|-----------|-----------------|---------------------------|--------------------------| +| binary_classification | optional | `f"{Source} has {Any:label}"` | `f"{Source}"` | +| multiclass_classification | optional | `f"{Source} has {Any:label}"` | `f"{Source}"` | +| multilabel_classification | optional | `f"{Source} has {Any:label}"` | `f"{Source}"` | +| regression | optional | `f"{Source} has {Any:value}"` | `f"{Source}"` | +| link_prediction | False | `f"{Source} has {Target}"` | `f"{Source}"` | +| repeated_link_prediction | True | `f"{Source} at {Any:ts} has {Target}"` | `f"{Source} at {Any:ts}"` | From 0693155512d0947bc0424e3f03abf698426d1f5a Mon Sep 17 00:00:00 2001 From: ifountalis Date: Thu, 2 Apr 2026 14:44:59 -0400 Subject: [PATCH 2/5] Add node regression examples to predictive modeling and training skills Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/rai-predictive-modeling/SKILL.md | 20 +++++ .../examples/node_regression_snowflake.py | 84 +++++++++++++++++++ skills/rai-predictive-training/SKILL.md | 22 +++++ .../examples/train_node_regression.py | 47 +++++++++++ 4 files changed, 173 insertions(+) create mode 100644 skills/rai-predictive-modeling/examples/node_regression_snowflake.py create mode 100644 skills/rai-predictive-training/examples/train_node_regression.py diff --git a/skills/rai-predictive-modeling/SKILL.md b/skills/rai-predictive-modeling/SKILL.md index 3c04b26..51ce523 100644 --- a/skills/rai-predictive-modeling/SKILL.md +++ b/skills/rai-predictive-modeling/SKILL.md @@ -172,6 +172,25 @@ Val = Relationship(f"{User} has {Any:target}") Test = Relationship(f"{User}") ``` +### Node Regression (with time) + +```python +Train = Relationship(f"{Article} at {Any:timestamp} has {Any:sales}") +define(Train(Article, train_table_concept.timestamp, train_table_concept.sales)).where( + Article.a_article_id == train_table_concept.article_id +) + +Val = Relationship(f"{Article} at {Any:timestamp} has {Any:sales}") +define(Val(Article, val_table_concept.timestamp, val_table_concept.sales)).where( + Article.a_article_id == val_table_concept.article_id +) + +Test = Relationship(f"{Article} at {Any:timestamp}") +define(Test(Article, test_table_concept.timestamp)).where( + Article.a_article_id == test_table_concept.article_id +) +``` + ### Link Prediction (with time / repeated_link_prediction) ```python @@ -298,6 +317,7 @@ For the full feature type reference including drop patterns, see [references/pro | Pattern | Description | File | |---------|-------------|------| | Node classification | Binary classification with temporal features (User/Event/EventAttendee) | [examples/node_classification_snowflake.py](examples/node_classification_snowflake.py) | +| Node regression | Regression predicting article sales on H&M data (Customer/Article/Transaction) | [examples/node_regression_snowflake.py](examples/node_regression_snowflake.py) | | Link prediction | Repeated link prediction on H&M data (Customer/Article/Transaction) | [examples/link_prediction_snowflake.py](examples/link_prediction_snowflake.py) | --- diff --git a/skills/rai-predictive-modeling/examples/node_regression_snowflake.py b/skills/rai-predictive-modeling/examples/node_regression_snowflake.py new file mode 100644 index 0000000..45bea3b --- /dev/null +++ b/skills/rai-predictive-modeling/examples/node_regression_snowflake.py @@ -0,0 +1,84 @@ +""" +GNN Node Regression — Data Modeling (Phases 1-6) +================================================= +Regression predicting article sales from H&M data in Snowflake. +Demonstrates: concepts, population, task relationships, graph, and features. + +For training and prediction, see `rai-predictive-training`. +""" + +# ── Phase 1: Imports & Model Setup ────────────────────────────────────────── +from relationalai.semantics import Model, select, define, Integer, Any +from relationalai.semantics.reasoners.graph import Graph +from relationalai.semantics.reasoners.predictive import GNN, PropertyTransformer + +model = Model("gnn_node_regression_example") +Concept, Table, Relationship = model.Concept, model.Table, model.Relationship + +# ── Phase 2: Define Concepts ──────────────────────────────────────────────── +# graph concepts +Customer = Concept("Customer", identify_by={"C_customer_id": Integer}) +Article = Concept("Article", identify_by={"A_article_id": Integer}) +Transaction = Concept("Transaction") + +# task table concepts +train_table_concept = Concept("TrainTable") +val_table_concept = Concept("ValidationTable") +test_table_concept = Concept("TestTable") + +# ── Phase 3: Populate Concepts (from Snowflake) ───────────────────────────── +define(Customer.new(Table("HM_MINI.PUBLIC.CUSTOMERS").to_schema())) +define(Article.new(Table("HM_MINI.PUBLIC.ARTICLES").to_schema())) +define(Transaction.new(Table("HM_MINI.PUBLIC.TRANSACTIONS_DEDUP").to_schema())) + +define(train_table_concept.new(Table("HM_MINI.TASK_SALES.TRAIN").to_schema())) +define(val_table_concept.new(Table("HM_MINI.TASK_SALES.VAL").to_schema())) +define(test_table_concept.new(Table("HM_MINI.TASK_SALES.TEST").to_schema())) + +# ── Phase 4: Setup Task Relationships ───────────────────────────────────────── +Train = Relationship(f"{Article} at {Any:timestamp} has {Any:sales}") +define(Train(Article, train_table_concept.timestamp, train_table_concept.sales)).where( + Article.a_article_id == train_table_concept.article_id +) + +Val = Relationship(f"{Article} at {Any:timestamp} has {Any:sales}") +define(Val(Article, val_table_concept.timestamp, val_table_concept.sales)).where( + Article.a_article_id == val_table_concept.article_id +) + +Test = Relationship(f"{Article} at {Any:timestamp}") +define(Test(Article, test_table_concept.timestamp)).where( + Article.a_article_id == test_table_concept.article_id +) + +# ── Phase 5: Build Graph & Edges ──────────────────────────────────────────── +gnn_graph = Graph(model, directed=True, weighted=False, aggregator="sum") +Edge = gnn_graph.Edge + +define(Edge.new(src=Transaction, dst=Customer)).where( + Transaction.t_customer_id == Customer.C_customer_id) +define(Edge.new(src=Transaction, dst=Article)).where( + Transaction.t_article_id == Article.a_article_id) + +# ── Phase 6: Configure PropertyTransformer ────────────────────────────────── +# Customer features +category_customer = [Customer.FN, Customer.ACTIVE, Customer.POSTAL_CODE, + Customer.CLUB_MEMBER_STATUS, Customer.FASHION_NEWS_FREQUENCY] +continuous_customer = [Customer.AGE] + +# Article features +category_article = [Article.PRODUCT_CODE] +text_article = [Article.PROD_NAME] + +# Transaction features +category_transaction = [Transaction.SALES_CHANNEL_ID] +continuous_transaction = [Transaction.PRICE] +datetime_transaction = [Transaction.T_DAT] + +pt = PropertyTransformer( + category=[*category_customer, *category_article, *category_transaction], + continuous=[*continuous_customer, *continuous_transaction], + datetime=[*datetime_transaction], + text=[*text_article], + time_col=[Transaction.T_DAT], +) diff --git a/skills/rai-predictive-training/SKILL.md b/skills/rai-predictive-training/SKILL.md index 390ec1a..5677826 100644 --- a/skills/rai-predictive-training/SKILL.md +++ b/skills/rai-predictive-training/SKILL.md @@ -110,6 +110,27 @@ gnn = GNN( gnn.fit() ``` +### Node Regression Example (temporal) + +```python +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, + pt=pt, + train=Train, + validation=Val, + task_type="regression", + eval_metric="rmse", + has_time_column=True, + device="cuda", + n_epochs=5, + lr=0.005, + head_layers=2, +) +gnn.fit() +``` + ### Link Prediction Example (temporal) ```python @@ -249,6 +270,7 @@ For the full prediction attributes reference, see [references/prediction-attribu | Pattern | Description | File | |---------|-------------|------| | Node classification | Binary classification training + prediction (User/Event) | [examples/train_node_classification.py](examples/train_node_classification.py) | +| Node regression | Regression training + prediction for article sales (Article) | [examples/train_node_regression.py](examples/train_node_regression.py) | | Link prediction | Repeated link prediction training + prediction (Customer/Article) | [examples/train_link_prediction.py](examples/train_link_prediction.py) | --- diff --git a/skills/rai-predictive-training/examples/train_node_regression.py b/skills/rai-predictive-training/examples/train_node_regression.py new file mode 100644 index 0000000..854f659 --- /dev/null +++ b/skills/rai-predictive-training/examples/train_node_regression.py @@ -0,0 +1,47 @@ +""" +GNN Node Regression — Training & Prediction (Phases 7-8) +========================================================= +Regression training predicting article sales. + +Assumes data model from `rai-predictive-modeling`: + - gnn_graph: Graph with edges defined + - pt: PropertyTransformer configured + - Train, Val, Test: Relationship objects + - Article: source concept (head of Relationship template) +""" + +# ── Phase 7: Train GNN ────────────────────────────────────────────────────── +gnn = GNN( + database="MY_DB", schema="MY_SCHEMA", + exp_database="MY_DB", exp_schema="EXPERIMENTS", + graph=gnn_graph, + pt=pt, + train=Train, + validation=Val, + task_type="regression", + eval_metric="rmse", + has_time_column=True, + export_csv=True, + skip_cdc=True, + device="cuda", + n_epochs=5, + train_batch_size=256, + lr=0.005, + head_layers=2, +) +gnn.fit() + +# ── Phase 8: Predict & Inspect ────────────────────────────────────────────── +Article.predictions = gnn.predictions(domain=Test) + +select( + Article.a_article_id, + Article.predictions.predicted_value, +).where(Article.predictions).inspect() + +df = select( + Article.a_article_id, + Article.predictions.predicted_value, +).where(Article.predictions).to_df() + +print(f"Predictions: {len(df)} rows, {len(df.dropna())} after dropping NaNs") From 8c33be2162730302c4b8b0ae50195c059f49ceec Mon Sep 17 00:00:00 2001 From: ifountalis Date: Mon, 6 Apr 2026 21:25:32 -0400 Subject: [PATCH 3/5] Address PR #12 review feedback from cafzal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add predictive routing in rai-problem-discovery post-discovery section - Fix case inconsistency (c_customer_id → C_customer_id) in link prediction example - Add Graph constructor disambiguation in rai-graph-analysis and rai-predictive-modeling - Standardize DB/SCHEMA placeholders across all examples and SKILL.md code blocks - Add file pointers to paired modeling examples in training example headers - Add failure consequences (error types) to pitfall tables across all three skills - Remove non-pitfall row (pt=None) from training pitfalls table - Uncomment required graph/PT rebuild in register_and_load.py Session 2 - Shorten rai-predictive-modeling frontmatter description Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/rai-graph-analysis/SKILL.md | 1 + skills/rai-predictive-management/SKILL.md | 44 +++++++++---------- .../examples/register_and_load.py | 21 ++++----- skills/rai-predictive-modeling/SKILL.md | 21 ++++----- .../examples/link_prediction_snowflake.py | 18 ++++---- .../examples/node_regression_snowflake.py | 12 ++--- skills/rai-predictive-training/SKILL.md | 15 +++---- .../examples/train_link_prediction.py | 12 +++-- .../examples/train_node_classification.py | 8 ++-- .../examples/train_node_regression.py | 12 +++-- skills/rai-problem-discovery/SKILL.md | 7 ++- 11 files changed, 86 insertions(+), 85 deletions(-) diff --git a/skills/rai-graph-analysis/SKILL.md b/skills/rai-graph-analysis/SKILL.md index 5100db8..a30c606 100644 --- a/skills/rai-graph-analysis/SKILL.md +++ b/skills/rai-graph-analysis/SKILL.md @@ -37,6 +37,7 @@ description: Graph algorithm selection and execution on PyRel v1 models. Covers - Ontology design decisions (concept modeling, data mapping) — see `rai-ontology-design` - Optimization formulation (variables, constraints, objectives) — see `rai-prescriptive-problem-formulation` - Business rule authoring (validation, classification, alerting) — see `rai-rules-authoring` +- GNN graph construction for predictive pipelines — see `rai-predictive-modeling` **Overview (process steps):** 1. Identify network structure in the ontology (node concepts, edge relationships, directedness) diff --git a/skills/rai-predictive-management/SKILL.md b/skills/rai-predictive-management/SKILL.md index d671ada..502ff12 100644 --- a/skills/rai-predictive-management/SKILL.md +++ b/skills/rai-predictive-management/SKILL.md @@ -32,7 +32,7 @@ description: Register trained GNN models to Snowflake Model Registry and load pr **Register:** ```python gnn.register_model( - model_database="MY_DB", + model_database="DB", model_schema="MODEL_REGISTRY", model_name="fraud_detector", version_name="v1", @@ -42,10 +42,10 @@ gnn.register_model( **Load by registry key:** ```python gnn = GNN( - database="MY_DB", schema="MY_SCHEMA", - exp_database="MY_DB", exp_schema="EXPERIMENTS", + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", graph=gnn_graph, pt=pt, - model_database="MY_DB", model_schema="MODEL_REGISTRY", + model_database="DB", model_schema="MODEL_REGISTRY", model_name="fraud_detector", version_name="v1", ) gnn.load() @@ -54,8 +54,8 @@ gnn.load() **Load by run ID:** ```python gnn = GNN( - database="MY_DB", schema="MY_SCHEMA", - exp_database="MY_DB", exp_schema="EXPERIMENTS", + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", graph=gnn_graph, pt=pt, model_run_id="01c2d9a0-0711-c54d-000a-1dc707f7a1e6", ) @@ -79,7 +79,7 @@ After `gnn.fit()` completes, register the model to the Snowflake Model Registry: ```python gnn.register_model( - model_database="MY_DB", + model_database="DB", model_schema="MODEL_REGISTRY", model_name="fraud_detector", version_name="v1", @@ -97,11 +97,11 @@ The combination of `(model_database, model_schema, model_name, version_name)` un ```python gnn = GNN( - database="MY_DB", schema="MY_SCHEMA", - exp_database="MY_DB", exp_schema="EXPERIMENTS", + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", graph=gnn_graph, pt=pt, - model_database="MY_DB", + model_database="DB", model_schema="MODEL_REGISTRY", model_name="fraud_detector", version_name="v1", @@ -115,8 +115,8 @@ User.predictions = gnn.predictions(domain=Test) ```python gnn = GNN( - database="MY_DB", schema="MY_SCHEMA", - exp_database="MY_DB", exp_schema="EXPERIMENTS", + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", graph=gnn_graph, pt=pt, model_run_id="01c2d9a0-0711-c54d-000a-1dc707f7a1e6", @@ -138,8 +138,8 @@ A typical multi-session workflow: ```python gnn = GNN( - database="MY_DB", schema="MY_SCHEMA", - exp_database="MY_DB", exp_schema="EXPERIMENTS", + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", graph=gnn_graph, pt=pt, train=Train, validation=Val, task_type="binary_classification", eval_metric="roc_auc", @@ -148,7 +148,7 @@ gnn = GNN( ) gnn.fit() gnn.register_model( - model_database="MY_DB", model_schema="MODEL_REGISTRY", + model_database="DB", model_schema="MODEL_REGISTRY", model_name="fraud_detector", version_name="v1", ) ``` @@ -164,10 +164,10 @@ gnn_graph = Graph(model, directed=True, weighted=False, aggregator="sum") pt = PropertyTransformer(...) gnn = GNN( - database="MY_DB", schema="MY_SCHEMA", - exp_database="MY_DB", exp_schema="EXPERIMENTS", + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", graph=gnn_graph, pt=pt, - model_database="MY_DB", model_schema="MODEL_REGISTRY", + model_database="DB", model_schema="MODEL_REGISTRY", model_name="fraud_detector", version_name="v1", ) gnn.load() @@ -180,10 +180,10 @@ User.predictions = gnn.predictions(domain=Test) | Mistake | Cause | Fix | |---------|-------|-----| -| Calling `register_model()` before `fit()` | Model must be trained first | Always call `gnn.fit()` before `gnn.register_model()` | -| Omitting `graph` or `pt` when loading | Loaded models still need the graph structure | Provide the same `graph` and `pt` used during training | -| Passing `train`, `validation`, or hyperparameters when loading | These are training-only parameters | Omit `train`, `validation`, `task_type`, `eval_metric`, and all hyperparameters | -| Reusing the same `(name, version)` tuple | Registry keys must be unique | Use a new `version_name` for each registration | +| Calling `register_model()` before `fit()` | Model must be trained first — `RuntimeError`, no weights to serialize | Always call `gnn.fit()` before `gnn.register_model()` | +| Omitting `graph` or `pt` when loading | Loaded models still need the graph structure — `RuntimeError` | Provide the same `graph` and `pt` used during training | +| Passing `train`, `validation`, or hyperparameters when loading | These are training-only parameters — ignored or unexpected behavior | Omit `train`, `validation`, `task_type`, `eval_metric`, and all hyperparameters | +| Reusing the same `(name, version)` tuple | Registry keys must be unique — `RegistryError` | Use a new `version_name` for each registration | --- diff --git a/skills/rai-predictive-management/examples/register_and_load.py b/skills/rai-predictive-management/examples/register_and_load.py index 5db1f6c..2dce118 100644 --- a/skills/rai-predictive-management/examples/register_and_load.py +++ b/skills/rai-predictive-management/examples/register_and_load.py @@ -12,8 +12,8 @@ # gnn_graph, pt, Train, Val, Test, User gnn = GNN( - database="MY_DB", schema="MY_SCHEMA", - exp_database="MY_DB", exp_schema="EXPERIMENTS", + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", graph=gnn_graph, pt=pt, train=Train, validation=Val, task_type="binary_classification", eval_metric="roc_auc", @@ -23,7 +23,7 @@ gnn.fit() gnn.register_model( - model_database="MY_DB", + model_database="DB", model_schema="MODEL_REGISTRY", model_name="fraud_detector", version_name="v1", @@ -32,16 +32,17 @@ # ── Session 2: Load and Predict ───────────────────────────────────────────── -# Rebuild graph and PropertyTransformer (same structure as training session) -# gnn_graph = Graph(model, directed=True, weighted=False, aggregator="sum") -# ... define edges ... -# pt = PropertyTransformer(...) +# REQUIRED: Rebuild the same graph and PT structure used during training +gnn_graph = Graph(model, directed=True, weighted=False, aggregator="sum") +Edge = gnn_graph.Edge +# ... define edges (same as training session) ... +pt = PropertyTransformer(...) # same config as training session gnn = GNN( - database="MY_DB", schema="MY_SCHEMA", - exp_database="MY_DB", exp_schema="EXPERIMENTS", + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", graph=gnn_graph, pt=pt, - model_database="MY_DB", + model_database="DB", model_schema="MODEL_REGISTRY", model_name="fraud_detector", version_name="v1", diff --git a/skills/rai-predictive-modeling/SKILL.md b/skills/rai-predictive-modeling/SKILL.md index 51ce523..48d977f 100644 --- a/skills/rai-predictive-modeling/SKILL.md +++ b/skills/rai-predictive-modeling/SKILL.md @@ -1,6 +1,6 @@ --- name: rai-predictive-modeling -description: Build GNN data models with concepts, Snowflake population, task relationships, graph edges, and feature transformation. Use when defining entity types, loading data, configuring graph structure, or setting up PropertyTransformer for a predictive GNN pipeline. +description: Build GNN data models — concepts, Snowflake data loading, task relationships, graph edges, and PropertyTransformer features. --- # Predictive Modeling @@ -20,6 +20,7 @@ description: Build GNN data models with concepts, Snowflake population, task rel **When NOT to use:** - Training a GNN model or generating predictions — see `rai-predictive-training` - Registering or loading saved models — see `rai-predictive-management` +- Running graph algorithms (centrality, community, etc.) — see `rai-graph-analysis` **Overview:** 1. Imports and Model setup @@ -300,15 +301,15 @@ For the full feature type reference including drop patterns, see [references/pro | Mistake | Cause | Fix | |---------|-------|-----| -| Concept name is plural (e.g. "Customers") | Naming convention | Use singular names: `Concept("Customer")` | -| Task table concept has `identify_by` | Task tables don't need primary keys | Use plain `Concept("TrainTable")` with no `identify_by` | -| Snowflake table name not fully qualified | Missing database or schema prefix | Use `"DATABASE.SCHEMA.TABLE"` format | -| Test Relationship includes label/target | Test data should not contain the answer | Omit the "has" clause: `f"{Source}"` or `f"{Source} at {Any:ts}"` | -| Positional args in `define(Train(...))` don't match template | Template and population call must align | Match the order: source, [timestamp], [label/target] | -| Self-referential edge without `.ref()` | Same concept on both sides creates ambiguity | Use `PostRef = Post.ref()` for the destination | -| `time_col` fields not in `datetime` list | Both lists must include the field | Add time columns to both `datetime=[...]` and `time_col=[...]` | -| Task table concept used in edge definition | Only graph concepts participate in edges | Edges connect domain entities, not task tables | -| Missing type import | e.g. using `Date` without importing it | Add missing types to the import line | +| Concept name is plural (e.g. "Customers") | Naming convention — inconsistent concept references | Use singular names: `Concept("Customer")` | +| Task table concept has `identify_by` | Task tables don't need primary keys — causes unexpected join behavior | Use plain `Concept("TrainTable")` with no `identify_by` | +| Snowflake table name not fully qualified | Missing database or schema prefix — `TableNotFoundError` | Use `"DATABASE.SCHEMA.TABLE"` format | +| Test Relationship includes label/target | Test data should not contain the answer — data leakage, meaningless results | Omit the "has" clause: `f"{Source}"` or `f"{Source} at {Any:ts}"` | +| Positional args in `define(Train(...))` don't match template | Template and population call must align — runtime error or silent wrong column binding | Match the order: source, [timestamp], [label/target] | +| Self-referential edge without `.ref()` | Same concept on both sides creates ambiguity — runtime error | Use `PostRef = Post.ref()` for the destination | +| `time_col` fields not in `datetime` list | Both lists must include the field — time column not encoded as temporal feature | Add time columns to both `datetime=[...]` and `time_col=[...]` | +| Task table concept used in edge definition | Only graph concepts participate in edges — invalid graph structure | Edges connect domain entities, not task tables | +| Missing type import | e.g. using `Date` without importing it — `NameError` | Add missing types to the import line | --- diff --git a/skills/rai-predictive-modeling/examples/link_prediction_snowflake.py b/skills/rai-predictive-modeling/examples/link_prediction_snowflake.py index ca705c7..c159409 100644 --- a/skills/rai-predictive-modeling/examples/link_prediction_snowflake.py +++ b/skills/rai-predictive-modeling/examples/link_prediction_snowflake.py @@ -27,30 +27,30 @@ test_table_concept = Concept("TestTable") # ── Phase 3: Populate Concepts (from Snowflake) ───────────────────────────── -define(Customer.new(Table("HM_MINI.PUBLIC.CUSTOMERS").to_schema())) -define(Article.new(Table("HM_MINI.PUBLIC.ARTICLES").to_schema())) -define(Transaction.new(Table("HM_MINI.PUBLIC.TRANSACTIONS_DEDUP").to_schema())) +define(Customer.new(Table("DB.SCHEMA.CUSTOMERS").to_schema())) +define(Article.new(Table("DB.SCHEMA.ARTICLES").to_schema())) +define(Transaction.new(Table("DB.SCHEMA.TRANSACTIONS_DEDUP").to_schema())) -define(train_table_concept.new(Table("HM_MINI.PUBLIC.TRAIN_LINK").to_schema())) -define(val_table_concept.new(Table("HM_MINI.PUBLIC.VALIDATION_LINK").to_schema())) -define(test_table_concept.new(Table("HM_MINI.PUBLIC.TEST_LINK").to_schema())) +define(train_table_concept.new(Table("DB.SCHEMA.TRAIN_LINK").to_schema())) +define(val_table_concept.new(Table("DB.SCHEMA.VALIDATION_LINK").to_schema())) +define(test_table_concept.new(Table("DB.SCHEMA.TEST_LINK").to_schema())) # ── Phase 4: Setup Task Relationships ───────────────────────────────────────── Train = Relationship(f"{Customer} at {Any:timestamp} has {Article}") define(Train(Customer, train_table_concept.timestamp, Article)).where( - Customer.c_customer_id == train_table_concept.customer_id, + Customer.C_customer_id == train_table_concept.customer_id, Article.a_article_id == train_table_concept.article_id, ) Val = Relationship(f"{Customer} at {Any:timestamp} has {Article}") define(Val(Customer, val_table_concept.timestamp, Article)).where( - Customer.c_customer_id == val_table_concept.customer_id, + Customer.C_customer_id == val_table_concept.customer_id, Article.a_article_id == val_table_concept.article_id, ) Test = Relationship(f"{Customer} at {Any:timestamp}") define(Test(Customer, test_table_concept.timestamp)).where( - Customer.c_customer_id == test_table_concept.customer_id, + Customer.C_customer_id == test_table_concept.customer_id, ) # ── Phase 5: Build Graph & Edges ──────────────────────────────────────────── diff --git a/skills/rai-predictive-modeling/examples/node_regression_snowflake.py b/skills/rai-predictive-modeling/examples/node_regression_snowflake.py index 45bea3b..854267a 100644 --- a/skills/rai-predictive-modeling/examples/node_regression_snowflake.py +++ b/skills/rai-predictive-modeling/examples/node_regression_snowflake.py @@ -27,13 +27,13 @@ test_table_concept = Concept("TestTable") # ── Phase 3: Populate Concepts (from Snowflake) ───────────────────────────── -define(Customer.new(Table("HM_MINI.PUBLIC.CUSTOMERS").to_schema())) -define(Article.new(Table("HM_MINI.PUBLIC.ARTICLES").to_schema())) -define(Transaction.new(Table("HM_MINI.PUBLIC.TRANSACTIONS_DEDUP").to_schema())) +define(Customer.new(Table("DB.SCHEMA.CUSTOMERS").to_schema())) +define(Article.new(Table("DB.SCHEMA.ARTICLES").to_schema())) +define(Transaction.new(Table("DB.SCHEMA.TRANSACTIONS_DEDUP").to_schema())) -define(train_table_concept.new(Table("HM_MINI.TASK_SALES.TRAIN").to_schema())) -define(val_table_concept.new(Table("HM_MINI.TASK_SALES.VAL").to_schema())) -define(test_table_concept.new(Table("HM_MINI.TASK_SALES.TEST").to_schema())) +define(train_table_concept.new(Table("DB.SCHEMA.TRAIN").to_schema())) +define(val_table_concept.new(Table("DB.SCHEMA.VAL").to_schema())) +define(test_table_concept.new(Table("DB.SCHEMA.TEST").to_schema())) # ── Phase 4: Setup Task Relationships ───────────────────────────────────────── Train = Relationship(f"{Article} at {Any:timestamp} has {Any:sales}") diff --git a/skills/rai-predictive-training/SKILL.md b/skills/rai-predictive-training/SKILL.md index 5677826..2189dd0 100644 --- a/skills/rai-predictive-training/SKILL.md +++ b/skills/rai-predictive-training/SKILL.md @@ -254,14 +254,13 @@ For the full prediction attributes reference, see [references/prediction-attribu | Mistake | Cause | Fix | |---------|-------|-----| -| Passing `select(...)` fragments to `train=` or `validation=` | GNN expects Relationship objects | Use `train=Train` with the Relationship object directly | -| Missing `has_time_column=True` | Relationships use "at" keyword but flag not set | Set `has_time_column=True` when templates contain "at" | -| Treating hyperparameters as named GNN parameters | Hyperparameters are `**kwargs` | Pass them directly: `GNN(..., device="cuda", n_epochs=5)` | -| Expecting `gnn.predictions()` to return results | Predictions are assigned to a concept attribute | Use `Source.predictions = gnn.predictions(domain=Test)` | -| Using `.predicted_Article` (uppercase) | Attribute name is always lowercase | Use `.predicted_article` regardless of concept casing | -| Passing wrong object to `domain=` | Must be the Test Relationship | Use `domain=Test` with the Relationship object | -| Omitting `pt=None` when no PropertyTransformer | Not a real issue | `pt` defaults to `None`; omitting it is fine | -| Invalid task_type/metric combination | Not all metrics work with all task types | Check the valid pairs in [references/task-types-and-metrics.md](references/task-types-and-metrics.md) | +| Passing `select(...)` fragments to `train=` or `validation=` | GNN expects Relationship objects — `TypeError` | Use `train=Train` with the Relationship object directly | +| Missing `has_time_column=True` | Relationships use "at" keyword but flag not set — temporal ordering ignored, degraded accuracy | Set `has_time_column=True` when templates contain "at" | +| Treating hyperparameters as named GNN parameters | Hyperparameters are `**kwargs` — `TypeError` | Pass them directly: `GNN(..., device="cuda", n_epochs=5)` | +| Expecting `gnn.predictions()` to return results | Predictions are assigned to a concept attribute — returns `None` | Use `Source.predictions = gnn.predictions(domain=Test)` | +| Using `.predicted_Article` (uppercase) | Attribute name is always lowercase — `AttributeError` | Use `.predicted_article` regardless of concept casing | +| Passing wrong object to `domain=` | Must be the Test Relationship — `TypeError` or wrong prediction scope | Use `domain=Test` with the Relationship object | +| Invalid task_type/metric combination | Not all metrics work with all task types — `ValueError` | Check the valid pairs in [references/task-types-and-metrics.md](references/task-types-and-metrics.md) | --- diff --git a/skills/rai-predictive-training/examples/train_link_prediction.py b/skills/rai-predictive-training/examples/train_link_prediction.py index 6732342..79393ee 100644 --- a/skills/rai-predictive-training/examples/train_link_prediction.py +++ b/skills/rai-predictive-training/examples/train_link_prediction.py @@ -3,17 +3,15 @@ ========================================================= Repeated link prediction training and prediction on H&M data. -Assumes data model from `rai-predictive-modeling`: - - gnn_graph: Graph with edges defined - - pt: PropertyTransformer configured - - Train, Val, Test: Relationship objects - - Customer: source concept, Article: target concept +Assumes data model from `rai-predictive-modeling`. +See: examples/link_prediction_snowflake.py for the full data model. +Required variables: gnn_graph, pt, Train, Val, Test, Customer, Article """ # ── Phase 7: Train GNN ────────────────────────────────────────────────────── gnn = GNN( - database="MY_DB", schema="MY_SCHEMA", - exp_database="MY_DB", exp_schema="EXPERIMENTS", + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", graph=gnn_graph, pt=pt, train=Train, diff --git a/skills/rai-predictive-training/examples/train_node_classification.py b/skills/rai-predictive-training/examples/train_node_classification.py index a7d89f6..3cfe116 100644 --- a/skills/rai-predictive-training/examples/train_node_classification.py +++ b/skills/rai-predictive-training/examples/train_node_classification.py @@ -3,11 +3,9 @@ ============================================================= Binary classification training and prediction on user data. -Assumes data model from `rai-predictive-modeling`: - - gnn_graph: Graph with edges defined - - pt: PropertyTransformer configured - - Train, Val, Test: Relationship objects - - User: source concept (head of Relationship template) +Assumes data model from `rai-predictive-modeling`. +See: examples/node_classification_snowflake.py for the full data model. +Required variables: gnn_graph, pt, Train, Val, Test, User """ # ── Phase 7: Train GNN ────────────────────────────────────────────────────── diff --git a/skills/rai-predictive-training/examples/train_node_regression.py b/skills/rai-predictive-training/examples/train_node_regression.py index 854f659..8da843d 100644 --- a/skills/rai-predictive-training/examples/train_node_regression.py +++ b/skills/rai-predictive-training/examples/train_node_regression.py @@ -3,17 +3,15 @@ ========================================================= Regression training predicting article sales. -Assumes data model from `rai-predictive-modeling`: - - gnn_graph: Graph with edges defined - - pt: PropertyTransformer configured - - Train, Val, Test: Relationship objects - - Article: source concept (head of Relationship template) +Assumes data model from `rai-predictive-modeling`. +See: examples/node_regression_snowflake.py for the full data model. +Required variables: gnn_graph, pt, Train, Val, Test, Article """ # ── Phase 7: Train GNN ────────────────────────────────────────────────────── gnn = GNN( - database="MY_DB", schema="MY_SCHEMA", - exp_database="MY_DB", exp_schema="EXPERIMENTS", + database="DB", schema="SCHEMA", + exp_database="DB", exp_schema="EXPERIMENTS", graph=gnn_graph, pt=pt, train=Train, diff --git a/skills/rai-problem-discovery/SKILL.md b/skills/rai-problem-discovery/SKILL.md index 70bc8fa..add4457 100644 --- a/skills/rai-problem-discovery/SKILL.md +++ b/skills/rai-problem-discovery/SKILL.md @@ -362,7 +362,12 @@ Each suggestion includes a `reasoners` field — an ordered list specifying the } ``` -After discovery, use the appropriate formulation skill for the chosen reasoner type. +After discovery, route to the appropriate skill based on the `reasoners` tag: + +- **prescriptive** → `rai-prescriptive-problem-formulation` +- **graph** → `rai-graph-analysis` +- **rules** → `rai-rules-authoring` +- **predictive** → `rai-predictive-modeling` --- From cc21d18639e474baf51ae8809c2e0fe5ff391eaf Mon Sep 17 00:00:00 2001 From: ifountalis Date: Tue, 7 Apr 2026 09:14:15 -0400 Subject: [PATCH 4/5] Fix link prediction issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add guidance that agents must not choose between link_prediction and repeated_link_prediction — present both options and ask the user. Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/rai-predictive-modeling/SKILL.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/skills/rai-predictive-modeling/SKILL.md b/skills/rai-predictive-modeling/SKILL.md index 48d977f..f3dd6b9 100644 --- a/skills/rai-predictive-modeling/SKILL.md +++ b/skills/rai-predictive-modeling/SKILL.md @@ -192,6 +192,9 @@ define(Test(Article, test_table_concept.timestamp)).where( ) ``` +> **Link prediction has two task types — do not choose for the user.** +> `link_prediction` (static, no timestamps) and `repeated_link_prediction` (temporal, with timestamps) serve different data shapes. The correct choice depends on whether the user's data has temporal ordering. Present both options and ask the user which applies to their data. + ### Link Prediction (with time / repeated_link_prediction) ```python From 957b938f4fe7f971933b51cf2432a808194dd070 Mon Sep 17 00:00:00 2001 From: ifountalis Date: Thu, 9 Apr 2026 11:26:51 -0400 Subject: [PATCH 5/5] Remove export_csv and skip_cdc options from predictive training skill export_csv now always defaults to True internally so users don't need to set it. CDC-related options (skip_cdc) are removed entirely. Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/rai-predictive-training/SKILL.md | 4 ---- .../rai-predictive-training/examples/train_link_prediction.py | 2 -- .../examples/train_node_classification.py | 2 -- .../rai-predictive-training/examples/train_node_regression.py | 2 -- skills/rai-predictive-training/references/hyperparameters.md | 2 -- 5 files changed, 12 deletions(-) diff --git a/skills/rai-predictive-training/SKILL.md b/skills/rai-predictive-training/SKILL.md index 2189dd0..1b8213b 100644 --- a/skills/rai-predictive-training/SKILL.md +++ b/skills/rai-predictive-training/SKILL.md @@ -87,8 +87,6 @@ The `GNN` constructor takes data locations, graph structure, task configuration, |-----------|---------|-------------| | `pt` | None | PropertyTransformer (omit for auto-inference) | | `has_time_column` | False | Set `True` when Relationships use the "at" keyword | -| `export_csv` | True | Export tables as CSV instead of Snowflake staging | -| `skip_cdc` | True | Skip CDC for faster data loading | | `stream_logs` | True | Stream training logs to console | ### Node Classification Example @@ -144,8 +142,6 @@ gnn = GNN( task_type="repeated_link_prediction", eval_metric="link_prediction_precision@5", has_time_column=True, - export_csv=True, - skip_cdc=True, device="cuda", n_epochs=5, lr=0.005, diff --git a/skills/rai-predictive-training/examples/train_link_prediction.py b/skills/rai-predictive-training/examples/train_link_prediction.py index 79393ee..84ec184 100644 --- a/skills/rai-predictive-training/examples/train_link_prediction.py +++ b/skills/rai-predictive-training/examples/train_link_prediction.py @@ -19,8 +19,6 @@ task_type="repeated_link_prediction", eval_metric="link_prediction_precision@5", has_time_column=True, - export_csv=True, - skip_cdc=True, device="cuda", n_epochs=5, train_batch_size=256, diff --git a/skills/rai-predictive-training/examples/train_node_classification.py b/skills/rai-predictive-training/examples/train_node_classification.py index 3cfe116..ead6acc 100644 --- a/skills/rai-predictive-training/examples/train_node_classification.py +++ b/skills/rai-predictive-training/examples/train_node_classification.py @@ -19,8 +19,6 @@ task_type="binary_classification", eval_metric="roc_auc", has_time_column=True, - export_csv=True, - skip_cdc=True, device="cuda", n_epochs=5, ) diff --git a/skills/rai-predictive-training/examples/train_node_regression.py b/skills/rai-predictive-training/examples/train_node_regression.py index 8da843d..14352cf 100644 --- a/skills/rai-predictive-training/examples/train_node_regression.py +++ b/skills/rai-predictive-training/examples/train_node_regression.py @@ -19,8 +19,6 @@ task_type="regression", eval_metric="rmse", has_time_column=True, - export_csv=True, - skip_cdc=True, device="cuda", n_epochs=5, train_batch_size=256, diff --git a/skills/rai-predictive-training/references/hyperparameters.md b/skills/rai-predictive-training/references/hyperparameters.md index 3f5dae6..13da305 100644 --- a/skills/rai-predictive-training/references/hyperparameters.md +++ b/skills/rai-predictive-training/references/hyperparameters.md @@ -36,9 +36,7 @@ These are named parameters on `GNN(...)`, not train_params: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `test_batch_size` | int | None | Batch size for prediction/inference | -| `export_csv` | bool | True | Export tables as CSV instead of Snowflake staging | | `stream_logs` | bool | True | Stream training logs to console | -| `skip_cdc` | bool | True | Skip CDC for faster data loading | | `extract_embeddings` | bool | False | Extract node embeddings during prediction | | `use_current_time` | bool | True | Use current time for temporal models |