Download store.py from phamluan/itschool: direct link, hf CLI and curl.
- Browser
- Download file 9.07 kB
-
https://huggingface.co/spaces/phamluan/itschool/resolve/main/store.py
- Command line
-
hf download hf://spaces/phamluan/itschool/store.py
-
curl -L -o store.py https://huggingface.co/spaces/phamluan/itschool/resolve/main/store.py
9.07 kB
| """Nap du lieu da build va cung cap tra cuu / truy xuat ngu canh. | |
| Tat ca file nam trong space/data/, sinh boi build_data.py. Chi muc TF-IDF duoc | |
| dung mot lan luc khoi dong (1.322 doan ngu canh, duoi mot giay). | |
| """ | |
| import json | |
| import math | |
| import random | |
| import re | |
| import unicodedata | |
| from pathlib import Path | |
| from typing import Dict, List, Optional | |
| DATA = Path(__file__).parent / "data" | |
| MISSING = ( | |
| "Khong co du lieu trong space/data. Chay truoc:\n" | |
| ' python build_data.py "<duong-dan>/Thom Paper"' | |
| ) | |
| def _load(name: str, default=None): | |
| path = DATA / name | |
| if not path.exists(): | |
| return default | |
| return json.loads(path.read_text(encoding="utf-8")) | |
| dataset: List[dict] = _load("dataset.json", []) | |
| facets: dict = _load("facets.json", {}) | |
| stats_cache: dict = _load("stats.json", {}) | |
| lessons: list = _load("lessons.json", []) | |
| metrics: dict = _load("metrics.json", {}) | |
| testset: List[dict] = _load("testset.json", []) | |
| error_groups: list = _load("errors.json", []) | |
| build_info: dict = _load("built.json", {}) | |
| has_data = bool(dataset) | |
| by_id = {it["id"]: it for it in dataset} | |
| test_by_id = {it["id"]: it for it in testset} | |
| WORD = re.compile(r"[^\w\s]", re.UNICODE) | |
| def fold(text: str) -> List[str]: | |
| text = unicodedata.normalize("NFC", (text or "").lower()).replace("_", " ") | |
| return WORD.sub(" ", text).split() | |
| # --------------------------------------------- chi muc ngu canh cho truy xuat | |
| contexts: List[dict] = [] | |
| _seen = set() | |
| for _it in dataset: | |
| if _it["contextId"] in _seen: | |
| continue | |
| _seen.add(_it["contextId"]) | |
| contexts.append( | |
| { | |
| "contextId": _it["contextId"], | |
| "context": _it["context"], | |
| "grade": _it["grade"], | |
| "topic": _it["topic"], | |
| "lesson": _it["lesson"], | |
| "page": _it["page"], | |
| "section": _it["section"], | |
| } | |
| ) | |
| _df: Dict[str, int] = {} | |
| for _c in contexts: | |
| for _term in set(fold(_c["context"])): | |
| _df[_term] = _df.get(_term, 0) + 1 | |
| _N = len(contexts) or 1 | |
| def _idf(term: str) -> float: | |
| return math.log(1 + _N / (1 + _df.get(term, 0))) | |
| for _c in contexts: | |
| _tf: Dict[str, int] = {} | |
| for _term in fold(_c["context"]): | |
| _tf[_term] = _tf.get(_term, 0) + 1 | |
| _weights = {t: (1 + math.log(n)) * _idf(t) for t, n in _tf.items()} | |
| _c["_weights"] = _weights | |
| _c["_norm"] = math.sqrt(sum(w * w for w in _weights.values())) or 1.0 | |
| context_count = len(contexts) | |
| def _public(c: dict, score: Optional[float] = None) -> dict: | |
| out = {k: v for k, v in c.items() if not k.startswith("_")} | |
| if score is not None: | |
| out["score"] = round(score, 4) | |
| return out | |
| def retrieve_contexts( | |
| question: str, | |
| grade=None, | |
| lesson: Optional[str] = None, | |
| topic: Optional[str] = None, | |
| limit: int = 3, | |
| ) -> List[dict]: | |
| """Tim doan ngu canh khop nhat voi cau hoi, co the gioi han theo lop / bai.""" | |
| terms = fold(question) | |
| if not terms or not contexts: | |
| return [] | |
| tf: Dict[str, int] = {} | |
| for term in terms: | |
| tf[term] = tf.get(term, 0) + 1 | |
| q_weights = {t: (1 + math.log(n)) * _idf(t) for t, n in tf.items()} | |
| q_norm = math.sqrt(sum(w * w for w in q_weights.values())) or 1.0 | |
| grade_num = int(grade) if grade not in (None, "") else None | |
| def pick(use_lesson: bool, use_topic: bool): | |
| return [ | |
| c | |
| for c in contexts | |
| if (grade_num is None or c["grade"] == grade_num) | |
| and (not use_lesson or not lesson or c["lesson"] == lesson) | |
| and (not use_topic or not topic or c["topic"] == topic) | |
| ] | |
| # Noi long dan: bai hoc -> chu de -> ca lop. KHONG BAO GIO vuot ra ngoai lop | |
| # da chon, vi tra dap an lop 4 cho cau hoi lop 3 la sai hon la khong tra loi. | |
| candidates = pick(True, True) or pick(False, True) or pick(False, False) | |
| if not candidates: | |
| return [] | |
| scored = [] | |
| for c in candidates: | |
| dot = 0.0 | |
| weights = c["_weights"] | |
| for term, w in q_weights.items(): | |
| cw = weights.get(term) | |
| if cw: | |
| dot += w * cw | |
| if dot > 0: | |
| scored.append((dot / (q_norm * c["_norm"]), c)) | |
| if not scored: | |
| return [_public(candidates[0], 0.0)] if candidates else [] | |
| scored.sort(key=lambda pair: pair[0], reverse=True) | |
| return [_public(c, score) for score, c in scored[:limit]] | |
| # -------------------------------------------------------------------- tra cuu | |
| def search_dataset( | |
| q: Optional[str] = None, | |
| grade=None, | |
| topic: Optional[str] = None, | |
| lesson: Optional[str] = None, | |
| difficulty: Optional[str] = None, | |
| question_type: Optional[str] = None, | |
| split: Optional[str] = None, | |
| limit: int = 25, | |
| offset: int = 0, | |
| ) -> dict: | |
| limit = max(1, min(int(limit or 25), 200)) | |
| offset = max(0, int(offset or 0)) | |
| needle = (q or "").strip().lower() | |
| grade_num = int(grade) if grade not in (None, "") else None | |
| matched = [] | |
| for it in dataset: | |
| if grade_num is not None and it["grade"] != grade_num: | |
| continue | |
| if topic and it["topic"] != topic: | |
| continue | |
| if lesson and it["lesson"] != lesson: | |
| continue | |
| if difficulty and it["difficulty"] != difficulty: | |
| continue | |
| if question_type and it["questionType"] != question_type: | |
| continue | |
| if split and it["split"] != split: | |
| continue | |
| if needle: | |
| hay = f"{it['id']} {it['question']} {it['answer']} {it['context']} {it['lesson']}".lower() | |
| if needle not in hay: | |
| continue | |
| matched.append(it) | |
| page = [ | |
| {**it, "hasPredictions": it["split"] == "test" and it["id"] in test_by_id} | |
| for it in matched[offset : offset + limit] | |
| ] | |
| return {"total": len(matched), "offset": offset, "limit": limit, "items": page} | |
| def sample(grade=None) -> Optional[dict]: | |
| grade_num = int(grade) if grade not in (None, "") else None | |
| pool = [t for t in testset if grade_num is None or t["grade"] == grade_num] if grade_num else testset | |
| return random.choice(pool) if pool else None | |
| def dataset_stats() -> dict: | |
| return stats_cache or {"total": len(dataset), "contexts": context_count} | |
| # ------------------------------- chi muc cau hoi cua tap test (goi y thay the) | |
| _q_df: Dict[str, int] = {} | |
| for _t in testset: | |
| for _term in set(fold(_t["question"])): | |
| _q_df[_term] = _q_df.get(_term, 0) + 1 | |
| _QN = len(testset) or 1 | |
| def _q_idf(term: str) -> float: | |
| return math.log(1 + _QN / (1 + _q_df.get(term, 0))) | |
| _q_index = [] | |
| for _t in testset: | |
| _tf: Dict[str, int] = {} | |
| for _term in fold(_t["question"]): | |
| _tf[_term] = _tf.get(_term, 0) + 1 | |
| _w = {t: (1 + math.log(n)) * _q_idf(t) for t, n in _tf.items()} | |
| _q_index.append((_t, _w, math.sqrt(sum(v * v for v in _w.values())) or 1.0)) | |
| def nearest_questions(question: str, grade=None, limit: int = 3) -> List[dict]: | |
| """Cau hoi trong tap test gan nghia nhat — dung khi chua co checkpoint.""" | |
| terms = fold(question) | |
| if not terms or not _q_index: | |
| return [] | |
| tf: Dict[str, int] = {} | |
| for term in terms: | |
| tf[term] = tf.get(term, 0) + 1 | |
| q_weights = {t: (1 + math.log(n)) * _q_idf(t) for t, n in tf.items()} | |
| q_norm = math.sqrt(sum(w * w for w in q_weights.values())) or 1.0 | |
| grade_num = int(grade) if grade not in (None, "") else None | |
| scored = [] | |
| for item, weights, norm in _q_index: | |
| if grade_num is not None and item["grade"] != grade_num: | |
| continue | |
| dot = 0.0 | |
| for term, w in q_weights.items(): | |
| iw = weights.get(term) | |
| if iw: | |
| dot += w * iw | |
| if dot > 0: | |
| scored.append((dot / (q_norm * norm), item)) | |
| scored.sort(key=lambda pair: pair[0], reverse=True) | |
| return [ | |
| { | |
| "id": it["id"], | |
| "question": it["question"], | |
| "grade": it["grade"], | |
| "lesson": it["lesson"], | |
| "gold": it["gold"], | |
| "score": round(score, 4), | |
| } | |
| for score, it in scored[:limit] | |
| ] | |
| # ------------------------------- tra dap an chuan trong sach theo cau hoi | |
| # Thuc nghiem chi sinh du doan cho 1.073 cau test, nhung ca 10.961 cau deu co | |
| # dap an chuan. Khi chua co checkpoint, it nhat tra duoc dap an trong sach. | |
| _by_question: Dict[str, List[dict]] = {} | |
| for _it in dataset: | |
| _by_question.setdefault(" ".join(fold(_it["question"])), []).append(_it) | |
| def lookup_question(question: str, grade=None) -> Optional[dict]: | |
| """Tim cau hoi trung khop trong bo du lieu (ca train/dev/test).""" | |
| hits = _by_question.get(" ".join(fold(question))) | |
| if not hits: | |
| return None | |
| grade_num = int(grade) if grade not in (None, "") else None | |
| if grade_num is None: | |
| return hits[0] | |
| # Cung mot cau hoi co the xuat hien o nhieu lop voi dap an khac nhau — | |
| # chi nhan cau cua dung lop dang chon. | |
| for it in hits: | |
| if it["grade"] == grade_num: | |
| return it | |
| return None | |