| """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 |
|
|
| |
| 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), |
| } |
|
|