From 5b1c996d46156a300fb7f6d45f46024b0cf14d41 Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Mon, 9 Feb 2026 16:11:28 +0100 Subject: [PATCH 01/16] feat: raven dataset loaders; MLP backbone tailored to raven's problem --- rsseval/rss/backbones/raven_encoder.py | 65 +++++++++ rsseval/rss/datasets/raven.py | 86 +++++++++++ rsseval/rss/datasets/utils/raven_creation.py | 146 +++++++++++++++++++ 3 files changed, 297 insertions(+) create mode 100644 rsseval/rss/backbones/raven_encoder.py create mode 100644 rsseval/rss/datasets/raven.py create mode 100644 rsseval/rss/datasets/utils/raven_creation.py diff --git a/rsseval/rss/backbones/raven_encoder.py b/rsseval/rss/backbones/raven_encoder.py new file mode 100644 index 00000000..17308d22 --- /dev/null +++ b/rsseval/rss/backbones/raven_encoder.py @@ -0,0 +1,65 @@ +import torch.nn as nn + +class RavenPanelEncoder(nn.Module): + """ + Encoder for a single RAVEN panel (160x160). + """ + def __init__(self, latent_dim=31): + super(RavenPanelEncoder, self).__init__() + self.latent_dim = latent_dim + self.backbone = nn.Sequential( + nn.Flatten(), + nn.Linear(160 * 160, 512), + nn.ReLU(), + nn.Linear(512, 128), + nn.ReLU(), + nn.Linear(128, self.latent_dim), + ) + + def forward(self, x): + return self.backbone(x) + + +class RavenMLP(nn.Module): + """ + Simple MLP for RAVEN 160x160 grayscale images. + Input: [batch, 16, 1, 160, 160] (processed one by one) + Output: [batch, 16, latent_dim] + """ + NAME = "RavenMLP" + + def __init__(self, latent_dim=30): + super(RavenMLP, self).__init__() + + # 31 dimensions: + # Type (5) + Size (6) + Color (10) + Number (9) = 30 + self.latent_dim = latent_dim + + # Use the independent panel encoder + self.panel_encoder = RavenPanelEncoder(latent_dim=self.latent_dim) + + def forward(self, x): + # x shape: [batch, 16, 1, 160, 160] + + # Flatten batch and n_images for efficient parallel processing + b, n, c, h, w = x.shape + # Reshape to [B*N, C*H*W] compatible with Flatten in panel_encoder + # treat every single panel as an independent sample to pass it through the encoder + x_flat = x.reshape(b * n, -1) + + # Process all panels in one go (Efficient View) + z = self.panel_encoder(x_flat) # [b*16, latent_dim] + + # Reshape back to [batch, 16, latent_dim] + z = z.view(b, n, -1) + + # Return tuple (output, None) to match interface expected by DPL models + return z, None + +if __name__ == "__main__": + import torch + encoder = RavenMLP() + x = torch.randn(1, 16, 1, 160, 160) + z, _ = encoder(x) + print(f"z shape: {z.shape}") + print(z) \ No newline at end of file diff --git a/rsseval/rss/datasets/raven.py b/rsseval/rss/datasets/raven.py new file mode 100644 index 00000000..b194d7f1 --- /dev/null +++ b/rsseval/rss/datasets/raven.py @@ -0,0 +1,86 @@ + +from datasets.utils.base_dataset import BaseDataset +from datasets.utils.raven_creation import RAVEN_Dataset +from backbones.raven_encoder import RavenMLP +import time +from torch.utils.data import DataLoader + +class RAVEN(BaseDataset): + NAME = "raven" + + def get_data_loaders(self): + start = time.time() + + # We can pass specific args for configuration if needed via self.args + # For now defaults to center_single + + config = getattr(self.args, 'raven_config', "center_single") + + self.dataset_train = RAVEN_Dataset( + base_path="data/RAVEN-10000", + config=config, + split="train", + ) + + self.dataset_val = RAVEN_Dataset( + base_path="data/RAVEN-10000", + config=config, + split="val", + ) + + self.dataset_test = RAVEN_Dataset( + base_path="data/RAVEN-10000", + config=config, + split="test", + ) + + print(f"Loaded datasets in {time.time()-start:.2f} s.") + self.print_stats() + + # Create DataLoaders + # Note: BaseDataset usually expects get_data_loaders to return loaders, not just set datasets + + train_loader = DataLoader(self.dataset_train, batch_size=self.args.batch_size, shuffle=True, num_workers=4) + val_loader = DataLoader(self.dataset_val, batch_size=self.args.batch_size, shuffle=False, num_workers=4) + test_loader = DataLoader(self.dataset_test, batch_size=self.args.batch_size, shuffle=False, num_workers=4) + + return train_loader, val_loader, test_loader + + def get_backbone(self, args=None): + # Return the encoder compatible with 160x160 input + # We return (encoder, decoder). Decoder is None for now. + return RavenMLP(), None + + def get_split(self): + # 16 images per sample + return 16, () + + def get_concept_labels(self): + # Return labels for visualization/confusion matrices + # We have 4 concepts: Type, Size, Color, Number + return ["Type", "Size", "Color", "Number"], [ + ["Triangle", "Square", "Pentagon", "Hexagon", "Circle"], # Type (0-4) + ["0", "1", "2", "3", "4", "5"], # Size (0-5) + [str(i) for i in range(10)], # Color (0-9) + [str(i) for i in range(9)] # Number (0-8) + ] + + def get_labels(self): + # 8 choices (0-7) + return [str(i) for i in range(8)] + + def print_stats(self): + print("## Statistics ##") + print("Train samples", len(self.dataset_train)) + print("Validation samples", len(self.dataset_val)) + print("Test samples", len(self.dataset_test)) + +if __name__ == "__main__": + from argparse import Namespace + dataset = RAVEN( + args=Namespace(batch_size=32) + ) + train_loader, val_loader, test_loader = dataset.get_data_loaders() + print(f"Training number of batches: {len(train_loader)}") + print(f"Validation number of batches: {len(val_loader)}") + print(f"Test number of batches: {len(test_loader)}") \ No newline at end of file diff --git a/rsseval/rss/datasets/utils/raven_creation.py b/rsseval/rss/datasets/utils/raven_creation.py new file mode 100644 index 00000000..f95a331f --- /dev/null +++ b/rsseval/rss/datasets/utils/raven_creation.py @@ -0,0 +1,146 @@ + +import os +import glob +import numpy as np +import torch +import xml.etree.ElementTree as ET +from torch.utils.data import Dataset +from torchvision import transforms + +class RAVEN_Dataset(Dataset): + """ + RAVEN Dataset for RSBench. + Loads RAVEN-10000 .npz files and filters them based on the 'Phase 1' curriculum: + Phase 1 = Samples where exactly one attribute varies according to a non-Constant rule, + and all other attributes are Constant. + """ + def __init__(self, base_path, config="center_single", split="train"): + self.base_path = base_path + self.config = config + self.split = split + + # Search pattern for files + pattern = os.path.join(self.base_path, self.config, f"RAVEN_*_{self.split}.npz") + self.all_files = sorted(glob.glob(pattern)) + + if len(self.all_files) == 0: + print(f"Warning: No files found for {pattern}") + self.files = [] + else: + self.files = self.all_files + + def __len__(self): + return len(self.files) + + def __getitem__(self, idx): + file_path = self.files[idx] + data = np.load(file_path) + + # Images: [16, 160, 160] + # Normalize to [0, 1] and add channel dimension: [16, 1, 160, 160] + images = data['image'].astype(np.float32) / 255.0 + images = torch.from_numpy(images).unsqueeze(1) + + # Target: 0-7 + target = torch.tensor(data['target'], dtype=torch.long) + + # Concepts: Extract attributes for all 16 panels (8 context + 8 choices) + # We need to parse XML again or trust meta_matrix? + # meta_matrix does NOT contain entity values (like Type=Triangle), only Rule activity. + # So we must parse XML to get ground truth concepts for supervision. + + concepts = self._extract_concepts_from_xml(file_path.replace('.npz', '.xml')) + + return images, target, concepts + + def _extract_concepts_from_xml(self, xml_path): + """ + Extract concept values for all 16 panels. + Returns tensor of shape [16, 4] -> (Type, Size, Color, Number) + Values are indices. + """ + tree = ET.parse(xml_path) + root = tree.getroot() + + panels_data = [] + all_panels = root.findall('.//Panel') + # We expect 16 panels (8 context + 8 choices) + # Note: XML might have more structure, but usually flattened order matches NPZ image stack. + + # Mappings based on RAVEN/src/dataset/const.py + # Type: 0:none, 1:triangle, 2:square, 3:pentagon, 4:hexagon, 5:circle + # Size: 0..5 + # Color: 0..9 + # Number: 0..8 (1-9) in const.py, but usually 0-indexed in XML attribute? + + for panel in all_panels[:16]: + entity = panel.find('.//Entity') + layout = panel.find('.//Layout') + + # Defaults + p_c = [0, 0, 0, 0] # Type, Size, Color, Number + + if entity is not None: + try: p_c[0] = int(entity.get('Type', 0)) - 1 # Shift 1-5 to 0-4 + except: pass + try: p_c[1] = int(entity.get('Size', 0)) + except: pass + try: p_c[2] = int(entity.get('Color', 0)) + except: pass + + if layout is not None: + try: p_c[3] = int(layout.get('Number', 0)) + except: pass + + panels_data.append(p_c) + + return torch.tensor(panels_data, dtype=torch.long) + + + +if __name__ == "__main__": + # Initialize dataset without filtering to check a broad range of files + dataset = RAVEN_Dataset( + base_path="data/RAVEN-10000", + config="center_single", + split="train", + ) + + + print(f"Analyzing first 1000 files out of {len(dataset.all_files)} for attribute value ranges...") + + attr_counts = { + 'Type': set(), + 'Size': set(), + 'Color': set(), + 'Number': set() + } + + # Check first 1000 files to get a good sample + for i, f in enumerate(dataset.all_files[:1000]): + xml_path = f.replace('.npz', '.xml') + try: + tree = ET.parse(xml_path) + root = tree.getroot() + + for panel in root.findall('.//Panel'): + entity = panel.find('.//Entity') + layout = panel.find('.//Layout') + + if entity is not None: + if 'Type' in entity.attrib: attr_counts['Type'].add(int(entity.get('Type'))) + if 'Size' in entity.attrib: attr_counts['Size'].add(int(entity.get('Size'))) + if 'Color' in entity.attrib: attr_counts['Color'].add(int(entity.get('Color'))) + + if layout is not None: + if 'Number' in layout.attrib: attr_counts['Number'].add(int(layout.get('Number'))) + + except Exception as e: + print(f"Error parsing {xml_path}: {e}") + + print("\nValue Ranges Found in XML:") + for attr, values in attr_counts.items(): + sorted_vals = sorted(list(values)) + min_val = min(sorted_vals) if sorted_vals else 'N/A' + max_val = max(sorted_vals) if sorted_vals else 'N/A' + print(f"{attr}: {sorted_vals} (Min: {min_val}, Max: {max_val})") \ No newline at end of file From 66fbcf41ead8a4a72647ac4b25dcdfb025225ecf Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Sat, 21 Feb 2026 12:00:05 +0100 Subject: [PATCH 02/16] feat: concept matching loss for raven mlp model --- rsseval/rss/utils/losses.py | 49 ++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/rsseval/rss/utils/losses.py b/rsseval/rss/utils/losses.py index 9bd6e9ef..35aeaa06 100644 --- a/rsseval/rss/utils/losses.py +++ b/rsseval/rss/utils/losses.py @@ -837,4 +837,51 @@ def MNMATH_Cumulative(out_dict: dict, args): mitigation += args.w_c * loss3 losses.update(losses3) - return loss + args.gamma * mitigation, losses \ No newline at end of file + return loss + args.gamma * mitigation, losses + + +def RAVEN_Concept_Match(out_dict: dict): + """RAVEN concept supervision loss on the 8 context panels only. + + Applies per-attribute CrossEntropyLoss on the raw encoder logits (CS) + against ground-truth concept labels (CONCEPTS). Only the 8 context panels + (indices 0-7) are supervised; the 8 candidate panels are left unsupervised + so the model learns to score them through symbolic reasoning, not direct + concept supervision. + + Attribute layout in z: Type(3) | Size(3) | Color(3) = 9 total + + Args: + out_dict: output dictionary containing: + "CS" [B, 16, n_facts]: raw encoder logits + "CONCEPTS" [B, 16, n_attrs]: ground-truth concept indices + + Returns: + loss: scalar loss value (averaged over attributes) + losses: dictionary with "c-loss" entry + """ + # Only supervise context panels (0-7), not the 8 candidate choices + z = out_dict["CS"][:, :8] # [B, 8, n_facts] + targets = out_dict["CONCEPTS"][:, :8].to(torch.long) # [B, 8, n_attrs] + + loss_f = torch.nn.CrossEntropyLoss() + + # Type: logits [0:3], target column 0 + z_type = z[..., 0:3] + l_type = loss_f(z_type.reshape(-1, 3), targets[..., 0].reshape(-1)) + + # Size: logits [3:6], target column 1 + z_size = z[..., 3:6] + l_size = loss_f(z_size.reshape(-1, 3), targets[..., 1].reshape(-1)) + + # Color: logits [6:9], target column 2 + z_color = z[..., 6:9] + l_color = loss_f(z_color.reshape(-1, 3), targets[..., 2].reshape(-1)) + + n_attrs = 3 + loss = l_type + l_size + l_color + + # Average over attributes + loss /= n_attrs + + return loss, {"c-loss": loss.item()} \ No newline at end of file From e106d671951722d3ae58e10eb0c3cee3eb4a8baf Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Wed, 15 Apr 2026 17:37:16 +0200 Subject: [PATCH 03/16] simiplify the problem to a 3x3x3 dimension and test if the encoder is learning --- rsseval/rss/backbones/raven_encoder.py | 150 +++++++++++++- rsseval/rss/datasets/raven.py | 17 +- rsseval/rss/datasets/utils/raven_creation.py | 202 ++++++++++++++----- rsseval/rss/utils/losses.py | 41 +++- 4 files changed, 336 insertions(+), 74 deletions(-) diff --git a/rsseval/rss/backbones/raven_encoder.py b/rsseval/rss/backbones/raven_encoder.py index 17308d22..8fca0240 100644 --- a/rsseval/rss/backbones/raven_encoder.py +++ b/rsseval/rss/backbones/raven_encoder.py @@ -1,10 +1,12 @@ +import torch import torch.nn as nn + class RavenPanelEncoder(nn.Module): """ Encoder for a single RAVEN panel (160x160). """ - def __init__(self, latent_dim=31): + def __init__(self, latent_dim=9): super(RavenPanelEncoder, self).__init__() self.latent_dim = latent_dim self.backbone = nn.Sequential( @@ -25,14 +27,16 @@ class RavenMLP(nn.Module): Simple MLP for RAVEN 160x160 grayscale images. Input: [batch, 16, 1, 160, 160] (processed one by one) Output: [batch, 16, latent_dim] + + 3x3x3 attribute layout: + Type(3) | Size(3) | Color(3) = 9 total """ NAME = "RavenMLP" - def __init__(self, latent_dim=30): + def __init__(self, latent_dim=9): super(RavenMLP, self).__init__() - # 31 dimensions: - # Type (5) + Size (6) + Color (10) + Number (9) = 30 + # 9 dimensions: Type(3) + Size(3) + Color(3) self.latent_dim = latent_dim # Use the independent panel encoder @@ -57,9 +61,135 @@ def forward(self, x): return z, None if __name__ == "__main__": - import torch - encoder = RavenMLP() - x = torch.randn(1, 16, 1, 160, 160) - z, _ = encoder(x) - print(f"z shape: {z.shape}") - print(z) \ No newline at end of file + """ + Standalone training loop to verify that RavenMLP can learn to predict + concept attributes (Type, Size, Color) from 160x160 panel images. + + This is a *concept-only* sanity check — no answer-classification head. + We train with CrossEntropyLoss per attribute on the 8 context panels + and report per-attribute accuracy on the validation set. + """ + import sys, os + # Allow imports from the rss root when running as a script + sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + + from torch.utils.data import DataLoader + from datasets.utils.raven_creation import RAVEN_Dataset + from utils.losses import RAVEN_Concept_Match + + # ── Hyperparameters ───────────────────────────────────────────── + BATCH_SIZE = 64 + LR = 1e-3 + EPOCHS = 20 + DEVICE = "cuda" if torch.cuda.is_available() else "cpu" + DATA_PATH = "data/RAVEN-3x3x3" + + ATTR_NAMES = ["Type", "Size", "Color"] + ATTR_SLICES = [(0, 3), (3, 6), (6, 9)] # logit slices in 9-dim output + + # ── Data ──────────────────────────────────────────────────────── + train_ds = RAVEN_Dataset(base_path=DATA_PATH, config="center_single", split="train") + val_ds = RAVEN_Dataset(base_path=DATA_PATH, config="center_single", split="val") + + train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True, num_workers=4, pin_memory=True) + val_loader = DataLoader(val_ds, batch_size=BATCH_SIZE, shuffle=False, num_workers=4, pin_memory=True) + + print(f"Train: {len(train_ds)} samples | Val: {len(val_ds)} samples") + print(f"Device: {DEVICE}") + + # ── Model & Optimizer ─────────────────────────────────────────── + model = RavenMLP(latent_dim=9).to(DEVICE) + optimizer = torch.optim.Adam(model.parameters(), lr=LR) + scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=EPOCHS) + + n_params = sum(p.numel() for p in model.parameters() if p.requires_grad) + print(f"Model parameters: {n_params:,}") + + # ── Evaluation helper ─────────────────────────────────────────── + @torch.no_grad() + def evaluate(loader): + model.eval() + total_loss = 0.0 + correct = {name: 0 for name in ATTR_NAMES} + total = 0 + + for images, target, concepts in loader: + images = images.to(DEVICE) + concepts = concepts.to(DEVICE) + + z, _ = model(images) + + out_dict = {"CS": z, "CONCEPTS": concepts} + loss, _ = RAVEN_Concept_Match(out_dict) + total_loss += loss.item() * images.size(0) + + # Per-attribute accuracy on context panels (0-7) + z_ctx = z[:, :8] # [B, 8, 9] + c_ctx = concepts[:, :8] # [B, 8, 4] + + for i, (name, (lo, hi)) in enumerate(zip(ATTR_NAMES, ATTR_SLICES)): + preds = z_ctx[..., lo:hi].argmax(dim=-1) # [B, 8] + correct[name] += (preds == c_ctx[..., i]).sum().item() + + total += z_ctx.shape[0] * z_ctx.shape[1] # B * 8 + + avg_loss = total_loss / len(loader.dataset) + accs = {name: correct[name] / total * 100 for name in ATTR_NAMES} + return avg_loss, accs + + # ── Training loop ─────────────────────────────────────────────── + print(f"\n{'Epoch':>5} | {'Train Loss':>10} | {'Val Loss':>8} | " + + " | ".join(f"{n:>7}" for n in ATTR_NAMES) + + " | Avg Acc") + print("-" * 75) + + best_avg_acc = 0.0 + best_val_loss = float("inf") + patience = 3 + min_delta = 0.01 + wait = 0 + + for epoch in range(1, EPOCHS + 1): + model.train() + epoch_loss = 0.0 + + for images, target, concepts in train_loader: + images = images.to(DEVICE) + concepts = concepts.to(DEVICE) + + z, _ = model(images) + + out_dict = {"CS": z, "CONCEPTS": concepts} + loss, _ = RAVEN_Concept_Match(out_dict) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + epoch_loss += loss.item() * images.size(0) + + scheduler.step() + train_loss = epoch_loss / len(train_ds) + + # Validation + val_loss, val_accs = evaluate(val_loader) + avg_acc = sum(val_accs.values()) / len(val_accs) + + print(f"{epoch:5d} | {train_loss:10.4f} | {val_loss:8.4f} | " + + " | ".join(f"{val_accs[n]:6.2f}%" for n in ATTR_NAMES) + + f" | {avg_acc:5.2f}%") + + if avg_acc > best_avg_acc: + best_avg_acc = avg_acc + + # Early stopping on validation loss + if val_loss < best_val_loss - min_delta: + best_val_loss = val_loss + wait = 0 + else: + wait += 1 + if wait >= patience: + print(f"\nEarly stopping at epoch {epoch} (no val loss improvement for {patience} epochs)") + break + + print(f"\nBest average validation accuracy: {best_avg_acc:.2f}%") \ No newline at end of file diff --git a/rsseval/rss/datasets/raven.py b/rsseval/rss/datasets/raven.py index b194d7f1..f4da1a20 100644 --- a/rsseval/rss/datasets/raven.py +++ b/rsseval/rss/datasets/raven.py @@ -17,19 +17,19 @@ def get_data_loaders(self): config = getattr(self.args, 'raven_config', "center_single") self.dataset_train = RAVEN_Dataset( - base_path="data/RAVEN-10000", + base_path="data/RAVEN-3x3x3", config=config, split="train", ) self.dataset_val = RAVEN_Dataset( - base_path="data/RAVEN-10000", + base_path="data/RAVEN-3x3x3", config=config, split="val", ) self.dataset_test = RAVEN_Dataset( - base_path="data/RAVEN-10000", + base_path="data/RAVEN-3x3x3", config=config, split="test", ) @@ -56,13 +56,12 @@ def get_split(self): return 16, () def get_concept_labels(self): - # Return labels for visualization/confusion matrices - # We have 4 concepts: Type, Size, Color, Number + # 3x3x3 constrained dataset: 3 Types, 3 Sizes, 3 Colors return ["Type", "Size", "Color", "Number"], [ - ["Triangle", "Square", "Pentagon", "Hexagon", "Circle"], # Type (0-4) - ["0", "1", "2", "3", "4", "5"], # Size (0-5) - [str(i) for i in range(10)], # Color (0-9) - [str(i) for i in range(9)] # Number (0-8) + ["Triangle", "Pentagon", "Circle"], # Type (0-2) + ["Small", "Medium", "Large"], # Size (0-2) + ["White", "Gray", "Black"], # Color (0-2) + [str(i) for i in range(9)] # Number (0-8) ] def get_labels(self): diff --git a/rsseval/rss/datasets/utils/raven_creation.py b/rsseval/rss/datasets/utils/raven_creation.py index f95a331f..b22f0c27 100644 --- a/rsseval/rss/datasets/utils/raven_creation.py +++ b/rsseval/rss/datasets/utils/raven_creation.py @@ -5,14 +5,12 @@ import torch import xml.etree.ElementTree as ET from torch.utils.data import Dataset -from torchvision import transforms class RAVEN_Dataset(Dataset): """ RAVEN Dataset for RSBench. - Loads RAVEN-10000 .npz files and filters them based on the 'Phase 1' curriculum: - Phase 1 = Samples where exactly one attribute varies according to a non-Constant rule, - and all other attributes are Constant. + Loads RAVEN-3x3x3 .npz files (3 Types x 3 Sizes x 3 Colors). + All concept values are already clean 0-2 per attribute — no filtering needed. """ def __init__(self, base_path, config="center_single", split="train"): self.base_path = base_path @@ -45,7 +43,6 @@ def __getitem__(self, idx): target = torch.tensor(data['target'], dtype=torch.long) # Concepts: Extract attributes for all 16 panels (8 context + 8 choices) - # We need to parse XML again or trust meta_matrix? # meta_matrix does NOT contain entity values (like Type=Triangle), only Rule activity. # So we must parse XML to get ground truth concepts for supervision. @@ -57,21 +54,18 @@ def _extract_concepts_from_xml(self, xml_path): """ Extract concept values for all 16 panels. Returns tensor of shape [16, 4] -> (Type, Size, Color, Number) - Values are indices. + Values are indices 0-2 for Type/Size/Color (3x3x3 dataset). """ tree = ET.parse(xml_path) root = tree.getroot() panels_data = [] all_panels = root.findall('.//Panel') - # We expect 16 panels (8 context + 8 choices) - # Note: XML might have more structure, but usually flattened order matches NPZ image stack. - # Mappings based on RAVEN/src/dataset/const.py - # Type: 0:none, 1:triangle, 2:square, 3:pentagon, 4:hexagon, 5:circle - # Size: 0..5 - # Color: 0..9 - # Number: 0..8 (1-9) in const.py, but usually 0-indexed in XML attribute? + # 3x3x3 constrained dataset: + # Type: 1=triangle, 2=pentagon, 3=circle (in XML) -> 0-2 after -1 shift + # Size: 0=small(0.4), 1=medium(0.6), 2=large(0.9) + # Color: 0=white(255), 1=gray(140), 2=black(0) for panel in all_panels[:16]: entity = panel.find('.//Entity') @@ -81,7 +75,7 @@ def _extract_concepts_from_xml(self, xml_path): p_c = [0, 0, 0, 0] # Type, Size, Color, Number if entity is not None: - try: p_c[0] = int(entity.get('Type', 0)) - 1 # Shift 1-5 to 0-4 + try: p_c[0] = int(entity.get('Type', 0)) - 1 # Shift 1-3 to 0-2 except: pass try: p_c[1] = int(entity.get('Size', 0)) except: pass @@ -99,48 +93,148 @@ def _extract_concepts_from_xml(self, xml_path): if __name__ == "__main__": - # Initialize dataset without filtering to check a broad range of files + import matplotlib.pyplot as plt + import matplotlib.patches as mpatches + from collections import Counter + + TYPE_LABELS = ["Triangle", "Pentagon", "Circle"] + SIZE_LABELS = ["Small", "Medium", "Large"] + COLOR_LABELS = ["White", "Gray", "Black"] + dataset = RAVEN_Dataset( - base_path="data/RAVEN-10000", + base_path="data/RAVEN-3x3x3", config="center_single", split="train", ) + print(f"Dataset size: {len(dataset)} samples") - - print(f"Analyzing first 1000 files out of {len(dataset.all_files)} for attribute value ranges...") - - attr_counts = { - 'Type': set(), - 'Size': set(), - 'Color': set(), - 'Number': set() - } - - # Check first 1000 files to get a good sample - for i, f in enumerate(dataset.all_files[:1000]): - xml_path = f.replace('.npz', '.xml') - try: - tree = ET.parse(xml_path) - root = tree.getroot() - - for panel in root.findall('.//Panel'): - entity = panel.find('.//Entity') - layout = panel.find('.//Layout') - - if entity is not None: - if 'Type' in entity.attrib: attr_counts['Type'].add(int(entity.get('Type'))) - if 'Size' in entity.attrib: attr_counts['Size'].add(int(entity.get('Size'))) - if 'Color' in entity.attrib: attr_counts['Color'].add(int(entity.get('Color'))) - - if layout is not None: - if 'Number' in layout.attrib: attr_counts['Number'].add(int(layout.get('Number'))) - - except Exception as e: - print(f"Error parsing {xml_path}: {e}") - - print("\nValue Ranges Found in XML:") - for attr, values in attr_counts.items(): - sorted_vals = sorted(list(values)) - min_val = min(sorted_vals) if sorted_vals else 'N/A' - max_val = max(sorted_vals) if sorted_vals else 'N/A' - print(f"{attr}: {sorted_vals} (Min: {min_val}, Max: {max_val})") \ No newline at end of file + def extract_rules(xml_path): + """Parse elements from XML and return dict {attr: rule_name}.""" + tree = ET.parse(xml_path) + root = tree.getroot() + rules = {} + for rule_el in root.findall('.//Rule'): + attr = rule_el.get('attr', '') + name = rule_el.get('name', '') + rules[attr] = name + return rules + + # ── Fig 1: Three sample puzzles ────────────────────────────────── + n_samples = 3 + fig, axes = plt.subplots(n_samples, 5, figsize=(16, 3.5 * n_samples + 1), + gridspec_kw={"width_ratios": [3, 0.15, 2, 0.15, 2]}) + fig.suptitle("RAVEN-3×3×3 — Sample Puzzles", fontsize=15, fontweight="bold", y=0.98) + + for row in range(n_samples): + images, target, concepts = dataset[row] + imgs = images[:, 0].numpy() # [16, 160, 160] + target_idx = target.item() + c = concepts.numpy() # [16, 4] + + # Extract rules from XML + xml_path = dataset.files[row].replace('.npz', '.xml') + rules = extract_rules(xml_path) + + # --- Left: 3×3 context matrix (panel 9 = "?") --- + grid = np.ones((160 * 3 + 4, 160 * 3 + 4)) * 0.85 + for i in range(9): + r, col = divmod(i, 3) + y0 = r * (160 + 2) + x0 = col * (160 + 2) + if i < 8: + grid[y0:y0 + 160, x0:x0 + 160] = imgs[i] + else: + patch = np.ones((160, 160)) * 0.6 + # draw a "?" + grid[y0:y0 + 160, x0:x0 + 160] = patch + ax = axes[row, 0] + ax.imshow(grid, cmap="gray", vmin=0, vmax=1) + # Build rule annotation string + rule_parts = [] + for attr in ["Type", "Size", "Color"]: + rname = rules.get(attr, "?") + rule_parts.append(f"{attr}: {rname}") + rule_str = " | ".join(rule_parts) + ax.set_title(f"Sample {row}\n{rule_str}", fontsize=10, fontweight="bold") + ax.axis("off") + + # spacer + axes[row, 1].axis("off") + axes[row, 3].axis("off") + + # --- Middle: answer choices 0-3 --- + ans_grid = np.ones((160 * 2 + 2, 160 * 2 + 2)) * 0.85 + for j in range(4): + r, col = divmod(j, 2) + y0 = r * (160 + 2) + x0 = col * (160 + 2) + ans_grid[y0:y0 + 160, x0:x0 + 160] = imgs[8 + j] + ax = axes[row, 2] + ax.imshow(ans_grid, cmap="gray", vmin=0, vmax=1) + # highlight correct answer with a green rectangle + if target_idx < 4: + r, col = divmod(target_idx, 2) + rect = plt.Rectangle((col * 162 - 1, r * 162 - 1), 162, 162, + linewidth=3, edgecolor="limegreen", facecolor="none") + ax.add_patch(rect) + ax.set_title("Choices 0-3", fontsize=10) + ax.axis("off") + + # --- Right: answer choices 4-7 --- + ans_grid2 = np.ones((160 * 2 + 2, 160 * 2 + 2)) * 0.85 + for j in range(4): + r, col = divmod(j, 2) + y0 = r * (160 + 2) + x0 = col * (160 + 2) + ans_grid2[y0:y0 + 160, x0:x0 + 160] = imgs[12 + j] + ax = axes[row, 4] + ax.imshow(ans_grid2, cmap="gray", vmin=0, vmax=1) + if target_idx >= 4: + r, col = divmod(target_idx - 4, 2) + rect = plt.Rectangle((col * 162 - 1, r * 162 - 1), 162, 162, + linewidth=3, edgecolor="limegreen", facecolor="none") + ax.add_patch(rect) + ax.set_title("Choices 4-7", fontsize=10) + ax.axis("off") + + plt.tight_layout(rect=[0, 0, 1, 0.95]) + plt.savefig("raven_3x3x3_samples.png", dpi=150, bbox_inches="tight") + print("Saved raven_3x3x3_samples.png") + plt.show() + + # ── Fig 2: Attribute distributions ─────────────────────────────── + n_check = min(500, len(dataset)) + type_counts = Counter() + size_counts = Counter() + color_counts = Counter() + + for idx in range(n_check): + _, _, concepts = dataset[idx] + # Use context panels 0-7 + correct candidate (panel 8+target) + type_counts.update(concepts[:8, 0].tolist()) + size_counts.update(concepts[:8, 1].tolist()) + color_counts.update(concepts[:8, 2].tolist()) + + fig, axes = plt.subplots(1, 3, figsize=(12, 4)) + fig.suptitle(f"Attribute Distributions (first {n_check} samples, context panels)", + fontsize=13, fontweight="bold") + + bar_colors = ["#4C72B0", "#55A868", "#C44E52"] + + for ax, counts, labels, name in [ + (axes[0], type_counts, TYPE_LABELS, "Type"), + (axes[1], size_counts, SIZE_LABELS, "Size"), + (axes[2], color_counts, COLOR_LABELS, "Color"), + ]: + vals = [counts.get(i, 0) for i in range(3)] + bars = ax.bar(labels, vals, color=bar_colors, edgecolor="black", linewidth=0.5) + ax.set_title(name, fontsize=12, fontweight="bold") + ax.set_ylabel("Count") + for bar, v in zip(bars, vals): + ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + max(vals) * 0.01, + str(v), ha="center", va="bottom", fontsize=9) + + plt.tight_layout() + plt.savefig("raven_3x3x3_distributions_test.png", dpi=150, bbox_inches="tight") + print("Saved raven_3x3x3_distributions_test.png") + plt.show() \ No newline at end of file diff --git a/rsseval/rss/utils/losses.py b/rsseval/rss/utils/losses.py index 35aeaa06..72d41439 100644 --- a/rsseval/rss/utils/losses.py +++ b/rsseval/rss/utils/losses.py @@ -884,4 +884,43 @@ def RAVEN_Concept_Match(out_dict: dict): # Average over attributes loss /= n_attrs - return loss, {"c-loss": loss.item()} \ No newline at end of file + return loss, {"c-loss": loss.item()} + + +def RAVEN_Classification(out_dict: dict, args): + """RAVEN classification loss. + + YS is a softmaxed [B, 8] probability distribution over the 8 candidates. + We apply NLL loss on log(YS), clamping before log to avoid -inf. + + Args: + out_dict: output dictionary containing "YS" [B, 8] and "LABELS" [B] + args: command line arguments + + Returns: + loss: scalar loss value + losses: dictionary with "y-loss" entry + """ + out = out_dict["YS"] # [B, 8] softmaxed choice probabilities + labels = out_dict["LABELS"].to(torch.long) # [B] ground-truth choice index (0-7) + + # Clamp before log to prevent -inf (same numerical safety as normalize_concepts) + loss = F.nll_loss(out.clamp(min=1e-5).log(), labels, reduction="mean") + + assert loss > 0, f"{loss}, {out}, {labels}" + + losses = {"y-loss": loss.item()} + return loss, losses + + +def RAVEN_Cumulative(out_dict: dict, args): + """RAVEN cumulative loss: task classification + optional concept supervision.""" + loss, losses = RAVEN_Classification(out_dict, args) + + mitigation = 0 + if args.c_sup > 0: + loss_c, losses_c = RAVEN_Concept_Match(out_dict) + mitigation += args.w_c * loss_c + losses.update(losses_c) + + return loss + args.gamma * mitigation, losses \ No newline at end of file From 8e26d164f3d8fb9cabcd1e4dc85f85b9541d485f Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Mon, 11 May 2026 16:27:23 +0200 Subject: [PATCH 04/16] feat(raven-dataset): add partial concept supervision (c_sup/which_c) --- rsseval/rss/datasets/raven.py | 5 +-- rsseval/rss/datasets/utils/raven_creation.py | 43 ++++++++++++++++++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/rsseval/rss/datasets/raven.py b/rsseval/rss/datasets/raven.py index f4da1a20..6029c2a0 100644 --- a/rsseval/rss/datasets/raven.py +++ b/rsseval/rss/datasets/raven.py @@ -20,6 +20,8 @@ def get_data_loaders(self): base_path="data/RAVEN-3x3x3", config=config, split="train", + c_sup=self.args.c_sup, + which_c=self.args.which_c, ) self.dataset_val = RAVEN_Dataset( @@ -36,9 +38,6 @@ def get_data_loaders(self): print(f"Loaded datasets in {time.time()-start:.2f} s.") self.print_stats() - - # Create DataLoaders - # Note: BaseDataset usually expects get_data_loaders to return loaders, not just set datasets train_loader = DataLoader(self.dataset_train, batch_size=self.args.batch_size, shuffle=True, num_workers=4) val_loader = DataLoader(self.dataset_val, batch_size=self.args.batch_size, shuffle=False, num_workers=4) diff --git a/rsseval/rss/datasets/utils/raven_creation.py b/rsseval/rss/datasets/utils/raven_creation.py index b22f0c27..853314a4 100644 --- a/rsseval/rss/datasets/utils/raven_creation.py +++ b/rsseval/rss/datasets/utils/raven_creation.py @@ -10,12 +10,21 @@ class RAVEN_Dataset(Dataset): """ RAVEN Dataset for RSBench. Loads RAVEN-3x3x3 .npz files (3 Types x 3 Sizes x 3 Colors). - All concept values are already clean 0-2 per attribute — no filtering needed. + + Supports partial concept supervision on the training split through: + - c_sup: fraction of training samples with concept supervision + - which_c: attribute indices to supervise (0=Type, 1=Size, 2=Color) + + Unsupervised concept targets are masked with -1, analogously to the + other rsbench datasets. """ - def __init__(self, base_path, config="center_single", split="train"): + def __init__(self, base_path, config="center_single", split="train", c_sup=1, which_c=[-1]): self.base_path = base_path self.config = config self.split = split + self.c_sup = c_sup + self.which_c = which_c + self.is_train = split == "train" # Search pattern for files pattern = os.path.join(self.base_path, self.config, f"RAVEN_*_{self.split}.npz") @@ -27,6 +36,10 @@ def __init__(self, base_path, config="center_single", split="train"): else: self.files = self.all_files + # Deterministic supervision mask, analogous to other rsbench datasets. + rng = np.random.RandomState(0) + self.r_seq = rng.rand(len(self.files)) if len(self.files) > 0 else np.array([]) + def __len__(self): return len(self.files) @@ -47,9 +60,33 @@ def __getitem__(self, idx): # So we must parse XML to get ground truth concepts for supervision. concepts = self._extract_concepts_from_xml(file_path.replace('.npz', '.xml')) - + + if self.is_train: + concepts = self._apply_concept_supervision_mask(concepts, idx) + return images, target, concepts + def _apply_concept_supervision_mask(self, concepts, idx): + """Mask concepts with -1 according to c_sup / which_c. + + We only supervise the first 3 modeled attributes (Type, Size, Color) + on the 8 context panels, mirroring RAVEN_Concept_Match. + """ + concepts = concepts.clone() + + # Sample-level supervision fraction. + if self.r_seq[idx] > self.c_sup: + concepts[:8, :3] = -1 + return concepts + + # Attribute-level supervision subset. + if not (len(self.which_c) == 1 and self.which_c[0] == -1): + for attr_idx in range(3): + if attr_idx not in self.which_c: + concepts[:8, attr_idx] = -1 + + return concepts + def _extract_concepts_from_xml(self, xml_path): """ Extract concept values for all 16 panels. From 0b9aece227b9a6259b6c67c1fdb7fc16f0185d70 Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Mon, 11 May 2026 16:28:43 +0200 Subject: [PATCH 05/16] feat(problog): add and_rule matrix for 3-row rule agreement --- rsseval/rss/models/utils/utils_problog.py | 75 ++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/rsseval/rss/models/utils/utils_problog.py b/rsseval/rss/models/utils/utils_problog.py index b56164ca..09f74e51 100644 --- a/rsseval/rss/models/utils/utils_problog.py +++ b/rsseval/rss/models/utils/utils_problog.py @@ -1022,4 +1022,77 @@ def create_mnist_and(sequence_len=0, n_digits=0, task="mnmath"): w_q[w, 1] = 1 return w_q else: - NotImplementedError("Wrong choice") \ No newline at end of file + NotImplementedError("Wrong choice") + + +def build_worlds_queries_matrix_RAVEN(n_vals, rules=["Constant"]): + """ + Build logic matrices for factorized RAVEN attribute experts. + + Args: + n_vals: number of possible values for the attribute. + For RAVEN-3x3x3 this is 3 for Type/Size/Color. + rules: list of rule names to compile. Supported rules are + "Constant", "Progression", "Arithmetic", and + "Distribute_Three". + + Returns: + w_q: tensor of shape [n_vals^3, n_rules], where each row corresponds + to one world (v1, v2, v3) and each column to a rule. + and_rule: tensor of shape [n_rules^3, 2] encoding explicit agreement + over three row-level rule variables `(r1, r2, r3)`. + Column 1 = valid iff `r1 == r2 == r3`, column 0 = invalid otherwise. + """ + possible_worlds = list(product(range(n_vals), repeat=3)) + n_worlds = len(possible_worlds) + n_rules = len(rules) + + and_rule = torch.zeros(n_rules**3, 2) + possible_rule_triples = list(product(range(n_rules), repeat=3)) + for idx, (r1, r2, r3) in enumerate(possible_rule_triples): + if r1 == r2 and r2 == r3: + and_rule[idx, 1] = 1.0 + else: + and_rule[idx, 0] = 1.0 + + w_q = torch.zeros(n_worlds, n_rules) + look_up = {i: c for i, c in zip(range(n_worlds), possible_worlds)} + + for w in range(n_worlds): + v1, v2, v3 = look_up[w] + + for rule_idx, rule in enumerate(rules): + if rule == "Constant": + if v1 == v2 and v2 == v3: + w_q[w, rule_idx] = 1.0 + + elif rule == "Progression": + # Generic ordered-domain progression. For {0,1,2}, this reduces + # naturally to the only valid sequences: (0,1,2) and (2,1,0). + if (v2 - v1) == 1 and (v3 - v2) == 1: + w_q[w, rule_idx] = 1.0 + elif (v2 - v1) == -1 and (v3 - v2) == -1: + w_q[w, rule_idx] = 1.0 + elif (v2 - v1) == 2 and (v3 - v2) == 2: + w_q[w, rule_idx] = 1.0 + elif (v2 - v1) == -2 and (v3 - v2) == -2: + w_q[w, rule_idx] = 1.0 + + elif rule == "Arithmetic": + # A+B=C or A-B=C + if v1 + v2 == v3: + w_q[w, rule_idx] = 1.0 + elif v1 - v2 == v3: + w_q[w, rule_idx] = 1.0 + + elif rule == "Distribute_Three": + # Sample 3 values and permute them across rows. + # In the 3x3x3 setting, each attribute has exactly 3 possible + # values, so this is equivalent to "all distinct". + if v1 != v2 and v1 != v3 and v2 != v3: + w_q[w, rule_idx] = 1.0 + + else: + raise ValueError(f"Unsupported RAVEN rule: {rule}") + + return w_q, and_rule \ No newline at end of file From 128570e2d3d40530e5529650f9c4deef71e6febc Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Mon, 11 May 2026 16:32:14 +0200 Subject: [PATCH 06/16] feat(ravendpl): add model with log-space probabilistic reasoning --- rsseval/rss/models/__init__.py | 3 +- rsseval/rss/models/ravendpl.py | 484 +++++++++++++++++++++++++++++++++ 2 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 rsseval/rss/models/ravendpl.py diff --git a/rsseval/rss/models/__init__.py b/rsseval/rss/models/__init__.py index 0335c075..9cba9888 100644 --- a/rsseval/rss/models/__init__.py +++ b/rsseval/rss/models/__init__.py @@ -52,7 +52,8 @@ def get_model(args, encoder, decoder, n_images, c_split): "xordpl", "mnmathnn", "mnmathcbm", - "mnmathdpl" + "mnmathdpl", + "ravendpl" ]: return names[args.model]( encoder, n_images=n_images, c_split=c_split, args=args diff --git a/rsseval/rss/models/ravendpl.py b/rsseval/rss/models/ravendpl.py new file mode 100644 index 00000000..95ba314f --- /dev/null +++ b/rsseval/rss/models/ravendpl.py @@ -0,0 +1,484 @@ +# RAVEN for DPL +import torch +import torch.nn as nn +import torch.nn.functional as F +from models.utils.deepproblog_modules import DeepProblogModel +from models.utils.utils_problog import build_worlds_queries_matrix_RAVEN +from models.utils.ops import outer_product +from utils.args import * +from utils.conf import get_device +from utils.dpl_loss import RAVEN_DPL +from utils.losses import RAVEN_Cumulative + + +def get_parser() -> ArgumentParser: + """Returns the parser + + Returns: + argparse: argument parser + """ + parser = ArgumentParser(description="Learning via" "Concept Extractor .") + add_management_args(parser) + add_experiment_args(parser) + return parser + + +class RavenDPL(DeepProblogModel): + """RAVEN DeepProbLog model with factorized logic per attribute.""" + + NAME = "ravendpl" + + def __init__( + self, + encoder, + n_images=16, + c_split=(), + model_dict=None, + n_facts=9, + nr_classes=8, + args=None, + ): + """Initialize method + + Args: + self: instance + encoder (nn.Module): encoder + n_images (int, default=16): number of images (8 context + 8 choices) + c_split: concept splits + model_dict (default=None): model dictionary + n_facts (int, default=9): number of concepts (3+3+3 for RAVEN-3x3x3) + nr_classes (int, default=8): number of choice candidates + args: command line arguments + + Returns: + None: This function does not return a value. + """ + super(RavenDPL, self).__init__( + encoder=encoder, + model_dict=model_dict, + n_facts=n_facts, + nr_classes=nr_classes, + ) + + # how many images and explicit split of concepts + self.n_images = n_images + self.c_split = c_split + + # Concept dimensions per attribute + self.raven_config = args.raven_config if args else "center_single" + if self.raven_config == "center_single": + self.dims = {"Type": 3, "Size": 3, "Color": 3} + self.n_facts = 9 # 3+3+3 + + self.nr_classes = nr_classes # 8 candidate choices + self.n_and_classes = 2 # invalid / valid for explicit row-rule agreement + + # Per-attribute rules. + # In RAVEN-3x3x3 we also support Distribute_Three. For a 3-value domain, + # this means the row contains all three distinct values in some order. + # Arithmetic does not apply to Type. + self.attr_rules = {} + for attr in self.dims: + if attr == "Type": + self.attr_rules[attr] = ["Constant", "Progression", "Distribute_Three"] + else: + self.attr_rules[attr] = [ + "Constant", + "Progression", + "Arithmetic", + "Distribute_Three", + ] + + # Worlds-queries matrices (one per attribute, with attribute-specific rules) + # plus attribute-specific explicit row-agreement tables over rule pairs. + self.wq_matrices = {} + self.and_rules = {} + self.device = get_device() + for attr, dim in self.dims.items(): + rules = self.attr_rules[attr] + mat, and_rule = build_worlds_queries_matrix_RAVEN(dim, rules) + self.wq_matrices[attr] = mat.float().to(self.device) + self.and_rules[attr] = and_rule.float().to(self.device) + + + def forward(self, x): + """Forward method + + Args: + self: instance + x (torch.tensor): input vector [batch, 16, 1, 160, 160] + + Returns: + out_dict: output dictionary + """ + # 1. Encoding + z, _ = self.encoder(x) # [B, 16, n_facts] + + # 2. Concept Extraction (softmax + clamp per attribute) + concepts_probs = self.normalize_concepts(z) + + # Concatenate all attribute probs for supervision + extracted_probs = [concepts_probs[attr] for attr in self.dims] + pCS = torch.cat(extracted_probs, dim=-1) # [B, 16, n_facts] + + # 3. Factorized Symbolic Reasoning + # Compute row-level rule distributions for the two observed context rows. + row_rule_dists = {} + for attr, probs in concepts_probs.items(): + wq = self.wq_matrices[attr] + + # Row 1: panels [0, 1, 2] -> rule distribution [B, n_rules] + p_rule_r1 = self.problog_inference(probs[:, [0, 1, 2], :], wq) + # Row 2: panels [3, 4, 5] -> rule distribution [B, n_rules] + p_rule_r2 = self.problog_inference(probs[:, [3, 4, 5], :], wq) + + row_rule_dists[attr] = {"r1": p_rule_r1, "r2": p_rule_r2} + + # Score the 8 candidate choices via explicit 3-row agreement. + ys = self.score_choices(concepts_probs, row_rule_dists) + + # Prepare output dictionary + out_dict = {"YS": ys, "CS": z, "pCS": pCS} + + # Add row-level rule distributions for debugging/analysis. + for attr, row_dists in row_rule_dists.items(): + out_dict[f"{attr}_R1_PREDS"] = row_dists["r1"] + out_dict[f"{attr}_R2_PREDS"] = row_dists["r2"] + + return out_dict + + def normalize_concepts(self, z): + """Computes the probability for each ProbLog fact given the latent vector z, + applying softmax with epsilon clamping per attribute group. + + Args: + self: instance + z (torch.tensor): encoder output [B, 16, n_facts] + + Returns: + concepts_probs: dict mapping attribute name -> [B, 16, dim] probability tensor + """ + + def soft_clamp(h, dim=-1): + h = nn.Softmax(dim=dim)(h) + eps = 1e-5 + h = h + eps + with torch.no_grad(): + Z = torch.sum(h, dim=dim, keepdim=True) + h = h / Z + return h + + # Split z into per-attribute logits and apply softmax + clamp + concepts_probs = {} + offset = 0 + for attr, dim in self.dims.items(): + concepts_probs[attr] = soft_clamp(z[..., offset : offset + dim]) + offset += dim + + return concepts_probs + + def problog_inference(self, row_probs, wq): + """Problog inference for a single attribute over a single row of 3 panels. + + Computes the outer product of 3 panel concept distributions to build + the world probability vector, then maps to rule distributions via w_q. + + Args: + self: instance + row_probs (torch.tensor): [B, 3, n_vals] concept probs for 3 panels + wq (torch.tensor): [n_vals^3, n_rules] logic matrix + + Returns: + rule_dist: [B, n_rules] probability of each rule given this row + """ + # Number of rules is determined by the w_q matrix (varies per attribute) + n_rules = wq.shape[1] + + # Outer product of 3 panel distributions -> world probabilities + worlds_tensor = outer_product( + row_probs[:, 0], row_probs[:, 1], row_probs[:, 2] + ) # [B, N, N, N] + + worlds_prob = worlds_tensor.reshape(worlds_tensor.shape[0], -1) # [B, N^3] + + # Compute rule distribution: sum over worlds weighted by w_q + rule_dist = torch.zeros( + size=(worlds_prob.shape[0], n_rules), device=worlds_prob.device + ) + for i in range(n_rules): + rule_dist[:, i] = self.compute_query(i, worlds_prob, wq).view(-1) + + return rule_dist + + def problog_inference_log(self, row_log_probs, wq): + """Log-space ProbLog inference for a single attribute over a single row. + + Computes log P(rule) = logsumexp over worlds where the rule holds, + using log-space throughout to avoid gradient starvation from + near-uniform probability distributions. + + Args: + self: instance + row_log_probs (torch.tensor): [B, 3, n_vals] log concept probs for 3 panels + wq (torch.tensor): [n_vals^3, n_rules] binary logic matrix + + Returns: + log_rule_dist: [B, n_rules] log-probability of each rule given this row + """ + n_rules = wq.shape[1] + n_worlds = wq.shape[0] + batch_size = row_log_probs.shape[0] + + # Sum log-probs across 3 panels for each world (AND in log-space) + # row_log_probs: [B, 3, n_vals] + # worlds_log_prob[b, i*j*k] = row_log_probs[b,0,i] + row_log_probs[b,1,j] + row_log_probs[b,2,k] + log_p0 = row_log_probs[:, 0, :] # [B, n_vals] + log_p1 = row_log_probs[:, 1, :] # [B, n_vals] + log_p2 = row_log_probs[:, 2, :] # [B, n_vals] + + # Compute all world log-probs via broadcast + # [B, n_vals, 1, 1] + [B, 1, n_vals, 1] + [B, 1, 1, n_vals] -> [B, n_vals, n_vals, n_vals] + worlds_log = log_p0.unsqueeze(2).unsqueeze(3) + log_p1.unsqueeze(1).unsqueeze(3) + log_p2.unsqueeze(1).unsqueeze(2) + worlds_log = worlds_log.reshape(batch_size, n_worlds) # [B, n_vals^3] + + # Compute log rule distribution: logsumexp over worlds where rule holds + log_rule_dist = torch.full( + (batch_size, n_rules), -1e10, device=worlds_log.device + ) + for i in range(n_rules): + # Mask: which worlds have this rule + mask = wq[:, i].bool() # [n_worlds] + if mask.any(): + # logsumexp over the masked worlds + masked_log = worlds_log[:, mask] # [B, n_valid_worlds] + log_rule_dist[:, i] = torch.logsumexp(masked_log, dim=1) + + return log_rule_dist + + def compute_query(self, query, worlds_prob, wq): + """Computes query probability given the worlds probability P(w). + + Args: + self: instance + query (int): query index (rule index) + worlds_prob (torch.tensor): [B, N^3] world probabilities + wq (torch.tensor): [N^3, n_rules] logic matrix + + Returns: + query_prob: [B, 1] probability of the query + """ + # Select the column of w_q matrix corresponding to the current query + w_q = wq[:, query] + # Compute query probability by summing the probability of all worlds where the query is true + query_prob = torch.sum(w_q * worlds_prob, dim=1, keepdim=True) + return query_prob + + def score_choices(self, concepts_probs, row_rule_dists): + """Score the 8 candidate choices in log-space via explicit 3-row rule agreement. + + For each candidate and attribute, the model computes the log rule + distributions of row 1, row 2, and the candidate-completed row 3, + builds a joint world over ``(r1, r2, r3)``, and uses an attribute-specific + ``and_rule`` to compute the log-probability of agreement (r1==r2==r3). + + Log-space scoring avoids gradient starvation: when concept probabilities + are near-uniform, probability-space scores for all candidates are nearly + identical (differing by ~3e-5), producing vanishing gradients. In log + space, the same differences are amplified ~9x per attribute, keeping + the gradient signal alive through the reasoning layer. + + This mirrors how KandDPL (the rsbench Kand-Logic model) computes its + output: raw probabilities from the reasoning layer, with log applied in + the loss function. No intermediate softmax is applied. + + Args: + self: instance + concepts_probs: dict mapping attribute name -> [B, 16, dim] probs + row_rule_dists: dict mapping attribute name -> + {"r1": [B, n_rules], "r2": [B, n_rules]} (probability-space, kept for analysis) + + Returns: + ys: [B, 8] log-softmaxed choice log-probabilities + """ + # Compute log concept probabilities + concepts_log_probs = { + attr: probs.clamp(min=1e-8).log() + for attr, probs in concepts_probs.items() + } + + # Pre-compute log rule distributions for context rows (in log-space) + row_log_rule_dists = {} + for attr, log_probs in concepts_log_probs.items(): + wq = self.wq_matrices[attr] + log_rule_r1 = self.problog_inference_log( + log_probs[:, [0, 1, 2], :], wq + ) + log_rule_r2 = self.problog_inference_log( + log_probs[:, [3, 4, 5], :], wq + ) + row_log_rule_dists[attr] = {"r1": log_rule_r1, "r2": log_rule_r2} + + # Score each candidate + choice_log_scores = [] + + for c_idx in range(8, 16): + attr_log_scores = [] + + for attr, log_probs in concepts_log_probs.items(): + wq = self.wq_matrices[attr] + log_rule_r1 = row_log_rule_dists[attr]["r1"] + log_rule_r2 = row_log_rule_dists[attr]["r2"] + + # Candidate-completed Row 3 in log-space + log_rule_r3 = self.problog_inference_log( + log_probs[:, [6, 7, c_idx], :], wq + ) # [B, n_rules] + + # Compute log agreement probability + log_agreement = self.compute_three_row_rule_agreement_log( + log_rule_r1, log_rule_r2, log_rule_r3, attr + ) # [B] + + attr_log_scores.append(log_agreement) + + # Combine attribute log-scores by summing (AND across independent attributes) + total_log_score = torch.stack(attr_log_scores, dim=1).sum(dim=1) # [B] + choice_log_scores.append(total_log_score) + + log_scores = torch.stack(choice_log_scores, dim=1) # [B, 8] + + # Return log-softmax (no intermediate exp/softmax that kills gradients) + ys = F.log_softmax(log_scores, dim=-1) + + return ys + + def compute_three_row_rule_agreement_log(self, log_rule_r1, log_rule_r2, log_rule_r3, attr): + """Compute log-probability of agreement (r1==r2==r3) in log-space. + + For each valid rule triplet (r, r, r), computes: + log P(r1=r, r2=r, r3=r) = log_rule_r1[r] + log_rule_r2[r] + log_rule_r3[r] + Then marginalizes over all valid triplets via logsumexp. + + Args: + self: instance + log_rule_r1 (torch.tensor): [B, n_rules] log rule distribution for row 1 + log_rule_r2 (torch.tensor): [B, n_rules] log rule distribution for row 2 + log_rule_r3 (torch.tensor): [B, n_rules] log rule distribution for row 3 + attr (str): attribute name + + Returns: + log_agreement (torch.tensor): [B] log-probability of valid agreement + """ + and_rule = self.and_rules[attr] # [n_rules^3, 2] + # Valid triplets: where and_rule[:, 1] == 1 + valid_mask = and_rule[:, 1].bool() # [n_rules^3] + + if not valid_mask.any(): + return torch.full( + (log_rule_r1.shape[0],), -1e10, device=log_rule_r1.device + ) + + n_rules = log_rule_r1.shape[1] + + # Build log-probability for each rule triplet world + # log P(r1, r2, r3) = log_rule_r1[r1] + log_rule_r2[r2] + log_rule_r3[r3] + # Using broadcast: [B, n_rules, 1, 1] + [B, 1, n_rules, 1] + [B, 1, 1, n_rules] + log_triplet = ( + log_rule_r1.unsqueeze(2).unsqueeze(3) + + log_rule_r2.unsqueeze(1).unsqueeze(3) + + log_rule_r3.unsqueeze(1).unsqueeze(2) + ) # [B, n_rules, n_rules, n_rules] + log_triplet = log_triplet.reshape(log_rule_r1.shape[0], n_rules**3) # [B, n_rules^3] + + # Select only valid triplets and logsumexp + log_valid_triplets = log_triplet[:, valid_mask] # [B, n_valid] + log_agreement = torch.logsumexp(log_valid_triplets, dim=1) # [B] + + return log_agreement + + def compute_three_row_rule_agreement(self, p_rule_r1, p_rule_r2, p_rule_r3, attr): + """Compute explicit agreement over the latent rules of rows 1, 2, and 3. + + Kept for backward compatibility and analysis (confusion matrices, etc.). + The log-space version ``compute_three_row_rule_agreement_log`` is used + for training. + + Args: + self: instance + p_rule_r1 (torch.tensor): [B, n_rules] rule distribution for row 1 + p_rule_r2 (torch.tensor): [B, n_rules] rule distribution for row 2 + p_rule_r3 (torch.tensor): [B, n_rules] rule distribution for the + candidate-completed row 3 + attr (str): attribute name (`Type`, `Size`, or `Color`) + + Returns: + agreement_probs (torch.tensor): [B, 2] where column 0 is invalid + (`r1, r2, r3` not all equal) and column 1 is valid + (`r1 == r2 == r3`) + """ + n_rules = p_rule_r1.shape[1] + assert p_rule_r2.shape[1] == n_rules and p_rule_r3.shape[1] == n_rules, ( + p_rule_r1.shape, + p_rule_r2.shape, + p_rule_r3.shape, + ) + + rule_triplet_worlds = outer_product( + p_rule_r1, p_rule_r2, p_rule_r3 + ).reshape(-1, n_rules**3) + + agreement_probs = torch.zeros( + size=(p_rule_r1.shape[0], self.n_and_classes), device=p_rule_r1.device + ) + + and_rule = self.and_rules[attr] + for i in range(self.n_and_classes): + agreement_probs[:, i] = torch.sum( + and_rule[:, i] * rule_triplet_worlds, dim=1 + ) + + return agreement_probs + + + + @staticmethod + def get_loss(args): + """Returns the loss function + + Args: + args: command line arguments + + Returns: + loss: loss function + + Raises: + err: NotImplementedError if dataset is not specified + """ + if args.dataset == "raven": + return RAVEN_DPL(RAVEN_Cumulative) + else: + return NotImplementedError("Wrong dataset choice") + + def start_optim(self, args): + """Starts the optimizer + + Args: + self: instance + args: command line arguments + + Returns: + None: This function does not return a value. + """ + self.opt = torch.optim.Adam( + self.parameters(), + args.lr, + weight_decay=1e-5, + ) + + # override of to + def to(self, device): + super().to(device) + for attr in self.dims: + self.wq_matrices[attr] = self.wq_matrices[attr].to(device) + self.and_rules[attr] = self.and_rules[attr].to(device) + return self From b0a9288c04d0a057a2247b3a0fabb12dea059fa9 Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Mon, 11 May 2026 16:35:25 +0200 Subject: [PATCH 07/16] feat(losses): add RAVEN entropy loss, fix classification loss for log-space output --- rsseval/rss/utils/dpl_loss.py | 33 +++++++++++++ rsseval/rss/utils/losses.py | 93 ++++++++++++++++++++++++++--------- 2 files changed, 102 insertions(+), 24 deletions(-) diff --git a/rsseval/rss/utils/dpl_loss.py b/rsseval/rss/utils/dpl_loss.py index 058bcf40..ab7ab453 100644 --- a/rsseval/rss/utils/dpl_loss.py +++ b/rsseval/rss/utils/dpl_loss.py @@ -172,6 +172,39 @@ def __init__(self, loss, nr_classes=2) -> None: self.base_loss = loss self.nr_classes = nr_classes + def forward(self, out_dict, args): + """Forward method + + Args: + self: instance + out_dict: output dictionary + args: command line arguments + + Returns: + loss: loss value + losses: losses dictionary + """ + loss, losses = self.base_loss(out_dict, args) + return loss, losses + + +class RAVEN_DPL(torch.nn.Module): + """RAVEN DPL loss""" + def __init__(self, loss, nr_classes=8) -> None: + """Initialize method + + Args: + self: instance + loss: loss function + nr_classes: number of classes + + Returns: + None: This function does not return a value. + """ + super().__init__() + self.base_loss = loss + self.nr_classes = nr_classes + def forward(self, out_dict, args): """Forward method diff --git a/rsseval/rss/utils/losses.py b/rsseval/rss/utils/losses.py index 72d41439..c5dda02d 100644 --- a/rsseval/rss/utils/losses.py +++ b/rsseval/rss/utils/losses.py @@ -849,6 +849,9 @@ def RAVEN_Concept_Match(out_dict: dict): so the model learns to score them through symbolic reasoning, not direct concept supervision. + Targets with value -1 are ignored, analogously to Kandinsky and the other + rsbench datasets supporting partial concept supervision. + Attribute layout in z: Type(3) | Size(3) | Color(3) = 9 total Args: @@ -857,55 +860,93 @@ def RAVEN_Concept_Match(out_dict: dict): "CONCEPTS" [B, 16, n_attrs]: ground-truth concept indices Returns: - loss: scalar loss value (averaged over attributes) + loss: scalar loss value (averaged over supervised attributes) losses: dictionary with "c-loss" entry """ - # Only supervise context panels (0-7), not the 8 candidate choices z = out_dict["CS"][:, :8] # [B, 8, n_facts] targets = out_dict["CONCEPTS"][:, :8].to(torch.long) # [B, 8, n_attrs] - loss_f = torch.nn.CrossEntropyLoss() + loss = torch.tensor(0.0, device=z.device) + n_supervised_attrs = 0 - # Type: logits [0:3], target column 0 - z_type = z[..., 0:3] - l_type = loss_f(z_type.reshape(-1, 3), targets[..., 0].reshape(-1)) + for attr_idx, (lo, hi) in enumerate([(0, 3), (3, 6), (6, 9)]): + target = targets[..., attr_idx].reshape(-1) + mask = target != -1 + if mask.sum() > 0: + logits = z[..., lo:hi].reshape(-1, 3) + loss += torch.nn.CrossEntropyLoss()(logits[mask], target[mask]) + n_supervised_attrs += 1 - # Size: logits [3:6], target column 1 - z_size = z[..., 3:6] - l_size = loss_f(z_size.reshape(-1, 3), targets[..., 1].reshape(-1)) + if n_supervised_attrs > 0: + loss /= n_supervised_attrs - # Color: logits [6:9], target column 2 - z_color = z[..., 6:9] - l_color = loss_f(z_color.reshape(-1, 3), targets[..., 2].reshape(-1)) + return loss, {"c-loss": loss.item()} - n_attrs = 3 - loss = l_type + l_size + l_color - # Average over attributes - loss /= n_attrs +def RAVEN_Entropy(out_dict, args): + """RAVEN entropy loss. - return loss, {"c-loss": loss.item()} + Follows the MiniKandinsky/Kandinsky structure, but applies it to the + 8 context panels only. Each context panel contributes 3 concept slots + (Type, Size, Color), each of size 3. + + Args: + out_dict: output dictionary containing "pCS" [B, 16, 9] + args: command line arguments + + Returns: + loss: entropy penalty value + losses: dictionary with "H-loss" entry + """ + # Only regularize the context panels, mirroring concept supervision. + pCs = out_dict["pCS"][:, :8] # [B, 8, 9] + # pCs = out_dict["pCS"] + + # Split [Type(3), Size(3), Color(3)] and flatten panel/attribute slots. + pc_i = torch.cat(torch.split(pCs, 3, dim=-1), dim=1) # [B, 24, 3] + + # Mean predicted concept distribution across the batch for each slot. + p_mean = torch.mean(pc_i, dim=0) # [24, 3] + + p_mean += 1e-5 + # renormalization per slot distribution + with torch.no_grad(): + Z = torch.sum(p_mean, dim=1, keepdim=True) + p_mean /= Z + + loss = 0 + for i in range(p_mean.size(0)): + # loss -= torch.sum(p_mean[i] * p_mean[i].log()) / np.log(10) / p_mean.size(0) + loss -= torch.sum(p_mean[i] * p_mean[i].log()) / np.log(p_mean.size(1)) / p_mean.size(0) + + losses = {"H-loss": 1 - loss} + + assert (1 - loss) > 0, loss + + return 1 - loss, losses def RAVEN_Classification(out_dict: dict, args): """RAVEN classification loss. - YS is a softmaxed [B, 8] probability distribution over the 8 candidates. - We apply NLL loss on log(YS), clamping before log to avoid -inf. + YS is a log-softmaxed [B, 8] log-probability distribution over the 8 + candidates (returned by score_choices in log-space). We apply NLL loss + directly on the log-probabilities, matching how KandDPL handles its output. Args: - out_dict: output dictionary containing "YS" [B, 8] and "LABELS" [B] + out_dict: output dictionary containing "YS" [B, 8] log-probs and "LABELS" [B] args: command line arguments Returns: loss: scalar loss value losses: dictionary with "y-loss" entry """ - out = out_dict["YS"] # [B, 8] softmaxed choice probabilities + out = out_dict["YS"] # [B, 8] log-softmax choice log-probabilities labels = out_dict["LABELS"].to(torch.long) # [B] ground-truth choice index (0-7) - # Clamp before log to prevent -inf (same numerical safety as normalize_concepts) - loss = F.nll_loss(out.clamp(min=1e-5).log(), labels, reduction="mean") + # YS is already log-probabilities, so NLL loss applies directly. + # No need for .log() (which would be log-log) or .clamp(). + loss = F.nll_loss(out, labels, reduction="mean") assert loss > 0, f"{loss}, {out}, {labels}" @@ -914,10 +955,14 @@ def RAVEN_Classification(out_dict: dict, args): def RAVEN_Cumulative(out_dict: dict, args): - """RAVEN cumulative loss: task classification + optional concept supervision.""" + """RAVEN cumulative loss: task classification + optional entropy + optional concept supervision.""" loss, losses = RAVEN_Classification(out_dict, args) mitigation = 0 + if args.entropy: + loss_h, losses_h = RAVEN_Entropy(out_dict, args) + mitigation += args.w_h * loss_h + losses.update(losses_h) if args.c_sup > 0: loss_c, losses_c = RAVEN_Concept_Match(out_dict) mitigation += args.w_c * loss_c From 079f19fa8a020fd367fb42255f384b974cd0ca60 Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Mon, 11 May 2026 16:36:28 +0200 Subject: [PATCH 08/16] feat(metrics): add RAVEN eval, fix tloss for log-space YS --- rsseval/rss/utils/metrics.py | 65 +++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/rsseval/rss/utils/metrics.py b/rsseval/rss/utils/metrics.py index 80bd13a2..bd3c111c 100644 --- a/rsseval/rss/utils/metrics.py +++ b/rsseval/rss/utils/metrics.py @@ -193,7 +193,10 @@ def evaluate_metrics( "mnmath", ]: loss, ac, acc, f1 = MNMATH_eval_tloss_cacc_acc(out_dict, concepts) + elif args.dataset == "raven": + loss, ac, acc, f1 = RAVEN_eval_tloss_cacc_acc(out_dict, concepts) else: + print(f"Metrics not implemented for dataset: {args.dataset}") NotImplementedError() if not last: @@ -228,6 +231,26 @@ def evaluate_metrics( cs = np.split(c_pred, c_pred.shape[1], axis=1) p_cs = np.split(pc_pred, pc_pred.shape[1], axis=1) p_ys = y_pred + elif args.dataset == "raven": + # YS: [Batch, 8] -> [Batch] (candidate index) + y_true = y_true.astype(int).reshape(-1) + ys = np.argmax(y_pred, axis=1).astype(int).reshape(-1) + + p_cs_all = pc_pred + + # Current RavenDPL uses Type(3), Size(3), Color(3) + ptype = pc_pred[..., 0:3].argmax(axis=-1) + psize = pc_pred[..., 3:6].argmax(axis=-1) + pcolor = pc_pred[..., 6:9].argmax(axis=-1) + + ctype_max = pc_pred[..., 0:3].max(axis=-1) + csize_max = pc_pred[..., 3:6].max(axis=-1) + ccolor_max = pc_pred[..., 6:9].max(axis=-1) + + cs = np.stack([ptype, psize, pcolor], axis=-1) + p_cs = np.stack([ctype_max, csize_max, ccolor_max], axis=-1) + gs = c_true[..., :3] + p_ys = y_pred.max(axis=1) else: ys = np.argmax(y_pred, axis=1) @@ -236,7 +259,12 @@ def evaluate_metrics( p_cs = np.split(pc_pred, pc_pred.shape[1], axis=1) p_ys = y_pred - p_cs_all = p_cs + # For RAVEN, p_cs_all must remain the full concept-probability tensor + # [N, 16, 9] = [Type(3) | Size(3) | Color(3)] per panel. + # Do not overwrite it with p_cs, which only stores max confidences + # [N, 16, 3]. Other datasets keep the historical behavior. + if args.dataset != "raven": + p_cs_all = p_cs p_ys_all = y_pred assert len(gs) == len(cs), f"gs: {gs.shape}, cs: {cs.shape}" @@ -247,6 +275,7 @@ def evaluate_metrics( "presddoia", "clipboia", "clipsddoia", + "raven", ]: gs = np.concatenate(gs, axis=0).squeeze(1) if args.dataset in [ @@ -257,6 +286,8 @@ def evaluate_metrics( "clipsddoia", ]: cs = (cs >= 0.5).astype(np.int) + elif args.dataset == "raven": + pass elif args.dataset not in [ "kandinsky", "prekandinsky", @@ -288,6 +319,7 @@ def evaluate_metrics( "presddoia", "clipboia", "clipsddoia", + "raven", ]: p_cs_all = np.concatenate(p_cs_all, axis=0).squeeze( 1 @@ -321,6 +353,7 @@ def evaluate_metrics( "restrictedmnist", "clipsddoia", "clipshortmnist", + "raven", ]: if cf1: return tloss / L, cacc / L, yacc / L, f1sc / L, fcf1 / L @@ -1299,3 +1332,33 @@ def world_accuracy(world_prob: ndarray, world_true: ndarray, n_concepts: int): ).astype(int) return get_accuracy_and_counter(n_world, world_pred, world_true, True) + + +def RAVEN_eval_tloss_cacc_acc(out_dict, concepts): + """RAVEN evaluation for the current 3x3x3 factorized setup.""" + pCs = out_dict["pCS"] # [B, 16, 9] + + # Type(0:3), Size(3:6), Color(6:9) + ptype = pCs[..., 0:3] + psize = pCs[..., 3:6] + pcolor = pCs[..., 6:9] + + g_type = concepts[..., 0] + g_size = concepts[..., 1] + g_color = concepts[..., 2] + + acc_type = (ptype.argmax(dim=-1) == g_type).float().mean() + acc_size = (psize.argmax(dim=-1) == g_size).float().mean() + acc_color = (pcolor.argmax(dim=-1) == g_color).float().mean() + cacc = (acc_type + acc_size + acc_color) / 3.0 * 100.0 + + ys = out_dict["YS"] + labels = out_dict["LABELS"] + preds = ys.argmax(dim=-1) + acc = (preds == labels).float().mean().item() * 100.0 + f1 = f1_score(labels.cpu().numpy(), preds.cpu().numpy(), average="macro") * 100.0 + + # Real evaluation loss for the answer prediction. + # ys is already log-probabilities (log_softmax), so NLL applies directly. + loss = F.nll_loss(ys, labels.to(torch.long), reduction="mean") + return loss, cacc, acc, f1 From fc9dedfd9d674b5835916a81b8aaeb24066a9040 Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Mon, 11 May 2026 19:19:32 +0200 Subject: [PATCH 09/16] feat(train): add output_dir, early stopping, RAVEN concept diagnostics --- rsseval/rss/utils/args.py | 29 +++- rsseval/rss/utils/checkpoint.py | 21 ++- rsseval/rss/utils/train.py | 236 +++++++++++++++++++++++++++++--- 3 files changed, 257 insertions(+), 29 deletions(-) diff --git a/rsseval/rss/utils/args.py b/rsseval/rss/utils/args.py index 23b464d7..1b7ee514 100644 --- a/rsseval/rss/utils/args.py +++ b/rsseval/rss/utils/args.py @@ -37,10 +37,17 @@ def add_experiment_args(parser: ArgumentParser) -> None: "mini_patterns", "boia", "xor", - "mnmath" + "mnmath", + "raven", ], help="Which operation to choose.", ) + parser.add_argument( + "--raven_config", + type=str, + default="center_single", + help="RAVEN configuration (e.g. center_single)", + ) # model settings parser.add_argument( "--model", @@ -128,6 +135,18 @@ def add_experiment_args(parser: ArgumentParser) -> None: "--n_epochs", type=int, default=50, help="Number of epochs per task." ) parser.add_argument("--batch_size", type=int, default=64, help="Batch size.") + parser.add_argument( + "--early_stop_patience", + type=int, + default=-1, + help="Stop if val loss does not improve for this many epochs (-1 disables).", + ) + parser.add_argument( + "--early_stop_min_delta", + type=float, + default=0.0, + help="Minimum val-loss decrease to count as an improvement.", + ) # deep ensembles parser.add_argument( @@ -204,7 +223,7 @@ def add_management_args(parser: ArgumentParser) -> None: "--wandb", type=str, default=None, - help="Enable wandb logging -- set name of project", + help="Enable wandb logging -- set the wandb entity/user (project is set via --project)", ) # checkpoints parser.add_argument( @@ -213,6 +232,12 @@ def add_management_args(parser: ArgumentParser) -> None: default=None, help="location and path FROM where to load ckpt.", ) + parser.add_argument( + "--output_dir", + type=str, + default=".", + help="Directory where training artifacts (best model, plots, CSVs) are saved.", + ) parser.add_argument( "--checkout", action="store_true", diff --git a/rsseval/rss/utils/checkpoint.py b/rsseval/rss/utils/checkpoint.py index 6b5b927c..8817a10b 100644 --- a/rsseval/rss/utils/checkpoint.py +++ b/rsseval/rss/utils/checkpoint.py @@ -4,6 +4,10 @@ from utils.conf import create_path +def _get_output_dir(args): + return getattr(args, "output_dir", ".") or "." + + def _get_tag(args): """Get tag for the model name @@ -31,12 +35,13 @@ def create_load_ckpt(model, args): Returns: model (nn.Module): model """ - create_path("data/runs") - create_path("data/ckpts") + output_dir = _get_output_dir(args) + create_path(os.path.join(output_dir, "data/runs")) + create_path(os.path.join(output_dir, "data/ckpts")) tag = _get_tag(args) - PATH = f"data/runs/{args.dataset}-{args.model}-{tag}-start.pt" + PATH = os.path.join(output_dir, f"data/runs/{args.dataset}-{args.model}-{tag}-start.pt") if args.checkin is not None: model.load_state_dict(torch.load(args.checkin)) @@ -60,10 +65,11 @@ def save_model(model, args): Returns: None: This function does not return a value. """ - create_path("data/ckpts") + output_dir = _get_output_dir(args) + create_path(os.path.join(output_dir, "data/ckpts")) tag = _get_tag(args) - PATH = f"data/ckpts/{args.dataset}-{args.model}-{tag}-{args.seed}-end.pt" + PATH = os.path.join(output_dir, f"data/ckpts/{args.dataset}-{args.model}-{tag}-{args.seed}-end.pt") if args.checkout: print("Saved", PATH, "\n") @@ -93,13 +99,14 @@ def load_checkpoint(model, args, checkin=None): Returns: model (nn.Module): model """ - create_path("data/ckpts") + output_dir = _get_output_dir(args) + create_path(os.path.join(output_dir, "data/ckpts")) tag = _get_tag(args) if checkin is not None: PATH = checkin else: - PATH = f"data/ckpts/{args.dataset}-{args.model}-{tag}-{args.seed}-end.pt" + PATH = os.path.join(output_dir, f"data/ckpts/{args.dataset}-{args.model}-{tag}-{args.seed}-end.pt") if not os.path.exists(PATH): raise ValueError(f"You have to train the model first, missing {PATH}") diff --git a/rsseval/rss/utils/train.py b/rsseval/rss/utils/train.py index 635bafb7..b0614071 100644 --- a/rsseval/rss/utils/train.py +++ b/rsseval/rss/utils/train.py @@ -24,7 +24,12 @@ import matplotlib.pyplot as plt from warmup_scheduler import GradualWarmupScheduler -from sklearn.metrics import multilabel_confusion_matrix, confusion_matrix +from sklearn.metrics import ( + multilabel_confusion_matrix, + confusion_matrix, + accuracy_score, + f1_score, +) import numpy as np @@ -58,6 +63,68 @@ def compute_coverage(confusion_matrix): return coverage +def compute_concept_collapse(true_concepts, predicted_concepts, multilabel=False): + """Compute concept collapse from true/predicted concept categories.""" + if multilabel: + true_concepts = convert_to_categories(true_concepts.astype(int)) + predicted_concepts = convert_to_categories(predicted_concepts.astype(int)) + + return 1 - compute_coverage(confusion_matrix(true_concepts, predicted_concepts)) + + +def encode_raven_panel_states(concepts): + """Encode (Type, Size, Color) into a single 0..26 panel state id.""" + concepts = concepts.astype(int) + return concepts[:, 0] * 9 + concepts[:, 1] * 3 + concepts[:, 2] + + +def get_raven_state_labels(): + labels = [] + for t in range(3): + for s in range(3): + for c in range(3): + labels.append(f"T{t}-S{s}-C{c}") + return labels + + +def compute_raven_detailed_metrics(c_true, c_pred, concept_names): + """Compute detailed RAVEN concept metrics analogous to Kandinsky evaluation. + + Returns per-attribute accuracy/F1/collapse plus joint panel-state metrics. + """ + c_true_flat = c_true.reshape(-1, c_true.shape[-1]).astype(int) + c_pred_flat = c_pred.reshape(-1, c_pred.shape[-1]).astype(int) + + metrics = {"attributes": {}} + attr_collapses = [] + + for i, attr_name in enumerate(concept_names[: c_pred_flat.shape[1]]): + y_true = c_true_flat[:, i] + y_pred = c_pred_flat[:, i] + acc = accuracy_score(y_true, y_pred) + f1_macro = f1_score(y_true, y_pred, average="macro") + collapse = compute_concept_collapse(y_true, y_pred) + metrics["attributes"][attr_name] = { + "acc": acc, + "f1_macro": f1_macro, + "collapse": collapse, + } + attr_collapses.append(collapse) + + joint_true = encode_raven_panel_states(c_true_flat[:, :3]) + joint_pred = encode_raven_panel_states(c_pred_flat[:, :3]) + metrics["panel_state"] = { + "acc": accuracy_score(joint_true, joint_pred), + "f1_macro": f1_score(joint_true, joint_pred, average="macro"), + "collapse": compute_concept_collapse(joint_true, joint_pred), + "true": joint_true, + "pred": joint_pred, + } + metrics["mean_attr_collapse"] = float(np.mean(attr_collapses)) if attr_collapses else 0.0 + + return metrics + + def plot_confusion_matrix( y_true, y_pred, labels=None, title="Confusion Matrix", save_path=None ): @@ -285,6 +352,13 @@ def save_predictions_to_csv(model, test_set, csv_name, dataset): elif "mnmath" in dataset: cs = torch.argmax(cs, dim=2) cs_true = cs_true.reshape(cs_true.size(0), cs_true.size(1) * cs_true.size(2)) + elif "raven" in dataset: + if y_true.dim() == 1: + y_true = y_true.unsqueeze(1) + if cs.dim() > 2: + cs = cs.reshape(cs.shape[0], -1) + if cs_true.dim() > 2: + cs_true = cs_true.reshape(cs_true.shape[0], -1) concatenated_tensor = ( torch.concatenate((ys, y_true, cs, cs_true), dim=1).cpu().detach().numpy() @@ -311,11 +385,21 @@ def train(model: MnistDPL, dataset: BaseDataset, _loss: ADDMNIST_DPL, args): None: This function does not return a value. """ + output_dir = getattr(args, "output_dir", ".") or "." + os.makedirs(output_dir, exist_ok=True) + + def out_path(name): + return os.path.join(output_dir, name) + # name - csv_name = f"{args.dataset}-{args.model}-lr-{args.lr}.csv" + csv_name = out_path(f"{args.dataset}-{args.model}-lr-{args.lr}.csv") # best f1 best_f1 = 0.0 + best_tloss = float("inf") + early_stop_wait = 0 + early_stop_patience = int(getattr(args, "early_stop_patience", -1)) + early_stop_min_delta = float(getattr(args, "early_stop_min_delta", 0.0)) to_add = "" if args.model in ["kandcbm", "sddoiacbm", "boiacbm", "mnistcbm"]: @@ -324,7 +408,7 @@ def train(model: MnistDPL, dataset: BaseDataset, _loss: ADDMNIST_DPL, args): if args.dataset in ["shortmnist"] and args.joint: to_add += "_joint" - save_path = f"best_model_{args.dataset}_{args.model}_{args.seed}{to_add}.pth" + save_path = out_path(f"best_model_{args.dataset}_{args.model}_{args.seed}{to_add}.pth") # save embeddings variable save_embeddings_flag = False @@ -500,6 +584,22 @@ def train(model: MnistDPL, dataset: BaseDataset, _loss: ADDMNIST_DPL, args): lr=float(scheduler.get_last_lr()[0]), ) + # Early stopping based on validation loss. + if early_stop_patience > 0: + current_tloss = float(tloss) + if (best_tloss - current_tloss) > early_stop_min_delta: + best_tloss = current_tloss + early_stop_wait = 0 + else: + early_stop_wait += 1 + if early_stop_wait >= early_stop_patience: + print( + f"Early stopping at epoch {epoch}: " + f"val loss did not improve for {early_stop_patience} epochs " + f"(best={best_tloss:.6f}, current={current_tloss:.6f})." + ) + break + if args.dataset in ["clipshortmnist", "shortmnist"]: pass elif not args.tuning: @@ -550,13 +650,13 @@ def train(model: MnistDPL, dataset: BaseDataset, _loss: ADDMNIST_DPL, args): ] plot_multilabel_confusion_matrix( - y_true, y_pred, y_labels, "Labels", save_path="labels.png" + y_true, y_pred, y_labels, "Labels", save_path=out_path("labels.png") ) cfs = plot_actions_confusion_matrix( - c_true, c_pred, "Concepts", save_path="total_concepts_" + c_true, c_pred, "Concepts", save_path=out_path("total_concepts_") ) cf = plot_multilabel_confusion_matrix( - c_true, c_pred, concept_labels, "Concepts", save_path="total_concepts" + c_true, c_pred, concept_labels, "Concepts", save_path=out_path("total_concepts") ) print("Concept collapse", 1 - compute_coverage(cf)) @@ -570,17 +670,65 @@ def train(model: MnistDPL, dataset: BaseDataset, _loss: ADDMNIST_DPL, args): ["{i}" for i in range(10) for _ in range(4)] ] plot_multilabel_confusion_matrix( - y_true, y_pred, y_labels, "Labels", save_path="labels.png" + y_true, y_pred, y_labels, "Labels", save_path=out_path("labels.png") ) cf = plot_confusion_matrix( c_true, c_pred, labels=dataset.get_concept_labels(), title="Concepts", - save_path=f"concepts_{args.dataset}_{args.model}_lr_{args.lr}.png", + save_path=out_path(f"concepts_{args.dataset}_{args.model}_lr_{args.lr}.png"), ) print("Concept collapse", 1 - compute_coverage(cf)) + elif args.task == "raven": + plot_confusion_matrix( + y_true, + y_pred, + labels=dataset.get_labels(), + title="Labels", + save_path=out_path(f"labels_{args.dataset}_{args.model}_lr_{args.lr}.png"), + ) + + concept_names, concept_values = dataset.get_concept_labels() + raven_metrics = compute_raven_detailed_metrics(c_true, c_pred, concept_names) + + # Flatten [Batch, 16, Attr] -> [Total_Panels, Attr] + c_true_flat = c_true.reshape(-1, c_true.shape[-1]) + c_pred_flat = c_pred.reshape(-1, c_pred.shape[-1]) + + print("RAVEN detailed concept metrics:") + n_attrs = c_pred_flat.shape[1] + for i in range(n_attrs): + attr_name = concept_names[i] + attr_labels = concept_values[i] + cf_attr = plot_confusion_matrix( + c_true_flat[:, i], + c_pred_flat[:, i], + labels=attr_labels, + title=f"Concepts - {attr_name}", + save_path=out_path(f"concepts_{attr_name}_{args.dataset}_{args.model}_lr_{args.lr}.png"), + ) + attr_stats = raven_metrics["attributes"][attr_name] + print( + f" {attr_name}: ACC {attr_stats['acc']:.4f}, " + f"F1 {attr_stats['f1_macro']:.4f}, " + f"Collapse {attr_stats['collapse']:.4f}" + ) + + joint_cf = plot_confusion_matrix( + raven_metrics["panel_state"]["true"], + raven_metrics["panel_state"]["pred"], + labels=get_raven_state_labels(), + title="Concepts - Joint Panel State", + save_path=out_path(f"concepts_joint_panel_state_{args.dataset}_{args.model}_lr_{args.lr}.png"), + ) + print( + f" Joint panel state: ACC {raven_metrics['panel_state']['acc']:.4f}, " + f"F1 {raven_metrics['panel_state']['f1_macro']:.4f}, " + f"Collapse {raven_metrics['panel_state']['collapse']:.4f}" + ) + print(f" Mean attribute collapse: {raven_metrics['mean_attr_collapse']:.4f}") else: if args.task in ["patterns", "mini_patterns"]: @@ -592,7 +740,7 @@ def train(model: MnistDPL, dataset: BaseDataset, _loss: ADDMNIST_DPL, args): y_pred, labels=dataset.get_labels(), title="Labels", - save_path=f"labels_{args.dataset}_{args.model}_lr_{args.lr}.png", + save_path=out_path(f"labels_{args.dataset}_{args.model}_lr_{args.lr}.png"), ) if args.task in ["patterns", "mini_patterns"]: @@ -608,7 +756,7 @@ def train(model: MnistDPL, dataset: BaseDataset, _loss: ADDMNIST_DPL, args): p_shapes, labels=shapes_concepts, title="Concepts", - save_path=f"concepts_{args.dataset}_{args.model}_lr_{args.lr}-shapes.png", + save_path=out_path(f"concepts_{args.dataset}_{args.model}_lr_{args.lr}-shapes.png"), ) print("Concept collapse shapes", 1 - compute_coverage(cf_shapes)) @@ -617,7 +765,7 @@ def train(model: MnistDPL, dataset: BaseDataset, _loss: ADDMNIST_DPL, args): p_colors, labels=colors_concepts, title="Concepts", - save_path=f"concepts_{args.dataset}_{args.model}_lr_{args.lr}-colors.png", + save_path=out_path(f"concepts_{args.dataset}_{args.model}_lr_{args.lr}-colors.png"), ) print("Concept collapse colors", 1 - compute_coverage(cf_colors)) @@ -628,7 +776,7 @@ def train(model: MnistDPL, dataset: BaseDataset, _loss: ADDMNIST_DPL, args): c_pred, labels=dataset.get_concept_labels(), title="Concepts", - save_path=f"concepts_{args.dataset}_{args.model}_lr_{args.lr}.png", + save_path=out_path(f"concepts_{args.dataset}_{args.model}_lr_{args.lr}.png"), ) print("Concept collapse", 1 - compute_coverage(cf)) @@ -654,14 +802,62 @@ def train(model: MnistDPL, dataset: BaseDataset, _loss: ADDMNIST_DPL, args): ), } ) - K = max(np.max(c_pred), np.max(c_true)) - wandb.log( - { - "cf-concepts": wandb.plot.confusion_matrix( - None, c_true, c_pred, class_names=[str(i) for i in range(K + 1)] - ), - } - ) + + if args.dataset == "raven": + concept_names, concept_values = dataset.get_concept_labels() + c_true_flat = c_true.reshape(-1, c_true.shape[-1]) + c_pred_flat = c_pred.reshape(-1, c_pred.shape[-1]) + raven_metrics = compute_raven_detailed_metrics(c_true, c_pred, concept_names) + + for attr_name, attr_stats in raven_metrics["attributes"].items(): + wandb.log( + { + f"test-c-acc-{attr_name}": attr_stats["acc"] * 100, + f"test-c-f1-{attr_name}": attr_stats["f1_macro"] * 100, + f"test-c-collapse-{attr_name}": attr_stats["collapse"], + } + ) + + wandb.log( + { + "test-c-acc-panel-state": raven_metrics["panel_state"]["acc"] * 100, + "test-c-f1-panel-state": raven_metrics["panel_state"]["f1_macro"] * 100, + "test-c-collapse-panel-state": raven_metrics["panel_state"]["collapse"], + "test-c-collapse-mean-attr": raven_metrics["mean_attr_collapse"], + } + ) + + for i in range(c_pred_flat.shape[1]): + wandb.log( + { + f"cf-concepts-{concept_names[i]}": wandb.plot.confusion_matrix( + None, + c_true_flat[:, i], + c_pred_flat[:, i], + class_names=concept_values[i], + ) + } + ) + + wandb.log( + { + "cf-concepts-panel-state": wandb.plot.confusion_matrix( + None, + raven_metrics["panel_state"]["true"], + raven_metrics["panel_state"]["pred"], + class_names=get_raven_state_labels(), + ) + } + ) + else: + K = max(np.max(c_pred), np.max(c_true)) + wandb.log( + { + "cf-concepts": wandb.plot.confusion_matrix( + None, c_true, c_pred, class_names=[str(i) for i in range(K + 1)] + ), + } + ) if hasattr(model, "decoder"): list_images = make_grid( From 642aef4661ac57c1e053cdb1be95076665fba9d4 Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Wed, 3 Jun 2026 01:45:40 +0200 Subject: [PATCH 10/16] fix concept collapse computation + add joint/pairwise infrastructure --- rsseval/rss/utils/metrics.py | 222 +++++++++++++++++++++++++++++++++-- rsseval/rss/utils/train.py | 146 +++++------------------ 2 files changed, 237 insertions(+), 131 deletions(-) diff --git a/rsseval/rss/utils/metrics.py b/rsseval/rss/utils/metrics.py index bd3c111c..ae0a40cb 100644 --- a/rsseval/rss/utils/metrics.py +++ b/rsseval/rss/utils/metrics.py @@ -8,7 +8,7 @@ import torch.nn as nn import torch import torch.nn.functional as F -from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score +from sklearn.metrics import accuracy_score, confusion_matrix, f1_score from scipy.special import softmax @@ -194,7 +194,8 @@ def evaluate_metrics( ]: loss, ac, acc, f1 = MNMATH_eval_tloss_cacc_acc(out_dict, concepts) elif args.dataset == "raven": - loss, ac, acc, f1 = RAVEN_eval_tloss_cacc_acc(out_dict, concepts) + loss, ac, acc, f1, rcf1 = RAVEN_eval_tloss_cacc_acc(out_dict, concepts, cf1=True) + fcf1 += rcf1 else: print(f"Metrics not implemented for dataset: {args.dataset}") NotImplementedError() @@ -1334,23 +1335,216 @@ def world_accuracy(world_prob: ndarray, world_true: ndarray, n_concepts: int): return get_accuracy_and_counter(n_world, world_pred, world_true, True) -def RAVEN_eval_tloss_cacc_acc(out_dict, concepts): - """RAVEN evaluation for the current 3x3x3 factorized setup.""" +def raven_joint_concept_collapse(c_true, c_pred, n_vals=3): + """Joint concept collapse over the (Type, Size, Color) product space. + + Encodes (T,S,C) as code = T*n_vals^2 + S*n_vals + C, computes a + full-grid (n_vals^3 x n_vals^3) confusion matrix, and returns + 1 - coverage (higher = more collapse) and the matrix itself. + + Args: + c_true: (N, 3) integer array, columns = [Type, Size, Color] + c_pred: (N, 3) integer array, same layout + n_vals: cardinality per attribute (3 for 3x3x3) + + Returns: + collapse: float in [0, 1] + cm: (n_vals^3, n_vals^3) confusion matrix + """ + c_true = np.asarray(c_true) + c_pred = np.asarray(c_pred) + + assert c_true.shape[1] == 3, f"Expected (N, 3) with columns [Type, Size, Color]; got {c_true.shape}" + + # Drop rows where any ground-truth attribute is masked (-1). + # Without this, -1 values produce negative codes that confusion_matrix + # silently drops, losing data without warning. + valid = (c_true >= 0).all(axis=1) & (c_true < n_vals).all(axis=1) + c_true = c_true[valid] + c_pred = c_pred[valid] + + code_t = c_true[:, 0] * n_vals ** 2 + c_true[:, 1] * n_vals + c_true[:, 2] + code_p = c_pred[:, 0] * n_vals ** 2 + c_pred[:, 1] * n_vals + c_pred[:, 2] + + labels = list(range(n_vals ** 3)) + cm = confusion_matrix(code_t, code_p, labels=labels) + + max_per_col = np.max(cm, axis=0) + coverage = np.sum(np.clip(max_per_col, 0, 1)) / len(max_per_col) + return 1.0 - coverage, cm + + +def raven_pairwise_joint_collapse(c_true, c_pred, n_vals=3): + """Pairwise joint concept collapse for all attribute pairs. + + Computes three 9x9 confusion matrices (TypexSize, TypexColor, + SizexColor) by encoding each pair as code = first * n_vals + second, + then computing collapse = 1 - coverage on the full (n_vals^2)x(n_vals^2) grid. + + Args: + c_true: (N, 3) integer array, columns = [Type, Size, Color] + c_pred: (N, 3) integer array, same layout + n_vals: cardinality per attribute (3 for 3x3x3) + + Returns: + dict mapping pair name to (collapse, cm) tuples: + "TypexSize": (collapse_ts, cm_ts), + "TypexColor": (collapse_tc, cm_tc), + "SizexColor": (collapse_sc, cm_sc), + """ + c_true = np.asarray(c_true) + c_pred = np.asarray(c_pred) + assert c_true.shape[1] == 3, f"Expected (N, 3); got {c_true.shape}" + + valid = (c_true >= 0).all(axis=1) & (c_true < n_vals).all(axis=1) + c_true = c_true[valid] + c_pred = c_pred[valid] + + pairs = [(0, 1, "TypexSize"), (0, 2, "TypexColor"), (1, 2, "SizexColor")] + n2 = n_vals ** 2 + labels = list(range(n2)) + result = {} + for i, j, name in pairs: + code_t = c_true[:, i] * n_vals + c_true[:, j] + code_p = c_pred[:, i] * n_vals + c_pred[:, j] + cm = confusion_matrix(code_t, code_p, labels=labels) + max_per_col = np.max(cm, axis=0) + coverage = np.sum(np.clip(max_per_col, 0, 1)) / len(max_per_col) + result[name] = (1.0 - coverage, cm) + return result + + +def plot_raven_pairwise_confusion_matrix(cm, attr_i, attr_j, n_vals=3, save_path=None): + """Plot a 9x9 pairwise joint confusion matrix.""" + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + + n = n_vals ** 2 + assert cm.shape == (n, n), f"Expected ({n},{n}), got {cm.shape}" + + attr_names = ["T", "S", "C"] + tick_labels = [f"{attr_names[attr_i]}{c // n_vals}{attr_names[attr_j]}{c % n_vals}" for c in range(n)] + + fig, ax = plt.subplots(figsize=(8, 7)) + im = ax.imshow(cm + 1e-6, interpolation="nearest", cmap=plt.cm.Blues, + norm=matplotlib.colors.LogNorm()) + ax.set_title(f"Joint confusion: {attr_names[attr_i]}x{attr_names[attr_j]}") + fig.colorbar(im, ax=ax) + + ax.set_xticks(np.arange(n)) + ax.set_yticks(np.arange(n)) + ax.set_xticklabels(tick_labels, rotation=90, fontsize=9) + ax.set_yticklabels(tick_labels, fontsize=9) + ax.set_xlabel(f"Predicted ({attr_names[attr_i]},{attr_names[attr_j]})") + ax.set_ylabel(f"True ({attr_names[attr_i]},{attr_names[attr_j]})") + + ax.set_xticks(np.arange(n + 1) - 0.5, minor=True) + ax.set_yticks(np.arange(n + 1) - 0.5, minor=True) + ax.grid(which="minor", color="lightgray", linestyle="-", linewidth=0.3) + ax.tick_params(which="minor", bottom=False, left=False) + + # Annotate cells with counts + for i in range(n): + for j in range(n): + val = cm[i, j] + if val > 0: + ax.text(j, i, str(int(val)), ha="center", va="center", + fontsize=7, color="white" if val > cm.max() / 2 else "black") + + plt.tight_layout() + if save_path: + plt.savefig(save_path, dpi=200, bbox_inches="tight") + plt.close() + + +def plot_raven_joint_confusion_matrix(cm, n_vals=3, save_path=None, title="Joint concept confusion (T,S,C)"): + """Plot the full-grid joint (Type, Size, Color) confusion matrix.""" + import matplotlib + matplotlib.use("Agg") + import matplotlib.pyplot as plt + + n = n_vals ** 3 + assert cm.shape == (n, n), f"Expected ({n},{n}), got {cm.shape}" + + tick_labels = [ + f"T{c // n_vals**2}S{(c // n_vals) % n_vals}C{c % n_vals}" + for c in range(n) + ] + + fig, ax = plt.subplots(figsize=(14, 12)) + im = ax.imshow(cm + 1e-6, interpolation="nearest", cmap=plt.cm.Blues, + norm=matplotlib.colors.LogNorm()) + ax.set_title(title) + fig.colorbar(im, ax=ax) + + ax.set_xticks(np.arange(n)) + ax.set_yticks(np.arange(n)) + ax.set_xticklabels(tick_labels, rotation=90, fontsize=7) + ax.set_yticklabels(tick_labels, fontsize=7) + ax.set_xlabel("Predicted (T,S,C)") + ax.set_ylabel("True (T,S,C)") + + ax.set_xticks(np.arange(n + 1) - 0.5, minor=True) + ax.set_yticks(np.arange(n + 1) - 0.5, minor=True) + ax.grid(which="minor", color="lightgray", linestyle="-", linewidth=0.3) + ax.tick_params(which="minor", bottom=False, left=False) + + plt.tight_layout() + if save_path: + plt.savefig(save_path, dpi=200, bbox_inches="tight") + plt.close() + + +def RAVEN_eval_tloss_cacc_acc(out_dict, concepts, cf1=False): + """RAVEN evaluation for the current 3x3x3 factorized setup. + + Per-attribute concept accuracy and F1, plus label accuracy and F1. + + Args: + out_dict: dictionary of model outputs + concepts: ground-truth concept tensor + cf1: if True, also return concept macro F1 as 5th value + + Returns: + loss: NLL loss on answer prediction + cacc: mean concept accuracy across attributes (%) + acc: label accuracy (%) + f1: label macro F1 (%) + cf1: mean concept macro F1 across attributes (%), only if cf1=True + """ pCs = out_dict["pCS"] # [B, 16, 9] # Type(0:3), Size(3:6), Color(6:9) - ptype = pCs[..., 0:3] - psize = pCs[..., 3:6] - pcolor = pCs[..., 6:9] + ptype = pCs[..., 0:3].argmax(dim=-1) + psize = pCs[..., 3:6].argmax(dim=-1) + pcolor = pCs[..., 6:9].argmax(dim=-1) g_type = concepts[..., 0] g_size = concepts[..., 1] g_color = concepts[..., 2] - acc_type = (ptype.argmax(dim=-1) == g_type).float().mean() - acc_size = (psize.argmax(dim=-1) == g_size).float().mean() - acc_color = (pcolor.argmax(dim=-1) == g_color).float().mean() - cacc = (acc_type + acc_size + acc_color) / 3.0 * 100.0 + # Flatten panels before metric computation. If concept supervision masks + # are present, ignore masked slots (-1). + pred_attrs = [ptype.reshape(-1), psize.reshape(-1), pcolor.reshape(-1)] + true_attrs = [g_type.reshape(-1), g_size.reshape(-1), g_color.reshape(-1)] + + accs, f1s = [], [] + for y_true, y_pred in zip(true_attrs, pred_attrs): + mask = y_true != -1 + y_true = y_true[mask] + y_pred = y_pred[mask] + accs.append((y_pred == y_true).float().mean().item()) + f1s.append( + f1_score( + y_true.detach().cpu().numpy(), + y_pred.detach().cpu().numpy(), + average="macro", + ) + ) + + cacc = float(np.mean(accs)) * 100.0 + rcf1 = float(np.mean(f1s)) * 100.0 ys = out_dict["YS"] labels = out_dict["LABELS"] @@ -1361,4 +1555,8 @@ def RAVEN_eval_tloss_cacc_acc(out_dict, concepts): # Real evaluation loss for the answer prediction. # ys is already log-probabilities (log_softmax), so NLL applies directly. loss = F.nll_loss(ys, labels.to(torch.long), reduction="mean") - return loss, cacc, acc, f1 + + if cf1: + return loss, cacc, acc, f1, rcf1 + else: + return loss, cacc, acc, f1 diff --git a/rsseval/rss/utils/train.py b/rsseval/rss/utils/train.py index b0614071..f658fcbb 100644 --- a/rsseval/rss/utils/train.py +++ b/rsseval/rss/utils/train.py @@ -18,6 +18,10 @@ evaluate_mix, mean_entropy, accuracy_binary, + raven_joint_concept_collapse, + plot_raven_joint_confusion_matrix, + raven_pairwise_joint_collapse, + plot_raven_pairwise_confusion_matrix, ) from utils.generative import conditional_gen, recon_visaulization from utils import fprint @@ -27,10 +31,7 @@ from sklearn.metrics import ( multilabel_confusion_matrix, confusion_matrix, - accuracy_score, - f1_score, ) -import numpy as np def convert_to_categories(elements): @@ -63,68 +64,6 @@ def compute_coverage(confusion_matrix): return coverage -def compute_concept_collapse(true_concepts, predicted_concepts, multilabel=False): - """Compute concept collapse from true/predicted concept categories.""" - if multilabel: - true_concepts = convert_to_categories(true_concepts.astype(int)) - predicted_concepts = convert_to_categories(predicted_concepts.astype(int)) - - return 1 - compute_coverage(confusion_matrix(true_concepts, predicted_concepts)) - - -def encode_raven_panel_states(concepts): - """Encode (Type, Size, Color) into a single 0..26 panel state id.""" - concepts = concepts.astype(int) - return concepts[:, 0] * 9 + concepts[:, 1] * 3 + concepts[:, 2] - - -def get_raven_state_labels(): - labels = [] - for t in range(3): - for s in range(3): - for c in range(3): - labels.append(f"T{t}-S{s}-C{c}") - return labels - - -def compute_raven_detailed_metrics(c_true, c_pred, concept_names): - """Compute detailed RAVEN concept metrics analogous to Kandinsky evaluation. - - Returns per-attribute accuracy/F1/collapse plus joint panel-state metrics. - """ - c_true_flat = c_true.reshape(-1, c_true.shape[-1]).astype(int) - c_pred_flat = c_pred.reshape(-1, c_pred.shape[-1]).astype(int) - - metrics = {"attributes": {}} - attr_collapses = [] - - for i, attr_name in enumerate(concept_names[: c_pred_flat.shape[1]]): - y_true = c_true_flat[:, i] - y_pred = c_pred_flat[:, i] - acc = accuracy_score(y_true, y_pred) - f1_macro = f1_score(y_true, y_pred, average="macro") - collapse = compute_concept_collapse(y_true, y_pred) - metrics["attributes"][attr_name] = { - "acc": acc, - "f1_macro": f1_macro, - "collapse": collapse, - } - attr_collapses.append(collapse) - - joint_true = encode_raven_panel_states(c_true_flat[:, :3]) - joint_pred = encode_raven_panel_states(c_pred_flat[:, :3]) - metrics["panel_state"] = { - "acc": accuracy_score(joint_true, joint_pred), - "f1_macro": f1_score(joint_true, joint_pred, average="macro"), - "collapse": compute_concept_collapse(joint_true, joint_pred), - "true": joint_true, - "pred": joint_pred, - } - metrics["mean_attr_collapse"] = float(np.mean(attr_collapses)) if attr_collapses else 0.0 - - return metrics - - def plot_confusion_matrix( y_true, y_pred, labels=None, title="Confusion Matrix", save_path=None ): @@ -691,44 +630,43 @@ def out_path(name): ) concept_names, concept_values = dataset.get_concept_labels() - raven_metrics = compute_raven_detailed_metrics(c_true, c_pred, concept_names) # Flatten [Batch, 16, Attr] -> [Total_Panels, Attr] c_true_flat = c_true.reshape(-1, c_true.shape[-1]) c_pred_flat = c_pred.reshape(-1, c_pred.shape[-1]) - print("RAVEN detailed concept metrics:") - n_attrs = c_pred_flat.shape[1] - for i in range(n_attrs): + for i in range(c_pred_flat.shape[1]): attr_name = concept_names[i] attr_labels = concept_values[i] - cf_attr = plot_confusion_matrix( + plot_confusion_matrix( c_true_flat[:, i], c_pred_flat[:, i], labels=attr_labels, title=f"Concepts - {attr_name}", save_path=out_path(f"concepts_{attr_name}_{args.dataset}_{args.model}_lr_{args.lr}.png"), ) - attr_stats = raven_metrics["attributes"][attr_name] - print( - f" {attr_name}: ACC {attr_stats['acc']:.4f}, " - f"F1 {attr_stats['f1_macro']:.4f}, " - f"Collapse {attr_stats['collapse']:.4f}" - ) - - joint_cf = plot_confusion_matrix( - raven_metrics["panel_state"]["true"], - raven_metrics["panel_state"]["pred"], - labels=get_raven_state_labels(), - title="Concepts - Joint Panel State", - save_path=out_path(f"concepts_joint_panel_state_{args.dataset}_{args.model}_lr_{args.lr}.png"), - ) - print( - f" Joint panel state: ACC {raven_metrics['panel_state']['acc']:.4f}, " - f"F1 {raven_metrics['panel_state']['f1_macro']:.4f}, " - f"Collapse {raven_metrics['panel_state']['collapse']:.4f}" + cf_attr=confusion_matrix(c_true_flat[:, i], c_pred_flat[:, i], labels=list(range(len(attr_labels)))) + print(f"Concept collapse {attr_name}", 1 - compute_coverage(cf_attr)) + + # Joint (Type, Size, Color) concept collapse + joint_collapse, joint_cm = raven_joint_concept_collapse(c_true_flat, c_pred_flat) + print(f"Joint concept collapse (27-class): {joint_collapse:.4f}") + plot_raven_joint_confusion_matrix( + joint_cm, + save_path=out_path(f"joint_concepts_{args.dataset}_{args.model}_lr_{args.lr}.png"), ) - print(f" Mean attribute collapse: {raven_metrics['mean_attr_collapse']:.4f}") + + # Pairwise joint collapse (9×9 each) + pairwise = raven_pairwise_joint_collapse(c_true_flat, c_pred_flat) + pair_idx = {"Type×Size": (0, 1), "Type×Color": (0, 2), "Size×Color": (1, 2)} + for pair_name, (clp, cm) in pairwise.items(): + i, j = pair_idx[pair_name] + safe_name = pair_name.replace("×", "x") + print(f"Pairwise collapse {pair_name} (9-class): {clp:.4f}") + plot_raven_pairwise_confusion_matrix( + cm, i, j, + save_path=out_path(f"pairwise_{safe_name}_{args.dataset}_{args.model}_lr_{args.lr}.png"), + ) else: if args.task in ["patterns", "mini_patterns"]: @@ -807,25 +745,6 @@ def out_path(name): concept_names, concept_values = dataset.get_concept_labels() c_true_flat = c_true.reshape(-1, c_true.shape[-1]) c_pred_flat = c_pred.reshape(-1, c_pred.shape[-1]) - raven_metrics = compute_raven_detailed_metrics(c_true, c_pred, concept_names) - - for attr_name, attr_stats in raven_metrics["attributes"].items(): - wandb.log( - { - f"test-c-acc-{attr_name}": attr_stats["acc"] * 100, - f"test-c-f1-{attr_name}": attr_stats["f1_macro"] * 100, - f"test-c-collapse-{attr_name}": attr_stats["collapse"], - } - ) - - wandb.log( - { - "test-c-acc-panel-state": raven_metrics["panel_state"]["acc"] * 100, - "test-c-f1-panel-state": raven_metrics["panel_state"]["f1_macro"] * 100, - "test-c-collapse-panel-state": raven_metrics["panel_state"]["collapse"], - "test-c-collapse-mean-attr": raven_metrics["mean_attr_collapse"], - } - ) for i in range(c_pred_flat.shape[1]): wandb.log( @@ -838,17 +757,6 @@ def out_path(name): ) } ) - - wandb.log( - { - "cf-concepts-panel-state": wandb.plot.confusion_matrix( - None, - raven_metrics["panel_state"]["true"], - raven_metrics["panel_state"]["pred"], - class_names=get_raven_state_labels(), - ) - } - ) else: K = max(np.max(c_pred), np.max(c_true)) wandb.log( From cf26f923ac85d7ae5e460f1a2bdda78aed5516fd Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Tue, 16 Jun 2026 00:27:50 +0200 Subject: [PATCH 11/16] feat: add support for higher number of attribute values to RAVEN dataset, encoder, and DPL model --- rsseval/rss/backbones/raven_encoder.py | 12 ++--- rsseval/rss/datasets/raven.py | 43 ++++++++++------ rsseval/rss/datasets/utils/raven_creation.py | 52 ++++++++++---------- rsseval/rss/models/ravendpl.py | 13 +++-- 4 files changed, 67 insertions(+), 53 deletions(-) diff --git a/rsseval/rss/backbones/raven_encoder.py b/rsseval/rss/backbones/raven_encoder.py index 8fca0240..84206ed8 100644 --- a/rsseval/rss/backbones/raven_encoder.py +++ b/rsseval/rss/backbones/raven_encoder.py @@ -35,11 +35,7 @@ class RavenMLP(nn.Module): def __init__(self, latent_dim=9): super(RavenMLP, self).__init__() - - # 9 dimensions: Type(3) + Size(3) + Color(3) self.latent_dim = latent_dim - - # Use the independent panel encoder self.panel_encoder = RavenPanelEncoder(latent_dim=self.latent_dim) def forward(self, x): @@ -82,10 +78,12 @@ def forward(self, x): LR = 1e-3 EPOCHS = 20 DEVICE = "cuda" if torch.cuda.is_available() else "cpu" - DATA_PATH = "data/RAVEN-3x3x3" + N_VALUES = 3 # 3 for 3x3x3, 4 for 4x4x4 + LATENT_DIM = N_VALUES * 3 + DATA_PATH = f"data/RAVEN-{N_VALUES}x{N_VALUES}x{N_VALUES}" ATTR_NAMES = ["Type", "Size", "Color"] - ATTR_SLICES = [(0, 3), (3, 6), (6, 9)] # logit slices in 9-dim output + ATTR_SLICES = [(0, N_VALUES), (N_VALUES, 2 * N_VALUES), (2 * N_VALUES, 3 * N_VALUES)] # ── Data ──────────────────────────────────────────────────────── train_ds = RAVEN_Dataset(base_path=DATA_PATH, config="center_single", split="train") @@ -98,7 +96,7 @@ def forward(self, x): print(f"Device: {DEVICE}") # ── Model & Optimizer ─────────────────────────────────────────── - model = RavenMLP(latent_dim=9).to(DEVICE) + model = RavenMLP(latent_dim=LATENT_DIM).to(DEVICE) optimizer = torch.optim.Adam(model.parameters(), lr=LR) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=EPOCHS) diff --git a/rsseval/rss/datasets/raven.py b/rsseval/rss/datasets/raven.py index 6029c2a0..ba4033fb 100644 --- a/rsseval/rss/datasets/raven.py +++ b/rsseval/rss/datasets/raven.py @@ -5,19 +5,32 @@ import time from torch.utils.data import DataLoader +# ── Concept labels per n_values ────────────────────────────────────── +_TYPE_LABELS = { + 3: ["Triangle", "Pentagon", "Circle"], + 4: ["Triangle", "Square", "Pentagon", "Circle"], +} +_SIZE_LABELS = { + 3: ["Small", "Medium", "Large"], + 4: ["Small", "Medium-small", "Medium-large", "Large"], +} +_COLOR_LABELS = { + 3: ["White", "Gray", "Black"], + 4: ["White", "Light-gray", "Dark-gray", "Black"], +} + class RAVEN(BaseDataset): NAME = "raven" def get_data_loaders(self): start = time.time() - - # We can pass specific args for configuration if needed via self.args - # For now defaults to center_single - + config = getattr(self.args, 'raven_config', "center_single") + n = getattr(self.args, 'n_values', 3) + base_path = f"data/RAVEN-{n}x{n}x{n}" self.dataset_train = RAVEN_Dataset( - base_path="data/RAVEN-3x3x3", + base_path=base_path, config=config, split="train", c_sup=self.args.c_sup, @@ -25,13 +38,13 @@ def get_data_loaders(self): ) self.dataset_val = RAVEN_Dataset( - base_path="data/RAVEN-3x3x3", + base_path=base_path, config=config, split="val", ) self.dataset_test = RAVEN_Dataset( - base_path="data/RAVEN-3x3x3", + base_path=base_path, config=config, split="test", ) @@ -46,21 +59,19 @@ def get_data_loaders(self): return train_loader, val_loader, test_loader def get_backbone(self, args=None): - # Return the encoder compatible with 160x160 input - # We return (encoder, decoder). Decoder is None for now. - return RavenMLP(), None + n = getattr(self.args, 'n_values', 3) if self.args else 3 + return RavenMLP(latent_dim=n * 3), None def get_split(self): - # 16 images per sample return 16, () def get_concept_labels(self): - # 3x3x3 constrained dataset: 3 Types, 3 Sizes, 3 Colors + n = getattr(self.args, 'n_values', 3) return ["Type", "Size", "Color", "Number"], [ - ["Triangle", "Pentagon", "Circle"], # Type (0-2) - ["Small", "Medium", "Large"], # Size (0-2) - ["White", "Gray", "Black"], # Color (0-2) - [str(i) for i in range(9)] # Number (0-8) + _TYPE_LABELS.get(n, _TYPE_LABELS[3]), + _SIZE_LABELS.get(n, _SIZE_LABELS[3]), + _COLOR_LABELS.get(n, _COLOR_LABELS[3]), + [str(i) for i in range(9)], ] def get_labels(self): diff --git a/rsseval/rss/datasets/utils/raven_creation.py b/rsseval/rss/datasets/utils/raven_creation.py index 853314a4..38e1c64e 100644 --- a/rsseval/rss/datasets/utils/raven_creation.py +++ b/rsseval/rss/datasets/utils/raven_creation.py @@ -9,16 +9,10 @@ class RAVEN_Dataset(Dataset): """ RAVEN Dataset for RSBench. - Loads RAVEN-3x3x3 .npz files (3 Types x 3 Sizes x 3 Colors). - - Supports partial concept supervision on the training split through: - - c_sup: fraction of training samples with concept supervision - - which_c: attribute indices to supervise (0=Type, 1=Size, 2=Color) - - Unsupervised concept targets are masked with -1, analogously to the - other rsbench datasets. + Supports partial concept supervision. """ - def __init__(self, base_path, config="center_single", split="train", c_sup=1, which_c=[-1]): + def __init__(self, base_path, config="center_single", split="train", + c_sup=1, which_c=[-1]): self.base_path = base_path self.config = config self.split = split @@ -26,7 +20,6 @@ def __init__(self, base_path, config="center_single", split="train", c_sup=1, wh self.which_c = which_c self.is_train = split == "train" - # Search pattern for files pattern = os.path.join(self.base_path, self.config, f"RAVEN_*_{self.split}.npz") self.all_files = sorted(glob.glob(pattern)) @@ -134,12 +127,12 @@ def _extract_concepts_from_xml(self, xml_path): import matplotlib.patches as mpatches from collections import Counter - TYPE_LABELS = ["Triangle", "Pentagon", "Circle"] - SIZE_LABELS = ["Small", "Medium", "Large"] - COLOR_LABELS = ["White", "Gray", "Black"] + TYPE_LABELS = ["Triangle", "Square", "Pentagon", "Hexagon"] + SIZE_LABELS = ["Small", "Medium-S", "Medium-L", "Large"] + COLOR_LABELS = ["White", "Gray-W", "Gray-B", "Black"] dataset = RAVEN_Dataset( - base_path="data/RAVEN-3x3x3", + base_path="data/RAVEN-4x4x4", config="center_single", split="train", ) @@ -235,8 +228,8 @@ def extract_rules(xml_path): ax.axis("off") plt.tight_layout(rect=[0, 0, 1, 0.95]) - plt.savefig("raven_3x3x3_samples.png", dpi=150, bbox_inches="tight") - print("Saved raven_3x3x3_samples.png") + plt.savefig("raven_samples.png", dpi=150, bbox_inches="tight") + print("Saved raven_samples.png") plt.show() # ── Fig 2: Attribute distributions ─────────────────────────────── @@ -252,19 +245,26 @@ def extract_rules(xml_path): size_counts.update(concepts[:8, 1].tolist()) color_counts.update(concepts[:8, 2].tolist()) - fig, axes = plt.subplots(1, 3, figsize=(12, 4)) + # ── Detect n_vals from the data ────────────────────────────── + n_type = max(type_counts.keys()) + 1 if type_counts else 3 + n_size = max(size_counts.keys()) + 1 if size_counts else 3 + n_color = max(color_counts.keys()) + 1 if color_counts else 3 + + fig, axes = plt.subplots(1, 3, figsize=(14, 4)) fig.suptitle(f"Attribute Distributions (first {n_check} samples, context panels)", fontsize=13, fontweight="bold") - bar_colors = ["#4C72B0", "#55A868", "#C44E52"] + bar_colors = ["#4C72B0", "#55A868", "#C44E52", "#DD8452", "#937860"] - for ax, counts, labels, name in [ - (axes[0], type_counts, TYPE_LABELS, "Type"), - (axes[1], size_counts, SIZE_LABELS, "Size"), - (axes[2], color_counts, COLOR_LABELS, "Color"), + for ax, counts, n_vals, name in [ + (axes[0], type_counts, n_type, "Type"), + (axes[1], size_counts, n_size, "Size"), + (axes[2], color_counts, n_color, "Color"), ]: - vals = [counts.get(i, 0) for i in range(3)] - bars = ax.bar(labels, vals, color=bar_colors, edgecolor="black", linewidth=0.5) + labels = [str(i) for i in range(n_vals)] + vals = [counts.get(i, 0) for i in range(n_vals)] + colors = bar_colors[:n_vals] + bars = ax.bar(labels, vals, color=colors, edgecolor="black", linewidth=0.5) ax.set_title(name, fontsize=12, fontweight="bold") ax.set_ylabel("Count") for bar, v in zip(bars, vals): @@ -272,6 +272,6 @@ def extract_rules(xml_path): str(v), ha="center", va="bottom", fontsize=9) plt.tight_layout() - plt.savefig("raven_3x3x3_distributions_test.png", dpi=150, bbox_inches="tight") - print("Saved raven_3x3x3_distributions_test.png") + plt.savefig("raven_distributions.png", dpi=150, bbox_inches="tight") + print("Saved raven_distributions.png") plt.show() \ No newline at end of file diff --git a/rsseval/rss/models/ravendpl.py b/rsseval/rss/models/ravendpl.py index 95ba314f..d920cb35 100644 --- a/rsseval/rss/models/ravendpl.py +++ b/rsseval/rss/models/ravendpl.py @@ -67,8 +67,9 @@ def __init__( # Concept dimensions per attribute self.raven_config = args.raven_config if args else "center_single" if self.raven_config == "center_single": - self.dims = {"Type": 3, "Size": 3, "Color": 3} - self.n_facts = 9 # 3+3+3 + n = getattr(args, 'n_values', 3) + self.dims = {"Type": n, "Size": n, "Color": n} + self.n_facts = n * 3 self.nr_classes = nr_classes # 8 candidate choices self.n_and_classes = 2 # invalid / valid for explicit row-rule agreement @@ -85,7 +86,8 @@ def __init__( self.attr_rules[attr] = [ "Constant", "Progression", - "Arithmetic", + "Arithmetic_Add", + "Arithmetic_Sub", "Distribute_Three", ] @@ -94,9 +96,12 @@ def __init__( self.wq_matrices = {} self.and_rules = {} self.device = get_device() + # Generator uses offset=+1 for Size arithmetic, 0 for Color. + _ARITH_OFFSETS = {"Size": 1, "Color": 0, "Type": 0} for attr, dim in self.dims.items(): rules = self.attr_rules[attr] - mat, and_rule = build_worlds_queries_matrix_RAVEN(dim, rules) + offset = _ARITH_OFFSETS.get(attr, 0) + mat, and_rule = build_worlds_queries_matrix_RAVEN(dim, rules, arithmetic_offset=offset) self.wq_matrices[attr] = mat.float().to(self.device) self.and_rules[attr] = and_rule.float().to(self.device) From 44e47ee230b485fc8acf0cb269f30c88d046602d Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Tue, 16 Jun 2026 00:29:09 +0200 Subject: [PATCH 12/16] add support for 4x4x4 in loss and add entropy annealing --- rsseval/rss/utils/losses.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/rsseval/rss/utils/losses.py b/rsseval/rss/utils/losses.py index c5dda02d..d93116e9 100644 --- a/rsseval/rss/utils/losses.py +++ b/rsseval/rss/utils/losses.py @@ -866,14 +866,15 @@ def RAVEN_Concept_Match(out_dict: dict): z = out_dict["CS"][:, :8] # [B, 8, n_facts] targets = out_dict["CONCEPTS"][:, :8].to(torch.long) # [B, 8, n_attrs] + n = z.shape[-1] // 3 # values per attribute loss = torch.tensor(0.0, device=z.device) n_supervised_attrs = 0 - for attr_idx, (lo, hi) in enumerate([(0, 3), (3, 6), (6, 9)]): + for attr_idx, (lo, hi) in enumerate([(0, n), (n, 2 * n), (2 * n, 3 * n)]): target = targets[..., attr_idx].reshape(-1) mask = target != -1 if mask.sum() > 0: - logits = z[..., lo:hi].reshape(-1, 3) + logits = z[..., lo:hi].reshape(-1, n) loss += torch.nn.CrossEntropyLoss()(logits[mask], target[mask]) n_supervised_attrs += 1 @@ -899,11 +900,11 @@ def RAVEN_Entropy(out_dict, args): losses: dictionary with "H-loss" entry """ # Only regularize the context panels, mirroring concept supervision. - pCs = out_dict["pCS"][:, :8] # [B, 8, 9] - # pCs = out_dict["pCS"] + pCs = out_dict["pCS"][:, :8] # [B, 8, n_facts] + n = pCs.shape[-1] // 3 # values per attribute - # Split [Type(3), Size(3), Color(3)] and flatten panel/attribute slots. - pc_i = torch.cat(torch.split(pCs, 3, dim=-1), dim=1) # [B, 24, 3] + # Split [Type(n), Size(n), Color(n)] and flatten panel/attribute slots. + pc_i = torch.cat(torch.split(pCs, n, dim=-1), dim=1) # [B, 24, n] # Mean predicted concept distribution across the batch for each slot. p_mean = torch.mean(pc_i, dim=0) # [24, 3] @@ -960,8 +961,18 @@ def RAVEN_Cumulative(out_dict: dict, args): mitigation = 0 if args.entropy: + # Entropy annealing: if entropy_anneal_epochs > 0, linearly decay w_h + # from its full value down to 0.1 over the specified number of epochs, + # then hold at 0.1. This gives the task signal room to compete once + # concepts are bootstrapped. + w_h = args.w_h + anneal = getattr(args, 'entropy_anneal_epochs', 0) + if anneal > 0: + epoch = getattr(args, '_current_epoch', 0) + frac = max(0.0, 1.0 - epoch / anneal) + w_h = args.w_h * frac + 0.1 * (1.0 - frac) loss_h, losses_h = RAVEN_Entropy(out_dict, args) - mitigation += args.w_h * loss_h + mitigation += w_h * loss_h losses.update(losses_h) if args.c_sup > 0: loss_c, losses_c = RAVEN_Concept_Match(out_dict) From 745f402de18281ad8e6f5f3e43342193333bd0ca Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Tue, 16 Jun 2026 00:31:04 +0200 Subject: [PATCH 13/16] fix: correct arithmetic encoding and distribute_three to use same subset values --- rsseval/rss/models/utils/utils_problog.py | 77 +++++++++++++---------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/rsseval/rss/models/utils/utils_problog.py b/rsseval/rss/models/utils/utils_problog.py index 09f74e51..ff163e8d 100644 --- a/rsseval/rss/models/utils/utils_problog.py +++ b/rsseval/rss/models/utils/utils_problog.py @@ -1025,50 +1025,60 @@ def create_mnist_and(sequence_len=0, n_digits=0, task="mnmath"): NotImplementedError("Wrong choice") -def build_worlds_queries_matrix_RAVEN(n_vals, rules=["Constant"]): +def build_worlds_queries_matrix_RAVEN(n_vals, rules=["Constant"], arithmetic_offset=0): """ Build logic matrices for factorized RAVEN attribute experts. Args: n_vals: number of possible values for the attribute. - For RAVEN-3x3x3 this is 3 for Type/Size/Color. - rules: list of rule names to compile. Supported rules are - "Constant", "Progression", "Arithmetic", and - "Distribute_Three". + rules: list of rule names to compile. + "Distribute_Three" is expanded into C(n_vals, 3) subset-specific + columns so that the AND-rule (r1==r2==r3) enforces that + all three rows use the *same* three values. + arithmetic_offset: bias added to the Arithmetic formula. + Generator uses 0 for Color, +1 for Size (v1+v2+1==v3). Returns: - w_q: tensor of shape [n_vals^3, n_rules], where each row corresponds - to one world (v1, v2, v3) and each column to a rule. - and_rule: tensor of shape [n_rules^3, 2] encoding explicit agreement - over three row-level rule variables `(r1, r2, r3)`. - Column 1 = valid iff `r1 == r2 == r3`, column 0 = invalid otherwise. + w_q: tensor of shape [n_vals^3, n_rules] (with expanded columns) + and_rule: tensor of shape [n_rules^3, 2] encoding r1==r2==r3 """ - possible_worlds = list(product(range(n_vals), repeat=3)) + from itertools import combinations, product as _product + + possible_worlds = list(_product(range(n_vals), repeat=3)) n_worlds = len(possible_worlds) - n_rules = len(rules) - and_rule = torch.zeros(n_rules**3, 2) - possible_rule_triples = list(product(range(n_rules), repeat=3)) - for idx, (r1, r2, r3) in enumerate(possible_rule_triples): + # Expand "Distribute_Three" into subset-specific columns. + expanded = [] + for rule in rules: + if rule == "Distribute_Three" and n_vals >= 3: + for s in combinations(range(n_vals), 3): + expanded.append(("Distribute_Three", s)) + elif rule == "Arithmetic_Add": + expanded.append(("Arithmetic_Add", arithmetic_offset)) + elif rule == "Arithmetic_Sub": + expanded.append(("Arithmetic_Sub", arithmetic_offset)) + else: + expanded.append((rule, None)) + n_rules = len(expanded) + + and_rule = torch.zeros(n_rules ** 3, 2) + for idx, (r1, r2, r3) in enumerate(_product(range(n_rules), repeat=3)): if r1 == r2 and r2 == r3: and_rule[idx, 1] = 1.0 else: and_rule[idx, 0] = 1.0 w_q = torch.zeros(n_worlds, n_rules) - look_up = {i: c for i, c in zip(range(n_worlds), possible_worlds)} for w in range(n_worlds): - v1, v2, v3 = look_up[w] + v1, v2, v3 = possible_worlds[w] - for rule_idx, rule in enumerate(rules): + for rule_idx, (rule, subset) in enumerate(expanded): if rule == "Constant": if v1 == v2 and v2 == v3: w_q[w, rule_idx] = 1.0 elif rule == "Progression": - # Generic ordered-domain progression. For {0,1,2}, this reduces - # naturally to the only valid sequences: (0,1,2) and (2,1,0). if (v2 - v1) == 1 and (v3 - v2) == 1: w_q[w, rule_idx] = 1.0 elif (v2 - v1) == -1 and (v3 - v2) == -1: @@ -1078,21 +1088,22 @@ def build_worlds_queries_matrix_RAVEN(n_vals, rules=["Constant"]): elif (v2 - v1) == -2 and (v3 - v2) == -2: w_q[w, rule_idx] = 1.0 - elif rule == "Arithmetic": - # A+B=C or A-B=C - if v1 + v2 == v3: - w_q[w, rule_idx] = 1.0 - elif v1 - v2 == v3: - w_q[w, rule_idx] = 1.0 + elif rule in ("Arithmetic", "Arithmetic_Add", "Arithmetic_Sub"): + # Split into separate columns so the AND-rule (r1==r2==r3) + # enforces the same operation across all three rows. + # Generator formulas differ per attribute: + # Color: v1+v2 == v3 | v1-v2 == v3 (offset=0) + # Size: v1+v2+1 == v3 | v1-v2-1 == v3 (offset=1) + o = arithmetic_offset + if rule in ("Arithmetic", "Arithmetic_Add"): + if v1 + v2 + o == v3: + w_q[w, rule_idx] = 1.0 + if rule in ("Arithmetic", "Arithmetic_Sub"): + if v1 - v2 - o == v3: + w_q[w, rule_idx] = 1.0 elif rule == "Distribute_Three": - # Sample 3 values and permute them across rows. - # In the 3x3x3 setting, each attribute has exactly 3 possible - # values, so this is equivalent to "all distinct". - if v1 != v2 and v1 != v3 and v2 != v3: + if set([v1, v2, v3]) == set(subset): w_q[w, rule_idx] = 1.0 - else: - raise ValueError(f"Unsupported RAVEN rule: {rule}") - return w_q, and_rule \ No newline at end of file From 7a3409767752dddf0437c7c7f5fa2ef64111d0d7 Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Tue, 16 Jun 2026 00:32:51 +0200 Subject: [PATCH 14/16] feat: add support for 4x4x4 RAVEN evaluation metrics and joint/pairwise collapse --- rsseval/rss/utils/metrics.py | 60 ++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/rsseval/rss/utils/metrics.py b/rsseval/rss/utils/metrics.py index ae0a40cb..eaad2b23 100644 --- a/rsseval/rss/utils/metrics.py +++ b/rsseval/rss/utils/metrics.py @@ -239,14 +239,15 @@ def evaluate_metrics( p_cs_all = pc_pred - # Current RavenDPL uses Type(3), Size(3), Color(3) - ptype = pc_pred[..., 0:3].argmax(axis=-1) - psize = pc_pred[..., 3:6].argmax(axis=-1) - pcolor = pc_pred[..., 6:9].argmax(axis=-1) + # Derive per-attribute dimension from the tensor shape itself. + n = pc_pred.shape[-1] // 3 # values per attribute (3 or 4) + ptype = pc_pred[..., 0:n].argmax(axis=-1) + psize = pc_pred[..., n:2*n].argmax(axis=-1) + pcolor = pc_pred[..., 2*n:3*n].argmax(axis=-1) - ctype_max = pc_pred[..., 0:3].max(axis=-1) - csize_max = pc_pred[..., 3:6].max(axis=-1) - ccolor_max = pc_pred[..., 6:9].max(axis=-1) + ctype_max = pc_pred[..., 0:n].max(axis=-1) + csize_max = pc_pred[..., n:2*n].max(axis=-1) + ccolor_max = pc_pred[..., 2*n:3*n].max(axis=-1) cs = np.stack([ptype, psize, pcolor], axis=-1) p_cs = np.stack([ctype_max, csize_max, ccolor_max], axis=-1) @@ -1335,7 +1336,7 @@ def world_accuracy(world_prob: ndarray, world_true: ndarray, n_concepts: int): return get_accuracy_and_counter(n_world, world_pred, world_true, True) -def raven_joint_concept_collapse(c_true, c_pred, n_vals=3): +def raven_joint_concept_collapse(c_true, c_pred, n_vals=None): """Joint concept collapse over the (Type, Size, Color) product space. Encodes (T,S,C) as code = T*n_vals^2 + S*n_vals + C, computes a @@ -1345,7 +1346,7 @@ def raven_joint_concept_collapse(c_true, c_pred, n_vals=3): Args: c_true: (N, 3) integer array, columns = [Type, Size, Color] c_pred: (N, 3) integer array, same layout - n_vals: cardinality per attribute (3 for 3x3x3) + n_vals: cardinality per attribute. Auto-detected from data if None. Returns: collapse: float in [0, 1] @@ -1354,6 +1355,9 @@ def raven_joint_concept_collapse(c_true, c_pred, n_vals=3): c_true = np.asarray(c_true) c_pred = np.asarray(c_pred) + if n_vals is None: + n_vals = int(max(c_true.max(), c_pred.max())) + 1 + assert c_true.shape[1] == 3, f"Expected (N, 3) with columns [Type, Size, Color]; got {c_true.shape}" # Drop rows where any ground-truth attribute is masked (-1). @@ -1374,28 +1378,27 @@ def raven_joint_concept_collapse(c_true, c_pred, n_vals=3): return 1.0 - coverage, cm -def raven_pairwise_joint_collapse(c_true, c_pred, n_vals=3): +def raven_pairwise_joint_collapse(c_true, c_pred, n_vals=None): """Pairwise joint concept collapse for all attribute pairs. - Computes three 9x9 confusion matrices (TypexSize, TypexColor, - SizexColor) by encoding each pair as code = first * n_vals + second, - then computing collapse = 1 - coverage on the full (n_vals^2)x(n_vals^2) grid. + Computes three (n_vals^2) x (n_vals^2) confusion matrices (TypexSize, + TypexColor, SizexColor) and returns collapse = 1 - coverage for each. Args: c_true: (N, 3) integer array, columns = [Type, Size, Color] c_pred: (N, 3) integer array, same layout - n_vals: cardinality per attribute (3 for 3x3x3) + n_vals: cardinality per attribute. Auto-detected from data if None. Returns: - dict mapping pair name to (collapse, cm) tuples: - "TypexSize": (collapse_ts, cm_ts), - "TypexColor": (collapse_tc, cm_tc), - "SizexColor": (collapse_sc, cm_sc), + dict mapping pair name to (collapse, cm) tuples """ c_true = np.asarray(c_true) c_pred = np.asarray(c_pred) assert c_true.shape[1] == 3, f"Expected (N, 3); got {c_true.shape}" + if n_vals is None: + n_vals = int(max(c_true.max(), c_pred.max())) + 1 + valid = (c_true >= 0).all(axis=1) & (c_true < n_vals).all(axis=1) c_true = c_true[valid] c_pred = c_pred[valid] @@ -1414,12 +1417,14 @@ def raven_pairwise_joint_collapse(c_true, c_pred, n_vals=3): return result -def plot_raven_pairwise_confusion_matrix(cm, attr_i, attr_j, n_vals=3, save_path=None): - """Plot a 9x9 pairwise joint confusion matrix.""" +def plot_raven_pairwise_confusion_matrix(cm, attr_i, attr_j, n_vals=None, save_path=None): + """Plot a pairwise joint confusion matrix.""" import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt + if n_vals is None: + n_vals = int(round(cm.shape[0] ** 0.5)) n = n_vals ** 2 assert cm.shape == (n, n), f"Expected ({n},{n}), got {cm.shape}" @@ -1458,12 +1463,14 @@ def plot_raven_pairwise_confusion_matrix(cm, attr_i, attr_j, n_vals=3, save_path plt.close() -def plot_raven_joint_confusion_matrix(cm, n_vals=3, save_path=None, title="Joint concept confusion (T,S,C)"): +def plot_raven_joint_confusion_matrix(cm, n_vals=None, save_path=None, title="Joint concept confusion (T,S,C)"): """Plot the full-grid joint (Type, Size, Color) confusion matrix.""" import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt + if n_vals is None: + n_vals = int(round(cm.shape[0] ** (1/3))) n = n_vals ** 3 assert cm.shape == (n, n), f"Expected ({n},{n}), got {cm.shape}" @@ -1513,12 +1520,13 @@ def RAVEN_eval_tloss_cacc_acc(out_dict, concepts, cf1=False): f1: label macro F1 (%) cf1: mean concept macro F1 across attributes (%), only if cf1=True """ - pCs = out_dict["pCS"] # [B, 16, 9] + pCs = out_dict["pCS"] # [B, 16, n_facts] + n = pCs.shape[-1] // 3 # values per attribute - # Type(0:3), Size(3:6), Color(6:9) - ptype = pCs[..., 0:3].argmax(dim=-1) - psize = pCs[..., 3:6].argmax(dim=-1) - pcolor = pCs[..., 6:9].argmax(dim=-1) + # Type(0:n), Size(n:2n), Color(2n:3n) + ptype = pCs[..., 0:n].argmax(dim=-1) + psize = pCs[..., n:2*n].argmax(dim=-1) + pcolor = pCs[..., 2*n:3*n].argmax(dim=-1) g_type = concepts[..., 0] g_size = concepts[..., 1] From 59173edcd48a22f2f7e98c74fb983b551aeaeb06 Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Tue, 16 Jun 2026 00:36:53 +0200 Subject: [PATCH 15/16] fix: misc RAVEN training fixes --- rsseval/rss/utils/train.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rsseval/rss/utils/train.py b/rsseval/rss/utils/train.py index f658fcbb..8df9bb77 100644 --- a/rsseval/rss/utils/train.py +++ b/rsseval/rss/utils/train.py @@ -375,7 +375,7 @@ def out_path(name): wandb.init( project=args.project, entity=args.wandb, - name=str(args.dataset) + "_" + str(args.model), + name=getattr(args, 'run_name', f"{args.dataset}_{args.model}"), config=args, ) @@ -391,6 +391,7 @@ def out_path(name): conc_sup = dataset.get_sup() for epoch in range(args.n_epochs): + args._current_epoch = epoch model.train() ys, y_true, cs, cs_true = None, None, None, None @@ -650,15 +651,16 @@ def out_path(name): # Joint (Type, Size, Color) concept collapse joint_collapse, joint_cm = raven_joint_concept_collapse(c_true_flat, c_pred_flat) - print(f"Joint concept collapse (27-class): {joint_collapse:.4f}") + n = int(round(joint_cm.shape[0] ** (1/3))) # derive n_vals from matrix size + print(f"Joint concept collapse ({n**3}-class): {joint_collapse:.4f}") plot_raven_joint_confusion_matrix( joint_cm, save_path=out_path(f"joint_concepts_{args.dataset}_{args.model}_lr_{args.lr}.png"), ) - # Pairwise joint collapse (9×9 each) + # Pairwise joint collapse pairwise = raven_pairwise_joint_collapse(c_true_flat, c_pred_flat) - pair_idx = {"Type×Size": (0, 1), "Type×Color": (0, 2), "Size×Color": (1, 2)} + pair_idx = {"TypexSize": (0, 1), "TypexColor": (0, 2), "SizexColor": (1, 2)} for pair_name, (clp, cm) in pairwise.items(): i, j = pair_idx[pair_name] safe_name = pair_name.replace("×", "x") From c5a8fa156135d90151417383bcb1f1d4bccfbe22 Mon Sep 17 00:00:00 2001 From: Juan Camacho Mohedano Date: Wed, 5 Aug 2026 00:16:25 +0200 Subject: [PATCH 16/16] Add RAVEN task documentation and --n_values CLI option --- rsseval/.github/raven-example.png | Bin 0 -> 288076 bytes rsseval/README.md | 82 +++++++++++++++++++++++++++++- rsseval/rss/utils/args.py | 7 +++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 rsseval/.github/raven-example.png diff --git a/rsseval/.github/raven-example.png b/rsseval/.github/raven-example.png new file mode 100644 index 0000000000000000000000000000000000000000..9ae7c4e7ed604492768359afa85e6f1909e089d2 GIT binary patch literal 288076 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%0A2RV;V`95*Jg(nLIGx1Ncd;Qsk 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 U;pA9u@?G}t)z!$`V|n5K0IY?p=Kufz literal 0 HcmV?d00001 diff --git a/rsseval/README.md b/rsseval/README.md index 43c82726..98aa7272 100644 --- a/rsseval/README.md +++ b/rsseval/README.md @@ -67,6 +67,86 @@ In this setting, which is the same as the one presented in [Marconato et al. (20 ![Kandinsky pattern illustration](.github/kand-illustration.png) +## RAVEN + +This branch adds a reasoning-shortcut task based on the RAVEN dataset, +introduced by [Zhang et al. (2019)](https://arxiv.org/abs/1903.02741). RAVEN is +a computer-vision dataset inspired by John C. Raven's original 1938 Raven's +Progressive Matrices (RPM) test, a non-verbal assessment of abstract reasoning. +The dataset name is an homage to Raven; its purpose is to evaluate structural, +relational, and analogical visual reasoning. + +An RPM problem contains a 3 by 3 matrix of grayscale panels: the first eight +panels form the context, the bottom-right panel is missing, and the model must +select the candidate that completes the matrix. Each RAVEN problem therefore +contains 16 panels: eight context panels and eight answer candidates, with a +target index from `0` through `7`. + +![Example RAVEN matrix and answer candidates](.github/raven-example.png) + +*Example from [Zhang et al.'s RAVEN paper](https://openaccess.thecvf.com/content_CVPR_2019/html/Zhang_RAVEN_A_Dataset_for_Relational_and_Analogical_Visual_REasoNing_CVPR_2019_paper.html): the upper matrix is the context and the lower panels are the answer candidates.* + +The current implementation supports the reduced `center_single` +configuration. Every panel contains one centered object, and the model reasons +over three rule-governed attributes: **Type**, **Size**, and **Color**. +`ravendpl` combines a shared panel encoder with factorized DeepProbLog +reasoning, scoring the candidate that is consistent with the rules inferred +from the first two rows. + +### Dataset layout + +RAVEN data is not included in the repository. The reduced datasets are +generated with a modified RAVEN generator, available at +[jucamohedano/RAVEN](https://github.com/jucamohedano/RAVEN) (a fork of +[WellyZhang/RAVEN](https://github.com/WellyZhang/RAVEN)): + +- branch [`raven-3x3x3`](https://github.com/jucamohedano/RAVEN/tree/raven-3x3x3) + (commit `65c6ba9`) for the three-value dataset; +- branch [`raven-4x4x4`](https://github.com/jucamohedano/RAVEN/tree/raven-4x4x4) + (commit `8fd5eb8`) for the four-value dataset. + +Each branch README documents the reduced attribute domains. From the desired +branch, generate the dataset with the generator's Python 2.7 environment: + +```sh +python src/dataset/main.py --num-samples 5000 --save-dir /RAVEN-3x3x3 +``` + +The defaults (`--val 2 --test 2 --seed 1234`) produce a 6:2:2 split of +3000 train / 1000 val / 1000 test samples. Use `--save-dir +/RAVEN-4x4x4` on the `raven-4x4x4` branch for the four-value +dataset. Then place the generated `.npz` panel data and matching `.xml` +metadata files under `rss/data`. The default three-value dataset is expected at: + +```text +rss/data/RAVEN-3x3x3/center_single/ + RAVEN_*_train.npz + RAVEN_*_train.xml + RAVEN_*_val.npz + RAVEN_*_val.xml + RAVEN_*_test.npz + RAVEN_*_test.xml +``` + +The implementation also supports a four-value-per-attribute dataset in +`RAVEN-4x4x4`. Select it with `--n_values 4`; the documented command below +uses the default 3x3x3 setting. + +### Train RavenDPL + +From the `rss` directory, train the default RAVEN-3x3x3 task with: + +```sh +python main.py --dataset raven --model ravendpl --task raven \ + --raven_config center_single --n_epochs 100 --batch_size 32 \ + --lr 0.0001 --weight_decay 0.00001 --entropy --w_h 0.1 \ + --c_sup 0 --seed 123 --output_dir ../outputs/raven-3x3x3-seed-123 +``` + +`--c_sup` controls the fraction of training samples with concept labels, while +`--which_c` selects the modeled attributes to supervise: `0` for Type, `1` for +Size, and `2` for Color. Use `--which_c -1` to supervise all three attributes. + ## Structure of the code * The code structure is similar to [Marconato et al. (2024) bears](https://github.com/samuelebortolotti/bears): @@ -218,4 +298,4 @@ cd docs; make html ## Libraries and extra tools -This code is adapted from [Marconato et al. (2024) bears](https://github.com/samuelebortolotti/bears). \ No newline at end of file +This code is adapted from [Marconato et al. (2024) bears](https://github.com/samuelebortolotti/bears). diff --git a/rsseval/rss/utils/args.py b/rsseval/rss/utils/args.py index 1b7ee514..abe04db0 100644 --- a/rsseval/rss/utils/args.py +++ b/rsseval/rss/utils/args.py @@ -48,6 +48,13 @@ def add_experiment_args(parser: ArgumentParser) -> None: default="center_single", help="RAVEN configuration (e.g. center_single)", ) + parser.add_argument( + "--n_values", + type=int, + choices=[3, 4], + default=3, + help="Number of values per RAVEN attribute.", + ) # model settings parser.add_argument( "--model",