Spaces:
Running on Zero
Running on Zero
| """PyTorch Dataset classes. | |
| We provide three: | |
| - ACSADataset text only, per-aspect labels -> Baseline 3 (no meta) | |
| - MetaACSADataset text + meta features -> Proposed model | |
| - OverallSentimentDataset text only, single 3-class -> Baseline 2 (BERT-overall) | |
| """ | |
| from typing import Optional | |
| import numpy as np | |
| import pandas as pd | |
| import torch | |
| from torch.utils.data import Dataset | |
| from . import config as cfg | |
| from .meta_encoder import MetaEncoder | |
| class _BaseTextDataset(Dataset): | |
| def __init__(self, df: pd.DataFrame, tokenizer, max_length: int = cfg.MAX_LENGTH, | |
| text_col: str = "full_text"): | |
| self.df = df.reset_index(drop=True) | |
| self.tokenizer = tokenizer | |
| self.max_length = max_length | |
| self.text_col = text_col | |
| def __len__(self): | |
| return len(self.df) | |
| def _encode_text(self, text: str): | |
| enc = self.tokenizer( | |
| text, | |
| max_length=self.max_length, | |
| padding="max_length", | |
| truncation=True, | |
| return_tensors="pt", | |
| ) | |
| return enc["input_ids"].squeeze(0), enc["attention_mask"].squeeze(0) | |
| class ACSADataset(_BaseTextDataset): | |
| """Per-aspect multi-head training, text-only. (Baseline 3.)""" | |
| def __init__(self, df, tokenizer, **kw): | |
| super().__init__(df, tokenizer, **kw) | |
| self.aspect_cols = [f"aspect_{a}" for a in cfg.ASPECTS] | |
| missing = [c for c in self.aspect_cols if c not in self.df.columns] | |
| if missing: | |
| raise KeyError(f"ACSADataset: missing aspect label columns {missing}. " | |
| f"Did you run weak labeling (script 03)?") | |
| def __getitem__(self, idx): | |
| row = self.df.iloc[idx] | |
| input_ids, attn = self._encode_text(str(row[self.text_col])) | |
| labels = torch.tensor([int(row[c]) for c in self.aspect_cols], dtype=torch.long) | |
| return {"input_ids": input_ids, "attention_mask": attn, "labels": labels} | |
| class MetaACSADataset(_BaseTextDataset): | |
| """Per-aspect multi-head training, text + metadata. (Proposed model.) | |
| The metadata is pre-encoded to a numpy matrix of shape (N, meta_dim) using | |
| a fit MetaEncoder, and indexed in lock-step with the text rows. | |
| If the df contains an 'overall_label' column (0=Neg, 1=Neu, 2=Pos), it is | |
| returned as part of each batch for the joint overall auxiliary loss. | |
| """ | |
| def __init__(self, df, tokenizer, meta_encoder: MetaEncoder, **kw): | |
| super().__init__(df, tokenizer, **kw) | |
| self.aspect_cols = [f"aspect_{a}" for a in cfg.ASPECTS] | |
| missing = [c for c in self.aspect_cols if c not in self.df.columns] | |
| if missing: | |
| raise KeyError(f"MetaACSADataset: missing aspect label columns {missing}.") | |
| self.has_overall = "overall_label" in self.df.columns | |
| # Pre-encode metadata once, in the same row order as self.df. | |
| self.meta_matrix = meta_encoder.transform(self.df) | |
| assert self.meta_matrix.shape[0] == len(self.df), \ | |
| "meta matrix and df length mismatch" | |
| def __getitem__(self, idx): | |
| row = self.df.iloc[idx] | |
| input_ids, attn = self._encode_text(str(row[self.text_col])) | |
| labels = torch.tensor([int(row[c]) for c in self.aspect_cols], dtype=torch.long) | |
| meta = torch.from_numpy(self.meta_matrix[idx]).float() | |
| item = { | |
| "input_ids": input_ids, | |
| "attention_mask": attn, | |
| "meta_features": meta, | |
| "labels": labels, | |
| } | |
| if self.has_overall: | |
| item["overall_labels"] = torch.tensor(int(row["overall_label"]), dtype=torch.long) | |
| return item | |
| class OverallSentimentDataset(_BaseTextDataset): | |
| """Single 3-class overall sentiment label. (Baseline 2: BERT-overall.)""" | |
| def __init__(self, df, tokenizer, label_col: str = "overall_label", **kw): | |
| super().__init__(df, tokenizer, **kw) | |
| self.label_col = label_col | |
| if self.label_col not in self.df.columns: | |
| raise KeyError(f"OverallSentimentDataset: missing {self.label_col} column.") | |
| def __getitem__(self, idx): | |
| row = self.df.iloc[idx] | |
| input_ids, attn = self._encode_text(str(row[self.text_col])) | |
| return { | |
| "input_ids": input_ids, | |
| "attention_mask": attn, | |
| "labels": torch.tensor(int(row[self.label_col]), dtype=torch.long), | |
| } | |