uzzam2121
Fix build: bundle code in Space repo
fc7db98
Raw
History Blame Contribute Delete
4.44 kB
"""Shared constants and helpers for the word complexity project."""
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent
DATA_DIR = PROJECT_ROOT / "data"
OUTPUT_DIR = PROJECT_ROOT / "outputs"
CHECKPOINT_DIR = PROJECT_ROOT / "checkpoints"
EXPORT_DIR = PROJECT_ROOT / "exported_models"
LEVEL_ORDER = ["Very Easy", "Easy", "Medium", "Hard", "Very Hard"]
REASON_ORDER = ["Lexical Rarity", "Contextual Ambiguity", "Syntactic Complexity"]
HARD_LEVELS = {"Hard", "Very Hard"}
LEVEL_TO_ID = {level: idx for idx, level in enumerate(LEVEL_ORDER)}
ID_TO_LEVEL = {idx: level for level, idx in LEVEL_TO_ID.items()}
REASON_TO_ID = {reason: idx for idx, reason in enumerate(REASON_ORDER)}
ID_TO_REASON = {idx: reason for reason, idx in REASON_TO_ID.items()}
NONE_REASON_ID = -1
TGT_TOKEN = "[TGT]"
TGT_END_TOKEN = "[/TGT]"
# Input encoding strategies (SemEval / ABSA literature)
ENCODING_SPAN_MARK = "span_mark" # [TGT] word [/TGT] inside sentence
ENCODING_PAIR_CAMBRIDGE = "pair_cambridge" # [CLS] target [SEP] sentence
ENCODING_PAIR_CONTEXT = "pair_context" # sentence [SEP] target (SemEval common)
# Pooling strategies for target-focused readout
POOLING_SPAN = "span" # mean of tokens inside marked span (default)
POOLING_CLS_CONCAT = "cls_concat" # legacy: [CLS] + first [TGT]
POOLING_TGT_MARKER = "tgt_marker" # first [TGT] token only
POOLING_CLS_ONLY = "cls_only" # [CLS] only (for pair encoding)
MODELS = {
"deberta": "microsoft/deberta-v3-base",
"distilbert": "distilbert-base-uncased",
"roberta": "roberta-base",
}
MERGE_KEYS = ["id", "sentence", "target_word", "complexity_level"]
def level_id(level: str) -> int:
return LEVEL_TO_ID[level]
def reason_id(reason: str) -> int:
if reason in ("NONE", None) or (isinstance(reason, float) and str(reason) == "nan"):
return NONE_REASON_ID
return REASON_TO_ID[reason]
def is_hard_level(level: str) -> bool:
return level in HARD_LEVELS
def wrap_target_word(sentence: str, target_word: str) -> str:
"""Wrap target word with open/close span markers (ABSA aspect-marker style)."""
if not target_word or target_word not in sentence:
return sentence
marked = f"{TGT_TOKEN} {target_word} {TGT_END_TOKEN}"
return sentence.replace(target_word, marked, 1)
def build_model_input(
sentence: str,
target_word: str,
encoding: str = ENCODING_SPAN_MARK,
corpus: str | None = None,
) -> str | tuple[str, str]:
"""
Build tokenizer input for LCP.
Returns a string for span marking, or (text_a, text_b) for pair encodings.
"""
sentence = str(sentence)
target_word = str(target_word)
if encoding == ENCODING_SPAN_MARK:
return wrap_target_word(sentence, target_word)
if encoding == ENCODING_PAIR_CAMBRIDGE:
return target_word, sentence
if encoding == ENCODING_PAIR_CONTEXT:
if corpus:
return f"{corpus} {target_word}", sentence
return sentence, target_word
raise ValueError(f"Unknown encoding: {encoding}")
def difficult_class_probability(level_probs) -> float:
"""P(Hard) + P(Very Hard) from the 5-class softmax — auxiliary only, not a regression score."""
if hasattr(level_probs, "tolist"):
level_probs = level_probs.tolist()
if isinstance(level_probs, dict):
return float(level_probs.get("Hard", 0) + level_probs.get("Very Hard", 0))
return float(level_probs[3] + level_probs[4])
def ensure_dirs() -> None:
DATA_DIR.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
CHECKPOINT_DIR.mkdir(parents=True, exist_ok=True)
EXPORT_DIR.mkdir(parents=True, exist_ok=True)
def tokenize_lcp_input(
tokenizer,
sentence: str,
target_word: str,
encoding: str = ENCODING_SPAN_MARK,
max_length: int = 192,
corpus: str | None = None,
):
"""Tokenize a (sentence, target_word) pair for the LCP model."""
built = build_model_input(sentence, target_word, encoding=encoding, corpus=corpus)
if isinstance(built, tuple):
return tokenizer(
built[0],
built[1],
truncation=True,
max_length=max_length,
padding="max_length",
return_tensors="pt",
)
return tokenizer(
built,
truncation=True,
max_length=max_length,
padding="max_length",
return_tensors="pt",
)