Spaces:
Runtime error
Runtime error
Download backend/app/ml/features.py from prathvi9607/vera-api: direct link, hf CLI and curl.
- Browser
- Download file 4.37 kB
-
https://huggingface.co/spaces/prathvi9607/vera-api/resolve/main/backend/app/ml/features.py
- Command line
-
hf download hf://spaces/prathvi9607/vera-api/backend/app/ml/features.py
-
curl -L -o features.py https://huggingface.co/spaces/prathvi9607/vera-api/resolve/main/backend/app/ml/features.py
4.37 kB
| """Fusion feature contract (PRD §10.1). | |
| This module is the SINGLE source of truth for the 40-dimensional feature vector. | |
| Notebook 05 (training), notebook 06 (calibration), and the production pipeline | |
| all call :func:`build_feature_vector`, so train/serve skew is impossible by | |
| construction. Changing the order or meaning of any feature requires retraining | |
| the ensemble and re-fitting the calibrator. | |
| Imputation of missing metadata happens upstream in Agent 5 | |
| (:class:`~app.ml.agents.quality.QualityAssessor`); the PRD's | |
| ``build_feature_vector(imputation=...)`` parameter was never actually used. | |
| """ | |
| from __future__ import annotations | |
| from typing import Any, Final | |
| import numpy as np | |
| from app.ml.constants import EARBUD_ASPECTS, SENTIMENT_TO_NUMERIC | |
| FEATURE_NAMES: Final[tuple[str, ...]] = ( | |
| # Base signals (3) | |
| "star_rating", | |
| "experience_score", | |
| "domain_score", | |
| # Consistency signals (4) | |
| "star_text_gap", | |
| "absolute_gap", | |
| "star_inflated", | |
| "consistency_score", | |
| # Quality signals (4) | |
| "quality_score", | |
| "helpfulness_score", | |
| "freshness_score", | |
| "verified_purchase", | |
| # Text quality signals (4) | |
| "word_count_norm", | |
| "vocab_richness", | |
| "avg_sentence_length_norm", | |
| "experience_model_uncertainty", | |
| # Aspect detection (10) + aspect sentiment (10) | |
| *(f"aspect_detected__{a}" for a in EARBUD_ASPECTS), | |
| *(f"aspect_sentiment__{a}" for a in EARBUD_ASPECTS), | |
| # Interactions (5) | |
| "star_x_experience", | |
| "abs_star_minus_domain", | |
| "experience_x_quality", | |
| "consistency_x_trust", | |
| "domain_confidence_x_domain_score", | |
| ) | |
| N_FEATURES: Final = len(FEATURE_NAMES) | |
| #: Feature-name groups used by the ablation study (PRD §13.2). | |
| FEATURE_GROUPS: Final[dict[str, tuple[str, ...]]] = { | |
| "star": ("star_rating",), | |
| "experience": ("experience_score", "experience_model_uncertainty", "star_x_experience"), | |
| "domain": ("domain_score", "abs_star_minus_domain", "domain_confidence_x_domain_score"), | |
| "consistency": ("star_text_gap", "absolute_gap", "star_inflated", "consistency_score"), | |
| "quality": ( | |
| "quality_score", | |
| "helpfulness_score", | |
| "freshness_score", | |
| "verified_purchase", | |
| "word_count_norm", | |
| "vocab_richness", | |
| "avg_sentence_length_norm", | |
| "experience_x_quality", | |
| "consistency_x_trust", | |
| ), | |
| "absa": tuple(n for n in FEATURE_NAMES if n.startswith("aspect_")), | |
| } | |
| def build_feature_vector(agent_outputs: dict[str, Any]) -> np.ndarray: | |
| """Assemble the fusion features from Agents 1–6. | |
| Args: | |
| agent_outputs: Mapping with keys ``preprocessor``, ``experience_extractor``, | |
| ``absa``, ``consistency``, ``quality``, ``domain_rating`` (each the | |
| agent's ``process``/``predict`` output) and ``star_rating``. | |
| Returns: | |
| ``float32`` array of shape ``(N_FEATURES,)``. | |
| """ | |
| pre = agent_outputs["preprocessor"] | |
| exp = agent_outputs["experience_extractor"] | |
| absa = agent_outputs["absa"]["aspects"] | |
| cons = agent_outputs["consistency"] | |
| qual = agent_outputs["quality"] | |
| dom = agent_outputs["domain_rating"] | |
| star = float(agent_outputs["star_rating"]) | |
| feats: list[float] = [ | |
| star, | |
| exp["predicted_score"], | |
| dom["domain_score"], | |
| cons["star_text_gap"], | |
| cons["absolute_gap"], | |
| float(cons["star_inflated"]), | |
| cons["consistency_score"], | |
| qual["quality_score"], | |
| qual["helpfulness_score"], | |
| qual["freshness_score"], | |
| float(qual["verified_purchase"]), | |
| pre["word_count"] / 500, | |
| pre["vocab_richness"], | |
| pre["avg_sentence_length"] / 20, | |
| exp["model_uncertainty"], | |
| ] | |
| feats += [float(absa[a]["detected"]) for a in EARBUD_ASPECTS] | |
| feats += [float(SENTIMENT_TO_NUMERIC[absa[a]["sentiment"]]) for a in EARBUD_ASPECTS] | |
| feats += [ | |
| star * exp["predicted_score"], | |
| abs(star - dom["domain_score"]), | |
| exp["predicted_score"] * qual["quality_score"], | |
| cons["consistency_score"] * qual["trust_weight"], | |
| dom["domain_confidence"] * dom["domain_score"], | |
| ] | |
| vector = np.asarray(feats, dtype=np.float32) | |
| if vector.shape != (N_FEATURES,): # guards against silent contract drift | |
| raise AssertionError(f"feature vector has {vector.shape[0]} dims, expected {N_FEATURES}") | |
| return vector | |