Spaces:
Sleeping
Sleeping
ktsn-ud commited on
Commit ·
59424a5
1
Parent(s): 5feeb19
cc.ja.300.binのパスの参照先をfiles.jsonに一本化
Browse files- api/search/engine.py +51 -23
- config/search_model.json +0 -1
api/search/engine.py
CHANGED
|
@@ -96,9 +96,11 @@ class SearchEngine:
|
|
| 96 |
word_sim_enable=bool(search("word_sim.enable")),
|
| 97 |
word_sim_alpha=float(search("word_sim.alpha")),
|
| 98 |
word_sim_topk_k=int(search("word_sim.topk_k", 3)),
|
| 99 |
-
word_sim_rerank=(
|
|
|
|
|
|
|
| 100 |
query_subword_enable=bool(search("query_subword.enable")),
|
| 101 |
-
query_subword_path=
|
| 102 |
query_subword_oov_weight=float(search("query_subword.oov_weight")),
|
| 103 |
org_boost_exact=float(search("organization.boost.exact", 1.0)),
|
| 104 |
org_boost_prefix=float(search("organization.boost.prefix", 0.7)),
|
|
@@ -132,7 +134,9 @@ class SearchEngine:
|
|
| 132 |
log.warning(f"failed to load synonyms_cache: {e}")
|
| 133 |
|
| 134 |
try:
|
| 135 |
-
custom_path = field_getter("config/search_model.json")(
|
|
|
|
|
|
|
| 136 |
if custom_path and os.path.exists(custom_path):
|
| 137 |
with open(custom_path, encoding="utf-8") as f:
|
| 138 |
self.custom_synonyms = json.load(f)
|
|
@@ -143,8 +147,14 @@ class SearchEngine:
|
|
| 143 |
with open(files("projects.projects_json"), encoding="utf-8") as f:
|
| 144 |
self.projects = json.load(f)
|
| 145 |
self.project_map = {p["projectId"]: p for p in self.projects}
|
| 146 |
-
self.org_norms = {
|
| 147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
|
| 149 |
# BM25F assets
|
| 150 |
with open(files("bm25.bm25_meta"), encoding="utf-8") as f:
|
|
@@ -169,7 +179,11 @@ class SearchEngine:
|
|
| 169 |
# doc_vectors.npy は topk 方式では不要
|
| 170 |
|
| 171 |
# fastText OOV
|
| 172 |
-
if
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
try:
|
| 174 |
import fasttext
|
| 175 |
|
|
@@ -246,7 +260,7 @@ class SearchEngine:
|
|
| 246 |
denom_sum = 0.0
|
| 247 |
num_sum = 0.0
|
| 248 |
for field, weight in fw.items():
|
| 249 |
-
fobj =
|
| 250 |
tf = float((fobj.get("tf") or {}).get(t, 0))
|
| 251 |
if tf <= 0.0:
|
| 252 |
continue
|
|
@@ -314,8 +328,8 @@ class SearchEngine:
|
|
| 314 |
fields = d.get("fields") or {}
|
| 315 |
doc_terms = set()
|
| 316 |
for fname in self.cfg.target_fields:
|
| 317 |
-
fobj =
|
| 318 |
-
tf =
|
| 319 |
doc_terms.update(tf.keys())
|
| 320 |
if not doc_terms:
|
| 321 |
sims_all[i] = 0.0
|
|
@@ -371,8 +385,8 @@ class SearchEngine:
|
|
| 371 |
fields = d.get("fields") or {}
|
| 372 |
doc_terms = set()
|
| 373 |
for fname in self.cfg.target_fields:
|
| 374 |
-
fobj =
|
| 375 |
-
tf =
|
| 376 |
doc_terms.update(tf.keys())
|
| 377 |
if not doc_terms:
|
| 378 |
sims_all[i] = 0.0
|
|
@@ -442,9 +456,15 @@ class SearchEngine:
|
|
| 442 |
# Relative threshold anchored to the top fused score
|
| 443 |
score_with_boost = fused_filter + boost
|
| 444 |
top = float(np.max(score_with_boost)) if score_with_boost.size > 0 else 0.0
|
| 445 |
-
rel_cut =
|
|
|
|
|
|
|
| 446 |
fused_cut = max(float(self.cfg.fused_min), rel_cut)
|
| 447 |
-
keep = (
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
order = np.argsort(-score_with_boost) # descending by fused
|
| 449 |
selected_idx: List[int] = []
|
| 450 |
for i in order:
|
|
@@ -454,7 +474,11 @@ class SearchEngine:
|
|
| 454 |
break
|
| 455 |
# Single-step fallback: if zero, relax the relative cut and use absolute thresholds only
|
| 456 |
if len(selected_idx) == 0:
|
| 457 |
-
keep2 = (
|
|
|
|
|
|
|
|
|
|
|
|
|
| 458 |
for i in order:
|
| 459 |
if keep2[i]:
|
| 460 |
selected_idx.append(int(i))
|
|
@@ -477,15 +501,19 @@ class SearchEngine:
|
|
| 477 |
# build debug details for selected docs
|
| 478 |
details = []
|
| 479 |
for i in selected_idx:
|
| 480 |
-
details.append(
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 489 |
return pairs, {"details": details}
|
| 490 |
|
| 491 |
def get_projects(self) -> List[Dict[str, Any]]:
|
|
|
|
| 96 |
word_sim_enable=bool(search("word_sim.enable")),
|
| 97 |
word_sim_alpha=float(search("word_sim.alpha")),
|
| 98 |
word_sim_topk_k=int(search("word_sim.topk_k", 3)),
|
| 99 |
+
word_sim_rerank=(
|
| 100 |
+
search("word_sim.rerank", "pair_avg") or "pair_avg"
|
| 101 |
+
).lower(),
|
| 102 |
query_subword_enable=bool(search("query_subword.enable")),
|
| 103 |
+
query_subword_path=files("embeddings.fasttext_bin"),
|
| 104 |
query_subword_oov_weight=float(search("query_subword.oov_weight")),
|
| 105 |
org_boost_exact=float(search("organization.boost.exact", 1.0)),
|
| 106 |
org_boost_prefix=float(search("organization.boost.prefix", 0.7)),
|
|
|
|
| 134 |
log.warning(f"failed to load synonyms_cache: {e}")
|
| 135 |
|
| 136 |
try:
|
| 137 |
+
custom_path = field_getter("config/search_model.json")(
|
| 138 |
+
"synonyms.sources.custom_json"
|
| 139 |
+
)
|
| 140 |
if custom_path and os.path.exists(custom_path):
|
| 141 |
with open(custom_path, encoding="utf-8") as f:
|
| 142 |
self.custom_synonyms = json.load(f)
|
|
|
|
| 147 |
with open(files("projects.projects_json"), encoding="utf-8") as f:
|
| 148 |
self.projects = json.load(f)
|
| 149 |
self.project_map = {p["projectId"]: p for p in self.projects}
|
| 150 |
+
self.org_norms = {
|
| 151 |
+
p["projectId"]: normalize_text_for_org(p.get("organization") or "")
|
| 152 |
+
for p in self.projects
|
| 153 |
+
}
|
| 154 |
+
self.reading_norms = {
|
| 155 |
+
p["projectId"]: normalize_text_for_org(p.get("reading") or "")
|
| 156 |
+
for p in self.projects
|
| 157 |
+
}
|
| 158 |
|
| 159 |
# BM25F assets
|
| 160 |
with open(files("bm25.bm25_meta"), encoding="utf-8") as f:
|
|
|
|
| 179 |
# doc_vectors.npy は topk 方式では不要
|
| 180 |
|
| 181 |
# fastText OOV
|
| 182 |
+
if (
|
| 183 |
+
self.cfg.query_subword_enable
|
| 184 |
+
and self.cfg.query_subword_path
|
| 185 |
+
and os.path.exists(self.cfg.query_subword_path)
|
| 186 |
+
):
|
| 187 |
try:
|
| 188 |
import fasttext
|
| 189 |
|
|
|
|
| 260 |
denom_sum = 0.0
|
| 261 |
num_sum = 0.0
|
| 262 |
for field, weight in fw.items():
|
| 263 |
+
fobj = fields.get(field) or {}
|
| 264 |
tf = float((fobj.get("tf") or {}).get(t, 0))
|
| 265 |
if tf <= 0.0:
|
| 266 |
continue
|
|
|
|
| 328 |
fields = d.get("fields") or {}
|
| 329 |
doc_terms = set()
|
| 330 |
for fname in self.cfg.target_fields:
|
| 331 |
+
fobj = fields.get(fname) or {}
|
| 332 |
+
tf = fobj.get("tf") or {}
|
| 333 |
doc_terms.update(tf.keys())
|
| 334 |
if not doc_terms:
|
| 335 |
sims_all[i] = 0.0
|
|
|
|
| 385 |
fields = d.get("fields") or {}
|
| 386 |
doc_terms = set()
|
| 387 |
for fname in self.cfg.target_fields:
|
| 388 |
+
fobj = fields.get(fname) or {}
|
| 389 |
+
tf = fobj.get("tf") or {}
|
| 390 |
doc_terms.update(tf.keys())
|
| 391 |
if not doc_terms:
|
| 392 |
sims_all[i] = 0.0
|
|
|
|
| 456 |
# Relative threshold anchored to the top fused score
|
| 457 |
score_with_boost = fused_filter + boost
|
| 458 |
top = float(np.max(score_with_boost)) if score_with_boost.size > 0 else 0.0
|
| 459 |
+
rel_cut = (
|
| 460 |
+
top * float(self.cfg.fused_rel_top_ratio) if top > 0 else self.cfg.fused_min
|
| 461 |
+
)
|
| 462 |
fused_cut = max(float(self.cfg.fused_min), rel_cut)
|
| 463 |
+
keep = (
|
| 464 |
+
(bm25 >= self.cfg.bm25_min)
|
| 465 |
+
| (ws_filter >= self.cfg.word_sim_min)
|
| 466 |
+
| (score_with_boost >= self.cfg.fused_min)
|
| 467 |
+
) & (score_with_boost >= fused_cut)
|
| 468 |
order = np.argsort(-score_with_boost) # descending by fused
|
| 469 |
selected_idx: List[int] = []
|
| 470 |
for i in order:
|
|
|
|
| 474 |
break
|
| 475 |
# Single-step fallback: if zero, relax the relative cut and use absolute thresholds only
|
| 476 |
if len(selected_idx) == 0:
|
| 477 |
+
keep2 = (
|
| 478 |
+
(bm25 >= self.cfg.bm25_min)
|
| 479 |
+
| (ws_filter >= self.cfg.word_sim_min)
|
| 480 |
+
| (score_with_boost >= self.cfg.fused_min)
|
| 481 |
+
)
|
| 482 |
for i in order:
|
| 483 |
if keep2[i]:
|
| 484 |
selected_idx.append(int(i))
|
|
|
|
| 501 |
# build debug details for selected docs
|
| 502 |
details = []
|
| 503 |
for i in selected_idx:
|
| 504 |
+
details.append(
|
| 505 |
+
{
|
| 506 |
+
"projectId": ids[i],
|
| 507 |
+
"bm25": float(bm25[i]),
|
| 508 |
+
"ws_filter_topk": float(ws_filter[i]),
|
| 509 |
+
"ws_rerank_pairavg": float(ws_rerank[i])
|
| 510 |
+
if ws_rerank is not None
|
| 511 |
+
else None,
|
| 512 |
+
"org_boost": float(boost[i]),
|
| 513 |
+
"fused_filter": float(fused_filter[i]),
|
| 514 |
+
"fused_final": float(final_scores[i]),
|
| 515 |
+
}
|
| 516 |
+
)
|
| 517 |
return pairs, {"details": details}
|
| 518 |
|
| 519 |
def get_projects(self) -> List[Dict[str, Any]]:
|
config/search_model.json
CHANGED
|
@@ -51,7 +51,6 @@
|
|
| 51 |
},
|
| 52 |
"query_subword": {
|
| 53 |
"enable": true,
|
| 54 |
-
"path": "resources/embeddings/cc.ja.300.bin",
|
| 55 |
"oov_weight": 0.8,
|
| 56 |
"cache_size": 50000
|
| 57 |
},
|
|
|
|
| 51 |
},
|
| 52 |
"query_subword": {
|
| 53 |
"enable": true,
|
|
|
|
| 54 |
"oov_weight": 0.8,
|
| 55 |
"cache_size": 50000
|
| 56 |
},
|