A815 commited on
Commit
2d98a7a
·
1 Parent(s): bb5358f
nlp4web-codebase ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit 83f9afbbf7e372c116fdd04997a96449007f861f
nlp4web_codebase/__init__.py DELETED
File without changes
nlp4web_codebase/__pycache__/__init__.cpython-312.pyc DELETED
Binary file (171 Bytes)
 
nlp4web_codebase/ir/__init__.py DELETED
File without changes
nlp4web_codebase/ir/__pycache__/__init__.cpython-312.pyc DELETED
Binary file (174 Bytes)
 
nlp4web_codebase/ir/__pycache__/analysis.cpython-312.pyc DELETED
Binary file (7.58 kB)
 
nlp4web_codebase/ir/analysis.py DELETED
@@ -1,160 +0,0 @@
1
- import os
2
- from typing import Dict, List, Optional, Protocol
3
- import pandas as pd
4
- import tqdm
5
- import ujson
6
- from nlp4web_codebase.ir.data_loaders import IRDataset
7
-
8
-
9
- def round_dict(obj: Dict[str, float], ndigits: int = 4) -> Dict[str, float]:
10
- return {k: round(v, ndigits=ndigits) for k, v in obj.items()}
11
-
12
-
13
- def sort_dict(obj: Dict[str, float], reverse: bool = True) -> Dict[str, float]:
14
- return dict(sorted(obj.items(), key=lambda pair: pair[1], reverse=reverse))
15
-
16
-
17
- def save_ranking_results(
18
- output_dir: str,
19
- query_ids: List[str],
20
- rankings: List[Dict[str, float]],
21
- query_performances_lists: List[Dict[str, float]],
22
- cid2tweights_lists: Optional[List[Dict[str, Dict[str, float]]]] = None,
23
- ):
24
- os.makedirs(output_dir, exist_ok=True)
25
- output_path = os.path.join(output_dir, "ranking_results.jsonl")
26
- rows = []
27
- for i, (query_id, ranking, query_performances) in enumerate(
28
- zip(query_ids, rankings, query_performances_lists)
29
- ):
30
- row = {
31
- "query_id": query_id,
32
- "ranking": round_dict(ranking),
33
- "query_performances": round_dict(query_performances),
34
- "cid2tweights": {},
35
- }
36
- if cid2tweights_lists is not None:
37
- row["cid2tweights"] = {
38
- cid: round_dict(tws) for cid, tws in cid2tweights_lists[i].items()
39
- }
40
- rows.append(row)
41
- pd.DataFrame(rows).to_json(
42
- output_path,
43
- orient="records",
44
- lines=True,
45
- )
46
-
47
-
48
- class TermWeightingFunction(Protocol):
49
- def __call__(self, query: str, cid: str) -> Dict[str, float]: ...
50
-
51
-
52
- def compare(
53
- dataset: IRDataset,
54
- results_path1: str,
55
- results_path2: str,
56
- output_dir: str,
57
- main_metric: str = "recip_rank",
58
- system1: Optional[str] = None,
59
- system2: Optional[str] = None,
60
- term_weighting_fn1: Optional[TermWeightingFunction] = None,
61
- term_weighting_fn2: Optional[TermWeightingFunction] = None,
62
- ) -> None:
63
- os.makedirs(output_dir, exist_ok=True)
64
- df1 = pd.read_json(results_path1, orient="records", lines=True)
65
- df2 = pd.read_json(results_path2, orient="records", lines=True)
66
- assert len(df1) == len(df2)
67
- all_qrels = {}
68
- for split in dataset.split2qrels:
69
- all_qrels.update(dataset.get_qrels_dict(split))
70
- qid2query = {query.query_id: query for query in dataset.queries}
71
- cid2doc = {doc.collection_id: doc for doc in dataset.corpus}
72
- diff_col = f"{main_metric}:qp1-qp2"
73
- merged = pd.merge(df1, df2, on="query_id", how="outer")
74
- rows = []
75
- for _, example in tqdm.tqdm(merged.iterrows(), desc="Comparing", total=len(merged)):
76
- docs = {cid: cid2doc[cid].text for cid in dict(example["ranking_x"])}
77
- docs.update({cid: cid2doc[cid].text for cid in dict(example["ranking_y"])})
78
- query_id = example["query_id"]
79
- row = {
80
- "query_id": query_id,
81
- "query": qid2query[query_id].text,
82
- diff_col: example["query_performances_x"][main_metric]
83
- - example["query_performances_y"][main_metric],
84
- "ranking1": ujson.dumps(example["ranking_x"], indent=4),
85
- "ranking2": ujson.dumps(example["ranking_y"], indent=4),
86
- "docs": ujson.dumps(docs, indent=4),
87
- "query_performances1": ujson.dumps(
88
- example["query_performances_x"], indent=4
89
- ),
90
- "query_performances2": ujson.dumps(
91
- example["query_performances_y"], indent=4
92
- ),
93
- "qrels": ujson.dumps(all_qrels[query_id], indent=4),
94
- }
95
- if term_weighting_fn1 is not None and term_weighting_fn2 is not None:
96
- all_cids = set(example["ranking_x"]) | set(example["ranking_y"])
97
- cid2tweights1 = {}
98
- cid2tweights2 = {}
99
- ranking1 = {}
100
- ranking2 = {}
101
- for cid in all_cids:
102
- tweights1 = term_weighting_fn1(query=qid2query[query_id].text, cid=cid)
103
- tweights2 = term_weighting_fn2(query=qid2query[query_id].text, cid=cid)
104
- ranking1[cid] = sum(tweights1.values())
105
- ranking2[cid] = sum(tweights2.values())
106
- cid2tweights1[cid] = tweights1
107
- cid2tweights2[cid] = tweights2
108
- ranking1 = sort_dict(ranking1)
109
- ranking2 = sort_dict(ranking2)
110
- row["ranking1"] = ujson.dumps(ranking1, indent=4)
111
- row["ranking2"] = ujson.dumps(ranking2, indent=4)
112
- cid2tweights1 = {cid: cid2tweights1[cid] for cid in ranking1}
113
- cid2tweights2 = {cid: cid2tweights2[cid] for cid in ranking2}
114
- row["cid2tweights1"] = ujson.dumps(cid2tweights1, indent=4)
115
- row["cid2tweights2"] = ujson.dumps(cid2tweights2, indent=4)
116
- rows.append(row)
117
- table = pd.DataFrame(rows).sort_values(by=diff_col, ascending=False)
118
- output_path = os.path.join(output_dir, f"compare-{system1}_vs_{system2}.tsv")
119
- table.to_csv(output_path, sep="\t", index=False)
120
-
121
-
122
- # if __name__ == "__main__":
123
- # # python -m lecture2.bm25.analysis
124
- # from nlp4web_codebase.ir.data_loaders.sciq import load_sciq
125
- # from lecture2.bm25.bm25_retriever import BM25Retriever
126
- # from lecture2.bm25.tfidf_retriever import TFIDFRetriever
127
- # import numpy as np
128
-
129
- # sciq = load_sciq()
130
- # system1 = "bm25"
131
- # system2 = "tfidf"
132
- # results_path1 = f"output/sciq-{system1}/results/ranking_results.jsonl"
133
- # results_path2 = f"output/sciq-{system2}/results/ranking_results.jsonl"
134
- # index_dir1 = f"output/sciq-{system1}"
135
- # index_dir2 = f"output/sciq-{system2}"
136
- # compare(
137
- # dataset=sciq,
138
- # results_path1=results_path1,
139
- # results_path2=results_path2,
140
- # output_dir=f"output/sciq-{system1}_vs_{system2}",
141
- # system1=system1,
142
- # system2=system2,
143
- # term_weighting_fn1=BM25Retriever(index_dir1).get_term_weights,
144
- # term_weighting_fn2=TFIDFRetriever(index_dir2).get_term_weights,
145
- # )
146
-
147
- # # bias on #shared_terms of TFIDF:
148
- # df1 = pd.read_json(results_path1, orient="records", lines=True)
149
- # df2 = pd.read_json(results_path2, orient="records", lines=True)
150
- # merged = pd.merge(df1, df2, on="query_id", how="outer")
151
- # nterms1 = []
152
- # nterms2 = []
153
- # for _, row in merged.iterrows():
154
- # nterms1.append(len(list(dict(row["cid2tweights_x"]).values())[0]))
155
- # nterms2.append(len(list(dict(row["cid2tweights_y"]).values())[0]))
156
- # percentiles = (5, 25, 50, 75, 95)
157
- # print(system1, np.percentile(nterms1, percentiles), np.mean(nterms1).round(2))
158
- # print(system2, np.percentile(nterms2, percentiles), np.mean(nterms2).round(2))
159
- # # bm25 [ 3. 4. 5. 7. 11.] 5.64
160
- # # tfidf [1. 2. 3. 5. 9.] 3.58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
nlp4web_codebase/ir/data_loaders/__init__.py DELETED
@@ -1,35 +0,0 @@
1
- from dataclasses import dataclass
2
- from enum import Enum
3
- from typing import Dict, List
4
- from nlp4web_codebase.ir.data_loaders.dm import Document, Query, QRel
5
-
6
-
7
- class Split(str, Enum):
8
- train = "train"
9
- dev = "dev"
10
- test = "test"
11
-
12
-
13
- @dataclass
14
- class IRDataset:
15
- corpus: List[Document]
16
- queries: List[Query]
17
- split2qrels: Dict[Split, List[QRel]]
18
-
19
- def get_stats(self) -> Dict[str, int]:
20
- stats = {"|corpus|": len(self.corpus), "|queries|": len(self.queries)}
21
- for split, qrels in self.split2qrels.items():
22
- stats[f"|qrels-{split}|"] = len(qrels)
23
- return stats
24
-
25
- def get_qrels_dict(self, split: Split) -> Dict[str, Dict[str, int]]:
26
- qrels_dict = {}
27
- for qrel in self.split2qrels[split]:
28
- qrels_dict.setdefault(qrel.query_id, {})
29
- qrels_dict[qrel.query_id][qrel.collection_id] = qrel.relevance
30
- return qrels_dict
31
-
32
- def get_split_queries(self, split: Split) -> List[Query]:
33
- qrels = self.split2qrels[split]
34
- qids = {qrel.query_id for qrel in qrels}
35
- return list(filter(lambda query: query.query_id in qids, self.queries))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
nlp4web_codebase/ir/data_loaders/__pycache__/__init__.cpython-312.pyc DELETED
Binary file (2.73 kB)
 
nlp4web_codebase/ir/data_loaders/__pycache__/dm.cpython-312.pyc DELETED
Binary file (1.05 kB)
 
nlp4web_codebase/ir/data_loaders/__pycache__/sciq.cpython-312.pyc DELETED
Binary file (3.4 kB)
 
nlp4web_codebase/ir/data_loaders/dm.py DELETED
@@ -1,22 +0,0 @@
1
- from dataclasses import dataclass
2
- from typing import Optional
3
-
4
-
5
- @dataclass
6
- class Document:
7
- collection_id: str
8
- text: str
9
-
10
-
11
- @dataclass
12
- class Query:
13
- query_id: str
14
- text: str
15
-
16
-
17
- @dataclass
18
- class QRel:
19
- query_id: str
20
- collection_id: str
21
- relevance: int
22
- answer: Optional[str] = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
nlp4web_codebase/ir/data_loaders/sciq.py DELETED
@@ -1,86 +0,0 @@
1
- from typing import Dict, List
2
- from nlp4web_codebase.ir.data_loaders import IRDataset, Split
3
- from nlp4web_codebase.ir.data_loaders.dm import Document, Query, QRel
4
- from datasets import load_dataset
5
- import joblib
6
-
7
-
8
- @(joblib.Memory(".cache").cache)
9
- def load_sciq(verbose: bool = False) -> IRDataset:
10
- train = load_dataset("allenai/sciq", split="train")
11
- validation = load_dataset("allenai/sciq", split="validation")
12
- test = load_dataset("allenai/sciq", split="test")
13
- data = {Split.train: train, Split.dev: validation, Split.test: test}
14
-
15
- # Each duplicated record is the same to each other:
16
- df = train.to_pandas() + validation.to_pandas() + test.to_pandas()
17
- for question, group in df.groupby("question"):
18
- assert len(set(group["support"].tolist())) == len(group)
19
- assert len(set(group["correct_answer"].tolist())) == len(group)
20
-
21
- # Build:
22
- corpus = []
23
- queries = []
24
- split2qrels: Dict[str, List[dict]] = {}
25
- question2id = {}
26
- support2id = {}
27
- for split, rows in data.items():
28
- if verbose:
29
- print(f"|raw_{split}|", len(rows))
30
- split2qrels[split] = []
31
- for i, row in enumerate(rows):
32
- example_id = f"{split}-{i}"
33
- support: str = row["support"]
34
- if len(support.strip()) == 0:
35
- continue
36
- question = row["question"]
37
- if len(support.strip()) == 0:
38
- continue
39
- if support in support2id:
40
- continue
41
- else:
42
- support2id[support] = example_id
43
- if question in question2id:
44
- continue
45
- else:
46
- question2id[question] = example_id
47
- doc = {"collection_id": example_id, "text": support}
48
- query = {"query_id": example_id, "text": row["question"]}
49
- qrel = {
50
- "query_id": example_id,
51
- "collection_id": example_id,
52
- "relevance": 1,
53
- "answer": row["correct_answer"],
54
- }
55
- corpus.append(Document(**doc))
56
- queries.append(Query(**query))
57
- split2qrels[split].append(QRel(**qrel))
58
-
59
- # Assembly and return:
60
- return IRDataset(corpus=corpus, queries=queries, split2qrels=split2qrels)
61
-
62
-
63
- if __name__ == "__main__":
64
- # python -m nlp4web_codebase.ir.data_loaders.sciq
65
- import ujson
66
- import time
67
-
68
- start = time.time()
69
- dataset = load_sciq(verbose=True)
70
- print(f"Loading costs: {time.time() - start}s")
71
- print(ujson.dumps(dataset.get_stats(), indent=4))
72
- # ________________________________________________________________________________
73
- # [Memory] Calling __main__--home-kwang-research-nlp4web-ir-exercise-nlp4web-nlp4web-ir-data_loaders-sciq.load_sciq...
74
- # load_sciq(verbose=True)
75
- # |raw_train| 11679
76
- # |raw_dev| 1000
77
- # |raw_test| 1000
78
- # ________________________________________________________load_sciq - 7.3s, 0.1min
79
- # Loading costs: 7.260092735290527s
80
- # {
81
- # "|corpus|": 12160,
82
- # "|queries|": 12160,
83
- # "|qrels-train|": 10409,
84
- # "|qrels-dev|": 875,
85
- # "|qrels-test|": 876
86
- # }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
nlp4web_codebase/ir/models/__init__.py DELETED
@@ -1,21 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from typing import Any, Dict, Type
3
-
4
-
5
- class BaseRetriever(ABC):
6
-
7
- @property
8
- @abstractmethod
9
- def index_class(self) -> Type[Any]:
10
- pass
11
-
12
- def get_term_weights(self, query: str, cid: str) -> Dict[str, float]:
13
- raise NotImplementedError
14
-
15
- @abstractmethod
16
- def score(self, query: str, cid: str) -> float:
17
- pass
18
-
19
- @abstractmethod
20
- def retrieve(self, query: str, topk: int = 10) -> Dict[str, float]:
21
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
nlp4web_codebase/ir/models/__pycache__/__init__.cpython-312.pyc DELETED
Binary file (1.4 kB)