Spaces:
Sleeping
Sleeping
Merge pull request #1 from chibafes-dev/feat/scripts
Browse files- .gitignore +3 -0
- .vscode/extensions.json +9 -0
- .vscode/settings.json +4 -0
- api/__init__.py +2 -0
- api/main.py +123 -0
- api/search/__init__.py +2 -0
- api/search/engine.py +495 -0
- config/files.json +25 -0
- config/search_model.json +74 -0
- docs/files.md +15 -0
- docs/old_index.md +245 -0
- docs/search_implementation_plan.md +141 -0
- docs/test.md +2063 -0
- requirements.txt +27 -0
- resources/stopwords.json +11 -0
- resources/synonyms_DO_NOT_EDIT.txt +0 -0
- resources/synonyms_custom.json +608 -0
- resources/test_data.csv +51 -0
- resources/user_dict.csv +75 -0
- schemas/__init__.py +0 -0
- schemas/projects.py +151 -0
- schemas/tf_token.py +22 -0
- scripts/1_build_dict.py +111 -0
- scripts/2_create_projects_data.py +67 -0
- scripts/3_build_synonyms_from_sudachi.py +171 -0
- scripts/4_prepare_bm25f_meta.py +117 -0
- scripts/5_prepare_tf_token.py +124 -0
- scripts/6_build_word_embeddings.py +146 -0
- scripts/build_all.py +53 -0
- scripts/sudachi.json +6 -0
- utils/__init__.py +0 -0
- utils/io.py +38 -0
- utils/json.py +93 -0
- utils/logger.py +10 -0
- utils/text_process.py +14 -0
.gitignore
CHANGED
|
@@ -42,3 +42,6 @@ resources/**/*.bin
|
|
| 42 |
.Trashes
|
| 43 |
ehthumbs.db
|
| 44 |
Thumbs.db
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
.Trashes
|
| 43 |
ehthumbs.db
|
| 44 |
Thumbs.db
|
| 45 |
+
|
| 46 |
+
# generated data
|
| 47 |
+
data/generated
|
.vscode/extensions.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"recommendations": [
|
| 3 |
+
"ms-python.python",
|
| 4 |
+
"charliermarsh.ruff", // formatter + linter
|
| 5 |
+
"usernamehw.errorlens",
|
| 6 |
+
"mhutchie.git-graph",
|
| 7 |
+
"mechatroner.rainbow-csv",
|
| 8 |
+
]
|
| 9 |
+
}
|
.vscode/settings.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"editor.defaultFormatter": "charliermarsh.ruff",
|
| 3 |
+
"editor.formatOnSave": true
|
| 4 |
+
}
|
api/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__all__ = []
|
| 2 |
+
|
api/main.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
import gzip
|
| 4 |
+
from contextlib import asynccontextmanager
|
| 5 |
+
|
| 6 |
+
from fastapi import FastAPI, Response, Depends, HTTPException, Security, Query
|
| 7 |
+
from fastapi.responses import RedirectResponse, JSONResponse
|
| 8 |
+
from fastapi.security import APIKeyHeader
|
| 9 |
+
from pydantic import BaseModel
|
| 10 |
+
|
| 11 |
+
from dotenv import load_dotenv
|
| 12 |
+
|
| 13 |
+
from utils.logger import setup_logger
|
| 14 |
+
from api.search.engine import SearchEngine
|
| 15 |
+
import schemas.projects as schema_projects
|
| 16 |
+
|
| 17 |
+
log = setup_logger(__name__)
|
| 18 |
+
|
| 19 |
+
load_dotenv()
|
| 20 |
+
|
| 21 |
+
# --- Auth ---
|
| 22 |
+
API_KEY_NAME = "X-API-KEY"
|
| 23 |
+
API_SECRET_KEY = os.getenv("API_SECRET_KEY")
|
| 24 |
+
api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=True)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
async def get_api_key(key: str = Security(api_key_header)):
|
| 28 |
+
"""APIキーを検証する依存関係"""
|
| 29 |
+
if not API_SECRET_KEY or key != API_SECRET_KEY:
|
| 30 |
+
raise HTTPException(status_code=403, detail="Could not validate credentials.")
|
| 31 |
+
return key
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
# --- App ---
|
| 35 |
+
@asynccontextmanager
|
| 36 |
+
async def lifespan(app: FastAPI):
|
| 37 |
+
"""アプリケーションの起動時と終了時に実行されるコード"""
|
| 38 |
+
# --- 起動時処理 ---
|
| 39 |
+
log.info("Initializing search engine and loading assets...")
|
| 40 |
+
load_dotenv()
|
| 41 |
+
engine.initialize()
|
| 42 |
+
log.info("Initialization complete.")
|
| 43 |
+
yield
|
| 44 |
+
|
| 45 |
+
# --- 終了時処理 ---
|
| 46 |
+
log.info("Shutting down search engine...")
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
engine = SearchEngine()
|
| 50 |
+
app = FastAPI(lifespan=lifespan)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
@app.get("/", include_in_schema=False)
|
| 54 |
+
def root():
|
| 55 |
+
return RedirectResponse("/docs")
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
@app.get("/api/health")
|
| 59 |
+
def health_check():
|
| 60 |
+
return {"status": "ok"}
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
@app.get(
|
| 64 |
+
"/api/projects",
|
| 65 |
+
response_model=list[schema_projects.ProjectSummary],
|
| 66 |
+
dependencies=[Depends(get_api_key)],
|
| 67 |
+
)
|
| 68 |
+
def get_summary_data():
|
| 69 |
+
summary_fields = set(schema_projects.ProjectSummary.model_fields.keys())
|
| 70 |
+
summaries = [
|
| 71 |
+
{k: v for k, v in p.items() if k in summary_fields}
|
| 72 |
+
for p in engine.get_projects()
|
| 73 |
+
]
|
| 74 |
+
content = json.dumps(summaries, ensure_ascii=False).encode("utf-8")
|
| 75 |
+
return Response(
|
| 76 |
+
content=gzip.compress(content),
|
| 77 |
+
headers={"Content-Encoding": "gzip", "Content-Type": "application/json"},
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
@app.get(
|
| 82 |
+
"/api/details",
|
| 83 |
+
response_model=schema_projects.ProjectDetail,
|
| 84 |
+
dependencies=[Depends(get_api_key)],
|
| 85 |
+
)
|
| 86 |
+
def get_project_detail(projectId: str = Query(..., description="取得したい企画のID")):
|
| 87 |
+
p = engine.get_project_map().get(projectId)
|
| 88 |
+
if not p:
|
| 89 |
+
raise HTTPException(status_code=404, detail="Project not found")
|
| 90 |
+
fields = set(schema_projects.ProjectDetail.model_fields.keys())
|
| 91 |
+
return {k: v for k, v in p.items() if k in fields}
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
class SearchRequest(BaseModel):
|
| 95 |
+
query: str
|
| 96 |
+
debug: bool = False
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
@app.post(
|
| 100 |
+
"/api/search",
|
| 101 |
+
response_model=schema_projects.ProjectIds,
|
| 102 |
+
dependencies=[Depends(get_api_key)],
|
| 103 |
+
)
|
| 104 |
+
def search(request: SearchRequest):
|
| 105 |
+
if not request.query:
|
| 106 |
+
raise HTTPException(status_code=400, detail="Query cannot be empty")
|
| 107 |
+
|
| 108 |
+
result = engine.search(request.query, debug=request.debug)
|
| 109 |
+
if request.debug:
|
| 110 |
+
pairs, diag = result # type: ignore
|
| 111 |
+
ids = [pid for pid, _ in pairs]
|
| 112 |
+
return JSONResponse(
|
| 113 |
+
content={
|
| 114 |
+
"projectIds": ids,
|
| 115 |
+
"scores": [
|
| 116 |
+
{"projectId": pid, "score": float(score)} for pid, score in pairs
|
| 117 |
+
],
|
| 118 |
+
"details": diag.get("details", []),
|
| 119 |
+
}
|
| 120 |
+
)
|
| 121 |
+
pairs = result # type: ignore
|
| 122 |
+
ids = [pid for pid, _ in pairs]
|
| 123 |
+
return schema_projects.ProjectIds(projectIds=ids)
|
api/search/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__all__ = []
|
| 2 |
+
|
api/search/engine.py
ADDED
|
@@ -0,0 +1,495 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 5 |
+
|
| 6 |
+
import numpy as np
|
| 7 |
+
from sudachipy import dictionary, tokenizer
|
| 8 |
+
|
| 9 |
+
from utils.json import field_getter
|
| 10 |
+
from utils.logger import setup_logger
|
| 11 |
+
|
| 12 |
+
log = setup_logger(__name__)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@dataclass
|
| 16 |
+
class SearchConfig:
|
| 17 |
+
target_pos_l1: List[str]
|
| 18 |
+
target_fields: List[str]
|
| 19 |
+
k1: float
|
| 20 |
+
b: float
|
| 21 |
+
field_weights: Dict[str, float]
|
| 22 |
+
synonyms_enable: bool
|
| 23 |
+
syn_limits: Dict[str, int]
|
| 24 |
+
banlist: List[str]
|
| 25 |
+
word_sim_enable: bool
|
| 26 |
+
word_sim_alpha: float
|
| 27 |
+
word_sim_topk_k: int
|
| 28 |
+
word_sim_rerank: str
|
| 29 |
+
query_subword_enable: bool
|
| 30 |
+
query_subword_path: str
|
| 31 |
+
query_subword_oov_weight: float
|
| 32 |
+
org_boost_exact: float
|
| 33 |
+
org_boost_prefix: float
|
| 34 |
+
org_boost_substring: float
|
| 35 |
+
org_boost_min_len: int
|
| 36 |
+
# filter
|
| 37 |
+
min_results: int
|
| 38 |
+
max_results: int
|
| 39 |
+
bm25_min: float
|
| 40 |
+
word_sim_min: float
|
| 41 |
+
fused_min: float
|
| 42 |
+
fused_rel_top_ratio: float
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def normalize_text_for_org(s: str) -> str:
|
| 46 |
+
try:
|
| 47 |
+
import unicodedata
|
| 48 |
+
|
| 49 |
+
s = unicodedata.normalize("NFKC", s)
|
| 50 |
+
except Exception:
|
| 51 |
+
pass
|
| 52 |
+
s = " ".join(s.split())
|
| 53 |
+
return s
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class SearchEngine:
|
| 57 |
+
def __init__(self):
|
| 58 |
+
self.cfg: Optional[SearchConfig] = None
|
| 59 |
+
self.tokenizer = None
|
| 60 |
+
self.mode = None
|
| 61 |
+
self.stopwords: set[str] = set()
|
| 62 |
+
self.custom_synonyms: Dict[str, List[str]] = {}
|
| 63 |
+
self.synonyms_cache: Dict[str, List[str]] = {}
|
| 64 |
+
|
| 65 |
+
# Data
|
| 66 |
+
self.projects: List[Dict[str, Any]] = []
|
| 67 |
+
self.project_map: Dict[str, Dict[str, Any]] = {}
|
| 68 |
+
self.org_norms: Dict[str, str] = {}
|
| 69 |
+
self.reading_norms: Dict[str, str] = {}
|
| 70 |
+
|
| 71 |
+
# BM25F assets
|
| 72 |
+
self.idf: Dict[str, float] = {}
|
| 73 |
+
self.avg_len: Dict[str, float] = {}
|
| 74 |
+
self.tf_token_docs: List[Dict[str, Any]] = []
|
| 75 |
+
|
| 76 |
+
# Vectors
|
| 77 |
+
self.word_vocab: Dict[str, int] = {}
|
| 78 |
+
self.word_vectors: Optional[np.ndarray] = None
|
| 79 |
+
self.ft_model = None
|
| 80 |
+
|
| 81 |
+
# ----- Init / Load -----
|
| 82 |
+
def initialize(self):
|
| 83 |
+
files = field_getter("config/files.json")
|
| 84 |
+
search = field_getter("config/search_model.json")
|
| 85 |
+
|
| 86 |
+
# Config
|
| 87 |
+
self.cfg = SearchConfig(
|
| 88 |
+
target_pos_l1=search("target_pos_l1"),
|
| 89 |
+
target_fields=search("target_fields"),
|
| 90 |
+
k1=float(search("bm25f.k1")),
|
| 91 |
+
b=float(search("bm25f.b")),
|
| 92 |
+
field_weights=search("bm25f.field_weights"),
|
| 93 |
+
synonyms_enable=bool(search("synonyms.enable")),
|
| 94 |
+
syn_limits=search("synonyms.limits"),
|
| 95 |
+
banlist=search("synonyms.banlist"),
|
| 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=(search("word_sim.rerank", "pair_avg") or "pair_avg").lower(),
|
| 100 |
+
query_subword_enable=bool(search("query_subword.enable")),
|
| 101 |
+
query_subword_path=search("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)),
|
| 105 |
+
org_boost_substring=float(search("organization.boost.substring", 0.5)),
|
| 106 |
+
org_boost_min_len=int(search("organization.boost.min_len", 2)),
|
| 107 |
+
min_results=int(search("filter.min_results", 20)),
|
| 108 |
+
max_results=int(search("filter.max_results", 50)),
|
| 109 |
+
bm25_min=float(search("filter.bm25_min", 0.1)),
|
| 110 |
+
word_sim_min=float(search("filter.word_sim_min", 0.35)),
|
| 111 |
+
fused_min=float(search("filter.fused_min", 0.12)),
|
| 112 |
+
fused_rel_top_ratio=float(search("filter.fused_rel_top_ratio", 0.5)),
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
# Tokenizer
|
| 116 |
+
sudachi_config_path = files("sudachi.sudachi_config")
|
| 117 |
+
tok = dictionary.Dictionary(config_path=sudachi_config_path).create()
|
| 118 |
+
self.tokenizer = tok
|
| 119 |
+
self.mode = tokenizer.Tokenizer.SplitMode.A
|
| 120 |
+
|
| 121 |
+
# Stopwords
|
| 122 |
+
with open(files("sudachi.stopwords"), encoding="utf-8") as f:
|
| 123 |
+
self.stopwords = set(json.load(f))
|
| 124 |
+
|
| 125 |
+
# Synonyms assets
|
| 126 |
+
try:
|
| 127 |
+
syn_cache_path = files("sudachi.synonyms_cache")
|
| 128 |
+
if os.path.exists(syn_cache_path):
|
| 129 |
+
with open(syn_cache_path, encoding="utf-8") as f:
|
| 130 |
+
self.synonyms_cache = json.load(f)
|
| 131 |
+
except Exception as e:
|
| 132 |
+
log.warning(f"failed to load synonyms_cache: {e}")
|
| 133 |
+
|
| 134 |
+
try:
|
| 135 |
+
custom_path = field_getter("config/search_model.json")("synonyms.sources.custom_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)
|
| 139 |
+
except Exception:
|
| 140 |
+
pass
|
| 141 |
+
|
| 142 |
+
# Projects
|
| 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 = {p["projectId"]: normalize_text_for_org(p.get("organization") or "") for p in self.projects}
|
| 147 |
+
self.reading_norms = {p["projectId"]: normalize_text_for_org(p.get("reading") or "") for p in self.projects}
|
| 148 |
+
|
| 149 |
+
# BM25F assets
|
| 150 |
+
with open(files("bm25.bm25_meta"), encoding="utf-8") as f:
|
| 151 |
+
meta = json.load(f)
|
| 152 |
+
self.idf = meta.get("idf", {})
|
| 153 |
+
self.avg_len = meta.get("avg_len", {})
|
| 154 |
+
|
| 155 |
+
with open(files("bm25.tf_token"), encoding="utf-8") as f:
|
| 156 |
+
self.tf_token_docs = json.load(f)
|
| 157 |
+
|
| 158 |
+
# Vectors
|
| 159 |
+
try:
|
| 160 |
+
vocab_path = files("embeddings.word_vocab")
|
| 161 |
+
vec_path = files("embeddings.word_vectors")
|
| 162 |
+
if os.path.exists(vocab_path) and os.path.exists(vec_path):
|
| 163 |
+
with open(vocab_path, encoding="utf-8") as f:
|
| 164 |
+
self.word_vocab = {k: int(v) for k, v in json.load(f).items()}
|
| 165 |
+
self.word_vectors = np.load(vec_path)["vectors"]
|
| 166 |
+
except Exception as e:
|
| 167 |
+
log.warning(f"word vectors not ready: {e}")
|
| 168 |
+
|
| 169 |
+
# doc_vectors.npy は topk 方式では不要
|
| 170 |
+
|
| 171 |
+
# fastText OOV
|
| 172 |
+
if self.cfg.query_subword_enable and self.cfg.query_subword_path and os.path.exists(self.cfg.query_subword_path):
|
| 173 |
+
try:
|
| 174 |
+
import fasttext
|
| 175 |
+
|
| 176 |
+
self.ft_model = fasttext.load_model(self.cfg.query_subword_path)
|
| 177 |
+
log.info("fastText .bin loaded for OOV")
|
| 178 |
+
except Exception as e:
|
| 179 |
+
log.warning(f"failed to load fastText .bin: {e}")
|
| 180 |
+
|
| 181 |
+
# ----- Tokenize / Synonyms -----
|
| 182 |
+
def _tokenize(self, text: str) -> List[str]:
|
| 183 |
+
if not text:
|
| 184 |
+
return []
|
| 185 |
+
out: List[str] = []
|
| 186 |
+
for m in self.tokenizer.tokenize(text, self.mode):
|
| 187 |
+
base = m.normalized_form().lower().strip()
|
| 188 |
+
if not base:
|
| 189 |
+
continue
|
| 190 |
+
pos = m.part_of_speech()
|
| 191 |
+
if pos[0] not in self.cfg.target_pos_l1:
|
| 192 |
+
continue
|
| 193 |
+
if base in self.stopwords or base in self.cfg.banlist:
|
| 194 |
+
continue
|
| 195 |
+
out.append(base)
|
| 196 |
+
return out
|
| 197 |
+
|
| 198 |
+
def _expand_synonyms(self, terms: List[str]) -> List[str]:
|
| 199 |
+
if not self.cfg.synonyms_enable:
|
| 200 |
+
return terms
|
| 201 |
+
max_exp = int(self.cfg.syn_limits.get("max_expansions_per_term", 4))
|
| 202 |
+
min_len = int(self.cfg.syn_limits.get("min_char_len", 2))
|
| 203 |
+
expanded: List[str] = []
|
| 204 |
+
for t in terms:
|
| 205 |
+
expanded.append(t)
|
| 206 |
+
cands = []
|
| 207 |
+
cands.extend(self.synonyms_cache.get(t, []))
|
| 208 |
+
cands.extend(self.custom_synonyms.get(t, []))
|
| 209 |
+
# filter/unique
|
| 210 |
+
uniq = []
|
| 211 |
+
seen = set()
|
| 212 |
+
for c in cands:
|
| 213 |
+
if c in seen or len(c) < min_len or c in self.cfg.banlist:
|
| 214 |
+
continue
|
| 215 |
+
seen.add(c)
|
| 216 |
+
uniq.append(c)
|
| 217 |
+
if len(uniq) >= max_exp:
|
| 218 |
+
break
|
| 219 |
+
expanded.extend(uniq)
|
| 220 |
+
# overall limit
|
| 221 |
+
max_q = int(self.cfg.syn_limits.get("max_query_variants", 5))
|
| 222 |
+
return expanded[: max_q * max_exp + len(terms)]
|
| 223 |
+
|
| 224 |
+
# ----- BM25F -----
|
| 225 |
+
def _bm25f_scores(self, terms: List[str]) -> np.ndarray:
|
| 226 |
+
N = len(self.tf_token_docs)
|
| 227 |
+
if N == 0:
|
| 228 |
+
return np.zeros((0,), dtype=np.float32)
|
| 229 |
+
k1 = self.cfg.k1
|
| 230 |
+
b = self.cfg.b
|
| 231 |
+
fw = self.cfg.field_weights
|
| 232 |
+
|
| 233 |
+
scores = np.zeros((N,), dtype=np.float32)
|
| 234 |
+
|
| 235 |
+
idf = self.idf
|
| 236 |
+
avg_len = self.avg_len
|
| 237 |
+
|
| 238 |
+
# For quick access, build list of per-doc per-field structures
|
| 239 |
+
for i, d in enumerate(self.tf_token_docs):
|
| 240 |
+
fields = d.get("fields") or {}
|
| 241 |
+
s = 0.0
|
| 242 |
+
for t in terms:
|
| 243 |
+
idf_t = float(idf.get(t, 0.0))
|
| 244 |
+
if idf_t <= 0.0:
|
| 245 |
+
continue
|
| 246 |
+
denom_sum = 0.0
|
| 247 |
+
num_sum = 0.0
|
| 248 |
+
for field, weight in fw.items():
|
| 249 |
+
fobj = (fields.get(field) or {})
|
| 250 |
+
tf = float((fobj.get("tf") or {}).get(t, 0))
|
| 251 |
+
if tf <= 0.0:
|
| 252 |
+
continue
|
| 253 |
+
len_f = float(fobj.get("len", 0))
|
| 254 |
+
avg_f = float(avg_len.get(field, 0.0)) or 1.0
|
| 255 |
+
norm = k1 * (1 - b + b * (len_f / avg_f))
|
| 256 |
+
num_sum += weight * tf * (k1 + 1.0)
|
| 257 |
+
denom_sum += weight * (tf + norm)
|
| 258 |
+
if denom_sum > 0:
|
| 259 |
+
s += idf_t * (num_sum / denom_sum)
|
| 260 |
+
scores[i] = s
|
| 261 |
+
return scores
|
| 262 |
+
|
| 263 |
+
# ----- Word similarity -----
|
| 264 |
+
def _get_token_vector(self, t: str) -> Tuple[Optional[np.ndarray], bool]:
|
| 265 |
+
if self.word_vectors is not None and t in self.word_vocab:
|
| 266 |
+
v = self.word_vectors[self.word_vocab[t]]
|
| 267 |
+
return v, False
|
| 268 |
+
if self.ft_model is not None:
|
| 269 |
+
try:
|
| 270 |
+
v = self.ft_model.get_word_vector(t)
|
| 271 |
+
v = v.astype(np.float32)
|
| 272 |
+
n = np.linalg.norm(v)
|
| 273 |
+
if n > 0:
|
| 274 |
+
v = v / n
|
| 275 |
+
return v, True
|
| 276 |
+
except Exception:
|
| 277 |
+
return None, True
|
| 278 |
+
return None, True
|
| 279 |
+
|
| 280 |
+
def _word_sim_scores_topk(self, terms: List[str]) -> Optional[np.ndarray]:
|
| 281 |
+
if not self.cfg.word_sim_enable:
|
| 282 |
+
return None
|
| 283 |
+
if self.word_vectors is None:
|
| 284 |
+
return None
|
| 285 |
+
|
| 286 |
+
# Build per-term vectors with weights (IDF; OOV down-weighted)
|
| 287 |
+
weights = []
|
| 288 |
+
vecs = []
|
| 289 |
+
for t in terms:
|
| 290 |
+
v, oov = self._get_token_vector(t)
|
| 291 |
+
if v is None:
|
| 292 |
+
continue
|
| 293 |
+
w = float(self.idf.get(t, 0.0))
|
| 294 |
+
if oov:
|
| 295 |
+
w *= float(self.cfg.query_subword_oov_weight)
|
| 296 |
+
if w <= 0:
|
| 297 |
+
continue
|
| 298 |
+
vecs.append(v)
|
| 299 |
+
weights.append(w)
|
| 300 |
+
|
| 301 |
+
if not vecs:
|
| 302 |
+
return None
|
| 303 |
+
|
| 304 |
+
V = np.stack(vecs).astype(np.float32) # T x D
|
| 305 |
+
W = np.asarray(weights, dtype=np.float32) # T
|
| 306 |
+
|
| 307 |
+
# top-k pooling over term-term cosine contributions (query terms x document terms)
|
| 308 |
+
k = max(1, int(self.cfg.word_sim_topk_k))
|
| 309 |
+
n_docs = len(self.tf_token_docs)
|
| 310 |
+
sims_all = np.zeros((n_docs,), dtype=np.float32)
|
| 311 |
+
Vq = V # Tq x D (normalized)
|
| 312 |
+
Wq = W # Tq
|
| 313 |
+
for i, d in enumerate(self.tf_token_docs):
|
| 314 |
+
fields = d.get("fields") or {}
|
| 315 |
+
doc_terms = set()
|
| 316 |
+
for fname in self.cfg.target_fields:
|
| 317 |
+
fobj = (fields.get(fname) or {})
|
| 318 |
+
tf = (fobj.get("tf") or {})
|
| 319 |
+
doc_terms.update(tf.keys())
|
| 320 |
+
if not doc_terms:
|
| 321 |
+
sims_all[i] = 0.0
|
| 322 |
+
continue
|
| 323 |
+
Vd_list = []
|
| 324 |
+
for t in doc_terms:
|
| 325 |
+
idx = self.word_vocab.get(t)
|
| 326 |
+
if idx is None:
|
| 327 |
+
continue
|
| 328 |
+
Vd_list.append(self.word_vectors[idx])
|
| 329 |
+
if not Vd_list:
|
| 330 |
+
sims_all[i] = 0.0
|
| 331 |
+
continue
|
| 332 |
+
Vd = np.stack(Vd_list).astype(np.float32) # Td x D
|
| 333 |
+
M = Vd @ Vq.T # Td x Tq
|
| 334 |
+
if Wq.size:
|
| 335 |
+
M = M * Wq[None, :]
|
| 336 |
+
M = np.maximum(M, 0.0)
|
| 337 |
+
Td, Tq = M.shape
|
| 338 |
+
total = Td * Tq
|
| 339 |
+
kk = min(k, total) if total > 0 else 0
|
| 340 |
+
if kk == 0:
|
| 341 |
+
sims_all[i] = 0.0
|
| 342 |
+
continue
|
| 343 |
+
flat = M.reshape(-1)
|
| 344 |
+
if kk == total:
|
| 345 |
+
top_vals = flat
|
| 346 |
+
else:
|
| 347 |
+
idxk = np.argpartition(flat, -kk)[-kk:]
|
| 348 |
+
top_vals = flat[idxk]
|
| 349 |
+
sims_all[i] = float(top_vals.mean()) if top_vals.size else 0.0
|
| 350 |
+
return sims_all
|
| 351 |
+
|
| 352 |
+
def _word_sim_scores_pairavg(self, terms: List[str]) -> Optional[np.ndarray]:
|
| 353 |
+
if not self.cfg.word_sim_enable:
|
| 354 |
+
return None
|
| 355 |
+
if self.word_vectors is None:
|
| 356 |
+
return None
|
| 357 |
+
# Build query term vectors (no weighting for pair-avg, simple mean over all pairs)
|
| 358 |
+
vecs = []
|
| 359 |
+
for t in terms:
|
| 360 |
+
v, _ = self._get_token_vector(t)
|
| 361 |
+
if v is None:
|
| 362 |
+
continue
|
| 363 |
+
vecs.append(v)
|
| 364 |
+
if not vecs:
|
| 365 |
+
return None
|
| 366 |
+
Vq = np.stack(vecs).astype(np.float32) # Tq x D
|
| 367 |
+
|
| 368 |
+
n_docs = len(self.tf_token_docs)
|
| 369 |
+
sims_all = np.zeros((n_docs,), dtype=np.float32)
|
| 370 |
+
for i, d in enumerate(self.tf_token_docs):
|
| 371 |
+
fields = d.get("fields") or {}
|
| 372 |
+
doc_terms = set()
|
| 373 |
+
for fname in self.cfg.target_fields:
|
| 374 |
+
fobj = (fields.get(fname) or {})
|
| 375 |
+
tf = (fobj.get("tf") or {})
|
| 376 |
+
doc_terms.update(tf.keys())
|
| 377 |
+
if not doc_terms:
|
| 378 |
+
sims_all[i] = 0.0
|
| 379 |
+
continue
|
| 380 |
+
Vd_list = []
|
| 381 |
+
for t in doc_terms:
|
| 382 |
+
idx = self.word_vocab.get(t)
|
| 383 |
+
if idx is None:
|
| 384 |
+
continue
|
| 385 |
+
Vd_list.append(self.word_vectors[idx])
|
| 386 |
+
if not Vd_list:
|
| 387 |
+
sims_all[i] = 0.0
|
| 388 |
+
continue
|
| 389 |
+
Vd = np.stack(Vd_list).astype(np.float32) # Td x D
|
| 390 |
+
M = Vd @ Vq.T # Td x Tq
|
| 391 |
+
M = np.maximum(M, 0.0)
|
| 392 |
+
sims_all[i] = float(M.mean()) if M.size else 0.0
|
| 393 |
+
return sims_all
|
| 394 |
+
|
| 395 |
+
# ----- Public API -----
|
| 396 |
+
def search(
|
| 397 |
+
self,
|
| 398 |
+
query: str,
|
| 399 |
+
debug: bool = False,
|
| 400 |
+
) -> List[Tuple[str, float]] | Tuple[List[Tuple[str, float]], Dict[str, Any]]:
|
| 401 |
+
terms = self._tokenize(query)
|
| 402 |
+
if self.cfg.synonyms_enable:
|
| 403 |
+
terms = self._expand_synonyms(terms)
|
| 404 |
+
|
| 405 |
+
# BM25F
|
| 406 |
+
bm25 = self._bm25f_scores(terms)
|
| 407 |
+
|
| 408 |
+
# word sim (filtering): top-k pooling
|
| 409 |
+
ws_filter = self._word_sim_scores_topk(terms)
|
| 410 |
+
if ws_filter is None:
|
| 411 |
+
ws_filter = np.zeros_like(bm25)
|
| 412 |
+
a = float(self.cfg.word_sim_alpha)
|
| 413 |
+
fused_filter = a * bm25 + (1.0 - a) * ws_filter
|
| 414 |
+
|
| 415 |
+
# Organization/reading auto-boost based on raw query substring match
|
| 416 |
+
qn = normalize_text_for_org(query)
|
| 417 |
+
if len(qn) >= int(self.cfg.org_boost_min_len):
|
| 418 |
+
exact = np.zeros((len(self.projects),), dtype=bool)
|
| 419 |
+
prefix = np.zeros_like(exact)
|
| 420 |
+
substr = np.zeros_like(exact)
|
| 421 |
+
for i, d in enumerate(self.projects):
|
| 422 |
+
pid = d.get("projectId")
|
| 423 |
+
on = self.org_norms.get(pid, "")
|
| 424 |
+
rn = self.reading_norms.get(pid, "")
|
| 425 |
+
if qn and (qn == on or (rn and qn == rn)):
|
| 426 |
+
exact[i] = True
|
| 427 |
+
elif qn and (on.startswith(qn) or (rn and rn.startswith(qn))):
|
| 428 |
+
prefix[i] = True
|
| 429 |
+
elif qn and ((qn in on) or (rn and qn in rn)):
|
| 430 |
+
substr[i] = True
|
| 431 |
+
|
| 432 |
+
boost = (
|
| 433 |
+
exact.astype(np.float32) * float(self.cfg.org_boost_exact)
|
| 434 |
+
+ prefix.astype(np.float32) * float(self.cfg.org_boost_prefix)
|
| 435 |
+
+ substr.astype(np.float32) * float(self.cfg.org_boost_substring)
|
| 436 |
+
)
|
| 437 |
+
pass
|
| 438 |
+
|
| 439 |
+
# collect results
|
| 440 |
+
ids = [d.get("projectId") for d in self.projects]
|
| 441 |
+
# Filtering to reduce false positives while keeping recall
|
| 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 = top * float(self.cfg.fused_rel_top_ratio) if top > 0 else self.cfg.fused_min
|
| 446 |
+
fused_cut = max(float(self.cfg.fused_min), rel_cut)
|
| 447 |
+
keep = ((bm25 >= self.cfg.bm25_min) | (ws_filter >= self.cfg.word_sim_min) | (score_with_boost >= self.cfg.fused_min)) & (score_with_boost >= fused_cut)
|
| 448 |
+
order = np.argsort(-score_with_boost) # descending by fused
|
| 449 |
+
selected_idx: List[int] = []
|
| 450 |
+
for i in order:
|
| 451 |
+
if keep[i]:
|
| 452 |
+
selected_idx.append(int(i))
|
| 453 |
+
if len(selected_idx) >= self.cfg.max_results:
|
| 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 = (bm25 >= self.cfg.bm25_min) | (ws_filter >= self.cfg.word_sim_min) | (score_with_boost >= self.cfg.fused_min)
|
| 458 |
+
for i in order:
|
| 459 |
+
if keep2[i]:
|
| 460 |
+
selected_idx.append(int(i))
|
| 461 |
+
if len(selected_idx) >= self.cfg.max_results:
|
| 462 |
+
break
|
| 463 |
+
# Rerank with pair-avg word similarity (if enabled)
|
| 464 |
+
ws_rerank = None
|
| 465 |
+
if self.cfg.word_sim_rerank == "pair_avg":
|
| 466 |
+
ws_rerank = self._word_sim_scores_pairavg(terms)
|
| 467 |
+
if ws_rerank is None:
|
| 468 |
+
ws_rerank = ws_filter
|
| 469 |
+
fused_rerank = a * bm25 + (1.0 - a) * ws_rerank
|
| 470 |
+
final_scores = fused_rerank + boost
|
| 471 |
+
pairs = [(ids[i], float(final_scores[i])) for i in selected_idx]
|
| 472 |
+
|
| 473 |
+
# sort
|
| 474 |
+
pairs.sort(key=lambda x: (-x[1], x[0]))
|
| 475 |
+
if not debug:
|
| 476 |
+
return pairs
|
| 477 |
+
# build debug details for selected docs
|
| 478 |
+
details = []
|
| 479 |
+
for i in selected_idx:
|
| 480 |
+
details.append({
|
| 481 |
+
"projectId": ids[i],
|
| 482 |
+
"bm25": float(bm25[i]),
|
| 483 |
+
"ws_filter_topk": float(ws_filter[i]),
|
| 484 |
+
"ws_rerank_pairavg": float(ws_rerank[i]) if ws_rerank is not None else None,
|
| 485 |
+
"org_boost": float(boost[i]),
|
| 486 |
+
"fused_filter": float(fused_filter[i]),
|
| 487 |
+
"fused_final": float(final_scores[i]),
|
| 488 |
+
})
|
| 489 |
+
return pairs, {"details": details}
|
| 490 |
+
|
| 491 |
+
def get_projects(self) -> List[Dict[str, Any]]:
|
| 492 |
+
return self.projects
|
| 493 |
+
|
| 494 |
+
def get_project_map(self) -> Dict[str, Dict[str, Any]]:
|
| 495 |
+
return self.project_map
|
config/files.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"sudachi": {
|
| 3 |
+
"sudachi_config": "scripts/sudachi.json",
|
| 4 |
+
"user_dict": "resources/user_dict.csv",
|
| 5 |
+
"user_dict_generated": "data/generated/user.dic",
|
| 6 |
+
"synonyms": "resources/synonyms_DO_NOT_EDIT.txt",
|
| 7 |
+
"synonyms_cache": "data/generated/synonyms_cache.json",
|
| 8 |
+
"stopwords": "resources/stopwords.json"
|
| 9 |
+
},
|
| 10 |
+
"projects": {
|
| 11 |
+
"original_csv": "resources/test_data.csv",
|
| 12 |
+
"projects_json": "data/generated/projects.json"
|
| 13 |
+
},
|
| 14 |
+
"bm25": {
|
| 15 |
+
"bm25_meta": "data/generated/bm25_meta.json",
|
| 16 |
+
"tf_token": "data/generated/tf_token.json"
|
| 17 |
+
},
|
| 18 |
+
"embeddings": {
|
| 19 |
+
"fasttext_vec": "resources/embeddings/cc.ja.300.vec",
|
| 20 |
+
"fasttext_bin": "resources/embeddings/cc.ja.300.bin",
|
| 21 |
+
"word_vocab": "data/generated/word_vocab.json",
|
| 22 |
+
"word_vectors": "data/generated/word_vectors.npz",
|
| 23 |
+
"doc_vectors": "data/generated/doc_vectors.npy"
|
| 24 |
+
}
|
| 25 |
+
}
|
config/search_model.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"target_pos_l1": [
|
| 3 |
+
"名詞",
|
| 4 |
+
"動詞",
|
| 5 |
+
"形容詞",
|
| 6 |
+
"形容動詞語幹"
|
| 7 |
+
],
|
| 8 |
+
"target_fields": [
|
| 9 |
+
"title",
|
| 10 |
+
"organization",
|
| 11 |
+
"description",
|
| 12 |
+
"prComment",
|
| 13 |
+
"prCommentLong",
|
| 14 |
+
"reading"
|
| 15 |
+
],
|
| 16 |
+
"synonyms": {
|
| 17 |
+
"enable": true,
|
| 18 |
+
"sources": {
|
| 19 |
+
"sudachi": true,
|
| 20 |
+
"custom_json": "resources/synonyms_custom.json"
|
| 21 |
+
},
|
| 22 |
+
"limits": {
|
| 23 |
+
"max_expansions_per_term": 4,
|
| 24 |
+
"max_query_variants": 5,
|
| 25 |
+
"min_char_len": 2
|
| 26 |
+
},
|
| 27 |
+
"banlist": [
|
| 28 |
+
"部",
|
| 29 |
+
"会",
|
| 30 |
+
"サークル"
|
| 31 |
+
]
|
| 32 |
+
},
|
| 33 |
+
"bm25f": {
|
| 34 |
+
"k1": 1.2,
|
| 35 |
+
"b": 0.75,
|
| 36 |
+
"field_weights": {
|
| 37 |
+
"title": 2.0,
|
| 38 |
+
"organization": 1.5,
|
| 39 |
+
"description": 1.0,
|
| 40 |
+
"prComment": 1.0,
|
| 41 |
+
"prCommentLong": 0.8,
|
| 42 |
+
"reading": 0.6
|
| 43 |
+
}
|
| 44 |
+
},
|
| 45 |
+
"word_sim": {
|
| 46 |
+
"enable": true,
|
| 47 |
+
"mode": "topk",
|
| 48 |
+
"alpha": 0.3,
|
| 49 |
+
"topk_k": 3,
|
| 50 |
+
"rerank": "pair_avg"
|
| 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 |
+
},
|
| 58 |
+
"organization": {
|
| 59 |
+
"boost": {
|
| 60 |
+
"exact": 1.0,
|
| 61 |
+
"prefix": 0.7,
|
| 62 |
+
"substring": 0.5,
|
| 63 |
+
"min_len": 2
|
| 64 |
+
}
|
| 65 |
+
},
|
| 66 |
+
"filter": {
|
| 67 |
+
"min_results": 10,
|
| 68 |
+
"max_results": 30,
|
| 69 |
+
"bm25_min": 0.15,
|
| 70 |
+
"word_sim_min": 0.40,
|
| 71 |
+
"fused_min": 0.15,
|
| 72 |
+
"fused_rel_top_ratio": 0.50
|
| 73 |
+
}
|
| 74 |
+
}
|
docs/files.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# オフライン生成
|
| 2 |
+
- `cc.ja.300.bin`: OOV(Out of Vocabrary)時に使用 更新不要
|
| 3 |
+
- `generated/`: 毎年更新
|
| 4 |
+
- `projects.json`: API返却用データ
|
| 5 |
+
- `scripts/1_build_dict.py`で生成
|
| 6 |
+
- `bm25_meta.json`: BM25Fメタデータ
|
| 7 |
+
- `scripts/4_prepare_bm25f_meta.py`で生成
|
| 8 |
+
- `tf_token.json`: フィールド別TF/トークン
|
| 9 |
+
- `scripts/5_prepare_tf_token.py`で生成
|
| 10 |
+
- `synonyms_cache.json`: Sudachi同義語キャッシュ
|
| 11 |
+
- `scripts/3_build_synonyms_from_sudachi.py`から生成
|
| 12 |
+
- `word_vocab.json`
|
| 13 |
+
- `scripts/6_build_word_embeddings.py`で生成
|
| 14 |
+
- `word_vectors.npz`
|
| 15 |
+
- `scripts/6_build_word_embeddings.py`で生成
|
docs/old_index.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```py
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
import json
|
| 5 |
+
import gzip
|
| 6 |
+
import logging
|
| 7 |
+
from dotenv import load_dotenv
|
| 8 |
+
from fastapi import FastAPI, Response, Depends, HTTPException, Security, Query
|
| 9 |
+
from fastapi.responses import RedirectResponse
|
| 10 |
+
from fastapi.security import APIKeyHeader
|
| 11 |
+
from pydantic import BaseModel
|
| 12 |
+
from sudachipy import dictionary
|
| 13 |
+
from dataclasses import asdict, fields
|
| 14 |
+
from typing import Optional, Literal, List, Dict
|
| 15 |
+
from pathlib import Path
|
| 16 |
+
|
| 17 |
+
# デバッグ用
|
| 18 |
+
from contextlib import asynccontextmanager
|
| 19 |
+
from fastapi.routing import APIRoute
|
| 20 |
+
|
| 21 |
+
# ローカルモジュールのインポート
|
| 22 |
+
from api import search_preprocess
|
| 23 |
+
from api import data_fetch
|
| 24 |
+
from api.search import pipeline as search_pipeline
|
| 25 |
+
from api.model import Project, ProjectSummary, ProjectDetail, ProjectIds
|
| 26 |
+
|
| 27 |
+
# 親ディレクトリをパスに追加して設定ファイルをインポート
|
| 28 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
| 29 |
+
from scripts.config import cfg_file, cfg_search_model, cfg_search_params
|
| 30 |
+
|
| 31 |
+
# --- ロギング設定 ---
|
| 32 |
+
_level_name = os.getenv("LOG_LEVEL", "INFO").upper()
|
| 33 |
+
_level = getattr(logging, _level_name, logging.INFO)
|
| 34 |
+
logging.basicConfig(level=_level, format="%(asctime)s [%(levelname)s]: %(message)s")
|
| 35 |
+
log = logging.getLogger(__name__)
|
| 36 |
+
log.setLevel(_level)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
# --- グローバル変数 ---
|
| 40 |
+
# このファイルの場所を基準にプロジェクトルートを特定
|
| 41 |
+
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
| 42 |
+
|
| 43 |
+
# 設定情報
|
| 44 |
+
settings_file = cfg_file()
|
| 45 |
+
settings_search_model = cfg_search_model()
|
| 46 |
+
settings_search_params = cfg_search_params()
|
| 47 |
+
|
| 48 |
+
# モデルとデータ
|
| 49 |
+
tokenizer_obj = None
|
| 50 |
+
sentence_model = None # kept for compatibility; no longer used
|
| 51 |
+
all_projects: List[Project] = [] # populated via search_pipeline
|
| 52 |
+
project_map: Dict[str, Project] = {}
|
| 53 |
+
docs_for_search: Dict[str, Dict] = {}
|
| 54 |
+
bm25_meta: Dict = {}
|
| 55 |
+
synonyms_cache: Dict = {}
|
| 56 |
+
custom_synonyms: Dict = {}
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
# --- 初期化処理 ---
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def initialize_sudachi_tokenizer() -> dictionary.Dictionary:
|
| 63 |
+
"""SudachiPyトークナイザをユーザー辞書と共に初期化する"""
|
| 64 |
+
sudachi_config_path = PROJECT_ROOT / "scripts/sudachi.json"
|
| 65 |
+
try:
|
| 66 |
+
if sudachi_config_path.exists():
|
| 67 |
+
log.info(
|
| 68 |
+
"SudachiPy設定ファイルが見つかりました。ユーザー辞書で初期化します。"
|
| 69 |
+
)
|
| 70 |
+
with open(sudachi_config_path, "r", encoding="utf-8") as f:
|
| 71 |
+
config = json.load(f)
|
| 72 |
+
|
| 73 |
+
# userDictのパスを絶対パスに変換
|
| 74 |
+
if "userDict" in config:
|
| 75 |
+
config["userDict"] = [str(PROJECT_ROOT / p) for p in config["userDict"]]
|
| 76 |
+
|
| 77 |
+
# configオブジェクトをJSON文字列に変換して渡す
|
| 78 |
+
return dictionary.Dictionary(config=json.dumps(config)).create()
|
| 79 |
+
else:
|
| 80 |
+
log.info(
|
| 81 |
+
"SudachiPy設定ファイルが見つかりません。システム辞書のみ使用します。"
|
| 82 |
+
)
|
| 83 |
+
return dictionary.Dictionary().create()
|
| 84 |
+
except Exception as e:
|
| 85 |
+
log.error(f"SudachiPyの初期化に失敗しました: {e}")
|
| 86 |
+
raise
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def load_all_data():
|
| 90 |
+
"""Deprecated: Data is now loaded by api.search.pipeline.initialize()."""
|
| 91 |
+
log.info("load_all_data() is deprecated; using search_pipeline.initialize().")
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
@asynccontextmanager
|
| 95 |
+
async def lifespan(app: FastAPI):
|
| 96 |
+
"""アプリケーションの起動時と終了時に実行される処理"""
|
| 97 |
+
# --- 起動時処理 ---
|
| 98 |
+
global tokenizer_obj, sentence_model, all_projects, project_map
|
| 99 |
+
|
| 100 |
+
log.info("アプリケーションを起動します...")
|
| 101 |
+
|
| 102 |
+
# 環境変数をロード
|
| 103 |
+
load_dotenv()
|
| 104 |
+
|
| 105 |
+
# トークナイザの初期化
|
| 106 |
+
tokenizer_obj = initialize_sudachi_tokenizer()
|
| 107 |
+
search_preprocess.set_tokenizer(tokenizer_obj)
|
| 108 |
+
|
| 109 |
+
# 生成物の確保(ローカル or HF Datasets からフェッチ)
|
| 110 |
+
try:
|
| 111 |
+
fetch_summary = data_fetch.orchestrate_fetch_and_validate(settings_search_model)
|
| 112 |
+
missing = fetch_summary.get("missing_before", [])
|
| 113 |
+
fetched = fetch_summary.get("fetched", {})
|
| 114 |
+
checks = fetch_summary.get("checks", {})
|
| 115 |
+
ok_list = [k for k, v in checks.items() if v]
|
| 116 |
+
log.info(
|
| 117 |
+
f"DATA PREP: missing_before={missing}, fetched={list(k for k,v in fetched.items() if v)}, checks_ok={ok_list}"
|
| 118 |
+
)
|
| 119 |
+
ws_conf = settings_search_model.get("word_sim", {}) or {}
|
| 120 |
+
ws_enabled = bool(ws_conf.get("enable"))
|
| 121 |
+
ws_mode = (ws_conf.get("mode") or "avg").lower()
|
| 122 |
+
ws_ready = (
|
| 123 |
+
checks.get("word_vectors.npz", False)
|
| 124 |
+
and checks.get("word_vocab.json", False)
|
| 125 |
+
and (True if ws_mode != "avg" else checks.get("doc_vectors.npy", False))
|
| 126 |
+
)
|
| 127 |
+
if ws_enabled:
|
| 128 |
+
log.info(f"WORD_SIM: requested mode={ws_mode}, assets_ready={ws_ready}")
|
| 129 |
+
except Exception as e:
|
| 130 |
+
log.warning(f"Data fetch/validate step failed: {e}")
|
| 131 |
+
|
| 132 |
+
# 検索パイプラインの初期化���データロード含む)
|
| 133 |
+
search_pipeline.initialize(tokenizer_obj)
|
| 134 |
+
# 既存エンドポイント互換のためローカル参照も持つ
|
| 135 |
+
all_projects = search_pipeline.get_projects()
|
| 136 |
+
project_map = search_pipeline.get_project_map()
|
| 137 |
+
|
| 138 |
+
# ルート情報のログ出力
|
| 139 |
+
for r in app.routes:
|
| 140 |
+
if isinstance(r, APIRoute):
|
| 141 |
+
log.info(f"ROUTE {list(r.methods)} {r.path}")
|
| 142 |
+
log.info(f"DOCS={app.docs_url} OPENAPI={app.openapi_url} REDOC={app.redoc_url}")
|
| 143 |
+
log.info("アプリケーションの準備が整いました。")
|
| 144 |
+
|
| 145 |
+
yield
|
| 146 |
+
|
| 147 |
+
# --- 終了時処理 ---
|
| 148 |
+
log.info("アプリケーションをシャットダウンします。")
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
# --- FastAPIアプリケーション設定 ---
|
| 152 |
+
app = FastAPI(lifespan=lifespan)
|
| 153 |
+
|
| 154 |
+
# .envファイルから環境変数を読み込む
|
| 155 |
+
load_dotenv()
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
# --- 認証設定 ---
|
| 159 |
+
API_KEY_NAME = "X-API-KEY"
|
| 160 |
+
API_SECRET_KEY = os.getenv("API_SECRET_KEY")
|
| 161 |
+
api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=True)
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
async def get_api_key(key: str = Security(api_key_header)):
|
| 165 |
+
"""APIキーを検証する依存関係"""
|
| 166 |
+
if not API_SECRET_KEY or key != API_SECRET_KEY:
|
| 167 |
+
raise HTTPException(status_code=403, detail="Could not validate credentials.")
|
| 168 |
+
return key
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
# --- APIエンドポイント ---
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
@app.get("/", include_in_schema=False)
|
| 175 |
+
def root():
|
| 176 |
+
"""ルートURLへのアクセスはドキュメントへリダイレクト"""
|
| 177 |
+
return RedirectResponse("/docs")
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
@app.get("/api/health")
|
| 181 |
+
def health_check():
|
| 182 |
+
"""ヘルスチェック用エンドポイント"""
|
| 183 |
+
return {"status": "ok"}
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
@app.get(
|
| 187 |
+
"/api/projects",
|
| 188 |
+
response_model=List[ProjectSummary],
|
| 189 |
+
dependencies=[Depends(get_api_key)],
|
| 190 |
+
)
|
| 191 |
+
def get_summary_data():
|
| 192 |
+
"""全企画のサマリー情報をGZIP圧縮して返す"""
|
| 193 |
+
summary_fields = {f.name for f in fields(ProjectSummary)}
|
| 194 |
+
summaries = [
|
| 195 |
+
{k: v for k, v in asdict(p).items() if k in summary_fields}
|
| 196 |
+
for p in search_pipeline.get_projects()
|
| 197 |
+
]
|
| 198 |
+
|
| 199 |
+
content = json.dumps(summaries, ensure_ascii=False).encode("utf-8")
|
| 200 |
+
return Response(
|
| 201 |
+
content=gzip.compress(content),
|
| 202 |
+
headers={"Content-Encoding": "gzip", "Content-Type": "application/json"},
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
@app.get(
|
| 207 |
+
"/api/details",
|
| 208 |
+
response_model=ProjectDetail,
|
| 209 |
+
dependencies=[Depends(get_api_key)],
|
| 210 |
+
)
|
| 211 |
+
def get_project_detail(projectId: str = Query(..., description="取得したい企画のID")):
|
| 212 |
+
"""指定されたIDの企画詳細情報を返す"""
|
| 213 |
+
project = search_pipeline.get_project_map().get(projectId)
|
| 214 |
+
if not project:
|
| 215 |
+
raise HTTPException(status_code=404, detail="Project not found")
|
| 216 |
+
|
| 217 |
+
detail_fields = {f.name for f in fields(ProjectDetail)}
|
| 218 |
+
project_dict = asdict(project)
|
| 219 |
+
return {key: project_dict[key] for key in detail_fields if key in project_dict}
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
# --- 検索エンドポイント ---
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
class SearchRequest(BaseModel):
|
| 226 |
+
query: str
|
| 227 |
+
# fusion は非推奨: 設定ファイルで制御し、ここでは受け取っても無視する
|
| 228 |
+
fusion: Optional[Literal["add", "mul"]] = None
|
| 229 |
+
debug: bool = False
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
@app.post(
|
| 233 |
+
"/api/search",
|
| 234 |
+
response_model=ProjectIds,
|
| 235 |
+
dependencies=[Depends(get_api_key)],
|
| 236 |
+
)
|
| 237 |
+
def search(request: SearchRequest) -> ProjectIds:
|
| 238 |
+
"""BM25F中心の新パイプラインで検索し、企画IDを返す。"""
|
| 239 |
+
if not request.query:
|
| 240 |
+
raise HTTPException(status_code=400, detail="Query cannot be empty")
|
| 241 |
+
result_ids = search_pipeline.search(request.query, request.debug)
|
| 242 |
+
log.info(f"検索完了: {len(result_ids)} 件を返却")
|
| 243 |
+
return ProjectIds(projectIds=result_ids)
|
| 244 |
+
|
| 245 |
+
```
|
docs/search_implementation_plan.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 検索実装計画書(BM25F + fastText OOV, .vec/.bin 運用)
|
| 2 |
+
|
| 3 |
+
目的
|
| 4 |
+
- BM25F を主軸に、任意の語義近接スコア(fastText 由来の事前生成行列)を融合し、精度と速度を両立する。
|
| 5 |
+
- OOV(語彙外)クエリは fastText のサブワードを `.bin` でランタイム生成して補う。
|
| 6 |
+
- Hugging Face Spaces 無料CPU(RAM 16GB)で安定運用できる構成にする。
|
| 7 |
+
|
| 8 |
+
結論(運用方針)
|
| 9 |
+
- `.vec`(1.82GB)は「ビルド専用」。語彙を間引いた行列 `word_vectors.npz` と `doc_vectors.npy` を生成して配布する。
|
| 10 |
+
- `.bin`(838MB)は「ランタイムの OOV 専用」。起動時に1回ロードし、未知語のベクトルを生成する。
|
| 11 |
+
- 量子化 `.ftz` は不使用(fastText 公式の quantize は教師ありモデルのみサポート)。
|
| 12 |
+
|
| 13 |
+
---
|
| 14 |
+
|
| 15 |
+
## 1. 全体アーキテクチャ
|
| 16 |
+
- 検索スコア
|
| 17 |
+
- BM25F: 既存のトークン化・フィールド重み(`config/search_model.json`)。
|
| 18 |
+
- 語義近接(word_sim): `word_vectors.npz/doc_vectors.npy` を利用。`mode=avg|soft` を設定可能。
|
| 19 |
+
- 融合: `score = alpha * bm25f + (1 - alpha) * word_sim`(`alpha` は 0.6–0.8 目安)。
|
| 20 |
+
- OOV 対応
|
| 21 |
+
- 既知語: `word_vocab.json + word_vectors.npz` から即時ルックアップ(L2 正規化済)。
|
| 22 |
+
- 未知語: fastText `.bin` をロードし `get_word_vector` でサブワードから生成(取得後に L2 正規化)。
|
| 23 |
+
- キャッシュ: OOV 語ベクトルに LRU キャッシュを適用(例: 50k 語)。
|
| 24 |
+
- フォールバック
|
| 25 |
+
- `.bin` が未配置/取得失敗時: OOV をゼロベクトル扱い → word_sim 寄与が自然に減衰、BM25F 単体でも整合。
|
| 26 |
+
|
| 27 |
+
## 2. ビルド(ローカル・年次)
|
| 28 |
+
- 入力
|
| 29 |
+
- `resources/embeddings/cc.ja.300.vec`(1.82GB):fastText の公開ベクトル、または自前学習の `.vec`。
|
| 30 |
+
- 注意: `.vec` と `.bin` は必ず同一モデル(同じ学習元/版)を用意する。
|
| 31 |
+
- 実行
|
| 32 |
+
- `python scripts/build_all.py`(Step 6 が `.vec` を検出した場合に実行)
|
| 33 |
+
- 出力(`data/generated/`)
|
| 34 |
+
- `projects.json`, `bm25_meta.json`, `tf_token.json`(BM25F 用)
|
| 35 |
+
- `word_vocab.json`, `word_vectors.npz`, `doc_vectors.npy`(word_sim 用)
|
| 36 |
+
- 配布
|
| 37 |
+
- Hugging Face Datasets(private)に `generated/` 一式をアップロード。年度タグ(例: `2025.0`)で管理。
|
| 38 |
+
|
| 39 |
+
## 3. ランタイム(Spaces, 16GB RAM)
|
| 40 |
+
- 起動時取得
|
| 41 |
+
- Datasets から `generated/` をダウンロード → `data/generated/` へ配置。
|
| 42 |
+
- Models から `cc.ja.300.bin` をダウンロード → `resources/embeddings/` へ配置。
|
| 43 |
+
- 起動時ロード
|
| 44 |
+
- `word_vectors.npz/doc_vectors.npy/word_vocab.json` を読み込み。
|
| 45 |
+
- 設定 `query_subword.enable=true` なら `fasttext.load_model(cc.ja.300.bin)` を1回だけ実行。
|
| 46 |
+
- メモリ目安
|
| 47 |
+
- `.bin` ロード: 1.0–1.5GB 程度
|
| 48 |
+
- 事前生成行列ほか: 10–40MB 程度
|
| 49 |
+
- 余裕を見て合計 1.5–2.5GB 程度(RAM 16GB 内に十分収まる)。
|
| 50 |
+
- コールドスタート
|
| 51 |
+
- `.bin` の取得(~0.8GB)+ロードで数十秒〜数分(I/O 依存)。起動時のみ発生。
|
| 52 |
+
|
| 53 |
+
## 4. クエリ処理フロー(擬似コード)
|
| 54 |
+
1) クエリを正規化 → トークン化(SudachiPy)。
|
| 55 |
+
2) 各トークンについて:
|
| 56 |
+
- 語彙に存在 → `word_vectors` から取得
|
| 57 |
+
- なければ(OOV) → `.bin` から `get_word_vector`(あれば LRU から)
|
| 58 |
+
- ベクトルは L2 正規化
|
| 59 |
+
3) word_sim を計算
|
| 60 |
+
- `avg`: IDF 加重平均ベクトルと `doc_vectors.npy` のコサイン類似
|
| 61 |
+
- `soft`: 各クエリ語に対し文書語群との最大コサインを合算(短クエリに強い)
|
| 62 |
+
4) BM25F と融合
|
| 63 |
+
- `score = alpha * bm25f + (1 - alpha) * word_sim`
|
| 64 |
+
- OOV が多いクエリは `oov_weight < 1.0` で寄与を減衰可能
|
| 65 |
+
|
| 66 |
+
## 5. 設定(例: `config/search_model.json`)
|
| 67 |
+
- 主要キー
|
| 68 |
+
- `bm25f`: 既存のフィールド定義/重み
|
| 69 |
+
- `word_sim.enable`: `true|false`
|
| 70 |
+
- `word_sim.mode`: `"soft"|"avg"`(既定は `soft` 推奨)
|
| 71 |
+
- `word_sim.alpha`: `0.6–0.8` 目安
|
| 72 |
+
- `query_subword.enable`: `true|false`
|
| 73 |
+
- `query_subword.path`: `"resources/embeddings/cc.ja.300.bin"`
|
| 74 |
+
- `query_subword.oov_weight`: `0.6–1.0`(既定 0.8 例)
|
| 75 |
+
- `query_subword.cache_size`: `50000` など
|
| 76 |
+
|
| 77 |
+
## 6. 環境変数(Spaces/ローカル)
|
| 78 |
+
- 取得制御
|
| 79 |
+
- `FETCH_FROM_HF_ON_STARTUP=true`
|
| 80 |
+
- `DATASET_REPO`, `DATASET_REVISION`, `HF_TOKEN`
|
| 81 |
+
- `MODEL_REPO`, `MODEL_REVISION`, `MODEL_FILENAME=cc.ja.300.bin`
|
| 82 |
+
- ログ
|
| 83 |
+
- `LOG_LEVEL=INFO|DEBUG`
|
| 84 |
+
- API
|
| 85 |
+
- `API_SECRET_KEY`(必須)
|
| 86 |
+
|
| 87 |
+
## 7. 運用(HF Hub レイアウト)
|
| 88 |
+
- Datasets(private): `generated/` 一式(年度タグで版管理)
|
| 89 |
+
- Models(private): `cc.ja.300.bin`(年度非依存、基本更新不要)
|
| 90 |
+
- フォールバック: 取得失敗時は BM25F のみで起動可(word_sim を自動無効化)
|
| 91 |
+
|
| 92 |
+
## 8. 検証・品質
|
| 93 |
+
- スモーク
|
| 94 |
+
- `GET /api/health` → 200
|
| 95 |
+
- `GET /api/projects`(要 `X-API-KEY`)
|
| 96 |
+
- 精度
|
| 97 |
+
- 代表クエリの MRR/nDCG を比較、`alpha` と `mode` を探索
|
| 98 |
+
- `.vec`/`.bin` 同一モデルであることを確認(空間ずれ防止)
|
| 99 |
+
- 性能
|
| 100 |
+
- OOV キャッシュを有効化(ヒット率確認)
|
| 101 |
+
- 大文字/小文字/正規化の統一でキャッシュ効率を上げる
|
| 102 |
+
|
| 103 |
+
## 9. 既存ドキュメントとの整合
|
| 104 |
+
- 量子化 `.ftz` は使用しない(fastText 公式の quantize は教師ありのみ)。
|
| 105 |
+
- 該当箇所の表記を `.bin` 前提へ統一(本コミットで主要2ファイルを修正)。
|
| 106 |
+
|
| 107 |
+
## 10. リスクと対策
|
| 108 |
+
- モデル不一致(`.vec` と `.bin` の学習元が異なる)
|
| 109 |
+
- 同一ソース/版を必須とし、ハッシュで管理
|
| 110 |
+
- コールドスタート時間
|
| 111 |
+
- `.bin` は Models(private)からの取得を前提。I/O が遅い場合は Space をスリープさせない運用も検討
|
| 112 |
+
- メモリ
|
| 113 |
+
- 16GB で十分だが、同時常駐データに注意。OOV キャッシュサイズを調整
|
| 114 |
+
|
| 115 |
+
---
|
| 116 |
+
|
| 117 |
+
実装タスクリスト(要約)
|
| 118 |
+
1) `.vec` を `resources/embeddings/` に配置し `build_all.py` 実行 → `generated/` 生成
|
| 119 |
+
2) `generated/` を HF Datasets(private, 年度タグ)へアップロード
|
| 120 |
+
3) `.bin` を HF Models(private)へアップロード
|
| 121 |
+
4) 起動時取得ロジックを有効化(環境変数と設定を反映)
|
| 122 |
+
5) `query_subword`(.bin ロード + LRU キャッシュ)を有効化
|
| 123 |
+
6) パラメータ(`alpha`, `mode`, `oov_weight`)をスモーク+代表クエリでチューニング
|
| 124 |
+
|
| 125 |
+
## 11. 組織名(organization)フィルタ/ブースト
|
| 126 |
+
- 目的: 固有名詞(団体名)に対して、シンプルな包含一致を検索条件として扱えるようにする。
|
| 127 |
+
- 正規化: NFKC 正規化 → 全角/半角の統一、空白の正規化(連続空白の1個化、前後トリム)。必要に応じてひらがな/カタカナの片寄せは行わない(誤爆回避)。
|
| 128 |
+
- モード
|
| 129 |
+
- filter: `organization` に部分一致するドキュメントのみを候補集合にする(候補ゼロ時は自動フォールバックで無視するオプションも可)。
|
| 130 |
+
- boost: 部分一致ドキュメントに定数加点(例: `org_boost=+0.2`)または乗算(例: `org_boost_mul=1.1`)。
|
| 131 |
+
- パラメータ(API; 例)
|
| 132 |
+
- `org`: 文字列。部分一致の対象
|
| 133 |
+
- `org_mode`: `filter|boost`(既定 `boost`)
|
| 134 |
+
- `org_boost`: `0.0–2.0`(既定 `0.2`)
|
| 135 |
+
- 実装は検索前処理で `organization` フィールドの正規化文字列へ substring 判定を実施
|
| 136 |
+
- 融合との関係
|
| 137 |
+
- filter: BM25F/word_sim の計算対象集合を限定
|
| 138 |
+
- boost: 最終スコアに加点(または BM25F スコアに加点)
|
| 139 |
+
- 注意点
|
| 140 |
+
- 正規化は検索側/索引側で同一処理を適用(ビルド時に `org_norm` を持たせると高速)
|
| 141 |
+
- 候補ゼロの UX を考慮し、`filter` 選択時のみ `fallback_if_empty=true` を許可
|
docs/test.md
ADDED
|
@@ -0,0 +1,2063 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 電子
|
| 2 |
+
{
|
| 3 |
+
"projectIds": [
|
| 4 |
+
"dummy-01",
|
| 5 |
+
"dummy-48",
|
| 6 |
+
"dummy-33",
|
| 7 |
+
"dummy-25",
|
| 8 |
+
"dummy-18",
|
| 9 |
+
"dummy-47",
|
| 10 |
+
"dummy-36",
|
| 11 |
+
"dummy-40",
|
| 12 |
+
"dummy-24",
|
| 13 |
+
"dummy-21",
|
| 14 |
+
"dummy-11",
|
| 15 |
+
"dummy-10",
|
| 16 |
+
"dummy-02",
|
| 17 |
+
"dummy-15",
|
| 18 |
+
"dummy-13",
|
| 19 |
+
"dummy-17",
|
| 20 |
+
"dummy-35",
|
| 21 |
+
"dummy-34",
|
| 22 |
+
"dummy-38",
|
| 23 |
+
"dummy-07",
|
| 24 |
+
"dummy-42",
|
| 25 |
+
"dummy-31",
|
| 26 |
+
"dummy-06",
|
| 27 |
+
"dummy-19",
|
| 28 |
+
"dummy-45",
|
| 29 |
+
"dummy-43",
|
| 30 |
+
"dummy-03",
|
| 31 |
+
"dummy-30",
|
| 32 |
+
"dummy-04",
|
| 33 |
+
"dummy-37",
|
| 34 |
+
"dummy-23",
|
| 35 |
+
"dummy-46",
|
| 36 |
+
"dummy-32",
|
| 37 |
+
"dummy-08",
|
| 38 |
+
"dummy-27",
|
| 39 |
+
"dummy-14",
|
| 40 |
+
"dummy-09",
|
| 41 |
+
"dummy-29",
|
| 42 |
+
"dummy-39",
|
| 43 |
+
"dummy-20",
|
| 44 |
+
"dummy-22",
|
| 45 |
+
"dummy-44",
|
| 46 |
+
"dummy-05",
|
| 47 |
+
"dummy-50",
|
| 48 |
+
"dummy-41",
|
| 49 |
+
"dummy-28",
|
| 50 |
+
"dummy-49",
|
| 51 |
+
"dummy-12",
|
| 52 |
+
"dummy-26",
|
| 53 |
+
"dummy-16"
|
| 54 |
+
],
|
| 55 |
+
"scores": [
|
| 56 |
+
{
|
| 57 |
+
"projectId": "dummy-01",
|
| 58 |
+
"score": 3.431400775909424
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"projectId": "dummy-48",
|
| 62 |
+
"score": 0.12993203103542328
|
| 63 |
+
},
|
| 64 |
+
{
|
| 65 |
+
"projectId": "dummy-33",
|
| 66 |
+
"score": 0.12524525821208954
|
| 67 |
+
},
|
| 68 |
+
{
|
| 69 |
+
"projectId": "dummy-25",
|
| 70 |
+
"score": 0.1251281201839447
|
| 71 |
+
},
|
| 72 |
+
{
|
| 73 |
+
"projectId": "dummy-18",
|
| 74 |
+
"score": 0.11960740387439728
|
| 75 |
+
},
|
| 76 |
+
{
|
| 77 |
+
"projectId": "dummy-47",
|
| 78 |
+
"score": 0.11708493530750275
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"projectId": "dummy-36",
|
| 82 |
+
"score": 0.11684656143188477
|
| 83 |
+
},
|
| 84 |
+
{
|
| 85 |
+
"projectId": "dummy-40",
|
| 86 |
+
"score": 0.11507218331098557
|
| 87 |
+
},
|
| 88 |
+
{
|
| 89 |
+
"projectId": "dummy-24",
|
| 90 |
+
"score": 0.11465374380350113
|
| 91 |
+
},
|
| 92 |
+
{
|
| 93 |
+
"projectId": "dummy-21",
|
| 94 |
+
"score": 0.11193694919347763
|
| 95 |
+
},
|
| 96 |
+
{
|
| 97 |
+
"projectId": "dummy-11",
|
| 98 |
+
"score": 0.10999565571546555
|
| 99 |
+
},
|
| 100 |
+
{
|
| 101 |
+
"projectId": "dummy-10",
|
| 102 |
+
"score": 0.1098007932305336
|
| 103 |
+
},
|
| 104 |
+
{
|
| 105 |
+
"projectId": "dummy-02",
|
| 106 |
+
"score": 0.10925329476594925
|
| 107 |
+
},
|
| 108 |
+
{
|
| 109 |
+
"projectId": "dummy-15",
|
| 110 |
+
"score": 0.10827929526567459
|
| 111 |
+
},
|
| 112 |
+
{
|
| 113 |
+
"projectId": "dummy-13",
|
| 114 |
+
"score": 0.10723763704299927
|
| 115 |
+
},
|
| 116 |
+
{
|
| 117 |
+
"projectId": "dummy-17",
|
| 118 |
+
"score": 0.10687872767448425
|
| 119 |
+
},
|
| 120 |
+
{
|
| 121 |
+
"projectId": "dummy-35",
|
| 122 |
+
"score": 0.10368791967630386
|
| 123 |
+
},
|
| 124 |
+
{
|
| 125 |
+
"projectId": "dummy-34",
|
| 126 |
+
"score": 0.10351861268281937
|
| 127 |
+
},
|
| 128 |
+
{
|
| 129 |
+
"projectId": "dummy-38",
|
| 130 |
+
"score": 0.1032002866268158
|
| 131 |
+
},
|
| 132 |
+
{
|
| 133 |
+
"projectId": "dummy-07",
|
| 134 |
+
"score": 0.10318154841661453
|
| 135 |
+
},
|
| 136 |
+
{
|
| 137 |
+
"projectId": "dummy-42",
|
| 138 |
+
"score": 0.10294974595308304
|
| 139 |
+
},
|
| 140 |
+
{
|
| 141 |
+
"projectId": "dummy-31",
|
| 142 |
+
"score": 0.10262186080217361
|
| 143 |
+
},
|
| 144 |
+
{
|
| 145 |
+
"projectId": "dummy-06",
|
| 146 |
+
"score": 0.10227564722299576
|
| 147 |
+
},
|
| 148 |
+
{
|
| 149 |
+
"projectId": "dummy-19",
|
| 150 |
+
"score": 0.1011614054441452
|
| 151 |
+
},
|
| 152 |
+
{
|
| 153 |
+
"projectId": "dummy-45",
|
| 154 |
+
"score": 0.10093759745359421
|
| 155 |
+
},
|
| 156 |
+
{
|
| 157 |
+
"projectId": "dummy-43",
|
| 158 |
+
"score": 0.10040473192930222
|
| 159 |
+
},
|
| 160 |
+
{
|
| 161 |
+
"projectId": "dummy-03",
|
| 162 |
+
"score": 0.0997525006532669
|
| 163 |
+
},
|
| 164 |
+
{
|
| 165 |
+
"projectId": "dummy-30",
|
| 166 |
+
"score": 0.09739913791418076
|
| 167 |
+
},
|
| 168 |
+
{
|
| 169 |
+
"projectId": "dummy-04",
|
| 170 |
+
"score": 0.09715365618467331
|
| 171 |
+
},
|
| 172 |
+
{
|
| 173 |
+
"projectId": "dummy-37",
|
| 174 |
+
"score": 0.09643036872148514
|
| 175 |
+
},
|
| 176 |
+
{
|
| 177 |
+
"projectId": "dummy-23",
|
| 178 |
+
"score": 0.09422503411769867
|
| 179 |
+
},
|
| 180 |
+
{
|
| 181 |
+
"projectId": "dummy-46",
|
| 182 |
+
"score": 0.09410383552312851
|
| 183 |
+
},
|
| 184 |
+
{
|
| 185 |
+
"projectId": "dummy-32",
|
| 186 |
+
"score": 0.09380743652582169
|
| 187 |
+
},
|
| 188 |
+
{
|
| 189 |
+
"projectId": "dummy-08",
|
| 190 |
+
"score": 0.09357142448425293
|
| 191 |
+
},
|
| 192 |
+
{
|
| 193 |
+
"projectId": "dummy-27",
|
| 194 |
+
"score": 0.09273825585842133
|
| 195 |
+
},
|
| 196 |
+
{
|
| 197 |
+
"projectId": "dummy-14",
|
| 198 |
+
"score": 0.09264349937438965
|
| 199 |
+
},
|
| 200 |
+
{
|
| 201 |
+
"projectId": "dummy-09",
|
| 202 |
+
"score": 0.09263898432254791
|
| 203 |
+
},
|
| 204 |
+
{
|
| 205 |
+
"projectId": "dummy-29",
|
| 206 |
+
"score": 0.0914716124534607
|
| 207 |
+
},
|
| 208 |
+
{
|
| 209 |
+
"projectId": "dummy-39",
|
| 210 |
+
"score": 0.09003062546253204
|
| 211 |
+
},
|
| 212 |
+
{
|
| 213 |
+
"projectId": "dummy-20",
|
| 214 |
+
"score": 0.08615680038928986
|
| 215 |
+
},
|
| 216 |
+
{
|
| 217 |
+
"projectId": "dummy-22",
|
| 218 |
+
"score": 0.08371936529874802
|
| 219 |
+
},
|
| 220 |
+
{
|
| 221 |
+
"projectId": "dummy-44",
|
| 222 |
+
"score": 0.08343670517206192
|
| 223 |
+
},
|
| 224 |
+
{
|
| 225 |
+
"projectId": "dummy-05",
|
| 226 |
+
"score": 0.08266183733940125
|
| 227 |
+
},
|
| 228 |
+
{
|
| 229 |
+
"projectId": "dummy-50",
|
| 230 |
+
"score": 0.08221913129091263
|
| 231 |
+
},
|
| 232 |
+
{
|
| 233 |
+
"projectId": "dummy-41",
|
| 234 |
+
"score": 0.08188144117593765
|
| 235 |
+
},
|
| 236 |
+
{
|
| 237 |
+
"projectId": "dummy-28",
|
| 238 |
+
"score": 0.07737957686185837
|
| 239 |
+
},
|
| 240 |
+
{
|
| 241 |
+
"projectId": "dummy-49",
|
| 242 |
+
"score": 0.07631941884756088
|
| 243 |
+
},
|
| 244 |
+
{
|
| 245 |
+
"projectId": "dummy-12",
|
| 246 |
+
"score": 0.07623115181922913
|
| 247 |
+
},
|
| 248 |
+
{
|
| 249 |
+
"projectId": "dummy-26",
|
| 250 |
+
"score": 0.0692358985543251
|
| 251 |
+
},
|
| 252 |
+
{
|
| 253 |
+
"projectId": "dummy-16",
|
| 254 |
+
"score": 0.06431126594543457
|
| 255 |
+
}
|
| 256 |
+
]
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
# こうさく
|
| 260 |
+
{
|
| 261 |
+
"projectIds": [
|
| 262 |
+
"dummy-01",
|
| 263 |
+
"dummy-19",
|
| 264 |
+
"dummy-40",
|
| 265 |
+
"dummy-42",
|
| 266 |
+
"dummy-25",
|
| 267 |
+
"dummy-13",
|
| 268 |
+
"dummy-34",
|
| 269 |
+
"dummy-48",
|
| 270 |
+
"dummy-38",
|
| 271 |
+
"dummy-27",
|
| 272 |
+
"dummy-02",
|
| 273 |
+
"dummy-17",
|
| 274 |
+
"dummy-24",
|
| 275 |
+
"dummy-35",
|
| 276 |
+
"dummy-03",
|
| 277 |
+
"dummy-10",
|
| 278 |
+
"dummy-45",
|
| 279 |
+
"dummy-26",
|
| 280 |
+
"dummy-14",
|
| 281 |
+
"dummy-33",
|
| 282 |
+
"dummy-29",
|
| 283 |
+
"dummy-23",
|
| 284 |
+
"dummy-50",
|
| 285 |
+
"dummy-21",
|
| 286 |
+
"dummy-32",
|
| 287 |
+
"dummy-36",
|
| 288 |
+
"dummy-49",
|
| 289 |
+
"dummy-04",
|
| 290 |
+
"dummy-30",
|
| 291 |
+
"dummy-05",
|
| 292 |
+
"dummy-12",
|
| 293 |
+
"dummy-15",
|
| 294 |
+
"dummy-39",
|
| 295 |
+
"dummy-31",
|
| 296 |
+
"dummy-37",
|
| 297 |
+
"dummy-08",
|
| 298 |
+
"dummy-11",
|
| 299 |
+
"dummy-09",
|
| 300 |
+
"dummy-44",
|
| 301 |
+
"dummy-06",
|
| 302 |
+
"dummy-43",
|
| 303 |
+
"dummy-18",
|
| 304 |
+
"dummy-46",
|
| 305 |
+
"dummy-20",
|
| 306 |
+
"dummy-22",
|
| 307 |
+
"dummy-28",
|
| 308 |
+
"dummy-41",
|
| 309 |
+
"dummy-07",
|
| 310 |
+
"dummy-47",
|
| 311 |
+
"dummy-16"
|
| 312 |
+
],
|
| 313 |
+
"scores": [
|
| 314 |
+
{
|
| 315 |
+
"projectId": "dummy-01",
|
| 316 |
+
"score": 2.958343744277954
|
| 317 |
+
},
|
| 318 |
+
{
|
| 319 |
+
"projectId": "dummy-19",
|
| 320 |
+
"score": 0.10967567563056946
|
| 321 |
+
},
|
| 322 |
+
{
|
| 323 |
+
"projectId": "dummy-40",
|
| 324 |
+
"score": 0.1078089252114296
|
| 325 |
+
},
|
| 326 |
+
{
|
| 327 |
+
"projectId": "dummy-42",
|
| 328 |
+
"score": 0.10442163795232773
|
| 329 |
+
},
|
| 330 |
+
{
|
| 331 |
+
"projectId": "dummy-25",
|
| 332 |
+
"score": 0.10362518578767776
|
| 333 |
+
},
|
| 334 |
+
{
|
| 335 |
+
"projectId": "dummy-13",
|
| 336 |
+
"score": 0.10124309360980988
|
| 337 |
+
},
|
| 338 |
+
{
|
| 339 |
+
"projectId": "dummy-34",
|
| 340 |
+
"score": 0.10120446979999542
|
| 341 |
+
},
|
| 342 |
+
{
|
| 343 |
+
"projectId": "dummy-48",
|
| 344 |
+
"score": 0.10025734454393387
|
| 345 |
+
},
|
| 346 |
+
{
|
| 347 |
+
"projectId": "dummy-38",
|
| 348 |
+
"score": 0.09987800568342209
|
| 349 |
+
},
|
| 350 |
+
{
|
| 351 |
+
"projectId": "dummy-27",
|
| 352 |
+
"score": 0.09944233298301697
|
| 353 |
+
},
|
| 354 |
+
{
|
| 355 |
+
"projectId": "dummy-02",
|
| 356 |
+
"score": 0.0974903553724289
|
| 357 |
+
},
|
| 358 |
+
{
|
| 359 |
+
"projectId": "dummy-17",
|
| 360 |
+
"score": 0.09713833779096603
|
| 361 |
+
},
|
| 362 |
+
{
|
| 363 |
+
"projectId": "dummy-24",
|
| 364 |
+
"score": 0.09597335755825043
|
| 365 |
+
},
|
| 366 |
+
{
|
| 367 |
+
"projectId": "dummy-35",
|
| 368 |
+
"score": 0.09521206468343735
|
| 369 |
+
},
|
| 370 |
+
{
|
| 371 |
+
"projectId": "dummy-03",
|
| 372 |
+
"score": 0.09464053064584732
|
| 373 |
+
},
|
| 374 |
+
{
|
| 375 |
+
"projectId": "dummy-10",
|
| 376 |
+
"score": 0.09453081339597702
|
| 377 |
+
},
|
| 378 |
+
{
|
| 379 |
+
"projectId": "dummy-45",
|
| 380 |
+
"score": 0.09366767108440399
|
| 381 |
+
},
|
| 382 |
+
{
|
| 383 |
+
"projectId": "dummy-26",
|
| 384 |
+
"score": 0.09298509359359741
|
| 385 |
+
},
|
| 386 |
+
{
|
| 387 |
+
"projectId": "dummy-14",
|
| 388 |
+
"score": 0.09244585782289505
|
| 389 |
+
},
|
| 390 |
+
{
|
| 391 |
+
"projectId": "dummy-33",
|
| 392 |
+
"score": 0.09236441552639008
|
| 393 |
+
},
|
| 394 |
+
{
|
| 395 |
+
"projectId": "dummy-29",
|
| 396 |
+
"score": 0.09188427776098251
|
| 397 |
+
},
|
| 398 |
+
{
|
| 399 |
+
"projectId": "dummy-23",
|
| 400 |
+
"score": 0.0905442014336586
|
| 401 |
+
},
|
| 402 |
+
{
|
| 403 |
+
"projectId": "dummy-50",
|
| 404 |
+
"score": 0.09030000120401382
|
| 405 |
+
},
|
| 406 |
+
{
|
| 407 |
+
"projectId": "dummy-21",
|
| 408 |
+
"score": 0.09017764776945114
|
| 409 |
+
},
|
| 410 |
+
{
|
| 411 |
+
"projectId": "dummy-32",
|
| 412 |
+
"score": 0.09004317224025726
|
| 413 |
+
},
|
| 414 |
+
{
|
| 415 |
+
"projectId": "dummy-36",
|
| 416 |
+
"score": 0.08987368643283844
|
| 417 |
+
},
|
| 418 |
+
{
|
| 419 |
+
"projectId": "dummy-49",
|
| 420 |
+
"score": 0.0897449180483818
|
| 421 |
+
},
|
| 422 |
+
{
|
| 423 |
+
"projectId": "dummy-04",
|
| 424 |
+
"score": 0.08967691659927368
|
| 425 |
+
},
|
| 426 |
+
{
|
| 427 |
+
"projectId": "dummy-30",
|
| 428 |
+
"score": 0.08961175382137299
|
| 429 |
+
},
|
| 430 |
+
{
|
| 431 |
+
"projectId": "dummy-05",
|
| 432 |
+
"score": 0.08941705524921417
|
| 433 |
+
},
|
| 434 |
+
{
|
| 435 |
+
"projectId": "dummy-12",
|
| 436 |
+
"score": 0.08816639333963394
|
| 437 |
+
},
|
| 438 |
+
{
|
| 439 |
+
"projectId": "dummy-15",
|
| 440 |
+
"score": 0.08616558462381363
|
| 441 |
+
},
|
| 442 |
+
{
|
| 443 |
+
"projectId": "dummy-39",
|
| 444 |
+
"score": 0.0843149945139885
|
| 445 |
+
},
|
| 446 |
+
{
|
| 447 |
+
"projectId": "dummy-31",
|
| 448 |
+
"score": 0.08405950665473938
|
| 449 |
+
},
|
| 450 |
+
{
|
| 451 |
+
"projectId": "dummy-37",
|
| 452 |
+
"score": 0.08368293941020966
|
| 453 |
+
},
|
| 454 |
+
{
|
| 455 |
+
"projectId": "dummy-08",
|
| 456 |
+
"score": 0.08284597098827362
|
| 457 |
+
},
|
| 458 |
+
{
|
| 459 |
+
"projectId": "dummy-11",
|
| 460 |
+
"score": 0.08270086348056793
|
| 461 |
+
},
|
| 462 |
+
{
|
| 463 |
+
"projectId": "dummy-09",
|
| 464 |
+
"score": 0.08264289796352386
|
| 465 |
+
},
|
| 466 |
+
{
|
| 467 |
+
"projectId": "dummy-44",
|
| 468 |
+
"score": 0.08196865022182465
|
| 469 |
+
},
|
| 470 |
+
{
|
| 471 |
+
"projectId": "dummy-06",
|
| 472 |
+
"score": 0.08190039545297623
|
| 473 |
+
},
|
| 474 |
+
{
|
| 475 |
+
"projectId": "dummy-43",
|
| 476 |
+
"score": 0.08157528936862946
|
| 477 |
+
},
|
| 478 |
+
{
|
| 479 |
+
"projectId": "dummy-18",
|
| 480 |
+
"score": 0.08130253106355667
|
| 481 |
+
},
|
| 482 |
+
{
|
| 483 |
+
"projectId": "dummy-46",
|
| 484 |
+
"score": 0.07963813096284866
|
| 485 |
+
},
|
| 486 |
+
{
|
| 487 |
+
"projectId": "dummy-20",
|
| 488 |
+
"score": 0.07923799008131027
|
| 489 |
+
},
|
| 490 |
+
{
|
| 491 |
+
"projectId": "dummy-22",
|
| 492 |
+
"score": 0.07898101210594177
|
| 493 |
+
},
|
| 494 |
+
{
|
| 495 |
+
"projectId": "dummy-28",
|
| 496 |
+
"score": 0.07825343310832977
|
| 497 |
+
},
|
| 498 |
+
{
|
| 499 |
+
"projectId": "dummy-41",
|
| 500 |
+
"score": 0.07740511000156403
|
| 501 |
+
},
|
| 502 |
+
{
|
| 503 |
+
"projectId": "dummy-07",
|
| 504 |
+
"score": 0.07709077000617981
|
| 505 |
+
},
|
| 506 |
+
{
|
| 507 |
+
"projectId": "dummy-47",
|
| 508 |
+
"score": 0.07670128345489502
|
| 509 |
+
},
|
| 510 |
+
{
|
| 511 |
+
"projectId": "dummy-16",
|
| 512 |
+
"score": 0.07050929963588715
|
| 513 |
+
}
|
| 514 |
+
]
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
# 空想地図
|
| 518 |
+
{
|
| 519 |
+
"projectIds": [
|
| 520 |
+
"dummy-02",
|
| 521 |
+
"dummy-32",
|
| 522 |
+
"dummy-20",
|
| 523 |
+
"dummy-27",
|
| 524 |
+
"dummy-50",
|
| 525 |
+
"dummy-19",
|
| 526 |
+
"dummy-31",
|
| 527 |
+
"dummy-37",
|
| 528 |
+
"dummy-18",
|
| 529 |
+
"dummy-21",
|
| 530 |
+
"dummy-03",
|
| 531 |
+
"dummy-30",
|
| 532 |
+
"dummy-39",
|
| 533 |
+
"dummy-01",
|
| 534 |
+
"dummy-06",
|
| 535 |
+
"dummy-09",
|
| 536 |
+
"dummy-10",
|
| 537 |
+
"dummy-23",
|
| 538 |
+
"dummy-11",
|
| 539 |
+
"dummy-46",
|
| 540 |
+
"dummy-08",
|
| 541 |
+
"dummy-35",
|
| 542 |
+
"dummy-22",
|
| 543 |
+
"dummy-07",
|
| 544 |
+
"dummy-17",
|
| 545 |
+
"dummy-48",
|
| 546 |
+
"dummy-40",
|
| 547 |
+
"dummy-47",
|
| 548 |
+
"dummy-45",
|
| 549 |
+
"dummy-26",
|
| 550 |
+
"dummy-05",
|
| 551 |
+
"dummy-36",
|
| 552 |
+
"dummy-24",
|
| 553 |
+
"dummy-44",
|
| 554 |
+
"dummy-13",
|
| 555 |
+
"dummy-15",
|
| 556 |
+
"dummy-38",
|
| 557 |
+
"dummy-42",
|
| 558 |
+
"dummy-14",
|
| 559 |
+
"dummy-41",
|
| 560 |
+
"dummy-29",
|
| 561 |
+
"dummy-34",
|
| 562 |
+
"dummy-25",
|
| 563 |
+
"dummy-43",
|
| 564 |
+
"dummy-33",
|
| 565 |
+
"dummy-12",
|
| 566 |
+
"dummy-04",
|
| 567 |
+
"dummy-28",
|
| 568 |
+
"dummy-49",
|
| 569 |
+
"dummy-16"
|
| 570 |
+
],
|
| 571 |
+
"scores": [
|
| 572 |
+
{
|
| 573 |
+
"projectId": "dummy-02",
|
| 574 |
+
"score": 5.931369781494141
|
| 575 |
+
},
|
| 576 |
+
{
|
| 577 |
+
"projectId": "dummy-32",
|
| 578 |
+
"score": 2.6352267265319824
|
| 579 |
+
},
|
| 580 |
+
{
|
| 581 |
+
"projectId": "dummy-20",
|
| 582 |
+
"score": 0.1919010877609253
|
| 583 |
+
},
|
| 584 |
+
{
|
| 585 |
+
"projectId": "dummy-27",
|
| 586 |
+
"score": 0.1807546764612198
|
| 587 |
+
},
|
| 588 |
+
{
|
| 589 |
+
"projectId": "dummy-50",
|
| 590 |
+
"score": 0.1795060932636261
|
| 591 |
+
},
|
| 592 |
+
{
|
| 593 |
+
"projectId": "dummy-19",
|
| 594 |
+
"score": 0.17730551958084106
|
| 595 |
+
},
|
| 596 |
+
{
|
| 597 |
+
"projectId": "dummy-31",
|
| 598 |
+
"score": 0.17708048224449158
|
| 599 |
+
},
|
| 600 |
+
{
|
| 601 |
+
"projectId": "dummy-37",
|
| 602 |
+
"score": 0.17596064507961273
|
| 603 |
+
},
|
| 604 |
+
{
|
| 605 |
+
"projectId": "dummy-18",
|
| 606 |
+
"score": 0.1759600043296814
|
| 607 |
+
},
|
| 608 |
+
{
|
| 609 |
+
"projectId": "dummy-21",
|
| 610 |
+
"score": 0.1722804307937622
|
| 611 |
+
},
|
| 612 |
+
{
|
| 613 |
+
"projectId": "dummy-03",
|
| 614 |
+
"score": 0.16904908418655396
|
| 615 |
+
},
|
| 616 |
+
{
|
| 617 |
+
"projectId": "dummy-30",
|
| 618 |
+
"score": 0.16836203634738922
|
| 619 |
+
},
|
| 620 |
+
{
|
| 621 |
+
"projectId": "dummy-39",
|
| 622 |
+
"score": 0.16769906878471375
|
| 623 |
+
},
|
| 624 |
+
{
|
| 625 |
+
"projectId": "dummy-01",
|
| 626 |
+
"score": 0.16594183444976807
|
| 627 |
+
},
|
| 628 |
+
{
|
| 629 |
+
"projectId": "dummy-06",
|
| 630 |
+
"score": 0.1649368703365326
|
| 631 |
+
},
|
| 632 |
+
{
|
| 633 |
+
"projectId": "dummy-09",
|
| 634 |
+
"score": 0.16446015238761902
|
| 635 |
+
},
|
| 636 |
+
{
|
| 637 |
+
"projectId": "dummy-10",
|
| 638 |
+
"score": 0.16347283124923706
|
| 639 |
+
},
|
| 640 |
+
{
|
| 641 |
+
"projectId": "dummy-23",
|
| 642 |
+
"score": 0.16043199598789215
|
| 643 |
+
},
|
| 644 |
+
{
|
| 645 |
+
"projectId": "dummy-11",
|
| 646 |
+
"score": 0.1600879430770874
|
| 647 |
+
},
|
| 648 |
+
{
|
| 649 |
+
"projectId": "dummy-46",
|
| 650 |
+
"score": 0.1599961817264557
|
| 651 |
+
},
|
| 652 |
+
{
|
| 653 |
+
"projectId": "dummy-08",
|
| 654 |
+
"score": 0.15916672348976135
|
| 655 |
+
},
|
| 656 |
+
{
|
| 657 |
+
"projectId": "dummy-35",
|
| 658 |
+
"score": 0.15472930669784546
|
| 659 |
+
},
|
| 660 |
+
{
|
| 661 |
+
"projectId": "dummy-22",
|
| 662 |
+
"score": 0.15420827269554138
|
| 663 |
+
},
|
| 664 |
+
{
|
| 665 |
+
"projectId": "dummy-07",
|
| 666 |
+
"score": 0.15418641269207
|
| 667 |
+
},
|
| 668 |
+
{
|
| 669 |
+
"projectId": "dummy-17",
|
| 670 |
+
"score": 0.15405218303203583
|
| 671 |
+
},
|
| 672 |
+
{
|
| 673 |
+
"projectId": "dummy-48",
|
| 674 |
+
"score": 0.1535654515028
|
| 675 |
+
},
|
| 676 |
+
{
|
| 677 |
+
"projectId": "dummy-40",
|
| 678 |
+
"score": 0.15275800228118896
|
| 679 |
+
},
|
| 680 |
+
{
|
| 681 |
+
"projectId": "dummy-47",
|
| 682 |
+
"score": 0.15079064667224884
|
| 683 |
+
},
|
| 684 |
+
{
|
| 685 |
+
"projectId": "dummy-45",
|
| 686 |
+
"score": 0.14808325469493866
|
| 687 |
+
},
|
| 688 |
+
{
|
| 689 |
+
"projectId": "dummy-26",
|
| 690 |
+
"score": 0.1474611759185791
|
| 691 |
+
},
|
| 692 |
+
{
|
| 693 |
+
"projectId": "dummy-05",
|
| 694 |
+
"score": 0.14566561579704285
|
| 695 |
+
},
|
| 696 |
+
{
|
| 697 |
+
"projectId": "dummy-36",
|
| 698 |
+
"score": 0.14557917416095734
|
| 699 |
+
},
|
| 700 |
+
{
|
| 701 |
+
"projectId": "dummy-24",
|
| 702 |
+
"score": 0.14543066918849945
|
| 703 |
+
},
|
| 704 |
+
{
|
| 705 |
+
"projectId": "dummy-44",
|
| 706 |
+
"score": 0.14542552828788757
|
| 707 |
+
},
|
| 708 |
+
{
|
| 709 |
+
"projectId": "dummy-13",
|
| 710 |
+
"score": 0.14524437487125397
|
| 711 |
+
},
|
| 712 |
+
{
|
| 713 |
+
"projectId": "dummy-15",
|
| 714 |
+
"score": 0.14392796158790588
|
| 715 |
+
},
|
| 716 |
+
{
|
| 717 |
+
"projectId": "dummy-38",
|
| 718 |
+
"score": 0.14303478598594666
|
| 719 |
+
},
|
| 720 |
+
{
|
| 721 |
+
"projectId": "dummy-42",
|
| 722 |
+
"score": 0.1425190418958664
|
| 723 |
+
},
|
| 724 |
+
{
|
| 725 |
+
"projectId": "dummy-14",
|
| 726 |
+
"score": 0.13991902768611908
|
| 727 |
+
},
|
| 728 |
+
{
|
| 729 |
+
"projectId": "dummy-41",
|
| 730 |
+
"score": 0.1398436278104782
|
| 731 |
+
},
|
| 732 |
+
{
|
| 733 |
+
"projectId": "dummy-29",
|
| 734 |
+
"score": 0.13916610181331635
|
| 735 |
+
},
|
| 736 |
+
{
|
| 737 |
+
"projectId": "dummy-34",
|
| 738 |
+
"score": 0.13802099227905273
|
| 739 |
+
},
|
| 740 |
+
{
|
| 741 |
+
"projectId": "dummy-25",
|
| 742 |
+
"score": 0.13733211159706116
|
| 743 |
+
},
|
| 744 |
+
{
|
| 745 |
+
"projectId": "dummy-43",
|
| 746 |
+
"score": 0.13702505826950073
|
| 747 |
+
},
|
| 748 |
+
{
|
| 749 |
+
"projectId": "dummy-33",
|
| 750 |
+
"score": 0.13551051914691925
|
| 751 |
+
},
|
| 752 |
+
{
|
| 753 |
+
"projectId": "dummy-12",
|
| 754 |
+
"score": 0.13336017727851868
|
| 755 |
+
},
|
| 756 |
+
{
|
| 757 |
+
"projectId": "dummy-04",
|
| 758 |
+
"score": 0.1325426697731018
|
| 759 |
+
},
|
| 760 |
+
{
|
| 761 |
+
"projectId": "dummy-28",
|
| 762 |
+
"score": 0.1268766224384308
|
| 763 |
+
},
|
| 764 |
+
{
|
| 765 |
+
"projectId": "dummy-49",
|
| 766 |
+
"score": 0.1265995353460312
|
| 767 |
+
},
|
| 768 |
+
{
|
| 769 |
+
"projectId": "dummy-16",
|
| 770 |
+
"score": 0.12136942893266678
|
| 771 |
+
}
|
| 772 |
+
]
|
| 773 |
+
}
|
| 774 |
+
|
| 775 |
+
# ジェミニシティ
|
| 776 |
+
{
|
| 777 |
+
"projectIds": [
|
| 778 |
+
"dummy-02",
|
| 779 |
+
"dummy-07",
|
| 780 |
+
"dummy-09",
|
| 781 |
+
"dummy-30",
|
| 782 |
+
"dummy-01",
|
| 783 |
+
"dummy-06",
|
| 784 |
+
"dummy-45",
|
| 785 |
+
"dummy-05",
|
| 786 |
+
"dummy-13",
|
| 787 |
+
"dummy-21",
|
| 788 |
+
"dummy-50",
|
| 789 |
+
"dummy-27",
|
| 790 |
+
"dummy-36",
|
| 791 |
+
"dummy-40",
|
| 792 |
+
"dummy-43",
|
| 793 |
+
"dummy-46",
|
| 794 |
+
"dummy-34",
|
| 795 |
+
"dummy-47",
|
| 796 |
+
"dummy-32",
|
| 797 |
+
"dummy-42",
|
| 798 |
+
"dummy-18",
|
| 799 |
+
"dummy-20",
|
| 800 |
+
"dummy-31",
|
| 801 |
+
"dummy-25",
|
| 802 |
+
"dummy-29",
|
| 803 |
+
"dummy-15",
|
| 804 |
+
"dummy-04",
|
| 805 |
+
"dummy-39",
|
| 806 |
+
"dummy-10",
|
| 807 |
+
"dummy-19",
|
| 808 |
+
"dummy-37",
|
| 809 |
+
"dummy-11",
|
| 810 |
+
"dummy-24",
|
| 811 |
+
"dummy-38",
|
| 812 |
+
"dummy-03",
|
| 813 |
+
"dummy-23",
|
| 814 |
+
"dummy-08",
|
| 815 |
+
"dummy-17",
|
| 816 |
+
"dummy-22",
|
| 817 |
+
"dummy-33",
|
| 818 |
+
"dummy-35",
|
| 819 |
+
"dummy-14",
|
| 820 |
+
"dummy-12",
|
| 821 |
+
"dummy-41",
|
| 822 |
+
"dummy-26",
|
| 823 |
+
"dummy-48",
|
| 824 |
+
"dummy-44",
|
| 825 |
+
"dummy-49",
|
| 826 |
+
"dummy-16",
|
| 827 |
+
"dummy-28"
|
| 828 |
+
],
|
| 829 |
+
"scores": [
|
| 830 |
+
{
|
| 831 |
+
"projectId": "dummy-02",
|
| 832 |
+
"score": 2.456648588180542
|
| 833 |
+
},
|
| 834 |
+
{
|
| 835 |
+
"projectId": "dummy-07",
|
| 836 |
+
"score": 0.162680983543396
|
| 837 |
+
},
|
| 838 |
+
{
|
| 839 |
+
"projectId": "dummy-09",
|
| 840 |
+
"score": 0.15619903802871704
|
| 841 |
+
},
|
| 842 |
+
{
|
| 843 |
+
"projectId": "dummy-30",
|
| 844 |
+
"score": 0.15498758852481842
|
| 845 |
+
},
|
| 846 |
+
{
|
| 847 |
+
"projectId": "dummy-01",
|
| 848 |
+
"score": 0.15436750650405884
|
| 849 |
+
},
|
| 850 |
+
{
|
| 851 |
+
"projectId": "dummy-06",
|
| 852 |
+
"score": 0.15225842595100403
|
| 853 |
+
},
|
| 854 |
+
{
|
| 855 |
+
"projectId": "dummy-45",
|
| 856 |
+
"score": 0.1520552635192871
|
| 857 |
+
},
|
| 858 |
+
{
|
| 859 |
+
"projectId": "dummy-05",
|
| 860 |
+
"score": 0.1514219343662262
|
| 861 |
+
},
|
| 862 |
+
{
|
| 863 |
+
"projectId": "dummy-13",
|
| 864 |
+
"score": 0.15118378400802612
|
| 865 |
+
},
|
| 866 |
+
{
|
| 867 |
+
"projectId": "dummy-21",
|
| 868 |
+
"score": 0.15101128816604614
|
| 869 |
+
},
|
| 870 |
+
{
|
| 871 |
+
"projectId": "dummy-50",
|
| 872 |
+
"score": 0.1507887840270996
|
| 873 |
+
},
|
| 874 |
+
{
|
| 875 |
+
"projectId": "dummy-27",
|
| 876 |
+
"score": 0.1502055525779724
|
| 877 |
+
},
|
| 878 |
+
{
|
| 879 |
+
"projectId": "dummy-36",
|
| 880 |
+
"score": 0.14961348474025726
|
| 881 |
+
},
|
| 882 |
+
{
|
| 883 |
+
"projectId": "dummy-40",
|
| 884 |
+
"score": 0.14926813542842865
|
| 885 |
+
},
|
| 886 |
+
{
|
| 887 |
+
"projectId": "dummy-43",
|
| 888 |
+
"score": 0.14891408383846283
|
| 889 |
+
},
|
| 890 |
+
{
|
| 891 |
+
"projectId": "dummy-46",
|
| 892 |
+
"score": 0.1488681137561798
|
| 893 |
+
},
|
| 894 |
+
{
|
| 895 |
+
"projectId": "dummy-34",
|
| 896 |
+
"score": 0.14862272143363953
|
| 897 |
+
},
|
| 898 |
+
{
|
| 899 |
+
"projectId": "dummy-47",
|
| 900 |
+
"score": 0.14832982420921326
|
| 901 |
+
},
|
| 902 |
+
{
|
| 903 |
+
"projectId": "dummy-32",
|
| 904 |
+
"score": 0.14759908616542816
|
| 905 |
+
},
|
| 906 |
+
{
|
| 907 |
+
"projectId": "dummy-42",
|
| 908 |
+
"score": 0.14659354090690613
|
| 909 |
+
},
|
| 910 |
+
{
|
| 911 |
+
"projectId": "dummy-18",
|
| 912 |
+
"score": 0.1465129405260086
|
| 913 |
+
},
|
| 914 |
+
{
|
| 915 |
+
"projectId": "dummy-20",
|
| 916 |
+
"score": 0.14555582404136658
|
| 917 |
+
},
|
| 918 |
+
{
|
| 919 |
+
"projectId": "dummy-31",
|
| 920 |
+
"score": 0.14508406817913055
|
| 921 |
+
},
|
| 922 |
+
{
|
| 923 |
+
"projectId": "dummy-25",
|
| 924 |
+
"score": 0.14451518654823303
|
| 925 |
+
},
|
| 926 |
+
{
|
| 927 |
+
"projectId": "dummy-29",
|
| 928 |
+
"score": 0.14375324547290802
|
| 929 |
+
},
|
| 930 |
+
{
|
| 931 |
+
"projectId": "dummy-15",
|
| 932 |
+
"score": 0.1436637043952942
|
| 933 |
+
},
|
| 934 |
+
{
|
| 935 |
+
"projectId": "dummy-04",
|
| 936 |
+
"score": 0.14246971905231476
|
| 937 |
+
},
|
| 938 |
+
{
|
| 939 |
+
"projectId": "dummy-39",
|
| 940 |
+
"score": 0.14222663640975952
|
| 941 |
+
},
|
| 942 |
+
{
|
| 943 |
+
"projectId": "dummy-10",
|
| 944 |
+
"score": 0.14208325743675232
|
| 945 |
+
},
|
| 946 |
+
{
|
| 947 |
+
"projectId": "dummy-19",
|
| 948 |
+
"score": 0.14109577238559723
|
| 949 |
+
},
|
| 950 |
+
{
|
| 951 |
+
"projectId": "dummy-37",
|
| 952 |
+
"score": 0.140916109085083
|
| 953 |
+
},
|
| 954 |
+
{
|
| 955 |
+
"projectId": "dummy-11",
|
| 956 |
+
"score": 0.14039728045463562
|
| 957 |
+
},
|
| 958 |
+
{
|
| 959 |
+
"projectId": "dummy-24",
|
| 960 |
+
"score": 0.13975919783115387
|
| 961 |
+
},
|
| 962 |
+
{
|
| 963 |
+
"projectId": "dummy-38",
|
| 964 |
+
"score": 0.13965779542922974
|
| 965 |
+
},
|
| 966 |
+
{
|
| 967 |
+
"projectId": "dummy-03",
|
| 968 |
+
"score": 0.13717900216579437
|
| 969 |
+
},
|
| 970 |
+
{
|
| 971 |
+
"projectId": "dummy-23",
|
| 972 |
+
"score": 0.13443152606487274
|
| 973 |
+
},
|
| 974 |
+
{
|
| 975 |
+
"projectId": "dummy-08",
|
| 976 |
+
"score": 0.13416489958763123
|
| 977 |
+
},
|
| 978 |
+
{
|
| 979 |
+
"projectId": "dummy-17",
|
| 980 |
+
"score": 0.13373295962810516
|
| 981 |
+
},
|
| 982 |
+
{
|
| 983 |
+
"projectId": "dummy-22",
|
| 984 |
+
"score": 0.13138151168823242
|
| 985 |
+
},
|
| 986 |
+
{
|
| 987 |
+
"projectId": "dummy-33",
|
| 988 |
+
"score": 0.13107235729694366
|
| 989 |
+
},
|
| 990 |
+
{
|
| 991 |
+
"projectId": "dummy-35",
|
| 992 |
+
"score": 0.1308753341436386
|
| 993 |
+
},
|
| 994 |
+
{
|
| 995 |
+
"projectId": "dummy-14",
|
| 996 |
+
"score": 0.1284683346748352
|
| 997 |
+
},
|
| 998 |
+
{
|
| 999 |
+
"projectId": "dummy-12",
|
| 1000 |
+
"score": 0.12798088788986206
|
| 1001 |
+
},
|
| 1002 |
+
{
|
| 1003 |
+
"projectId": "dummy-41",
|
| 1004 |
+
"score": 0.12646889686584473
|
| 1005 |
+
},
|
| 1006 |
+
{
|
| 1007 |
+
"projectId": "dummy-26",
|
| 1008 |
+
"score": 0.12549281120300293
|
| 1009 |
+
},
|
| 1010 |
+
{
|
| 1011 |
+
"projectId": "dummy-48",
|
| 1012 |
+
"score": 0.12391983717679977
|
| 1013 |
+
},
|
| 1014 |
+
{
|
| 1015 |
+
"projectId": "dummy-44",
|
| 1016 |
+
"score": 0.11978904157876968
|
| 1017 |
+
},
|
| 1018 |
+
{
|
| 1019 |
+
"projectId": "dummy-49",
|
| 1020 |
+
"score": 0.11888212710618973
|
| 1021 |
+
},
|
| 1022 |
+
{
|
| 1023 |
+
"projectId": "dummy-16",
|
| 1024 |
+
"score": 0.10489151626825333
|
| 1025 |
+
},
|
| 1026 |
+
{
|
| 1027 |
+
"projectId": "dummy-28",
|
| 1028 |
+
"score": 0.09725114703178406
|
| 1029 |
+
}
|
| 1030 |
+
]
|
| 1031 |
+
}
|
| 1032 |
+
|
| 1033 |
+
# ボードゲーム
|
| 1034 |
+
{
|
| 1035 |
+
"projectIds": [
|
| 1036 |
+
"dummy-03",
|
| 1037 |
+
"dummy-18",
|
| 1038 |
+
"dummy-14",
|
| 1039 |
+
"dummy-31",
|
| 1040 |
+
"dummy-26",
|
| 1041 |
+
"dummy-32",
|
| 1042 |
+
"dummy-01",
|
| 1043 |
+
"dummy-05",
|
| 1044 |
+
"dummy-38",
|
| 1045 |
+
"dummy-40",
|
| 1046 |
+
"dummy-23",
|
| 1047 |
+
"dummy-11",
|
| 1048 |
+
"dummy-13",
|
| 1049 |
+
"dummy-25",
|
| 1050 |
+
"dummy-48",
|
| 1051 |
+
"dummy-45",
|
| 1052 |
+
"dummy-43",
|
| 1053 |
+
"dummy-34",
|
| 1054 |
+
"dummy-15",
|
| 1055 |
+
"dummy-37",
|
| 1056 |
+
"dummy-21",
|
| 1057 |
+
"dummy-09",
|
| 1058 |
+
"dummy-08",
|
| 1059 |
+
"dummy-35",
|
| 1060 |
+
"dummy-29",
|
| 1061 |
+
"dummy-36",
|
| 1062 |
+
"dummy-10",
|
| 1063 |
+
"dummy-30",
|
| 1064 |
+
"dummy-39",
|
| 1065 |
+
"dummy-46",
|
| 1066 |
+
"dummy-42",
|
| 1067 |
+
"dummy-24",
|
| 1068 |
+
"dummy-41",
|
| 1069 |
+
"dummy-19",
|
| 1070 |
+
"dummy-47",
|
| 1071 |
+
"dummy-27",
|
| 1072 |
+
"dummy-07",
|
| 1073 |
+
"dummy-06",
|
| 1074 |
+
"dummy-02",
|
| 1075 |
+
"dummy-20",
|
| 1076 |
+
"dummy-44",
|
| 1077 |
+
"dummy-12",
|
| 1078 |
+
"dummy-50",
|
| 1079 |
+
"dummy-22",
|
| 1080 |
+
"dummy-17",
|
| 1081 |
+
"dummy-33",
|
| 1082 |
+
"dummy-49",
|
| 1083 |
+
"dummy-04",
|
| 1084 |
+
"dummy-28",
|
| 1085 |
+
"dummy-16"
|
| 1086 |
+
],
|
| 1087 |
+
"scores": [
|
| 1088 |
+
{
|
| 1089 |
+
"projectId": "dummy-03",
|
| 1090 |
+
"score": 4.6026997566223145
|
| 1091 |
+
},
|
| 1092 |
+
{
|
| 1093 |
+
"projectId": "dummy-18",
|
| 1094 |
+
"score": 3.9451470375061035
|
| 1095 |
+
},
|
| 1096 |
+
{
|
| 1097 |
+
"projectId": "dummy-14",
|
| 1098 |
+
"score": 1.7676340341567993
|
| 1099 |
+
},
|
| 1100 |
+
{
|
| 1101 |
+
"projectId": "dummy-31",
|
| 1102 |
+
"score": 1.6230387687683105
|
| 1103 |
+
},
|
| 1104 |
+
{
|
| 1105 |
+
"projectId": "dummy-26",
|
| 1106 |
+
"score": 0.1631765365600586
|
| 1107 |
+
},
|
| 1108 |
+
{
|
| 1109 |
+
"projectId": "dummy-32",
|
| 1110 |
+
"score": 0.15479955077171326
|
| 1111 |
+
},
|
| 1112 |
+
{
|
| 1113 |
+
"projectId": "dummy-01",
|
| 1114 |
+
"score": 0.15239496529102325
|
| 1115 |
+
},
|
| 1116 |
+
{
|
| 1117 |
+
"projectId": "dummy-05",
|
| 1118 |
+
"score": 0.15059471130371094
|
| 1119 |
+
},
|
| 1120 |
+
{
|
| 1121 |
+
"projectId": "dummy-38",
|
| 1122 |
+
"score": 0.15034374594688416
|
| 1123 |
+
},
|
| 1124 |
+
{
|
| 1125 |
+
"projectId": "dummy-40",
|
| 1126 |
+
"score": 0.14852960407733917
|
| 1127 |
+
},
|
| 1128 |
+
{
|
| 1129 |
+
"projectId": "dummy-23",
|
| 1130 |
+
"score": 0.14833544194698334
|
| 1131 |
+
},
|
| 1132 |
+
{
|
| 1133 |
+
"projectId": "dummy-11",
|
| 1134 |
+
"score": 0.14826737344264984
|
| 1135 |
+
},
|
| 1136 |
+
{
|
| 1137 |
+
"projectId": "dummy-13",
|
| 1138 |
+
"score": 0.147711381316185
|
| 1139 |
+
},
|
| 1140 |
+
{
|
| 1141 |
+
"projectId": "dummy-25",
|
| 1142 |
+
"score": 0.1473732441663742
|
| 1143 |
+
},
|
| 1144 |
+
{
|
| 1145 |
+
"projectId": "dummy-48",
|
| 1146 |
+
"score": 0.1472303867340088
|
| 1147 |
+
},
|
| 1148 |
+
{
|
| 1149 |
+
"projectId": "dummy-45",
|
| 1150 |
+
"score": 0.14652344584465027
|
| 1151 |
+
},
|
| 1152 |
+
{
|
| 1153 |
+
"projectId": "dummy-43",
|
| 1154 |
+
"score": 0.14301139116287231
|
| 1155 |
+
},
|
| 1156 |
+
{
|
| 1157 |
+
"projectId": "dummy-34",
|
| 1158 |
+
"score": 0.14235596358776093
|
| 1159 |
+
},
|
| 1160 |
+
{
|
| 1161 |
+
"projectId": "dummy-15",
|
| 1162 |
+
"score": 0.14173288643360138
|
| 1163 |
+
},
|
| 1164 |
+
{
|
| 1165 |
+
"projectId": "dummy-37",
|
| 1166 |
+
"score": 0.14097197353839874
|
| 1167 |
+
},
|
| 1168 |
+
{
|
| 1169 |
+
"projectId": "dummy-21",
|
| 1170 |
+
"score": 0.14076118171215057
|
| 1171 |
+
},
|
| 1172 |
+
{
|
| 1173 |
+
"projectId": "dummy-09",
|
| 1174 |
+
"score": 0.1392628401517868
|
| 1175 |
+
},
|
| 1176 |
+
{
|
| 1177 |
+
"projectId": "dummy-08",
|
| 1178 |
+
"score": 0.13878723978996277
|
| 1179 |
+
},
|
| 1180 |
+
{
|
| 1181 |
+
"projectId": "dummy-35",
|
| 1182 |
+
"score": 0.13797332346439362
|
| 1183 |
+
},
|
| 1184 |
+
{
|
| 1185 |
+
"projectId": "dummy-29",
|
| 1186 |
+
"score": 0.13737985491752625
|
| 1187 |
+
},
|
| 1188 |
+
{
|
| 1189 |
+
"projectId": "dummy-36",
|
| 1190 |
+
"score": 0.1361878663301468
|
| 1191 |
+
},
|
| 1192 |
+
{
|
| 1193 |
+
"projectId": "dummy-10",
|
| 1194 |
+
"score": 0.133790522813797
|
| 1195 |
+
},
|
| 1196 |
+
{
|
| 1197 |
+
"projectId": "dummy-30",
|
| 1198 |
+
"score": 0.1336868703365326
|
| 1199 |
+
},
|
| 1200 |
+
{
|
| 1201 |
+
"projectId": "dummy-39",
|
| 1202 |
+
"score": 0.13292568922042847
|
| 1203 |
+
},
|
| 1204 |
+
{
|
| 1205 |
+
"projectId": "dummy-46",
|
| 1206 |
+
"score": 0.13286401331424713
|
| 1207 |
+
},
|
| 1208 |
+
{
|
| 1209 |
+
"projectId": "dummy-42",
|
| 1210 |
+
"score": 0.1324540674686432
|
| 1211 |
+
},
|
| 1212 |
+
{
|
| 1213 |
+
"projectId": "dummy-24",
|
| 1214 |
+
"score": 0.1314363181591034
|
| 1215 |
+
},
|
| 1216 |
+
{
|
| 1217 |
+
"projectId": "dummy-41",
|
| 1218 |
+
"score": 0.1313837617635727
|
| 1219 |
+
},
|
| 1220 |
+
{
|
| 1221 |
+
"projectId": "dummy-19",
|
| 1222 |
+
"score": 0.1310305893421173
|
| 1223 |
+
},
|
| 1224 |
+
{
|
| 1225 |
+
"projectId": "dummy-47",
|
| 1226 |
+
"score": 0.13075384497642517
|
| 1227 |
+
},
|
| 1228 |
+
{
|
| 1229 |
+
"projectId": "dummy-27",
|
| 1230 |
+
"score": 0.130295529961586
|
| 1231 |
+
},
|
| 1232 |
+
{
|
| 1233 |
+
"projectId": "dummy-07",
|
| 1234 |
+
"score": 0.1289217174053192
|
| 1235 |
+
},
|
| 1236 |
+
{
|
| 1237 |
+
"projectId": "dummy-06",
|
| 1238 |
+
"score": 0.12773872911930084
|
| 1239 |
+
},
|
| 1240 |
+
{
|
| 1241 |
+
"projectId": "dummy-02",
|
| 1242 |
+
"score": 0.12749534845352173
|
| 1243 |
+
},
|
| 1244 |
+
{
|
| 1245 |
+
"projectId": "dummy-20",
|
| 1246 |
+
"score": 0.12316787987947464
|
| 1247 |
+
},
|
| 1248 |
+
{
|
| 1249 |
+
"projectId": "dummy-44",
|
| 1250 |
+
"score": 0.12286143749952316
|
| 1251 |
+
},
|
| 1252 |
+
{
|
| 1253 |
+
"projectId": "dummy-12",
|
| 1254 |
+
"score": 0.12097669392824173
|
| 1255 |
+
},
|
| 1256 |
+
{
|
| 1257 |
+
"projectId": "dummy-50",
|
| 1258 |
+
"score": 0.11809731274843216
|
| 1259 |
+
},
|
| 1260 |
+
{
|
| 1261 |
+
"projectId": "dummy-22",
|
| 1262 |
+
"score": 0.11734956502914429
|
| 1263 |
+
},
|
| 1264 |
+
{
|
| 1265 |
+
"projectId": "dummy-17",
|
| 1266 |
+
"score": 0.11496858298778534
|
| 1267 |
+
},
|
| 1268 |
+
{
|
| 1269 |
+
"projectId": "dummy-33",
|
| 1270 |
+
"score": 0.11183813214302063
|
| 1271 |
+
},
|
| 1272 |
+
{
|
| 1273 |
+
"projectId": "dummy-49",
|
| 1274 |
+
"score": 0.10802316665649414
|
| 1275 |
+
},
|
| 1276 |
+
{
|
| 1277 |
+
"projectId": "dummy-04",
|
| 1278 |
+
"score": 0.10751412063837051
|
| 1279 |
+
},
|
| 1280 |
+
{
|
| 1281 |
+
"projectId": "dummy-28",
|
| 1282 |
+
"score": 0.105849988758564
|
| 1283 |
+
},
|
| 1284 |
+
{
|
| 1285 |
+
"projectId": "dummy-16",
|
| 1286 |
+
"score": 0.09239037334918976
|
| 1287 |
+
}
|
| 1288 |
+
]
|
| 1289 |
+
}
|
| 1290 |
+
|
| 1291 |
+
# インタラクティブ
|
| 1292 |
+
{
|
| 1293 |
+
"projectIds": [
|
| 1294 |
+
"dummy-01",
|
| 1295 |
+
"dummy-18",
|
| 1296 |
+
"dummy-03",
|
| 1297 |
+
"dummy-21",
|
| 1298 |
+
"dummy-45",
|
| 1299 |
+
"dummy-32",
|
| 1300 |
+
"dummy-37",
|
| 1301 |
+
"dummy-05",
|
| 1302 |
+
"dummy-47",
|
| 1303 |
+
"dummy-27",
|
| 1304 |
+
"dummy-11",
|
| 1305 |
+
"dummy-13",
|
| 1306 |
+
"dummy-46",
|
| 1307 |
+
"dummy-25",
|
| 1308 |
+
"dummy-24",
|
| 1309 |
+
"dummy-31",
|
| 1310 |
+
"dummy-35",
|
| 1311 |
+
"dummy-09",
|
| 1312 |
+
"dummy-30",
|
| 1313 |
+
"dummy-34",
|
| 1314 |
+
"dummy-29",
|
| 1315 |
+
"dummy-15",
|
| 1316 |
+
"dummy-43",
|
| 1317 |
+
"dummy-48",
|
| 1318 |
+
"dummy-40",
|
| 1319 |
+
"dummy-50",
|
| 1320 |
+
"dummy-39",
|
| 1321 |
+
"dummy-20",
|
| 1322 |
+
"dummy-10",
|
| 1323 |
+
"dummy-26",
|
| 1324 |
+
"dummy-38",
|
| 1325 |
+
"dummy-14",
|
| 1326 |
+
"dummy-06",
|
| 1327 |
+
"dummy-02",
|
| 1328 |
+
"dummy-23",
|
| 1329 |
+
"dummy-36",
|
| 1330 |
+
"dummy-08",
|
| 1331 |
+
"dummy-07",
|
| 1332 |
+
"dummy-17",
|
| 1333 |
+
"dummy-22",
|
| 1334 |
+
"dummy-42",
|
| 1335 |
+
"dummy-19",
|
| 1336 |
+
"dummy-33",
|
| 1337 |
+
"dummy-41",
|
| 1338 |
+
"dummy-44",
|
| 1339 |
+
"dummy-49",
|
| 1340 |
+
"dummy-04",
|
| 1341 |
+
"dummy-12",
|
| 1342 |
+
"dummy-28",
|
| 1343 |
+
"dummy-16"
|
| 1344 |
+
],
|
| 1345 |
+
"scores": [
|
| 1346 |
+
{
|
| 1347 |
+
"projectId": "dummy-01",
|
| 1348 |
+
"score": 2.0224697589874268
|
| 1349 |
+
},
|
| 1350 |
+
{
|
| 1351 |
+
"projectId": "dummy-18",
|
| 1352 |
+
"score": 0.1717991828918457
|
| 1353 |
+
},
|
| 1354 |
+
{
|
| 1355 |
+
"projectId": "dummy-03",
|
| 1356 |
+
"score": 0.15875877439975739
|
| 1357 |
+
},
|
| 1358 |
+
{
|
| 1359 |
+
"projectId": "dummy-21",
|
| 1360 |
+
"score": 0.14773328602313995
|
| 1361 |
+
},
|
| 1362 |
+
{
|
| 1363 |
+
"projectId": "dummy-45",
|
| 1364 |
+
"score": 0.13539406657218933
|
| 1365 |
+
},
|
| 1366 |
+
{
|
| 1367 |
+
"projectId": "dummy-32",
|
| 1368 |
+
"score": 0.13098303973674774
|
| 1369 |
+
},
|
| 1370 |
+
{
|
| 1371 |
+
"projectId": "dummy-37",
|
| 1372 |
+
"score": 0.1267457902431488
|
| 1373 |
+
},
|
| 1374 |
+
{
|
| 1375 |
+
"projectId": "dummy-05",
|
| 1376 |
+
"score": 0.12650640308856964
|
| 1377 |
+
},
|
| 1378 |
+
{
|
| 1379 |
+
"projectId": "dummy-47",
|
| 1380 |
+
"score": 0.12536215782165527
|
| 1381 |
+
},
|
| 1382 |
+
{
|
| 1383 |
+
"projectId": "dummy-27",
|
| 1384 |
+
"score": 0.1250198483467102
|
| 1385 |
+
},
|
| 1386 |
+
{
|
| 1387 |
+
"projectId": "dummy-11",
|
| 1388 |
+
"score": 0.12491045892238617
|
| 1389 |
+
},
|
| 1390 |
+
{
|
| 1391 |
+
"projectId": "dummy-13",
|
| 1392 |
+
"score": 0.12490887939929962
|
| 1393 |
+
},
|
| 1394 |
+
{
|
| 1395 |
+
"projectId": "dummy-46",
|
| 1396 |
+
"score": 0.12423489987850189
|
| 1397 |
+
},
|
| 1398 |
+
{
|
| 1399 |
+
"projectId": "dummy-25",
|
| 1400 |
+
"score": 0.12377213686704636
|
| 1401 |
+
},
|
| 1402 |
+
{
|
| 1403 |
+
"projectId": "dummy-24",
|
| 1404 |
+
"score": 0.12340930104255676
|
| 1405 |
+
},
|
| 1406 |
+
{
|
| 1407 |
+
"projectId": "dummy-31",
|
| 1408 |
+
"score": 0.12207731604576111
|
| 1409 |
+
},
|
| 1410 |
+
{
|
| 1411 |
+
"projectId": "dummy-35",
|
| 1412 |
+
"score": 0.12206392735242844
|
| 1413 |
+
},
|
| 1414 |
+
{
|
| 1415 |
+
"projectId": "dummy-09",
|
| 1416 |
+
"score": 0.12170739471912384
|
| 1417 |
+
},
|
| 1418 |
+
{
|
| 1419 |
+
"projectId": "dummy-30",
|
| 1420 |
+
"score": 0.12082985788583755
|
| 1421 |
+
},
|
| 1422 |
+
{
|
| 1423 |
+
"projectId": "dummy-34",
|
| 1424 |
+
"score": 0.1205349788069725
|
| 1425 |
+
},
|
| 1426 |
+
{
|
| 1427 |
+
"projectId": "dummy-29",
|
| 1428 |
+
"score": 0.1192193254828453
|
| 1429 |
+
},
|
| 1430 |
+
{
|
| 1431 |
+
"projectId": "dummy-15",
|
| 1432 |
+
"score": 0.11767151951789856
|
| 1433 |
+
},
|
| 1434 |
+
{
|
| 1435 |
+
"projectId": "dummy-43",
|
| 1436 |
+
"score": 0.11537515372037888
|
| 1437 |
+
},
|
| 1438 |
+
{
|
| 1439 |
+
"projectId": "dummy-48",
|
| 1440 |
+
"score": 0.11534177511930466
|
| 1441 |
+
},
|
| 1442 |
+
{
|
| 1443 |
+
"projectId": "dummy-40",
|
| 1444 |
+
"score": 0.11425547301769257
|
| 1445 |
+
},
|
| 1446 |
+
{
|
| 1447 |
+
"projectId": "dummy-50",
|
| 1448 |
+
"score": 0.11390717327594757
|
| 1449 |
+
},
|
| 1450 |
+
{
|
| 1451 |
+
"projectId": "dummy-39",
|
| 1452 |
+
"score": 0.11368876695632935
|
| 1453 |
+
},
|
| 1454 |
+
{
|
| 1455 |
+
"projectId": "dummy-20",
|
| 1456 |
+
"score": 0.11208880692720413
|
| 1457 |
+
},
|
| 1458 |
+
{
|
| 1459 |
+
"projectId": "dummy-10",
|
| 1460 |
+
"score": 0.11158151179552078
|
| 1461 |
+
},
|
| 1462 |
+
{
|
| 1463 |
+
"projectId": "dummy-26",
|
| 1464 |
+
"score": 0.11129496991634369
|
| 1465 |
+
},
|
| 1466 |
+
{
|
| 1467 |
+
"projectId": "dummy-38",
|
| 1468 |
+
"score": 0.10989216715097427
|
| 1469 |
+
},
|
| 1470 |
+
{
|
| 1471 |
+
"projectId": "dummy-14",
|
| 1472 |
+
"score": 0.10894030332565308
|
| 1473 |
+
},
|
| 1474 |
+
{
|
| 1475 |
+
"projectId": "dummy-06",
|
| 1476 |
+
"score": 0.10804328322410583
|
| 1477 |
+
},
|
| 1478 |
+
{
|
| 1479 |
+
"projectId": "dummy-02",
|
| 1480 |
+
"score": 0.10758388787508011
|
| 1481 |
+
},
|
| 1482 |
+
{
|
| 1483 |
+
"projectId": "dummy-23",
|
| 1484 |
+
"score": 0.10621065646409988
|
| 1485 |
+
},
|
| 1486 |
+
{
|
| 1487 |
+
"projectId": "dummy-36",
|
| 1488 |
+
"score": 0.10569079965353012
|
| 1489 |
+
},
|
| 1490 |
+
{
|
| 1491 |
+
"projectId": "dummy-08",
|
| 1492 |
+
"score": 0.1055106520652771
|
| 1493 |
+
},
|
| 1494 |
+
{
|
| 1495 |
+
"projectId": "dummy-07",
|
| 1496 |
+
"score": 0.10404599457979202
|
| 1497 |
+
},
|
| 1498 |
+
{
|
| 1499 |
+
"projectId": "dummy-17",
|
| 1500 |
+
"score": 0.10389190167188644
|
| 1501 |
+
},
|
| 1502 |
+
{
|
| 1503 |
+
"projectId": "dummy-22",
|
| 1504 |
+
"score": 0.10377786308526993
|
| 1505 |
+
},
|
| 1506 |
+
{
|
| 1507 |
+
"projectId": "dummy-42",
|
| 1508 |
+
"score": 0.10317900776863098
|
| 1509 |
+
},
|
| 1510 |
+
{
|
| 1511 |
+
"projectId": "dummy-19",
|
| 1512 |
+
"score": 0.10277500003576279
|
| 1513 |
+
},
|
| 1514 |
+
{
|
| 1515 |
+
"projectId": "dummy-33",
|
| 1516 |
+
"score": 0.10180061310529709
|
| 1517 |
+
},
|
| 1518 |
+
{
|
| 1519 |
+
"projectId": "dummy-41",
|
| 1520 |
+
"score": 0.09484352916479111
|
| 1521 |
+
},
|
| 1522 |
+
{
|
| 1523 |
+
"projectId": "dummy-44",
|
| 1524 |
+
"score": 0.09008049964904785
|
| 1525 |
+
},
|
| 1526 |
+
{
|
| 1527 |
+
"projectId": "dummy-49",
|
| 1528 |
+
"score": 0.08740860968828201
|
| 1529 |
+
},
|
| 1530 |
+
{
|
| 1531 |
+
"projectId": "dummy-04",
|
| 1532 |
+
"score": 0.08725807070732117
|
| 1533 |
+
},
|
| 1534 |
+
{
|
| 1535 |
+
"projectId": "dummy-12",
|
| 1536 |
+
"score": 0.08704513311386108
|
| 1537 |
+
},
|
| 1538 |
+
{
|
| 1539 |
+
"projectId": "dummy-28",
|
| 1540 |
+
"score": 0.08287956565618515
|
| 1541 |
+
},
|
| 1542 |
+
{
|
| 1543 |
+
"projectId": "dummy-16",
|
| 1544 |
+
"score": 0.06936758756637573
|
| 1545 |
+
}
|
| 1546 |
+
]
|
| 1547 |
+
}
|
| 1548 |
+
|
| 1549 |
+
# LED
|
| 1550 |
+
{
|
| 1551 |
+
"projectIds": [
|
| 1552 |
+
"dummy-13",
|
| 1553 |
+
"dummy-01",
|
| 1554 |
+
"dummy-29",
|
| 1555 |
+
"dummy-33",
|
| 1556 |
+
"dummy-45",
|
| 1557 |
+
"dummy-38",
|
| 1558 |
+
"dummy-35",
|
| 1559 |
+
"dummy-43",
|
| 1560 |
+
"dummy-22",
|
| 1561 |
+
"dummy-39",
|
| 1562 |
+
"dummy-25",
|
| 1563 |
+
"dummy-24",
|
| 1564 |
+
"dummy-44",
|
| 1565 |
+
"dummy-04",
|
| 1566 |
+
"dummy-31",
|
| 1567 |
+
"dummy-09",
|
| 1568 |
+
"dummy-05",
|
| 1569 |
+
"dummy-08",
|
| 1570 |
+
"dummy-46",
|
| 1571 |
+
"dummy-27",
|
| 1572 |
+
"dummy-10",
|
| 1573 |
+
"dummy-37",
|
| 1574 |
+
"dummy-11",
|
| 1575 |
+
"dummy-42",
|
| 1576 |
+
"dummy-48",
|
| 1577 |
+
"dummy-07",
|
| 1578 |
+
"dummy-03",
|
| 1579 |
+
"dummy-41",
|
| 1580 |
+
"dummy-49",
|
| 1581 |
+
"dummy-18",
|
| 1582 |
+
"dummy-21",
|
| 1583 |
+
"dummy-06",
|
| 1584 |
+
"dummy-28",
|
| 1585 |
+
"dummy-30",
|
| 1586 |
+
"dummy-16",
|
| 1587 |
+
"dummy-14",
|
| 1588 |
+
"dummy-50",
|
| 1589 |
+
"dummy-47",
|
| 1590 |
+
"dummy-15",
|
| 1591 |
+
"dummy-40",
|
| 1592 |
+
"dummy-12",
|
| 1593 |
+
"dummy-20",
|
| 1594 |
+
"dummy-36",
|
| 1595 |
+
"dummy-19",
|
| 1596 |
+
"dummy-23",
|
| 1597 |
+
"dummy-17",
|
| 1598 |
+
"dummy-02",
|
| 1599 |
+
"dummy-32",
|
| 1600 |
+
"dummy-34",
|
| 1601 |
+
"dummy-26"
|
| 1602 |
+
],
|
| 1603 |
+
"scores": [
|
| 1604 |
+
{
|
| 1605 |
+
"projectId": "dummy-13",
|
| 1606 |
+
"score": 2.0778872966766357
|
| 1607 |
+
},
|
| 1608 |
+
{
|
| 1609 |
+
"projectId": "dummy-01",
|
| 1610 |
+
"score": 1.6898093223571777
|
| 1611 |
+
},
|
| 1612 |
+
{
|
| 1613 |
+
"projectId": "dummy-29",
|
| 1614 |
+
"score": 0.10570766776800156
|
| 1615 |
+
},
|
| 1616 |
+
{
|
| 1617 |
+
"projectId": "dummy-33",
|
| 1618 |
+
"score": 0.09935978055000305
|
| 1619 |
+
},
|
| 1620 |
+
{
|
| 1621 |
+
"projectId": "dummy-45",
|
| 1622 |
+
"score": 0.0951630100607872
|
| 1623 |
+
},
|
| 1624 |
+
{
|
| 1625 |
+
"projectId": "dummy-38",
|
| 1626 |
+
"score": 0.09483207762241364
|
| 1627 |
+
},
|
| 1628 |
+
{
|
| 1629 |
+
"projectId": "dummy-35",
|
| 1630 |
+
"score": 0.09441396594047546
|
| 1631 |
+
},
|
| 1632 |
+
{
|
| 1633 |
+
"projectId": "dummy-43",
|
| 1634 |
+
"score": 0.0936182290315628
|
| 1635 |
+
},
|
| 1636 |
+
{
|
| 1637 |
+
"projectId": "dummy-22",
|
| 1638 |
+
"score": 0.0923035740852356
|
| 1639 |
+
},
|
| 1640 |
+
{
|
| 1641 |
+
"projectId": "dummy-39",
|
| 1642 |
+
"score": 0.09194866567850113
|
| 1643 |
+
},
|
| 1644 |
+
{
|
| 1645 |
+
"projectId": "dummy-25",
|
| 1646 |
+
"score": 0.09147751331329346
|
| 1647 |
+
},
|
| 1648 |
+
{
|
| 1649 |
+
"projectId": "dummy-24",
|
| 1650 |
+
"score": 0.09100349992513657
|
| 1651 |
+
},
|
| 1652 |
+
{
|
| 1653 |
+
"projectId": "dummy-44",
|
| 1654 |
+
"score": 0.08923175185918808
|
| 1655 |
+
},
|
| 1656 |
+
{
|
| 1657 |
+
"projectId": "dummy-04",
|
| 1658 |
+
"score": 0.08885731548070908
|
| 1659 |
+
},
|
| 1660 |
+
{
|
| 1661 |
+
"projectId": "dummy-31",
|
| 1662 |
+
"score": 0.08837521076202393
|
| 1663 |
+
},
|
| 1664 |
+
{
|
| 1665 |
+
"projectId": "dummy-09",
|
| 1666 |
+
"score": 0.08757206797599792
|
| 1667 |
+
},
|
| 1668 |
+
{
|
| 1669 |
+
"projectId": "dummy-05",
|
| 1670 |
+
"score": 0.08745519071817398
|
| 1671 |
+
},
|
| 1672 |
+
{
|
| 1673 |
+
"projectId": "dummy-08",
|
| 1674 |
+
"score": 0.08742401003837585
|
| 1675 |
+
},
|
| 1676 |
+
{
|
| 1677 |
+
"projectId": "dummy-46",
|
| 1678 |
+
"score": 0.08670942485332489
|
| 1679 |
+
},
|
| 1680 |
+
{
|
| 1681 |
+
"projectId": "dummy-27",
|
| 1682 |
+
"score": 0.08608520030975342
|
| 1683 |
+
},
|
| 1684 |
+
{
|
| 1685 |
+
"projectId": "dummy-10",
|
| 1686 |
+
"score": 0.08546361327171326
|
| 1687 |
+
},
|
| 1688 |
+
{
|
| 1689 |
+
"projectId": "dummy-37",
|
| 1690 |
+
"score": 0.08545397222042084
|
| 1691 |
+
},
|
| 1692 |
+
{
|
| 1693 |
+
"projectId": "dummy-11",
|
| 1694 |
+
"score": 0.08495649695396423
|
| 1695 |
+
},
|
| 1696 |
+
{
|
| 1697 |
+
"projectId": "dummy-42",
|
| 1698 |
+
"score": 0.08428287506103516
|
| 1699 |
+
},
|
| 1700 |
+
{
|
| 1701 |
+
"projectId": "dummy-48",
|
| 1702 |
+
"score": 0.08367615938186646
|
| 1703 |
+
},
|
| 1704 |
+
{
|
| 1705 |
+
"projectId": "dummy-07",
|
| 1706 |
+
"score": 0.08359388262033463
|
| 1707 |
+
},
|
| 1708 |
+
{
|
| 1709 |
+
"projectId": "dummy-03",
|
| 1710 |
+
"score": 0.08242101967334747
|
| 1711 |
+
},
|
| 1712 |
+
{
|
| 1713 |
+
"projectId": "dummy-41",
|
| 1714 |
+
"score": 0.08240765333175659
|
| 1715 |
+
},
|
| 1716 |
+
{
|
| 1717 |
+
"projectId": "dummy-49",
|
| 1718 |
+
"score": 0.08240342140197754
|
| 1719 |
+
},
|
| 1720 |
+
{
|
| 1721 |
+
"projectId": "dummy-18",
|
| 1722 |
+
"score": 0.08168061077594757
|
| 1723 |
+
},
|
| 1724 |
+
{
|
| 1725 |
+
"projectId": "dummy-21",
|
| 1726 |
+
"score": 0.08138646185398102
|
| 1727 |
+
},
|
| 1728 |
+
{
|
| 1729 |
+
"projectId": "dummy-06",
|
| 1730 |
+
"score": 0.08057239651679993
|
| 1731 |
+
},
|
| 1732 |
+
{
|
| 1733 |
+
"projectId": "dummy-28",
|
| 1734 |
+
"score": 0.07947935163974762
|
| 1735 |
+
},
|
| 1736 |
+
{
|
| 1737 |
+
"projectId": "dummy-30",
|
| 1738 |
+
"score": 0.07906509935855865
|
| 1739 |
+
},
|
| 1740 |
+
{
|
| 1741 |
+
"projectId": "dummy-16",
|
| 1742 |
+
"score": 0.07739663869142532
|
| 1743 |
+
},
|
| 1744 |
+
{
|
| 1745 |
+
"projectId": "dummy-14",
|
| 1746 |
+
"score": 0.07670343667268753
|
| 1747 |
+
},
|
| 1748 |
+
{
|
| 1749 |
+
"projectId": "dummy-50",
|
| 1750 |
+
"score": 0.07632601261138916
|
| 1751 |
+
},
|
| 1752 |
+
{
|
| 1753 |
+
"projectId": "dummy-47",
|
| 1754 |
+
"score": 0.07593720406293869
|
| 1755 |
+
},
|
| 1756 |
+
{
|
| 1757 |
+
"projectId": "dummy-15",
|
| 1758 |
+
"score": 0.07524865865707397
|
| 1759 |
+
},
|
| 1760 |
+
{
|
| 1761 |
+
"projectId": "dummy-40",
|
| 1762 |
+
"score": 0.07505342364311218
|
| 1763 |
+
},
|
| 1764 |
+
{
|
| 1765 |
+
"projectId": "dummy-12",
|
| 1766 |
+
"score": 0.0750349760055542
|
| 1767 |
+
},
|
| 1768 |
+
{
|
| 1769 |
+
"projectId": "dummy-20",
|
| 1770 |
+
"score": 0.07358665019273758
|
| 1771 |
+
},
|
| 1772 |
+
{
|
| 1773 |
+
"projectId": "dummy-36",
|
| 1774 |
+
"score": 0.07355395704507828
|
| 1775 |
+
},
|
| 1776 |
+
{
|
| 1777 |
+
"projectId": "dummy-19",
|
| 1778 |
+
"score": 0.0733858272433281
|
| 1779 |
+
},
|
| 1780 |
+
{
|
| 1781 |
+
"projectId": "dummy-23",
|
| 1782 |
+
"score": 0.07254036515951157
|
| 1783 |
+
},
|
| 1784 |
+
{
|
| 1785 |
+
"projectId": "dummy-17",
|
| 1786 |
+
"score": 0.07079058140516281
|
| 1787 |
+
},
|
| 1788 |
+
{
|
| 1789 |
+
"projectId": "dummy-02",
|
| 1790 |
+
"score": 0.06835603713989258
|
| 1791 |
+
},
|
| 1792 |
+
{
|
| 1793 |
+
"projectId": "dummy-32",
|
| 1794 |
+
"score": 0.06082003191113472
|
| 1795 |
+
},
|
| 1796 |
+
{
|
| 1797 |
+
"projectId": "dummy-34",
|
| 1798 |
+
"score": 0.056489575654268265
|
| 1799 |
+
},
|
| 1800 |
+
{
|
| 1801 |
+
"projectId": "dummy-26",
|
| 1802 |
+
"score": 0.05596642196178436
|
| 1803 |
+
}
|
| 1804 |
+
]
|
| 1805 |
+
}
|
| 1806 |
+
|
| 1807 |
+
# カレー
|
| 1808 |
+
{
|
| 1809 |
+
"projectIds": [
|
| 1810 |
+
"dummy-04",
|
| 1811 |
+
"dummy-49",
|
| 1812 |
+
"dummy-22",
|
| 1813 |
+
"dummy-12",
|
| 1814 |
+
"dummy-28",
|
| 1815 |
+
"dummy-14",
|
| 1816 |
+
"dummy-33",
|
| 1817 |
+
"dummy-24",
|
| 1818 |
+
"dummy-41",
|
| 1819 |
+
"dummy-44",
|
| 1820 |
+
"dummy-31",
|
| 1821 |
+
"dummy-30",
|
| 1822 |
+
"dummy-39",
|
| 1823 |
+
"dummy-38",
|
| 1824 |
+
"dummy-25",
|
| 1825 |
+
"dummy-10",
|
| 1826 |
+
"dummy-07",
|
| 1827 |
+
"dummy-23",
|
| 1828 |
+
"dummy-09",
|
| 1829 |
+
"dummy-05",
|
| 1830 |
+
"dummy-26",
|
| 1831 |
+
"dummy-32",
|
| 1832 |
+
"dummy-43",
|
| 1833 |
+
"dummy-29",
|
| 1834 |
+
"dummy-03",
|
| 1835 |
+
"dummy-16",
|
| 1836 |
+
"dummy-47",
|
| 1837 |
+
"dummy-45",
|
| 1838 |
+
"dummy-20",
|
| 1839 |
+
"dummy-02",
|
| 1840 |
+
"dummy-06",
|
| 1841 |
+
"dummy-01",
|
| 1842 |
+
"dummy-37",
|
| 1843 |
+
"dummy-48",
|
| 1844 |
+
"dummy-15",
|
| 1845 |
+
"dummy-35",
|
| 1846 |
+
"dummy-36",
|
| 1847 |
+
"dummy-27",
|
| 1848 |
+
"dummy-18",
|
| 1849 |
+
"dummy-13",
|
| 1850 |
+
"dummy-40",
|
| 1851 |
+
"dummy-08",
|
| 1852 |
+
"dummy-19",
|
| 1853 |
+
"dummy-34",
|
| 1854 |
+
"dummy-50",
|
| 1855 |
+
"dummy-11",
|
| 1856 |
+
"dummy-21",
|
| 1857 |
+
"dummy-46",
|
| 1858 |
+
"dummy-17",
|
| 1859 |
+
"dummy-42"
|
| 1860 |
+
],
|
| 1861 |
+
"scores": [
|
| 1862 |
+
{
|
| 1863 |
+
"projectId": "dummy-04",
|
| 1864 |
+
"score": 2.6254711151123047
|
| 1865 |
+
},
|
| 1866 |
+
{
|
| 1867 |
+
"projectId": "dummy-49",
|
| 1868 |
+
"score": 0.18292959034442902
|
| 1869 |
+
},
|
| 1870 |
+
{
|
| 1871 |
+
"projectId": "dummy-22",
|
| 1872 |
+
"score": 0.1800815910100937
|
| 1873 |
+
},
|
| 1874 |
+
{
|
| 1875 |
+
"projectId": "dummy-12",
|
| 1876 |
+
"score": 0.1737019568681717
|
| 1877 |
+
},
|
| 1878 |
+
{
|
| 1879 |
+
"projectId": "dummy-28",
|
| 1880 |
+
"score": 0.16631010174751282
|
| 1881 |
+
},
|
| 1882 |
+
{
|
| 1883 |
+
"projectId": "dummy-14",
|
| 1884 |
+
"score": 0.14709749817848206
|
| 1885 |
+
},
|
| 1886 |
+
{
|
| 1887 |
+
"projectId": "dummy-33",
|
| 1888 |
+
"score": 0.14541301131248474
|
| 1889 |
+
},
|
| 1890 |
+
{
|
| 1891 |
+
"projectId": "dummy-24",
|
| 1892 |
+
"score": 0.13671286404132843
|
| 1893 |
+
},
|
| 1894 |
+
{
|
| 1895 |
+
"projectId": "dummy-41",
|
| 1896 |
+
"score": 0.13063141703605652
|
| 1897 |
+
},
|
| 1898 |
+
{
|
| 1899 |
+
"projectId": "dummy-44",
|
| 1900 |
+
"score": 0.1156868264079094
|
| 1901 |
+
},
|
| 1902 |
+
{
|
| 1903 |
+
"projectId": "dummy-31",
|
| 1904 |
+
"score": 0.1143571063876152
|
| 1905 |
+
},
|
| 1906 |
+
{
|
| 1907 |
+
"projectId": "dummy-30",
|
| 1908 |
+
"score": 0.1113339439034462
|
| 1909 |
+
},
|
| 1910 |
+
{
|
| 1911 |
+
"projectId": "dummy-39",
|
| 1912 |
+
"score": 0.11087211966514587
|
| 1913 |
+
},
|
| 1914 |
+
{
|
| 1915 |
+
"projectId": "dummy-38",
|
| 1916 |
+
"score": 0.11070612818002701
|
| 1917 |
+
},
|
| 1918 |
+
{
|
| 1919 |
+
"projectId": "dummy-25",
|
| 1920 |
+
"score": 0.1086856871843338
|
| 1921 |
+
},
|
| 1922 |
+
{
|
| 1923 |
+
"projectId": "dummy-10",
|
| 1924 |
+
"score": 0.10852580517530441
|
| 1925 |
+
},
|
| 1926 |
+
{
|
| 1927 |
+
"projectId": "dummy-07",
|
| 1928 |
+
"score": 0.10763086378574371
|
| 1929 |
+
},
|
| 1930 |
+
{
|
| 1931 |
+
"projectId": "dummy-23",
|
| 1932 |
+
"score": 0.10729862004518509
|
| 1933 |
+
},
|
| 1934 |
+
{
|
| 1935 |
+
"projectId": "dummy-09",
|
| 1936 |
+
"score": 0.10714579373598099
|
| 1937 |
+
},
|
| 1938 |
+
{
|
| 1939 |
+
"projectId": "dummy-05",
|
| 1940 |
+
"score": 0.10656001418828964
|
| 1941 |
+
},
|
| 1942 |
+
{
|
| 1943 |
+
"projectId": "dummy-26",
|
| 1944 |
+
"score": 0.10479174554347992
|
| 1945 |
+
},
|
| 1946 |
+
{
|
| 1947 |
+
"projectId": "dummy-32",
|
| 1948 |
+
"score": 0.10145216435194016
|
| 1949 |
+
},
|
| 1950 |
+
{
|
| 1951 |
+
"projectId": "dummy-43",
|
| 1952 |
+
"score": 0.09996487945318222
|
| 1953 |
+
},
|
| 1954 |
+
{
|
| 1955 |
+
"projectId": "dummy-29",
|
| 1956 |
+
"score": 0.09960292279720306
|
| 1957 |
+
},
|
| 1958 |
+
{
|
| 1959 |
+
"projectId": "dummy-03",
|
| 1960 |
+
"score": 0.0987028107047081
|
| 1961 |
+
},
|
| 1962 |
+
{
|
| 1963 |
+
"projectId": "dummy-16",
|
| 1964 |
+
"score": 0.09857852756977081
|
| 1965 |
+
},
|
| 1966 |
+
{
|
| 1967 |
+
"projectId": "dummy-47",
|
| 1968 |
+
"score": 0.09719719737768173
|
| 1969 |
+
},
|
| 1970 |
+
{
|
| 1971 |
+
"projectId": "dummy-45",
|
| 1972 |
+
"score": 0.09627760946750641
|
| 1973 |
+
},
|
| 1974 |
+
{
|
| 1975 |
+
"projectId": "dummy-20",
|
| 1976 |
+
"score": 0.09507821500301361
|
| 1977 |
+
},
|
| 1978 |
+
{
|
| 1979 |
+
"projectId": "dummy-02",
|
| 1980 |
+
"score": 0.09484118968248367
|
| 1981 |
+
},
|
| 1982 |
+
{
|
| 1983 |
+
"projectId": "dummy-06",
|
| 1984 |
+
"score": 0.09443477541208267
|
| 1985 |
+
},
|
| 1986 |
+
{
|
| 1987 |
+
"projectId": "dummy-01",
|
| 1988 |
+
"score": 0.09416937828063965
|
| 1989 |
+
},
|
| 1990 |
+
{
|
| 1991 |
+
"projectId": "dummy-37",
|
| 1992 |
+
"score": 0.09407279640436172
|
| 1993 |
+
},
|
| 1994 |
+
{
|
| 1995 |
+
"projectId": "dummy-48",
|
| 1996 |
+
"score": 0.09399788826704025
|
| 1997 |
+
},
|
| 1998 |
+
{
|
| 1999 |
+
"projectId": "dummy-15",
|
| 2000 |
+
"score": 0.09398657828569412
|
| 2001 |
+
},
|
| 2002 |
+
{
|
| 2003 |
+
"projectId": "dummy-35",
|
| 2004 |
+
"score": 0.09319526702165604
|
| 2005 |
+
},
|
| 2006 |
+
{
|
| 2007 |
+
"projectId": "dummy-36",
|
| 2008 |
+
"score": 0.09293168038129807
|
| 2009 |
+
},
|
| 2010 |
+
{
|
| 2011 |
+
"projectId": "dummy-27",
|
| 2012 |
+
"score": 0.09266433119773865
|
| 2013 |
+
},
|
| 2014 |
+
{
|
| 2015 |
+
"projectId": "dummy-18",
|
| 2016 |
+
"score": 0.09237142652273178
|
| 2017 |
+
},
|
| 2018 |
+
{
|
| 2019 |
+
"projectId": "dummy-13",
|
| 2020 |
+
"score": 0.0918302908539772
|
| 2021 |
+
},
|
| 2022 |
+
{
|
| 2023 |
+
"projectId": "dummy-40",
|
| 2024 |
+
"score": 0.0913495421409607
|
| 2025 |
+
},
|
| 2026 |
+
{
|
| 2027 |
+
"projectId": "dummy-08",
|
| 2028 |
+
"score": 0.091004878282547
|
| 2029 |
+
},
|
| 2030 |
+
{
|
| 2031 |
+
"projectId": "dummy-19",
|
| 2032 |
+
"score": 0.09041927009820938
|
| 2033 |
+
},
|
| 2034 |
+
{
|
| 2035 |
+
"projectId": "dummy-34",
|
| 2036 |
+
"score": 0.09039682894945145
|
| 2037 |
+
},
|
| 2038 |
+
{
|
| 2039 |
+
"projectId": "dummy-50",
|
| 2040 |
+
"score": 0.08850666880607605
|
| 2041 |
+
},
|
| 2042 |
+
{
|
| 2043 |
+
"projectId": "dummy-11",
|
| 2044 |
+
"score": 0.08840479701757431
|
| 2045 |
+
},
|
| 2046 |
+
{
|
| 2047 |
+
"projectId": "dummy-21",
|
| 2048 |
+
"score": 0.08591099828481674
|
| 2049 |
+
},
|
| 2050 |
+
{
|
| 2051 |
+
"projectId": "dummy-46",
|
| 2052 |
+
"score": 0.08536650240421295
|
| 2053 |
+
},
|
| 2054 |
+
{
|
| 2055 |
+
"projectId": "dummy-17",
|
| 2056 |
+
"score": 0.08424936980009079
|
| 2057 |
+
},
|
| 2058 |
+
{
|
| 2059 |
+
"projectId": "dummy-42",
|
| 2060 |
+
"score": 0.08145570755004883
|
| 2061 |
+
}
|
| 2062 |
+
]
|
| 2063 |
+
}
|
requirements.txt
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
annotated-types==0.7.0
|
| 2 |
+
anyio==4.10.0
|
| 3 |
+
click==8.2.1
|
| 4 |
+
dotenv==0.9.9
|
| 5 |
+
fastapi==0.116.1
|
| 6 |
+
fasttext==0.9.3
|
| 7 |
+
h11==0.16.0
|
| 8 |
+
idna==3.10
|
| 9 |
+
numpy==2.3.2
|
| 10 |
+
pandas==2.3.2
|
| 11 |
+
pybind11==3.0.1
|
| 12 |
+
pydantic==2.11.7
|
| 13 |
+
pydantic_core==2.33.2
|
| 14 |
+
python-dateutil==2.9.0.post0
|
| 15 |
+
python-dotenv==1.1.1
|
| 16 |
+
pytz==2025.2
|
| 17 |
+
setuptools==80.9.0
|
| 18 |
+
six==1.17.0
|
| 19 |
+
sniffio==1.3.1
|
| 20 |
+
starlette==0.47.3
|
| 21 |
+
SudachiDict-full==20250825
|
| 22 |
+
SudachiPy==0.6.10
|
| 23 |
+
tqdm==4.67.1
|
| 24 |
+
typing-inspection==0.4.1
|
| 25 |
+
typing_extensions==4.15.0
|
| 26 |
+
tzdata==2025.2
|
| 27 |
+
uvicorn==0.35.0
|
resources/stopwords.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
"こと", "もの", "よう", "ため", "ところ", "する", "ある", "いる",
|
| 3 |
+
"方", "人", "他", "の", "に", "を", "と", "が", "で", "です", "ます", "くださる", "下さる",
|
| 4 |
+
"ました", "ません", "いい", "良い", "楽しい", "面白い", "すごい",
|
| 5 |
+
"また", "ぜひ", "いろいろ", "様々", "誰でも", "どなたでも",
|
| 6 |
+
"1", "2", "3", "4", "5", "10", "100", "円", "人", "個", "回", "分",
|
| 7 |
+
"第", "日", "年", "〜", "・", "!", "?", "。", "、", "%",
|
| 8 |
+
"the", "a", "an", "in", "on", "for", "with", "from", "of", "and",
|
| 9 |
+
"to", "by", "this", "that", "it", "is", "are", "be", "have", "has",
|
| 10 |
+
"will", "can"
|
| 11 |
+
]
|
resources/synonyms_DO_NOT_EDIT.txt
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
resources/synonyms_custom.json
ADDED
|
@@ -0,0 +1,608 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"千葉大学": ["千葉大", "Chiba Univ."],
|
| 3 |
+
"焼きそば": ["やきそば"],
|
| 4 |
+
"軽音": ["バンド", "軽音楽", "軽音部"],
|
| 5 |
+
"バンド": ["軽音", "バンド演奏", "バンドサークル"],
|
| 6 |
+
|
| 7 |
+
"学園祭": ["大学祭", "文化祭", "学祭"],
|
| 8 |
+
"大学祭": ["学園祭", "学祭"],
|
| 9 |
+
"文化祭": ["学園祭", "学祭"],
|
| 10 |
+
"学祭": ["学園祭", "大学祭"],
|
| 11 |
+
|
| 12 |
+
"模擬店": ["出店", "屋台", "食販"],
|
| 13 |
+
"出店": ["模擬店", "屋台"],
|
| 14 |
+
"屋台": ["模擬店", "出店"],
|
| 15 |
+
"食販": ["飲食", "フード"],
|
| 16 |
+
"飲食": ["食販", "フード"],
|
| 17 |
+
"キッチンカー": ["フードトラック", "移動販売"],
|
| 18 |
+
"フードトラック": ["キッチンカー"],
|
| 19 |
+
"物販": ["販売", "グッズ"],
|
| 20 |
+
"グッズ": ["物販", "公式グッズ"],
|
| 21 |
+
|
| 22 |
+
"ステージ": ["屋外ステージ", "屋内ステージ"],
|
| 23 |
+
"ライブ": ["コンサート", "音楽ライブ"],
|
| 24 |
+
"コンサート": ["ライブ", "音楽ライブ"],
|
| 25 |
+
"音楽ライブ": ["ライブ", "コンサート"],
|
| 26 |
+
"吹奏楽": ["ブラスバンド", "吹奏"],
|
| 27 |
+
"ブラスバンド": ["吹奏楽"],
|
| 28 |
+
"合唱": ["コーラス", "混声合唱"],
|
| 29 |
+
"コーラス": ["合唱"],
|
| 30 |
+
"ダンス": ["ダンスショー", "パフォーマンス"],
|
| 31 |
+
"演劇": ["芝居", "劇"],
|
| 32 |
+
"お笑い": ["漫才", "コント"],
|
| 33 |
+
"芸人ライブ": ["お笑いライブ", "トークライブ"],
|
| 34 |
+
"トークショー": ["トークイベント"],
|
| 35 |
+
"マジック": ["手品", "奇術"],
|
| 36 |
+
"ジャグリング": ["パフォーマンス", "ジャグ"],
|
| 37 |
+
|
| 38 |
+
"ワークショップ": ["体験", "体験会"],
|
| 39 |
+
"体験": ["体験会", "参加型"],
|
| 40 |
+
"参加型": ["体験型", "体験"],
|
| 41 |
+
"展示": ["エキシビション", "展覧会"],
|
| 42 |
+
"研究発表": ["研究展示", "ポスター発表"],
|
| 43 |
+
"作品展示": ["展示", "個展"],
|
| 44 |
+
"写真展": ["フォト展", "写真展示"],
|
| 45 |
+
"美術": ["アート", "造形"],
|
| 46 |
+
"書道": ["書作品", "書展"],
|
| 47 |
+
"茶道": ["茶会", "お点前"],
|
| 48 |
+
"華道": ["生け花", "花道"],
|
| 49 |
+
|
| 50 |
+
"科学実験": ["サイエンスショー", "実験ショー"],
|
| 51 |
+
"模型": ["モデリング", "ジオラマ"],
|
| 52 |
+
"電子工作": ["マイコン工作", "回路工作"],
|
| 53 |
+
"プログラミング": ["コーディング", "ソフト開発"],
|
| 54 |
+
"ロボット": ["ロボティクス", "ロボコン"],
|
| 55 |
+
"eスポーツ": ["ゲーム大会", "esports"],
|
| 56 |
+
"ボードゲーム": ["アナログゲーム", "テーブルゲーム"],
|
| 57 |
+
"カードゲーム": ["TCG", "カード対戦"],
|
| 58 |
+
"アニメ": ["アニメーション"],
|
| 59 |
+
"漫画": ["コミック", "マンガ"],
|
| 60 |
+
"映画": ["シネマ", "フィルム"],
|
| 61 |
+
"鉄道": ["乗り鉄", "鉄道研究"],
|
| 62 |
+
"天文": ["天文学", "星空観察"],
|
| 63 |
+
"天体観測": ["星空観察", "望遠鏡観測"],
|
| 64 |
+
"旅行": ["トラベル", "旅"],
|
| 65 |
+
"写真": ["フォト", "カメラ"],
|
| 66 |
+
"手芸": ["ハンドメイド", "クラフト"],
|
| 67 |
+
"工芸": ["クラフト", "ものづくり"],
|
| 68 |
+
"ものづくり": ["メイカーズ", "ファブ"],
|
| 69 |
+
"DIY": ["自作", "ハンドメイド"],
|
| 70 |
+
|
| 71 |
+
"音楽": ["ミュージック", "サウンド"],
|
| 72 |
+
"合奏": ["アンサンブル", "セッション"],
|
| 73 |
+
"合バン": ["合同バンド", "コラボ演奏"],
|
| 74 |
+
"バンド演奏": ["ライブ演奏", "ステージ演奏"],
|
| 75 |
+
|
| 76 |
+
"模擬講義": ["体験授業", "体験講義"],
|
| 77 |
+
"模擬裁判": ["法学イベント", "体験裁判"],
|
| 78 |
+
|
| 79 |
+
"屋内": ["インドア", "室内"],
|
| 80 |
+
"屋外": ["アウトドア", "屋外会場"],
|
| 81 |
+
"会場": ["会場内", "会場エリア"],
|
| 82 |
+
"本部": ["運営本部", "インフォメーション"],
|
| 83 |
+
"インフォメーション": ["案内所", "総合案内"],
|
| 84 |
+
"案内": ["ガイド", "ナビ"],
|
| 85 |
+
"マップ": ["地図", "会場図"],
|
| 86 |
+
"タイムテーブル": ["スケジュール", "日程表"],
|
| 87 |
+
"パンフレット": ["パンフ", "冊子"],
|
| 88 |
+
"整理券": ["入場券", "整理札"],
|
| 89 |
+
"受付": ["レジストレーション", "チェックイン"],
|
| 90 |
+
"行列": ["待機列", "待ち列"],
|
| 91 |
+
"混雑": ["混み具合", "混雑状況"],
|
| 92 |
+
|
| 93 |
+
"雨天": ["雨天時", "雨天決行"],
|
| 94 |
+
"中止": ["キャンセル", "取り止め"],
|
| 95 |
+
"順延": ["延期", "延期開催"],
|
| 96 |
+
"注意事項": ["ご注意", "ルール"],
|
| 97 |
+
"禁止事項": ["持ち込み禁止", "禁止ルール"],
|
| 98 |
+
|
| 99 |
+
"ゴミ": ["廃棄物", "ごみ"],
|
| 100 |
+
"分別": ["仕分け", "セパレート"],
|
| 101 |
+
"清掃": ["クリーンアップ", "掃除"],
|
| 102 |
+
|
| 103 |
+
"ボランティア": ["スタッフ", "運営補助"],
|
| 104 |
+
"実行委員": ["実委", "実行委員会"],
|
| 105 |
+
"協賛": ["スポンサー", "サポート"],
|
| 106 |
+
"寄付": ["寄附", "ドネーション"],
|
| 107 |
+
"広報": ["PR", "プロモーション"],
|
| 108 |
+
"公式": ["オフィシャル", "公式サイト"],
|
| 109 |
+
|
| 110 |
+
"SNS": ["ソーシャル", "ソーシャルメディア"],
|
| 111 |
+
"X": ["Twitter", "旧Twitter"],
|
| 112 |
+
"Instagram": ["インスタ", "IG"],
|
| 113 |
+
"YouTube": ["ユーチューブ", "動画配信"],
|
| 114 |
+
"Webサイト": ["公式サイト", "ホームページ"],
|
| 115 |
+
|
| 116 |
+
"アクセス": ["交通", "行き方"],
|
| 117 |
+
"最寄り駅": ["駅", "近隣駅"],
|
| 118 |
+
"バス": ["シャトルバス", "送迎バス"],
|
| 119 |
+
"シャトル": ["シャトルバス", "送迎"],
|
| 120 |
+
"駐車場": ["パーキング", "駐車スペース"],
|
| 121 |
+
|
| 122 |
+
"家族": ["ファミリー", "子連れ"],
|
| 123 |
+
"子ども": ["キッズ", "こども"],
|
| 124 |
+
"学生": ["スチューデント", "学徒"],
|
| 125 |
+
"教員": ["先生", "教職員"],
|
| 126 |
+
"OB": ["卒業生", "同窓生"],
|
| 127 |
+
"同窓会": ["OB会", "同窓"],
|
| 128 |
+
|
| 129 |
+
"パフォーマンス": ["実演", "ショー"],
|
| 130 |
+
"デモンストレーション": ["デモ", "実演"],
|
| 131 |
+
"実演": ["デモ", "実技"],
|
| 132 |
+
|
| 133 |
+
"合気道": ["武道", "合気"],
|
| 134 |
+
"柔道": ["Judo", "武道"],
|
| 135 |
+
"剣道": ["Kendo", "武道"],
|
| 136 |
+
"弓道": ["Kyudo", "武道"],
|
| 137 |
+
"陸上": ["トラック&フィールド", "陸上競技"],
|
| 138 |
+
"サッカー": ["フットボール", "soccer"],
|
| 139 |
+
"バスケットボール": ["バスケ", "basketball"],
|
| 140 |
+
"野球": ["ベースボール", "baseball"],
|
| 141 |
+
"テニス": ["テニス部", "テニスサークル"],
|
| 142 |
+
"バドミントン": ["badminton", "シャトル"],
|
| 143 |
+
"卓球": ["ピンポン", "table tennis"],
|
| 144 |
+
|
| 145 |
+
"フリマ": ["フリーマーケット", "バザー"],
|
| 146 |
+
"バザー": ["チャリティバザー", "フリマ"],
|
| 147 |
+
"チャリティ": ["慈善", "募金"],
|
| 148 |
+
"募金": ["寄付", "ドネーション"],
|
| 149 |
+
|
| 150 |
+
"たこ焼き": ["たこやき", "タコ焼き"],
|
| 151 |
+
"焼き鳥": ["やきとり", "串焼き"],
|
| 152 |
+
"からあげ": ["唐揚げ", "フライドチキン"],
|
| 153 |
+
"フランクフルト": ["ソーセージ", "ホットソーセージ"],
|
| 154 |
+
"綿あめ": ["わたあめ", "コットンキャンディ"],
|
| 155 |
+
"かき氷": ["かきごおり", "シェーブアイス"],
|
| 156 |
+
"クレープ": ["スイーツクレープ", "デザートクレープ"],
|
| 157 |
+
"チュロス": ["揚げドーナツ", "シナモンスティック"],
|
| 158 |
+
"たい焼き": ["鯛焼き", "タイ焼き"],
|
| 159 |
+
"おでん": ["関東煮", "煮込みおでん"],
|
| 160 |
+
"肉まん": ["豚まん", "ぶたまん", "中華まん", "肉饅頭", "にくまん"],
|
| 161 |
+
"豚まん": ["肉まん", "ぶたまん", "中華まん", "肉饅頭"],
|
| 162 |
+
"ぶたまん": ["豚まん", "肉まん", "中華まん", "肉饅頭"],
|
| 163 |
+
"中華まん": ["肉まん", "豚まん", "ぶたまん", "肉饅頭"],
|
| 164 |
+
"うどん": ["温うどん", "ぶっかけうどん"],
|
| 165 |
+
"そば": ["蕎麦", "日本そば"],
|
| 166 |
+
"カレー": ["カレーライス", "CURRY"],
|
| 167 |
+
"ホットドッグ": ["ホットドッグパン", "ドッグパン"],
|
| 168 |
+
"ハンバーガー": ["バーガー", "ハンバーグサンド"],
|
| 169 |
+
"ポップコーン": ["ポップコーン販売", "塩ポップコーン"],
|
| 170 |
+
"ジュース": ["ソフトドリンク", "清涼飲料"],
|
| 171 |
+
"コーヒー": ["珈琲", "ホットコーヒー"],
|
| 172 |
+
"紅茶": ["ティー", "ホットティー"],
|
| 173 |
+
"レモネード": ["レモンソーダ", "レモンドリンク"],
|
| 174 |
+
"タピオカ": ["タピオカドリンク", "ミルクティー"],
|
| 175 |
+
"スムージー": ["フルーツスムージー", "スムージードリンク"],
|
| 176 |
+
|
| 177 |
+
"射的": ["的当て", "射撃ゲーム"],
|
| 178 |
+
"くじ引き": ["抽選会", "ガラポン"],
|
| 179 |
+
"輪投げ": ["わなげ", "リングトス"],
|
| 180 |
+
"スーパーボールすくい": ["スーパーボール", "ボールすくい"],
|
| 181 |
+
"金魚すくい": ["金魚救い", "ヨーヨー釣り"],
|
| 182 |
+
"ヨーヨー釣り": ["ヨーヨーつり", "水風船釣り"],
|
| 183 |
+
|
| 184 |
+
"体育館": ["ジム", "アリーナ"],
|
| 185 |
+
"講堂": ["大講堂", "ホール"],
|
| 186 |
+
"大講義室": ["大教室", "大教室棟"],
|
| 187 |
+
"中講義室": ["中教室", "中規模教室"],
|
| 188 |
+
"小講義室": ["小教室", "セミナー室"],
|
| 189 |
+
"屋上": ["ルーフ", "屋上スペース"],
|
| 190 |
+
"中庭": ["パティオ", "コートヤード"],
|
| 191 |
+
"正門": ["メインゲート", "正面入口"],
|
| 192 |
+
"西門": ["ウェストゲート", "西口"],
|
| 193 |
+
"東門": ["イーストゲート", "東口"],
|
| 194 |
+
"南門": ["サウスゲート", "南口"],
|
| 195 |
+
"北門": ["ノースゲート", "北口"],
|
| 196 |
+
|
| 197 |
+
"初日": ["1日目", "一日目"],
|
| 198 |
+
"二日目": ["2日目", "二日目"],
|
| 199 |
+
"三日目": ["3日目", "三日目"],
|
| 200 |
+
"前夜祭": ["前日祭", "イブフェス"],
|
| 201 |
+
"当日": ["当日開催", "当日券"] ,
|
| 202 |
+
|
| 203 |
+
"電源": ["コンセント", "AC電源"],
|
| 204 |
+
"Wi-Fi": ["ワイファイ", "無線LAN"],
|
| 205 |
+
"充電": ["モバイル充電", "チャージ"],
|
| 206 |
+
"休憩所": ["レストスペース", "休憩スペース"],
|
| 207 |
+
"授乳室": ["ベビーケアルーム", "授乳ブース"],
|
| 208 |
+
"おむつ替え": ["おむつ交換", "ベビーベッド"],
|
| 209 |
+
"多目的トイレ": ["バリアフリートイレ", "ユニバーサルトイレ"],
|
| 210 |
+
"エレベーター": ["昇降機", "リフト"],
|
| 211 |
+
"スロープ": ["斜路", "バリアフリースロープ"],
|
| 212 |
+
|
| 213 |
+
"迷子": ["はぐれ", "迷子案内"],
|
| 214 |
+
"迷子センター": ["迷子案内所", "インフォ迷子"],
|
| 215 |
+
"落とし物": ["遺失物", "忘れ物"],
|
| 216 |
+
"遺失物": ["落とし物", "忘れ物"],
|
| 217 |
+
"救護": ["救急", "医療ブース"],
|
| 218 |
+
"救護室": ["救護所", "メディカルルーム"],
|
| 219 |
+
"体調不良": ["具合が悪い", "気分が悪い"],
|
| 220 |
+
"AED": ["自動体外式除細動器", "AED設置"],
|
| 221 |
+
"警備": ["セキュリティ", "ガード"],
|
| 222 |
+
"消防": ["消防隊", "消火活動"],
|
| 223 |
+
|
| 224 |
+
"環境": ["エコ", "サステナビリティ"],
|
| 225 |
+
"SDGs": ["持続可能性", "サステナブル"],
|
| 226 |
+
"リサイクル": ["資源回収", "再資源化"],
|
| 227 |
+
"マイボトル": ["リユースボトル", "水筒"],
|
| 228 |
+
"リユース食器": ["再利用食器", "リユースカップ"],
|
| 229 |
+
"給水": ["ウォーターサーバー", "給水所"],
|
| 230 |
+
"フードロス": ["食品ロス", "食ロス"],
|
| 231 |
+
|
| 232 |
+
"入場": ["エントリー", "入館"],
|
| 233 |
+
"入場制限": ["入場規制", "人数制限"],
|
| 234 |
+
"事前予約": ["予約制", "事前申込"],
|
| 235 |
+
"抽選": ["ロッタリー", "くじ"],
|
| 236 |
+
|
| 237 |
+
"工学部": ["エンジニアリング", "工学"],
|
| 238 |
+
"理学部": ["サイエンス", "理学"],
|
| 239 |
+
"医学部": ["メディカル", "医"],
|
| 240 |
+
"薬学部": ["ファーマシー", "薬学"],
|
| 241 |
+
"法学部": ["ロー", "法"],
|
| 242 |
+
"経済学部": ["エコノミクス", "経済"],
|
| 243 |
+
"文学部": ["リベラルアーツ", "文"],
|
| 244 |
+
"教育学部": ["教育", "教育学"],
|
| 245 |
+
"看護学部": ["ナース", "看護"],
|
| 246 |
+
"園芸学部": ["園芸", "アグリ"],
|
| 247 |
+
|
| 248 |
+
"軽音楽部": ["軽音部", "音楽サークル"],
|
| 249 |
+
"吹奏楽部": ["吹奏", "ブラス"],
|
| 250 |
+
"和太鼓": ["太鼓", "和太鼓演奏"],
|
| 251 |
+
"落語": ["寄席", "落研"],
|
| 252 |
+
"漫研": ["漫画研究会", "コミック研究"],
|
| 253 |
+
"鉄研": ["鉄道研究会", "鉄道サークル"],
|
| 254 |
+
"写真部": ["フォト部", "写真サークル"],
|
| 255 |
+
"映画研究会": ["映研", "映画サークル"],
|
| 256 |
+
"演劇部": ["演劇サークル", "劇団"],
|
| 257 |
+
"美術部": ["アート部", "美術サークル"],
|
| 258 |
+
"書道部": ["書道サークル", "書芸"],
|
| 259 |
+
"茶道部": ["茶道サークル", "お茶"],
|
| 260 |
+
"華道部": ["華道サークル", "生け花"],
|
| 261 |
+
"囲碁": ["囲碁部", "碁"],
|
| 262 |
+
"将棋": ["将棋部", "チェス将棋"],
|
| 263 |
+
"合唱部": ["コーラス部", "合唱サークル"],
|
| 264 |
+
"ゴスペル": ["ゴスペル合唱", "アカペラ"],
|
| 265 |
+
"アカペラ": ["ヴォーカル", "ボイスパーカッション"],
|
| 266 |
+
|
| 267 |
+
"ギター": ["Gt.", "エレキギター"],
|
| 268 |
+
"ベース": ["Ba.", "ベースギター"],
|
| 269 |
+
"ドラム": ["Dr.", "ドラムス"],
|
| 270 |
+
"キーボード": ["Kb.", "シンセサイザー"],
|
| 271 |
+
"ピアノ": ["Piano", "鍵盤"],
|
| 272 |
+
"バイオリン": ["ヴァイオリン", "Vn."],
|
| 273 |
+
"チェロ": ["Vc.", "チェロ演奏"],
|
| 274 |
+
"トランペット": ["Tp.", "ラッパ"],
|
| 275 |
+
"トロンボーン": ["Tb.", "骨"],
|
| 276 |
+
"サックス": ["サクソフォン", "Sax"],
|
| 277 |
+
"フルート": ["Fl.", "横笛"],
|
| 278 |
+
"クラリネット": ["Cl.", "クラリネット演奏"],
|
| 279 |
+
"オーボエ": ["Ob.", "オーボエ演奏"],
|
| 280 |
+
"ファゴット": ["Fg.", "バスーン"],
|
| 281 |
+
"パーカッション": ["打楽器", "Perc."],
|
| 282 |
+
"マリンバ": ["鍵盤打楽器", "マレット"],
|
| 283 |
+
"ティンパニ": ["Kettledrum", "ティンパニー"],
|
| 284 |
+
"三味線": ["津軽三味線", "三味線演奏"],
|
| 285 |
+
"尺八": ["竹笛", "尺八演奏"],
|
| 286 |
+
"琴": ["箏", "和琴"],
|
| 287 |
+
|
| 288 |
+
"ロック": ["ROCK", "ロックバンド"],
|
| 289 |
+
"ポップス": ["J-POP", "ポップ"],
|
| 290 |
+
"ジャズ": ["JAZZ", "スウィング"],
|
| 291 |
+
"フュージョン": ["Fusion", "クロスオーバー"],
|
| 292 |
+
"クラシック": ["クラシカル", "古典音楽"],
|
| 293 |
+
"ブルース": ["Blues", "ブルースロック"],
|
| 294 |
+
"メタル": ["ヘヴィメタル", "HM"],
|
| 295 |
+
"ハードロック": ["HR", "ハード系ロック"],
|
| 296 |
+
"ヒップホップ": ["HIPHOP", "ラップ"],
|
| 297 |
+
"ラップ": ["RAP", "MC"],
|
| 298 |
+
"EDM": ["ダンスミュージック", "エレクトロ"],
|
| 299 |
+
"テクノ": ["Techno", "電子音楽"],
|
| 300 |
+
"ハウス": ["House", "クラブミュージック"],
|
| 301 |
+
"ゴスペル合唱": ["ゴスペル", "クワイア"],
|
| 302 |
+
|
| 303 |
+
"ラグビー": ["Rugby", "楕円球"],
|
| 304 |
+
"バレーボール": ["バレー", "volleyball"],
|
| 305 |
+
"ハンドボール": ["ハンド", "handball"],
|
| 306 |
+
"ラクロス": ["Lacrosse", "ラクロス部"],
|
| 307 |
+
"アメフト": ["アメリカンフットボール", "フットボール"],
|
| 308 |
+
"フットサル": ["室内サッカー", "Futsal"],
|
| 309 |
+
"ソフトボール": ["ソフト", "softball"],
|
| 310 |
+
"ソフトテニス": ["軟式テニス", "soft tennis"],
|
| 311 |
+
"ランニング": ["ジョギング", "走る"],
|
| 312 |
+
"マラソン": ["長距離走", "ロードレース"],
|
| 313 |
+
"短距離走": ["スプリント", "ダッシュ"],
|
| 314 |
+
"長距離走": ["ディスタンス", "スタミナ走"],
|
| 315 |
+
"水泳": ["スイミング", "競泳"],
|
| 316 |
+
"水球": ["ウォーターポロ", "水球競技"],
|
| 317 |
+
"登山": ["ハイキング", "トレッキング"],
|
| 318 |
+
"自転車": ["サイクリング", "ロードバイク"],
|
| 319 |
+
|
| 320 |
+
"大道芸": ["ストリートパフォーマンス", "大道パフォーマンス"],
|
| 321 |
+
"コスプレ": ["コスチュームプレイ", "レイヤー"],
|
| 322 |
+
"撮影会": ["フォトセッション", "写真撮影"],
|
| 323 |
+
"メイド喫茶": ["メイドカフェ", "執事喫茶"],
|
| 324 |
+
"カジノ企画": ["カジノ体験", "ポーカー��験"],
|
| 325 |
+
"脱出ゲーム": ["リアル脱出", "謎解き"],
|
| 326 |
+
"謎解き": ["クイズラリー", "パズルゲーム"],
|
| 327 |
+
|
| 328 |
+
"翻訳": ["トランスレーション", "通訳"],
|
| 329 |
+
"国際交流": ["インターナショナル交流", "異文化交流"],
|
| 330 |
+
"地域連携": ["地域協働", "地域コラボ"],
|
| 331 |
+
|
| 332 |
+
"表彰": ["アワード", "授与"],
|
| 333 |
+
"審査": ["ジャッジ", "評価"],
|
| 334 |
+
"投票": ["Vote", "人気投票"],
|
| 335 |
+
|
| 336 |
+
"新歓": ["新入生歓迎", "ウェルカム"],
|
| 337 |
+
"定演": ["定期演奏会", "定期公演"],
|
| 338 |
+
"学内": ["キャンパス内", "構内"],
|
| 339 |
+
"学外": ["オフキャンパス", "構外"],
|
| 340 |
+
|
| 341 |
+
"雨具": ["カッパ", "レインコート"],
|
| 342 |
+
"日除け": ["サンシェード", "日よけ"],
|
| 343 |
+
"防寒": ["防寒具", "ホッカイロ"],
|
| 344 |
+
|
| 345 |
+
"受付終了": ["締切", "クローズ"],
|
| 346 |
+
"空席": ["空き", "空き枠"],
|
| 347 |
+
"満席": ["満枠", "ソールドアウト"],
|
| 348 |
+
|
| 349 |
+
"記念品": ["ノベルティ", "粗品"],
|
| 350 |
+
"抽選券": ["くじ券", "抽選チケット"],
|
| 351 |
+
"割引": ["ディスカウント", "値引き"],
|
| 352 |
+
"前売り": ["前売券", "アドバンスチケット"],
|
| 353 |
+
"当日券": ["当券", "ウォークイン"],
|
| 354 |
+
|
| 355 |
+
"音響": ["PA", "サウンド"],
|
| 356 |
+
"照明": ["ライティング", "ステージライト"],
|
| 357 |
+
"舞台": ["ステージ", "プラットフォーム"],
|
| 358 |
+
"幕": ["緞帳", "カーテン"],
|
| 359 |
+
"リハーサル": ["ゲネプロ", "場当たり"],
|
| 360 |
+
"本番": ["ステージ本番", "本公演"],
|
| 361 |
+
"アンコール": ["再演", "Encore"],
|
| 362 |
+
|
| 363 |
+
"司会": ["MC", "進行"],
|
| 364 |
+
"進行表": ["台本", "タイムスケジュール"],
|
| 365 |
+
"台本": ["シナリオ", "進行台本"],
|
| 366 |
+
"連絡網": ["メーリングリスト", "ML"],
|
| 367 |
+
"集合場所": ["集合地点", "待ち合わせ場所"],
|
| 368 |
+
"搬入": ["機材搬入", "荷下ろし"],
|
| 369 |
+
"搬出": ["撤収", "荷積み"],
|
| 370 |
+
"撤収": ["片付け", "クリーンアップ"],
|
| 371 |
+
|
| 372 |
+
"設営": ["セッティング", "セットアップ"],
|
| 373 |
+
"撤去": ["バラシ", "解体"],
|
| 374 |
+
"動線": ["導線", "人の流れ"],
|
| 375 |
+
"前日準備": ["仕込み", "前準備"],
|
| 376 |
+
"打ち合わせ": ["ミーティング", "打合せ"],
|
| 377 |
+
|
| 378 |
+
"スタンプラリー": ["スタンプ集め", "ラリー企画"],
|
| 379 |
+
"クイズラリー": ["クイズ巡り", "クイズ企画"],
|
| 380 |
+
"スカベンジャーハント": ["宝探し", "探索ゲーム"],
|
| 381 |
+
"ミニゲーム": ["プチゲーム", "簡単ゲーム"],
|
| 382 |
+
"フォトコンテスト": ["フォトコン", "写真コンテスト"],
|
| 383 |
+
"絵画コンテスト": ["アートコンテスト", "イラストコン"],
|
| 384 |
+
"コンペ": ["コンペティション", "競技会"],
|
| 385 |
+
"コンテスト": ["大会", "選手権"],
|
| 386 |
+
|
| 387 |
+
"呼び込み": ["客引き", "アナウンス"],
|
| 388 |
+
"売り子": ["販売スタッフ", "呼び込みスタッフ"],
|
| 389 |
+
"会計": ["レジ", "精算"],
|
| 390 |
+
"釣り銭": ["つり銭", "おつり"],
|
| 391 |
+
"両替": ["ブレイクチェンジ", "両替所"],
|
| 392 |
+
"現金": ["キャッシュ", "現金支払い"],
|
| 393 |
+
"キャッシュレス": ["非現金", "電子決済"],
|
| 394 |
+
"QR決済": ["コード決済", "スマホ決済"],
|
| 395 |
+
"クレジットカード": ["クレカ", "カード決済"],
|
| 396 |
+
"PayPay": ["ペイペイ", "QR支払い"],
|
| 397 |
+
|
| 398 |
+
"衛生": ["サニテーション", "清潔"],
|
| 399 |
+
"アレルギー表示": ["アレルゲン表示", "アレルギー情報"],
|
| 400 |
+
"調理許可": ["飲食許可", "営業許可"],
|
| 401 |
+
"火気": ["オープンフレーム", "火器"],
|
| 402 |
+
"消火器": ["消火具", "消火設備"],
|
| 403 |
+
"ガス": ["プロパン", "LPガス"],
|
| 404 |
+
"発電機": ["ジェネレーター", "発電装置"],
|
| 405 |
+
|
| 406 |
+
"申請": ["届け出", "申込"],
|
| 407 |
+
"許可": ["許認可", "承認"],
|
| 408 |
+
"許諾": ["承諾", "ライセンス"],
|
| 409 |
+
|
| 410 |
+
"サウンドチェック": ["音出し", "サウチェク"],
|
| 411 |
+
"モニター": ["返し", "ステージモニター"],
|
| 412 |
+
"バックライン": ["常設機材", "バクライン"],
|
| 413 |
+
"マイク": ["MIC", "マイクロフォン"],
|
| 414 |
+
"ダイナミックマイク": ["ダイナミック", "SM58"],
|
| 415 |
+
"コンデンサーマイク": ["コンデンサー", "コンデンサ"],
|
| 416 |
+
"DI": ["ダイレクトボックス", "ダイレクトインジェクション"],
|
| 417 |
+
"ミキサー": ["卓", "ミキシングコンソール"],
|
| 418 |
+
"コンソール": ["ミキサー卓", "オーディオ卓"],
|
| 419 |
+
"ケーブル": ["配線", "コード"],
|
| 420 |
+
"XLR": ["キャノン", "三芯ケーブル"],
|
| 421 |
+
"TRS": ["フォーン", "ステレオフォーン"],
|
| 422 |
+
"スピーカー": ["SP", "音響スピーカー"],
|
| 423 |
+
"サブウーハー": ["サブウーファー", "サブウーハ"],
|
| 424 |
+
|
| 425 |
+
"スポットライト": ["スポット", "ピンスポ"],
|
| 426 |
+
"PARライト": ["PAR", "パーライト"],
|
| 427 |
+
"ムービングライト": ["ムービング", "ムビング"],
|
| 428 |
+
"DMX": ["DMX512", "ライティング制御"],
|
| 429 |
+
"調光": ["ディマー", "明るさ調整"],
|
| 430 |
+
"照明卓": ["ライティングコンソール", "照明コンソール"],
|
| 431 |
+
"フォグ": ["スモーク", "煙演出"],
|
| 432 |
+
"ヘイズ": ["ヘイザー", "薄煙"],
|
| 433 |
+
|
| 434 |
+
"舞台監督": ["舞監", "ステージマネージャー"],
|
| 435 |
+
"小屋入り": ["仕込み入り", "劇場入り"],
|
| 436 |
+
"バラシ": ["撤去", "解体作業"],
|
| 437 |
+
|
| 438 |
+
"取材": ["プレス取材", "報道取材"],
|
| 439 |
+
"報道": ["ニュース", "メディア"],
|
| 440 |
+
"記者": ["リポーター", "ジャーナリスト"],
|
| 441 |
+
|
| 442 |
+
"避難経路": ["エスケープルート", "避難動線"],
|
| 443 |
+
"立入禁止": ["入場禁止", "進入禁止"],
|
| 444 |
+
"危険物": ["危険な物", "ハザード"],
|
| 445 |
+
"注意喚起": ["アナウンス注意", "注意呼びかけ"],
|
| 446 |
+
"安全管理": ["セーフティマネジメント", "安全対策"],
|
| 447 |
+
"交通整理": ["誘導", "ガードマン"],
|
| 448 |
+
|
| 449 |
+
"車椅子": ["車いす", "車イス"],
|
| 450 |
+
"点字": ["ブライユ", "点字案内"],
|
| 451 |
+
"手話通訳": ["手話", "手話サポート"],
|
| 452 |
+
|
| 453 |
+
"物産展": ["物産ブース", "ご当地物産"],
|
| 454 |
+
"地域PR": ["自治体PR", "地域紹介"],
|
| 455 |
+
"産学連携": ["大学連携", "企業連携"],
|
| 456 |
+
"企業ブース": ["スポンサー出展", "法人出展"],
|
| 457 |
+
|
| 458 |
+
"荒天": ["荒天時", "暴風雨"],
|
| 459 |
+
"小雨決行": ["多少の雨", "雨でも開催"],
|
| 460 |
+
"雨天中止": ["雨天時中止", "悪天候中止"],
|
| 461 |
+
"台風": ["サイクロン", "タイフーン"],
|
| 462 |
+
"熱中症": ["熱射病", "暑熱障害"],
|
| 463 |
+
"猛暑": ["酷暑", "真夏日"],
|
| 464 |
+
"寒波": ["厳寒", "強い寒さ"],
|
| 465 |
+
|
| 466 |
+
"マスク": ["フェイスマスク", "不織布マスク"],
|
| 467 |
+
"消毒": ["アルコール消毒", "手指消毒"],
|
| 468 |
+
"検温": ["体温測定", "サーモチェック"],
|
| 469 |
+
|
| 470 |
+
"同好会": ["同好会サークル", "クラブ"],
|
| 471 |
+
"部活": ["部活動", "クラブ活動"],
|
| 472 |
+
"公認団体": ["大学公認", "公認サークル"],
|
| 473 |
+
"非公認": ["未公認", "非公認サークル"],
|
| 474 |
+
"学友会": ["学生会", "学友"],
|
| 475 |
+
|
| 476 |
+
"提灯": ["ちょうちん", "ランタン"],
|
| 477 |
+
"のぼり": ["幟", "旗"],
|
| 478 |
+
"横断幕": ["バナー", "横断バナー"],
|
| 479 |
+
"看板": ["サイン", "サイネージ"],
|
| 480 |
+
"ポスター": ["掲示ポスター", "張り紙"],
|
| 481 |
+
"チラシ": ["フライヤー", "ビラ"],
|
| 482 |
+
"ビラ配り": ["チラシ配布", "配布活動"],
|
| 483 |
+
|
| 484 |
+
"公式アプリ": ["フェスアプリ", "学祭アプリ"],
|
| 485 |
+
"特設サイト": ["特設ページ", "特設Web"],
|
| 486 |
+
|
| 487 |
+
"ギターボーカル": ["Gt.Vo", "ギタボ"],
|
| 488 |
+
"ベースボーカル": ["Ba.Vo", "ベボ"],
|
| 489 |
+
"ツインギター": ["2ギター", "ツインGt"],
|
| 490 |
+
"ツインボーカル": ["2Vo", "デュオボーカル"],
|
| 491 |
+
"インストバンド": ["インストゥルメンタル", "歌なしバンド"],
|
| 492 |
+
|
| 493 |
+
"合奏発表": ["アンサンブル発表", "演奏発表"],
|
| 494 |
+
"定期公演": ["定演", "定期ライブ"],
|
| 495 |
+
"野外ライブ": ["屋外ライブ", "アウトドアライブ"],
|
| 496 |
+
"室内ライブ": ["屋内ライブ", "インドアライブ"],
|
| 497 |
+
|
| 498 |
+
"新体操": ["リズム体操", "RG"],
|
| 499 |
+
"体操": ["体操競技", "Gymnastics"],
|
| 500 |
+
"チア": ["チアリーディング", "チアダンス"],
|
| 501 |
+
"ヨガ": ["Yoga", "ヨーガ"],
|
| 502 |
+
"ピラティス": ["Pilates", "ボディメイク"],
|
| 503 |
+
"ボクシング": ["Boxing", "拳闘"],
|
| 504 |
+
"キックボクシング": ["キック", "Kickboxing"],
|
| 505 |
+
"少林寺拳法": ["少林寺", "拳法"],
|
| 506 |
+
"相撲": ["Sumo", "すもう"],
|
| 507 |
+
"スケートボード": ["スケボー", "SK8"],
|
| 508 |
+
"ダブルダッチ": ["二重跳びロープ", "DD"],
|
| 509 |
+
|
| 510 |
+
"即売会": ["頒布会", "販売会"],
|
| 511 |
+
"頒布": ["配布", "頒布販売"],
|
| 512 |
+
"委託販売": ["委販", "委託"],
|
| 513 |
+
|
| 514 |
+
"学部紹介": ["学科紹介", "専攻紹介"],
|
| 515 |
+
"研究室公開": ["ラボ公開", "オープンラボ"],
|
| 516 |
+
"オープンキャンパス": ["OC", "学校見学"],
|
| 517 |
+
|
| 518 |
+
"アンプ": ["ギターアンプ", "ベースアンプ"],
|
| 519 |
+
"エフェクター": ["ペダル", "エフェクト"],
|
| 520 |
+
"ストラップ": ["ギターストラップ", "肩紐"],
|
| 521 |
+
"チューナー": ["チューニング", "チューニングメーター"],
|
| 522 |
+
"譜面台": ["楽譜立て", "ミュージックスタンド"],
|
| 523 |
+
"メトロノーム": ["拍子器", "テンポメーター"],
|
| 524 |
+
"カポタスト": ["カポ", "カポタ"],
|
| 525 |
+
|
| 526 |
+
"アンケート": ["サーベイ", "調査票"],
|
| 527 |
+
"意見箱": ["ご意見箱", "フィードバックボックス"],
|
| 528 |
+
"問い合わせ": ["お問い合わせ", "コンタクト"],
|
| 529 |
+
|
| 530 |
+
"学生証": ["IDカード", "学生ID"],
|
| 531 |
+
"身分証": ["身分証明書", "ID"],
|
| 532 |
+
"確認書": ["確認票", "確認レター"],
|
| 533 |
+
|
| 534 |
+
"紛失": ["失くす", "紛失物"],
|
| 535 |
+
"拾得物": ["拾い物", "拾得"],
|
| 536 |
+
|
| 537 |
+
"立て看板": ["立看", "立看板"],
|
| 538 |
+
"仮設テント": ["テント", "仮設ブース"],
|
| 539 |
+
"机": ["テーブル", "長机"],
|
| 540 |
+
"椅子": ["イス", "チェア"],
|
| 541 |
+
"養生": ["養生テープ", "床養生"],
|
| 542 |
+
"コードリール": ["延長ドラム", "延長コード"],
|
| 543 |
+
"延長コード": ["電源タップ", "タップ"],
|
| 544 |
+
|
| 545 |
+
"写真映え": ["フォトジェニック", "映え"],
|
| 546 |
+
"推し活": ["推しごと", "応援活動"],
|
| 547 |
+
"交流会": ["懇親会", "ミートアップ"],
|
| 548 |
+
"打ち上げ": ["打上げ", "アフターパーティー"],
|
| 549 |
+
"差し入れ": ["ドネーション", "差入れ"],
|
| 550 |
+
|
| 551 |
+
"開会式": ["オープニング", "オープニングセレモニー"],
|
| 552 |
+
"閉会式": ["クロージング", "クロージングセレモニー"],
|
| 553 |
+
"表彰式": ["アワードセレモニー", "授賞式"],
|
| 554 |
+
|
| 555 |
+
"MC台本": ["司会台本", "進行台本"],
|
| 556 |
+
"曲順": ["セットリスト", "セトリ"],
|
| 557 |
+
"転換": ["ステージ転換", "セッティング替え"],
|
| 558 |
+
|
| 559 |
+
"搬入口": ["搬入ゲート", "搬入扉"],
|
| 560 |
+
"関係者入口": ["スタッフ入口", "バックステージ入口"],
|
| 561 |
+
"バックステージ": ["舞台裏", "バックステージエリア"],
|
| 562 |
+
|
| 563 |
+
"記録撮影": ["ビデオ撮影", "記念撮影"],
|
| 564 |
+
"配信": ["ライブ配信", "ストリーミング"],
|
| 565 |
+
"録音": ["レコーディング", "音声記録"],
|
| 566 |
+
|
| 567 |
+
"ノベルティ配布": ["粗品配布", "記念品配布"],
|
| 568 |
+
"スタンプカード": ["ポイントカード", "集印帳"],
|
| 569 |
+
"抽選会": ["くじ引き", "抽選イベント"],
|
| 570 |
+
|
| 571 |
+
"雨天備品": ["雨具", "ブルーシート"],
|
| 572 |
+
"日除けテント": ["サンシェード", "日よけテント"],
|
| 573 |
+
"防寒具": ["カイロ", "ブランケット"],
|
| 574 |
+
|
| 575 |
+
"授乳": ["母乳", "ベビーケア"],
|
| 576 |
+
"おむつ": ["ダイパー", "紙おむつ"],
|
| 577 |
+
"多目的室": ["多目的スペース", "ユニバーサルルーム"],
|
| 578 |
+
|
| 579 |
+
"落雷": ["雷", "雷雨"],
|
| 580 |
+
"強風": ["暴風", "突風"],
|
| 581 |
+
"高温": ["猛暑", "酷暑"],
|
| 582 |
+
"低温": ["厳寒", "寒冷"],
|
| 583 |
+
|
| 584 |
+
"清掃ボランティア": ["クリーンボランティア", "清掃スタッフ"],
|
| 585 |
+
"資源回収": ["空き缶回収", "ペットボトル回収"],
|
| 586 |
+
"給水スポット": ["給水所", "ウォーターサーバー"],
|
| 587 |
+
|
| 588 |
+
"地域ボランティア": ["市民ボランティア", "地域スタッフ"],
|
| 589 |
+
"企業協賛": ["スポンサー", "協賛企業"],
|
| 590 |
+
"後援": ["サポート", "バックアップ"],
|
| 591 |
+
|
| 592 |
+
"観覧席": ["客席", "座席"],
|
| 593 |
+
"立見": ["立ち見", "スタンディング"],
|
| 594 |
+
"最前列": ["最前", "フロントロウ"],
|
| 595 |
+
|
| 596 |
+
"撮影禁止": ["写真NG", "動画NG"],
|
| 597 |
+
"飲食禁止": ["飲食NG", "飲食不可"],
|
| 598 |
+
"禁煙": ["ノースモーキング", "喫煙禁止"],
|
| 599 |
+
"喫煙所": ["スモーキングエリア", "喫煙スペース"],
|
| 600 |
+
|
| 601 |
+
"案内看板": ["案内サイン", "ディレクションサイン"],
|
| 602 |
+
"導線案内": ["動線案内", "ルート案内"],
|
| 603 |
+
"整理整頓": ["片付け", "整頓"],
|
| 604 |
+
|
| 605 |
+
"忘れ物": ["落とし物", "遺失物"],
|
| 606 |
+
"呼出": ["呼び出し", "アナウンス呼出"],
|
| 607 |
+
"迷子放送": ["迷子アナウンス", "迷子呼出"]
|
| 608 |
+
}
|
resources/test_data.csv
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
projectId,organization,reading,title,genre,day1,day2,day3,place,description,prComment,prCommentLong,note,imageName,prImageName1,prImageName2,prImageName3,prImageName4,prImageName5,urlX,urlInstagram,urlOfficialWebsite,urlYoutube,urlOther
|
| 2 |
+
dummy-01,電子工作愛好会,でんしこうさくあいこうかい,光る!電子アート展,展示,true,true,true,工学部2号館,電子工作で作ったアート作品を展示します。,キラキラ光る電子アートの世界へようこそ!部員が丹精込めて作った作品の数々をお楽しみください。初心者向けの体験コーナーもありますよ!,"当会は、電子工作を通じて創造の喜びを分かち合うサークルです。今回の展示では、LEDやセンサーを駆使したインタラクティブなアート作品を多数用意しました。来場者の動きに反応して光や音が変わる作品など、見て触って楽しめる展示を目指しています。ぜひ、不思議な電子アートの世界に足を踏み入れてみてください。",,dummy.jpg,,,,,,,,,,
|
| 3 |
+
dummy-02,空想地図作成サークル,くうそうちずさくせいさーくる,架空都市「ジェミニシティ」のすべて,展示,true,true,false,文学部棟ロビー,我々が創造した架空の都市の地図や設定資料を展示します。,存在しない都市の、あまりにもリアルな歴史と地理。あなたの知的好奇心を刺激する、壮大な空想の世界がここにあります。,"「ジェミニシティ」は、私たちが一年がかりで創り上げた架空の都市です。詳細な地図はもちろん、その歴史、文化、交通網に至るまで、細部にわたって設定を練り上げました。会場では、巨大な手書き地図のほか、住民台帳や市史年表などの資料も展示します。この街に迷い込んでみませんか?",,dummy.jpg,,,,,,,,,,
|
| 4 |
+
dummy-03,世界のボードゲームで遊ぶ会,せかいのぼーどげーむであそぶかい,ボードゲーム体験会,参加型,true,true,true,共通教育棟101教室,世界中の珍しいボードゲームで遊べます。ルールは部員が丁寧に説明します。,ドイツ、フランス、アメリカから集めた選りすぐりのボードゲームをご用意しました。初心者大歓迎!一緒に頭脳戦を楽しみましょう!,ルールが簡単でワイワイ楽しめるパーティーゲームから、じっくり考える戦略ゲームまで、50種類以上のボードゲームを準備しています。時間内は出入り自由、遊び放題です。お一人様でも、お友達とでも、お気軽にお立ち寄りください。,,dummy.jpg,,,,,,,,,,
|
| 5 |
+
dummy-04,謎の中華まん研究会,なぞのちゅうかまんけんきゅうかい,究極の中華まん,飲食,false,true,true,食堂前テント,研究の成果として生まれた、究極に美味しい中華まんを販売します。,肉汁たっぷりのジューシーな中華まんです。一度食べたら忘れられない味を、ぜひご賞味ください!,生地の発酵時間、餡の配合、蒸し時間。すべてを計算し尽くした、我々の研究の集大成がこの「究極の中華まん」です。味は「極・豚まん」と「秘伝・カレーまん」の二種類。熱々をご提供します!,売り切れ次第終了,dummy.jpg,,,,,,,,,,
|
| 6 |
+
dummy-05,大道芸パフォーマンス「ジェスターズ」,だいどうげいぱふぉーまんすじぇすたーず,ジャグリング&マジックショー,パフォーマンス,true,false,true,けやき広場,ジャグリングやマジックなどのパフォーマンスを披露します。,ボールが宙を舞い、トランプが奇跡を起こす!笑いと驚きの30分間。ご家族、ご友人お誘い合わせの上、ぜひご覧ください!,"私たちジェスターズは、ジャグリング、マジック、パントマイムなどを練習しているパフォーマンスサークルです。ステージでは、各メンバーが磨き上げた技を次々と披露します。お客様を巻き込んだ楽しいパフォーマンスも予定していますので、ぜひ手拍子で応援してください!",雨天中止,dummy.jpg,,,,,,,,,,
|
| 7 |
+
dummy-06,空飛ぶほうき研究会,そらとぶほうきけんきゅうかい,VRほうき飛行体験,参加型,true,true,true,体育館裏,VRゴーグルをつけて、空飛ぶほうきに乗る体験ができます。,あなたも魔法使いになれる!VRでリアルな飛行体験。風を感じながら、キャンパスの上空を散歩しよう!,最新のVR技術と、我々が開発した専用のほうき型デバイスを組み合わせることで、本当に空を飛んでいるかのような没入感を実現しました。3分間の短いフライトですが、忘れられない体験になること間違いなしです。,【注意】身長140cm未満の方、乗り物に酔いやすい方はご遠慮ください。,dummy.jpg,,,,,,,,,,
|
| 8 |
+
dummy-07,無重力睡眠研究会,むじゅうりょくすいみんけんきゅうかい,無重力ハンモック体験,その他,false,true,true,中庭,究極のリラックスを。無重力感覚のハンモックで癒やしのひととき。,ゆらゆら揺られて、まるで宇宙にいるみたい。5分間のリラックス体験はいかがですか?日頃の疲れを癒やしに来てください。,このハンモックは、体の圧力が一点に集中しないよう特殊な設計が施されており、まるで無重力空間にいるかのような浮遊感を味わうことができます。木陰で静かな音楽を聴きながら、究極のリラクゼーションをご体験ください。,,dummy.jpg,,,,,,,,,,
|
| 9 |
+
dummy-08,古代文字解読クラブ,こだいもじかいどくくらぶ,古代文字アクセサリー販売,物販,true,true,false,歴史資料館前,ヒエログリフや楔形文字をあしらった手作りアクセサリーを販売します。,古代の神秘をその手に。あなたの名前をヒエログリフにしたキーホルダーが人気です。世界に一つだけのアクセサリーを見つけに来てください。,古代エジプトのヒエログリフ、メソポタミアの楔形文字、マヤ文字など、様々な古代文字をモチーフにしたピアスやネックレス、キーホルダーを販売しています。すべて部員による手作りです。文字の意味を解説したカードもお付けします。,,dummy.jpg,,,,,,,,,,
|
| 10 |
+
dummy-09,即興ミュージカル劇団,そっきょうみゅーじかるげきだん,その場でミュージカル!,パフォーマンス,false,false,true,講堂,お客様から頂いたお題で、その場で即興のミュージカルを創ります。,何が起こるか分からない、一期一会のステージ!あなたのアイデアが、世界でたった一つのミュージカルになります。笑いと感動の渦に巻き込まれに来てください!,開演前にお客様から「場所」「登場人物」「決め台詞」などのお題を募集します。キャストとバンドは、そのお題を元に、打ち合わせなしの完全即興で歌と物語を紡ぎ出します。予測不能な展開をお楽しみください!,撮影・録音禁止,dummy.jpg,,,,,,,,,,
|
| 11 |
+
dummy-10,深海生物同好会,しんかいせいぶつどうこうかい,光る深海生物のレジンアート,物販,true,true,true,理学部棟1階,深海生物をモチーフにした、暗闇で光るレジンアート作品を販売します。,チョウチンアンコウやダイオウグソクムシが、あなたの部屋を怪しく照らす。マニアックな魅力あふれる作品たちです。,部員が一つ一つ手作りした、深海生物のレジン製キーホルダーや置物です。蓄光素材を混ぜ込んでいるため、暗い場所でぼんやりと光ります。デフォルメされた可愛いデザインから、リアルな造形のものまで、様々な作品をご用意してお待ちしております。,,dummy.jpg,,,,,,,,,,
|
| 12 |
+
dummy-11,タイポグラフィ研究会,たいぽぐらふぃけんきゅうかい,文字で魅せるポスター展,展示,true,true,true,デザイン棟ギャラリー,文字のデザインだけで構成されたポスターを展示します。,フォントの力、文字の配置、色の組み合わせ。言葉の意味を超えた、文字そのものの美しさを感じてください。,タイポグラフィとは、文字を読みやすく、美しく配置する技術です。本展示では、部員それぞれが選んだテーマを、タイポグラフィの技術のみで表現したポスター作品を展示しています。あなたの「好き」な文字がきっと見つかります。,,dummy.jpg,,,,,,,,,,
|
| 13 |
+
dummy-12,世界の紅茶を飲む会,せかいのこうちゃをのむかい,世界の紅茶とスコーンの店,飲食,true,true,true,学生会館2階,世界各国の紅茶と、手作りスコーンを提供します。,アッサム、ダージリン、アールグレイ。香り豊かな紅茶で、優雅なひとときを。紅茶に合う自家製スコーンも絶品です。,インド、スリランカ、中国、そして日本。世界中から取り寄せた20種類以上の茶葉の中から、お好きな紅茶をお選びいただけます。紅茶の専門知識を持つ部員が、あなたにぴったりの一杯をおすすめします。,,dummy.jpg,,,,,,,,,,
|
| 14 |
+
dummy-13,ドローンレース同好会,どろーんれーすどうこうかい,第一回ドローンレース選手権,パフォーマンス,false,true,false,グラウンド,部員によるドローンレースの大会です。白熱の空中戦をご覧ください。,目にも留まらぬ速さでコースを駆け抜けるドローン。操縦技術の限界に挑む、手に汗握るレース展開にご期待ください!,特設コースを舞台に、FPV(一人称視点)ドローンによるレースを行いま��。LEDで装飾されたドローンが、立体的なコースを高速で飛び回る様子は圧巻です。未来のスカイスポーツを、ぜひその目でご覧ください。,安全のため、コース内への立ち入りは固く禁じます。,dummy.jpg,,,,,,,,,,
|
| 15 |
+
dummy-14,VR蕎麦打ち体験会,ぶいあーるそばうちたいけんかい,VRであなたも蕎麦職人,参加型,true,true,true,情報科学棟VRラボ,VRで本格的な蕎麦打ちを体験できます。,仮想空間で、こねて、のばして、切る。あなただけの手打ち蕎麦を完成させよう!友達とスコアを競うのも楽しい!,VRコントローラーを使い、蕎麦粉をこねる感触から、麺を切る工程までをリアルに再現しました。ゲーム感覚で楽しみながら、蕎麦打ちの一連の流れを学ぶことができます。高得点者には景品もご用意しています。,,dummy.jpg,,,,,,,,,,
|
| 16 |
+
dummy-15,AIと俳句を作る会,えーあいとはいくをつくるかい,AI vs 人間 俳句バトル,参加型,true,false,true,大講義室,AIが作った俳句と人間が作った俳句、見分けられますか?,人工知能は、人の心を詠むことができるのか。来場者の皆様に審査員となっていただき、AIと人間の俳句対決を行います。,当会が開発した俳句生成AI「松尾」と、俳句歴20年の人間師範が、同じお題で俳句を詠みます。どちらが詠んだ句か隠した状態で皆様に鑑賞・投票していただき、その合計点で勝敗を決めます。皆様の参加をお待ちしております。,,dummy.jpg,,,,,,,,,,
|
| 17 |
+
dummy-16,多肉植物を愛でる会,たにくしょくぶつをめでるかい,ぷにぷに多肉植物の寄せ植え販売,物販,true,true,true,園芸学部温室,可愛い多肉植物の寄せ植えを販売します。,ぷにぷにした見た目に癒やされる、多肉植物はいかがですか?育てやすい種類ばかりなので、初めての方にもおすすめです。,部員が愛情を込めて育てた多肉植物を、おしゃれな鉢に寄せ植えして販売します。お部屋のインテリアにぴったりの一品がきっと見つかります。育て方の相談も、お気軽にどうぞ。,,dummy.jpg,,,,,,,,,,
|
| 18 |
+
dummy-17,鉱物収集サークル,こうぶつしゅうしゅうさーくる,きらめく鉱物の展示・即売会,物販,true,true,true,理学部博物館,国内外の美しい鉱物標本を展示・販売します。,水晶、蛍石、黄鉄鉱。地球が創り出した自然の芸術品。掌に乗る、きらめく小宇宙をあなたのお家に。,部員が全国各地で採集した鉱物や、海外から取り寄せた珍しい鉱物を展示・販売します。鉱物の名前や産地、見どころなどを解説したカードと共に、その魅力をお伝えします。,,dummy.jpg,,,,,,,,,,
|
| 19 |
+
dummy-18,アナログゲーム開発団,あなろぐげーむかいはつだん,自作アナログゲーム試遊会,参加型,true,true,true,サークル棟101,部員が制作したオリジナルのカードゲームやボードゲームで遊べます。,まだ誰も遊んだことのない、世界で初めてのゲーム体験をあなたに。制作者本人によるルール説明付きです!,企画からデザイン、制作まで、すべて部員の手で行ったオリジナルのアナログゲームが勢揃い。あなたのフィードバックが、未来の製品版を作る第一歩になるかもしれません。ぜひ遊びに来てください。,,dummy.jpg,,,,,,,,,,
|
| 20 |
+
dummy-19,歴史的建造物模型部,れきしてきけんぞうぶつもけいぶ,ミニチュア歴史建造物展,展示,true,true,true,図書館1階,歴史的な建物を精密な模型で再現しました。,あの有名なお城や教会が、手のひらサイズに。細部までこだわり抜いた、驚きの再現度をご覧ください。,設計図や古写真を元に、歴史的建造物を1/150スケールで再現しています。素材は木材や紙、プラスチックなど様々。模型を通じて、世界の建築史を巡る旅をお楽しみください。,,dummy.jpg,,,,,,,,,,
|
| 21 |
+
dummy-20,現代神話創作サークル,げんだいしんわそうさくさーくる,我々の神話を聞いてくれ,音楽,false,true,false,野外ステージ,創作した神話の朗読と、テーマソングの演奏を行います。,もし、現代に新たな神話が生まれたなら。壮大なスケールで描かれる、我々のオリジナル神話の発表会です。,現代社会を舞台にした、新たな神々や英雄たちの物語を創作しています。当日は、物語のハイライト部分の朗読と、物語の世界観を表現したオリジナル楽曲のバンド演奏を披露します。,,dummy.jpg,,,,,,,,,,
|
| 22 |
+
dummy-21,プロジェクションマッピング同好会,ぷろじぇくしょんまっぴんぐどうこうかい,校舎が動く!光のショー,パフォーマンス,false,false,true,図書館壁面,図書館の壁面にプロジェクションマッピングを投影します。,見慣れた校舎が、光のアートで生まれ変わる。音楽と映像が織りなす、幻想的な夜をお楽しみください。,閉祭式の後、図書館の壁面をスクリーンに、約10分間のプロジェクションマッピングショーを行います。今年のテーマは「祭り」。ダイナミックな映像にご期待ください。,閉祭式終了後、20:00から開始予定,dummy.jpg,,,,,,,,,,
|
| 23 |
+
dummy-22,世界のスープ研究会,せかいのすーぷけんきゅうかい,旅するスープ屋さん,飲食,true,true,true,国際交流会館前,週替わりならぬ日替わりで、世界各国のスープを提供します。,1日目はロシアのボルシチ、2日目はタイのトムヤムクン、3日目はスペインのガスパチョ。心も体も温まる一杯をどうぞ。,私たちは世界中のスープを研究し、その再現に情熱を燃やしています。各国の食文化が詰まった、本格的な味をお楽しみください。パンもセットで販売します。,,dummy.jpg,,,,,,,,,,
|
| 24 |
+
dummy-23,折り紙ヒコーキ部,おりがみひこーきぶ,誰が一番飛ぶか選手権,参加型,true,true,false,体育館,自分で折った紙ヒコーキの飛距離を競います。,たかが紙ヒコーキ、されど紙ヒコーキ。折り方一つで飛び方が変わる、奥深い世界へようこそ。大人も子供も、本気で遊ぼう!,指定の用紙と折り方テキストを配布します。練習時間内に、よく飛ぶように調整してください。競技は一人一投。最も遠くまで飛ばした方には豪華景品をプレゼント!,,dummy.jpg,,,,,,,,,,
|
| 25 |
+
dummy-24,食品サンプル制作サークル,しょくひんさんぷるせいさくさーくる,本物そっくり!食品サンプル工房,物販,true,true,true,家庭科調理室,本物そっくりの食品サンプルの制作体験と作品販売。,思わず食べちゃいそうになるくらいリアル!あなたも天ぷらやパフェの食品サンプルを作ってみませんか?,プロも使う専用の材料(蝋や樹脂)を使って、食品サンプルの制作体験ができます。スタッフが丁寧にサポートするので、初めての方でも安心です。部員が作ったハイクオリティな作品の販売も行っています。,,dummy.jpg,,,,,,,,,,
|
| 26 |
+
dummy-25,人力発電研究会,じんりきはつでんけんきゅうかい,自転車でスマホを充電しよう,参加型,true,true,true,環境エネルギー棟,自転車を漕いで、自分のスマートフォンを充電できる体験企画です。,運動不足解消にも、エコにもなる。自分の力で電気を作る達成感を味わおう!漕いだ分だけ、スマホが充電されます。,エアロバイクに発電機を接続した、当会オリジナルの充電ステーションです。USBケーブルを接続し、30分間思いっきり漕いでください。フィットネス感覚で、環境問題について考えるきっかけになれば幸いです。,,dummy.jpg,,,,,,,,,,
|
| 27 |
+
dummy-26,競技かるた会,きょうぎかるたかい,百人一首の世界,展示,true,true,false,和室,競技かるたのデモンストレーションと、百人一首の解説展示。,「ちはやふる」で話題の競技かるた。そのスピードと迫力を、目の前で体感しませんか?,畳の上で繰り広げられる、静かなる激闘。部員による競技かるたの模範試合を定時開催します。また、百人一首の歌の意味や背景を解説したパネル展示も行っています。日本の伝統文化の美しさに触れてみてください。,,dummy.jpg,,,,,,,,,,
|
| 28 |
+
dummy-27,錯視アート研究会,さくしあーとけんきゅうかい,脳が騙される!錯視美術館,展示,true,true,true,美術棟展示室,見ていて不思議な錯視・錯覚アート作品を展示しています。,止まっているはずの絵が動いて見える?同じ長さのはずなのに違って見える?あなたの脳が、きっと騙される。不思議なアートの世界をお楽しみください。,部員が制作した、様々な種類の錯視アートを展示しています。なぜそのように見えるのか、という科学的な解説パネルも用意していますので、自由研究のテーマ探しにも役立つかもしれません。,,dummy.jpg,,,,,,,,,,
|
| 29 |
+
dummy-28,燻製同好会,くんせいどうこうかい,大人の燻製おつまみ,飲食,true,true,true,キャンプエリア,チーズやナッツ、ベーコンなどを燻製にして販売します。,桜のチップでじっくり燻した、香り高いおつまみはいかがですか?お酒が好きな大人の方に、ぜひ味わっていただきたい逸品です。,燻製器も部員の手作りです。温度と煙の量を徹底管理し、それぞれの食材が最も美味しくなるように仕上��ました。燻製ならではの深い味わいと香りをお楽しみください。,,dummy.jpg,,,,,,,,,,
|
| 30 |
+
dummy-29,特殊メイク研究会,とくしゅめいくけんきゅうかい,ワンポイント・傷メイク体験,参加型,false,true,true,教室A-1,リアルな傷メイクをワンポイントで体験できます。,ハロウィンで使えること間違いなし!プロが使うような材料で、本物そっくりの切り傷や火傷のメイクを体験してみませんか?,映画やドラマで使われる特殊メイクの技術を、気軽に体験できる企画です。肌に安全な材料を使用し、5分程度でリアルな傷メイクを施します。SNS映えも抜群です!,,dummy.jpg,,,,,,,,,,
|
| 31 |
+
dummy-30,プラネタリウム制作サークル,ぷらねたりうむせいさくさーくる,手作りプラネタリウム上映会,パフォーマンス,true,true,true,視聴覚室,手作りのプラネタリウムで、秋の夜空を解説します。,満点の星空が、あなたを包み込む。部員による生解説で、星座の物語を巡る旅へ出発しましょう。,直径5mのエアドームの中に、我々が自作したピンホール式プラネタリウム投影機を設置しました。市販のプラネタリウムとは一味違う、温かみのある星空をお楽しみいただけます。各回定員20名、約15分間の上映です。,,dummy.jpg,,,,,,,,,,
|
| 32 |
+
dummy-31,書評サークル「ビブリオ」,しょひょうさーくるびぶりお,書評合戦ビブリオバトル,パフォーマンス,false,true,false,図書館ラーニングコモンズ,おすすめ本を持ち寄って、その魅力を5分間で語り合う書評イベント。,あなたもチャンプ本を予想しよう!発表者が5分間で本の魅力を語り、観客が一番読みたくなった本に投票する、知的書評ゲームです。,「人を通して本を知る、本を通して人を知る」をコンセプトにしたイベントです。誰でも観覧可能。あなたの運命の一冊が、ここで見つかるかもしれません。,,dummy.jpg,,,,,,,,,,
|
| 33 |
+
dummy-32,ジオラマ制作同好会,じおらませいさくどうこうかい,箱庭の世界展,展示,true,true,true,工芸室,様々なテーマのジオラマ(箱庭模型)を展示します。,懐かしい昭和の街並み、ファンタジーの世界、未来の都市。小さな箱の中に広がる、無限の物語をご覧ください。,部員がそれぞれの得意なテーマで制作したジオラマ作品を展示します。細部まで作り込まれた、こだわりの世界観が見どころです。思わず写真を撮りたくなるような作品ばかりです。,,dummy.jpg,,,,,,,,,,
|
| 34 |
+
dummy-33,実験クッキング部,じっけんくっきんぐぶ,アルギン酸で掴める水!?,飲食,true,true,false,化学実験室,食べられる化学実験。人工イクラの原理で、ジュースを球状にします。,見た目はまるで宝石。プチっと弾ける不思議な食感のスイーツです。味はぶどう、オレンジ、メロンの3種類。,食品添加物として使われるアルギン酸ナトリウムと乳酸カルシウムの化学反応を利用した、分子ガストロノミーの入門的スイーツです。安全な材料を使っているので、安心してお召し上がりいただけます。,,dummy.jpg,,,,,,,,,,
|
| 35 |
+
dummy-34,ロボット相撲研究会,ろぼっとずもうけんきゅうかい,自律型ロボット相撲大会,パフォーマンス,true,false,false,武道場,自分たちで開発したロボットによる相撲大会です。,プログラムとセンサーだけで動く、自律型ロボットたちの真剣勝負。土俵際での攻防は、手に汗握ること間違いなし!,全日本ロボット相撲大会のルールに準拠した、本格的なロボット相撲です。部員が設計からプログラミングまで、すべてを手掛けた個性豊かなロボットたちが土俵の上でぶつかり合います。,,dummy.jpg,,,,,,,,,,
|
| 36 |
+
dummy-35,ステンドグラス工房,すてんどぐらすこうぼう,光の小箱作りワークショップ,参加型,true,true,true,美術室,ステンドグラスの技法で、小さなランプシェードを作ります。,色とりどりのガラスが織りなす、光のアート。あなただけのオリジナルランプを作ってみませんか?,カット済みのガラス片を、銅テープとはんだごてを使って立体的に組み立てていきます。本格的なステンドグラスの技法を、安全に体験できます。スタッフがマンツーマンで指導しますので、初めての方でも素敵な作品が作れます。,参加費500円,dummy.jpg,,,,,,,,,,
|
| 37 |
+
dummy-36,世界の打楽器アンサンブル,せかいのだ楽器あんさんぶる,世界の打楽器に触れてみよう,音楽,true,true,true,音楽室,カホン、ジャンベ、コンガなど、世界の様々な打楽器を自由に叩けます��,叩けば音が出る、それが打楽器。難しいルールはありません。リズムに乗って、みんなでセッションを楽しみましょう!,普段あまり触れる機会のない、世界中の珍しい打楽器を集めました。見ているだけでも楽しいですが、ぜひ実際に触れて、その音や響きを体感してみてください。部員によるミニコンサートも随時開催します。,,dummy.jpg,,,,,,,,,,
|
| 38 |
+
dummy-37,サンドアート・パフォーマンス,さんどあーとぱふぉーまんす,砂で描く物語,パフォーマンス,false,true,true,小ホール,砂と光で、音楽に合わせて物語を描き出します。,ガラスの上の砂が、次々と形を変えて物語を紡いでいく。儚くも美しい、サンドアートの世界へようこそ。,バックライトで照らされたガラス板の上で、砂を使って絵を描き、それをスクリーンに投影するパフォーマンスです。音楽に合わせて砂の絵が変化していく様子は、まるでアニメーションのよう。幻想的な時間をお届けします。,,dummy.jpg,,,,,,,,,,
|
| 39 |
+
dummy-38,レザークラフトサークル,れざーくらふとさーくる,革のキーホルダー作り,物販,true,true,true,テラス席,本革を使ったキーホルダー作りのワークショップです。,トントン叩いて、自分だけの刻印を。使い込むほど味が出る、本革の魅力に触れてみませんか?,好きな形の革パーツを選び、名前や模様を刻印して、オリジナルのキーホルダーを作ります。簡単な作業なので、小さなお子様でも楽しめます。部員が作った革小物の販売も行っています。,,dummy.jpg,,,,,,,,,,
|
| 40 |
+
dummy-39,マーブリングアート体験会,まーぶりんぐあーとたいけんかい,水に浮かぶ魔法の絵の具,参加型,true,true,false,絵画室,水面に絵の具を垂らして、不思議なマーブル模様を作ります。,二度と同じ模様は作れない、一期一会のアート体験。作った模様は、紙に写し取ってお持ち帰りいただけます。,マーブリングとは、水面に特殊な絵の具を垂らし、その模様を紙などに写し取る技法です。うちわやコースターに模様を写し取る体験ができます。小さなお子様から大人まで、誰でもアーティスト気分を味わえます。,,dummy.jpg,,,,,,,,,,
|
| 41 |
+
dummy-40,巨大からくり装置研究会,きょだいからくりそうちけんきゅうかい,ピタゴラ的装置の展示,展示,true,true,true,物理実験室,ビー玉が転がって、様々な仕掛けが連鎖する巨大からくり装置。,ビー玉の旅を見守ろう。ゴールした時の達成感は格別です。あっと驚く仕掛けが満載!,教室いっぱいに広がる、巨大なからくり装置を展示しています。物理法則を応用した様々なギミックが、次々と連鎖してビー玉をゴールまで運びます。1時間に1回、実際にビー玉を転がす実演を行います。,,dummy.jpg,,,,,,,,,,
|
| 42 |
+
dummy-41,キャンドル作り同好会,きゃんどるづくりどうこうかい,アロマキャンドル販売,物販,true,true,true,中庭テント,手作りのアロマキャンドルを販売しています。,ラベンダー、ローズ、シトラス。優しい香りのキャンドルで、リラックスタイムを演出しませんか?,見た目も可愛いボタニカルキャンドルや、シンプルなソイキャンドルなど、様々な種類のアロマキャンドルを販売しています。火を灯さなくても、置いておくだけでふんわり香ります。プレゼントにもおすすめです。,,dummy.jpg,,,,,,,,,,
|
| 43 |
+
dummy-42,人力飛行機研究会,じんりきひこうきけんきゅうかい,人力飛行機の翼の展示,展示,true,true,false,格納庫,鳥人間コンテストに出場した、人力飛行機の実物大の翼を展示。,全長20メートルを超える、巨大な翼の迫力を間近で。設計の工夫や、素材の軽さをぜひ体感してください。,毎年夏に開催される鳥人間コンテストに向けて、我々が設計・製作した人力飛行機の主翼(片翼)を展示します。部員が常駐しておりますので、設計や製作に関する質問も大歓迎です。,,dummy.jpg,,,,,,,,,,
|
| 44 |
+
dummy-43,ストリート・トライアル部,すとりーととらいあるぶ,自転車の障害物越えパフォーマンス,パフォーマンス,true,false,true,噴水前広場,自転車で障害物を飛び越えたり、バランスを取ったりするパフォーマンス。,まるで自転車が体の一部になったかのよう。重力を無視したかのような、驚異のバイシクル・テクニック!,ストリート・トライアルとは、障害物を乗り越えながらコースを走破する自転車競技です。高い段差を飛び乗ったり、細い一本橋を渡ったりと、アクロバティックな技の数々を披露します。,,dummy.jpg,,,,,,,,,,
|
| 45 |
+
dummy-44,羊毛フェルト手芸部,ようもうふぇるとしゅげいぶ,ふわふわ動物マスコット,物販,true,true,true,手芸室,羊毛フェルトで作った、動物のマスコットを販売します。,手のひらサイズの、ふわふわで可愛い動物たち。一針一針、心を込めて作りました。あなたのお気に入りの子を見つけてください。,羊毛を専用の針で刺し固めて、様々な形を作っていく手芸です。犬や猫、うさぎといった定番の動物から、少しマニアックな動物まで、たくさんのマスコットを用意してお待ちしています。,,dummy.jpg,,,,,,,,,,
|
| 46 |
+
dummy-45,ディベートクラブ,でぃべーとくらぶ,公開ディベート「AIは人間を超えるか」,パフォーマンス,false,true,false,大ホール,「AIは人間を超えるか」をテーマに、公開ディベートを行います。,肯定側と否定側に分かれ、白熱の言葉の応酬。論理と情熱がぶつかり合う、知的なエンターテイメントです。,即興ではなく、入念な準備に基づいた本格的なディベートです。どちらの主張がより説得力があるか、観客の皆様も一緒に考えながらお楽しみください。ディベート終了後には、観客投票も行います。,,dummy.jpg,,,,,,,,,,
|
| 47 |
+
dummy-46,万華鏡制作サークル,まんげきょうせいさくさーくる,オリジナル万華鏡作り,参加型,true,true,true,工作室,自分だけのオリジナル万華鏡を作ることができます。,キラキラ光るビーズやガラスを選んで、世界に一つだけの万華鏡を。覗き込むたびに変わる、美しい模様に癒やされます。,用意されたキットを使い、簡単な手順で万華鏡を作ることができます。中に入れるビーズやオブジェクトは、たくさんの種類の中から自由に選べます。自分用にはもちろん、プレゼントにも最適です。,参加費300円,dummy.jpg,,,,,,,,,,
|
| 48 |
+
dummy-47,環境音楽研究会,かんきょうおんがくけんきゅうかい,癒やしのアンビエント音楽ライブ,音楽,true,true,true,屋上庭園,心地よいアンビエント音楽の生演奏を行います。,賑やかなお祭りの合間に、少しだけ休憩しませんか。風の音や鳥の声と調和する、穏やかな音楽が流れる空間です。,シンセサイザーや自然音を使い、その場の環境に溶け込むような、心地よい音楽を演奏します。読書や休憩など、ご自由にお過ごしください。演奏は一日中、断続的に行っています。,,dummy.jpg,,,,,,,,,,
|
| 49 |
+
dummy-48,特殊印刷工房,とくしゅいんさつこうぼう,活版印刷でメッセージカード作り,参加型,true,true,false,印刷室,昔ながらの活版印刷機で、メッセージカードを印刷する体験です。,一文字ずつ拾って、インクを乗せて、プレスする。デジタルにはない、温かみのある凹凸が魅力です。,金属活字を組んで版を作り、小型の活版印刷機(テキン)を使って、ご自身でカードを印刷していただきます。独特のかすれや凹みなど、一枚一枚異なる風合いが楽しめます。,,dummy.jpg,,,,,,,,,,
|
| 50 |
+
dummy-49,世界の麺料理研究会,せかいのめんりょうりけんきゅうかい,世界のまぜそば祭り,飲食,true,true,true,屋外フードコート,台湾まぜそば、タイ風まぜそば、イタリアンまぜそばを販売します。,汁なし麺の魅力、ここに集結!各国の特色を生かした、オリジナルまぜそばをご賞味あれ。,麺料理の中でも、特に「まぜそば」に特化して研究しています。今回は、部員が開発した自信作の3種類のまぜそばを販売します。追い飯セットもご用意していますので、最後までタレを味わい尽くしてください!,,dummy.jpg,,,,,,,,,,
|
| 51 |
+
dummy-50,影絵劇団「シルエット」,かげえげきだんしるえっと,光と影が紡ぐ物語,パフォーマンス,false,true,true,視聴覚ホール,スクリーンに映し出される、美しい影絵劇。,光と影だけで、こんなにも豊かな世界が表現できる。音楽と共に繰り広げられる、ノスタルジックな物語をお楽しみください。,誰もが知っている昔話を、当劇団オリジナルの脚本と演出で上演します。役者の身体や、細かく作られた人形を使い、繊細な影の動きで物語を表現します。お子様から大人まで楽しめる内容です。,上演時間はウェブサイトをご確認ください,dummy.jpg,,,,,,,,,,
|
resources/user_dict.csv
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ax,4786,4786,5000,ax,名詞,固有名詞,一般,*,*,*,アックス,ax,*,*,*,*,*
|
| 2 |
+
b.a.s.s.,4786,4786,5000,b.a.s.s.,名詞,固有名詞,一般,*,*,*,バス,b.a.s.s.,*,*,*,*,*
|
| 3 |
+
belinda,4786,4786,5000,belinda,名詞,固有名詞,一般,*,*,*,ベリンダ,belinda,*,*,*,*,*
|
| 4 |
+
cdc,4786,4786,5000,cdc,名詞,固有名詞,一般,*,*,*,シーディーシー,cdc,*,*,*,*,*
|
| 5 |
+
change!!,4786,4786,5000,change!!,名詞,固有名詞,一般,*,*,*,チェンジ,change!!,*,*,*,*,*
|
| 6 |
+
chiよren北天魁,4786,4786,5000,chiよren北天魁,名詞,固有名詞,一般,*,*,*,チヨレンホクテンカイ,chiよren北天魁,*,*,*,*,*
|
| 7 |
+
cisg,4786,4786,5000,cisg,名詞,固有名詞,一般,*,*,*,シーアイエスジー,cisg,*,*,*,*,*
|
| 8 |
+
cmc,4786,4786,5000,cmc,名詞,固有名詞,一般,*,*,*,シーエムシー,cmc,*,*,*,*,*
|
| 9 |
+
c-raft lab,4786,4786,5000,c-raft lab,名詞,固有名詞,一般,*,*,*,クラフトラボ,c-raft lab,*,*,*,*,*
|
| 10 |
+
cuad,4786,4786,5000,cuad,名詞,固有名詞,一般,*,*,*,シーユーエーディー,cuad,*,*,*,*,*
|
| 11 |
+
c-vol,4786,4786,5000,c-vol,名詞,固有名詞,一般,*,*,*,シーボル,c-vol,*,*,*,*,*
|
| 12 |
+
english house student society,4786,4786,5000,english house student society,名詞,固有名詞,一般,*,*,*,イングリッシュハウススチューデントソサエティ,english house student society,*,*,*,*,*
|
| 13 |
+
fabric,4786,4786,5000,fabric,名詞,固有名詞,一般,*,*,*,ファブリック,fabric,*,*,*,*,*
|
| 14 |
+
fc riflie,4786,4786,5000,fc riflie,名詞,固有名詞,一般,*,*,*,エフシーレフリエ,fc riflie,*,*,*,*,*
|
| 15 |
+
fファン,4786,4786,5000,fファン,名詞,固有名詞,一般,*,*,*,エフファン,fファン,*,*,*,*,*
|
| 16 |
+
geo,4786,4786,5000,geo,名詞,固有名詞,一般,*,*,*,ジオ,geo,*,*,*,*,*
|
| 17 |
+
hoop☆star,4786,4786,5000,hoop☆star,名詞,固有名詞,一般,*,*,*,フープスター,hoop☆star,*,*,*,*,*
|
| 18 |
+
las,4786,4786,5000,las,名詞,固有名詞,一般,*,*,*,ラス,las,*,*,*,*,*
|
| 19 |
+
maybe good,4786,4786,5000,maybe good,名詞,固有名詞,一般,*,*,*,メイビーグッド,maybe good,*,*,*,*,*
|
| 20 |
+
道しるべ,4786,4786,5000,道しるべ,名詞,固有名詞,一般,*,*,*,ミチシルベ,道しるべ,*,*,*,*,*
|
| 21 |
+
orita family club,4786,4786,5000,orita family club,名詞,固有名詞,一般,*,*,*,オリタファミリークラブ,orita family club,*,*,*,*,*
|
| 22 |
+
zoo,4786,4786,5000,zoo,名詞,固有名詞,一般,*,*,*,ズー,zoo,*,*,*,*,*
|
| 23 |
+
ubojchor,4786,4786,5000,ubojchor,名詞,固有名詞,一般,*,*,*,ウボイコール,ubojchor,*,*,*,*,*
|
| 24 |
+
verde,4786,4786,5000,verde,名詞,固有名詞,一般,*,*,*,ベルデ,verde,*,*,*,*,*
|
| 25 |
+
wood stock,4786,4786,5000,wood stock,名詞,固有名詞,一般,*,*,*,ウッドストック,wood stock,*,*,*,*,*
|
| 26 |
+
アイドルマスター,4786,4786,5000,アイドルマスター,名詞,固有名詞,一般,*,*,*,アイドルマスター,アイドルマスター,*,*,*,*,*
|
| 27 |
+
t.o.n.e.,4786,4786,5000,t.o.n.e.,名詞,固有名詞,一般,*,*,*,トーン,t.o.n.e.,*,*,*,*,*
|
| 28 |
+
あまりぃず,4786,4786,5000,あまりぃず,名詞,固有名詞,一般,*,*,*,アマリーズ,あまりぃず,*,*,*,*,*
|
| 29 |
+
アンプラグド,4786,4786,5000,アンプラグド,名詞,固有名詞,一般,*,*,*,アンプラグド,アンプラグド,*,*,*,*,*
|
| 30 |
+
えれちば,4786,4786,5000,えれちば,名詞,固有名詞,一般,*,*,*,エレチバ,えれちば,*,*,*,*,*
|
| 31 |
+
オリエンテーリング,4786,4786,5000,オリエンテーリング,名詞,固有名詞,一般,*,*,*,オリエンテーリング,オリエンテーリング,*,*,*,*,*
|
| 32 |
+
olioli,4786,4786,5000,olioli,名詞,固有名詞,一般,*,*,*,オリオリ,olioli,*,*,*,*,*
|
| 33 |
+
p-ritts,4786,4786,5000,p-ritts,名詞,固有名詞,一般,*,*,*,プリッツ,p-ritts,*,*,*,*,*
|
| 34 |
+
環境iso,4786,4786,5000,環境iso,名詞,固有名詞,一般,*,*,*,カンキョウアイエスオー,環境iso,*,*,*,*,*
|
| 35 |
+
ミルフィーユ,4786,4786,5000,ミルフィーユ,名詞,固有名詞,一般,*,*,*,ミルフィーユ,ミルフィーユ,*,*,*,*,*
|
| 36 |
+
くたびれもうけ,4786,4786,5000,くたびれもうけ,名詞,固有名詞,一般,*,*,*,クタビレモウケ,くたびれもうけ,*,*,*,*,*
|
| 37 |
+
劇団個人主義,4786,4786,5000,劇団個人主義,名詞,固有名詞,一般,*,*,*,ゲキダンコジンシュギ,劇団個人主義,*,*,*,*,*
|
| 38 |
+
劇団nonny,4786,4786,5000,劇団nonny,名詞,固有名詞,一般,*,*,*,ゲキダンノニー,劇団nonny,*,*,*,*,*
|
| 39 |
+
志剣,4786,4786,5000,志剣,名詞,固有名詞,一般,*,*,*,シケン,志剣,*,*,*,*,*
|
| 40 |
+
国教,4786,4786,5000,国教,名詞,固有名詞,一般,*,*,*,コッキョウ,国教,*,*,*,*,*
|
| 41 |
+
:drop,4786,4786,5000,:drop,名詞,固有名詞,一般,*,*,*,ドロップ,:drop,*,*,*,*,*
|
| 42 |
+
小山ゼミ,4786,4786,5000,小山ゼミ,名詞,固有名詞,一般,*,*,*,コヤマゼミ,小山ゼミ,*,*,*,*,*
|
| 43 |
+
コンフローレ,4786,4786,5000,コンフローレ,名詞,固有名詞,一般,*,*,*,コンフローレ,コンフローレ,*,*,*,*,*
|
| 44 |
+
サイエンスプロムナード,4786,4786,5000,サイエンスプロムナード,名詞,固有名詞,一般,*,*,*,サイエンスプロムナード,サイエンスプロムナード,*,*,*,*,*
|
| 45 |
+
トニカ,4786,4786,5000,トニカ,名詞,固有名詞,一般,*,*,*,トニカ,トニカ,*,*,*,*,*
|
| 46 |
+
紫千会,4786,4786,5000,紫千会,名詞,固有名詞,一般,*,*,*,シセンカイ,紫千会,*,*,*,*,*
|
| 47 |
+
シネマウント・フィルム・パーティー,4786,4786,5000,シネマウント・フィルム・パーティー,名詞,固有名詞,一般,*,*,*,シネマウントフィルムパーティー,シネマウント・フィルム・パーティー,*,*,*,*,*
|
| 48 |
+
清水ゼミ,4786,4786,5000,清水ゼミ,名詞,固有名詞,一般,*,*,*,シミズゼミ,清水ゼミ,*,*,*,*,*
|
| 49 |
+
possum,4786,4786,5000,possum,名詞,固有名詞,一般,*,*,*,ポッサム,possum,*,*,*,*,*
|
| 50 |
+
父の樹会,4786,4786,5000,父の樹会,名詞,固有名詞,一般,*,*,*,チチノキカイ,父の樹会,*,*,*,*,*
|
| 51 |
+
小国,4786,4786,5000,小国,名詞,固有名詞,一般,*,*,*,ショウコク,小国,*,*,*,*,*
|
| 52 |
+
ぼくじる,4786,4786,5000,ぼくじる,名詞,固有名詞,一般,*,*,*,ボクジル,ぼくじる,*,*,*,*,*
|
| 53 |
+
ショコラ,4786,4786,5000,ショコラ,名詞,固有名詞,一般,*,*,*,ショコラ,ショコラ,*,*,*,*,*
|
| 54 |
+
dlc,4786,4786,5000,dlc,名詞,固有名詞,一般,*,*,*,ディーエルシー,dlc,*,*,*,*,*
|
| 55 |
+
yell,4786,4786,5000,yell,名詞,固有名詞,一般,*,*,*,エール,yell,*,*,*,*,*
|
| 56 |
+
belinda,4786,4786,5000,belinda,名詞,固有名詞,一般,*,*,*,ベリンダ,belinda,*,*,*,*,*
|
| 57 |
+
lips,4786,4786,5000,lips,名詞,固有名詞,一般,*,*,*,リップス,lips,*,*,*,*,*
|
| 58 |
+
あらぐさ,4786,4786,5000,あらぐさ,名詞,固有名詞,一般,*,*,*,アラグサ,あらぐさ,*,*,*,*,*
|
| 59 |
+
地球科学科,4786,4786,5000,地球科学科,名詞,固有名詞,一般,*,*,*,チキュウカガクカ,地球科学科,*,*,*,*,*
|
| 60 |
+
ちのっち,4786,4786,5000,ちのっち,名詞,固有名詞,一般,*,*,*,チノッチ,ちのっち,*,*,*,*,*
|
| 61 |
+
千葉大祭実行委員会,4786,4786,5000,千葉大祭実行委員会,名詞,固有名詞,一般,*,*,*,チバダイサイジッコウイインカイ,千葉大祭実行委員会,*,*,*,*,*
|
| 62 |
+
ちばねこ,4786,4786,5000,ちばねこ,名詞,固有名詞,一般,*,*,*,チバネコ,ちばねこ,*,*,*,*,*
|
| 63 |
+
ちばポケ,4786,4786,5000,ちばポケ,名詞,固有名詞,一般,*,*,*,チバポケ,ちばポケ,*,*,*,*,*
|
| 64 |
+
ccs,4786,4786,5000,ccs,名詞,固有名詞,一般,*,*,*,シーシーエス,ccs,*,*,*,*,*
|
| 65 |
+
rpurb,4786,4786,5000,rpurb,名詞,固有名詞,一般,*,*,*,ルパーブ,rpurb,*,*,*,*,*
|
| 66 |
+
breakers,4786,4786,5000,breakers,名詞,固有名詞,一般,*,*,*,ブレーカーズ,breakers,*,*,*,*,*
|
| 67 |
+
ショパンの会,4786,4786,5000,ショパンの会,名詞,固有名詞,一般,*,*,*,ショパンノカイ,ショパンの会,*,*,*,*,*
|
| 68 |
+
ひとりぼっちの音楽祭,4786,4786,5000,ひとりぼっちの音楽祭,名詞,固有名詞,一般,*,*,*,ヒトリボッチノオンガクサイ,ひとりぼっちの音楽祭,*,*,*,*,*
|
| 69 |
+
プロセカ,4786,4786,5000,プロセカ,名詞,固有名詞,一般,*,*,*,プロセカ,プロセカ,*,*,*,*,*
|
| 70 |
+
ミストラル,4786,4786,5000,ミストラル,名詞,固有名詞,一般,*,*,*,ミストラル,ミストラル,*,*,*,*,*
|
| 71 |
+
麦,4786,4786,5000,麦,名詞,固有名詞,一般,*,*,*,ムギ,麦,*,*,*,*,*
|
| 72 |
+
八木澤研究室,4786,4786,5000,八木澤研究室,名詞,固有名詞,一般,*,*,*,ヤギサワケンキュウシツ,八木澤研究室,*,*,*,*,*
|
| 73 |
+
葉法会,4786,4786,5000,葉法会,名詞,固有名詞,一般,*,*,*,ヨウホウカイ,葉法会,*,*,*,*,*
|
| 74 |
+
bollon,4786,4786,5000,bollon,名詞,固有名詞,一般,*,*,*,ボロン,bollon,*,*,*,*,*
|
| 75 |
+
ワンダーフォーゲル,4786,4786,5000,ワンダーフォーゲル,名詞,固有名詞,一般,*,*,*,ワンダーフォーゲル,ワンダーフォーゲル,*,*,*,*,*
|
schemas/__init__.py
ADDED
|
File without changes
|
schemas/projects.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Literal
|
| 2 |
+
from pydantic import BaseModel
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class Project(BaseModel):
|
| 6 |
+
"""団体から提供された企画情報を格納するデータクラス。
|
| 7 |
+
- `projectId`: 企画ID (unique)
|
| 8 |
+
- `organization`: 団体名
|
| 9 |
+
- `title`: 企画名
|
| 10 |
+
- `genre`: 企画ジャンル
|
| 11 |
+
- `day1`: 1日目の実施有無
|
| 12 |
+
- `day2`: 2日目の実施有無
|
| 13 |
+
- `day3`: 3日目の実施有無
|
| 14 |
+
- `place`: 実施場所
|
| 15 |
+
- `description`: 企画内容
|
| 16 |
+
- `prComment`: PRコメント
|
| 17 |
+
- `prCommentLong`: PRコメント(詳細ページ用)
|
| 18 |
+
- `note`: 注意事項
|
| 19 |
+
- `imageName`: PR画像ファイル名
|
| 20 |
+
- `prImageName1`: 任意PR画像ファイル名1
|
| 21 |
+
- `prImageName2`: 任意PR画像ファイル名2
|
| 22 |
+
- `prImageName3`: 任意PR画像ファイル名3
|
| 23 |
+
- `prImageName4`: 任意PR画像ファイル名4
|
| 24 |
+
- `prImageName5`: 任意PR画像ファイル名5
|
| 25 |
+
- `urlX`: URL (X)
|
| 26 |
+
- `urlInstagram`: URL (Instagram)
|
| 27 |
+
- `urlOfficialWebsite`: URL (公式サイト)
|
| 28 |
+
- `urlYoutube`: URL (YouTube)
|
| 29 |
+
- `urlOther`: URL (その他)
|
| 30 |
+
- `reading`: 団体名読み仮名
|
| 31 |
+
"""
|
| 32 |
+
|
| 33 |
+
projectId: str
|
| 34 |
+
organization: str
|
| 35 |
+
title: str
|
| 36 |
+
genre: Literal["パフォーマンス", "飲食", "物販", "展示", "参加型", "音楽", "その他"]
|
| 37 |
+
day1: bool
|
| 38 |
+
day2: bool
|
| 39 |
+
day3: bool
|
| 40 |
+
place: str
|
| 41 |
+
description: str
|
| 42 |
+
prComment: str
|
| 43 |
+
prCommentLong: Optional[str]
|
| 44 |
+
note: Optional[str]
|
| 45 |
+
imageName: str
|
| 46 |
+
prImageName1: Optional[str]
|
| 47 |
+
prImageName2: Optional[str]
|
| 48 |
+
prImageName3: Optional[str]
|
| 49 |
+
prImageName4: Optional[str]
|
| 50 |
+
prImageName5: Optional[str]
|
| 51 |
+
urlX: Optional[str]
|
| 52 |
+
urlInstagram: Optional[str]
|
| 53 |
+
urlOfficialWebsite: Optional[str]
|
| 54 |
+
urlYoutube: Optional[str]
|
| 55 |
+
urlOther: Optional[str]
|
| 56 |
+
reading: Optional[str]
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class ProjectSummary(BaseModel):
|
| 60 |
+
"""/api/projectsのレスポンス用データクラス。企画一覧に表示されるデータ。
|
| 61 |
+
- `projectId`: 企画ID (unique)
|
| 62 |
+
- `organization`: 団体名
|
| 63 |
+
- `title`: 企画名
|
| 64 |
+
- `genre`: 企画ジャンル
|
| 65 |
+
- `day1`: 1日目の実施有無
|
| 66 |
+
- `day2`: 2日目の実施有無
|
| 67 |
+
- `day3`: 3日目の実施有無
|
| 68 |
+
- `place`: 実施場所
|
| 69 |
+
- `description`: 企画内容
|
| 70 |
+
- `prComment`: PRコメント
|
| 71 |
+
- `note`: 注意事項
|
| 72 |
+
- `imageName`: PR画像ファイル名
|
| 73 |
+
"""
|
| 74 |
+
|
| 75 |
+
projectId: str
|
| 76 |
+
organization: str
|
| 77 |
+
title: str
|
| 78 |
+
genre: Literal["パフォーマンス", "飲食", "物販", "展示", "参加型", "音楽", "その他"]
|
| 79 |
+
day1: bool
|
| 80 |
+
day2: bool
|
| 81 |
+
day3: bool
|
| 82 |
+
place: str
|
| 83 |
+
description: str
|
| 84 |
+
prComment: str
|
| 85 |
+
note: Optional[str]
|
| 86 |
+
imageName: str
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class ProjectDetail(BaseModel):
|
| 90 |
+
"""/api/details?projectId=xxxのレスポンス用データクラス。企画詳細ページに表示されるデータ。
|
| 91 |
+
- `projectId`: 企画ID (unique)
|
| 92 |
+
- `organization`: 団体名
|
| 93 |
+
- `title`: 企画名
|
| 94 |
+
- `genre`: 企画ジャンル
|
| 95 |
+
- `day1`: 1日目の実施有無
|
| 96 |
+
- `day2`: 2日目の実施有無
|
| 97 |
+
- `day3`: 3日目の実施有無
|
| 98 |
+
- `place`: 実施場所
|
| 99 |
+
- `description`: 企画内容
|
| 100 |
+
- `prComment`: PRコメント
|
| 101 |
+
- `prCommentLong`: PRコメント(詳細ページ用)
|
| 102 |
+
- `note`: 注意事項
|
| 103 |
+
- `imageName`: PR画像ファイル名
|
| 104 |
+
- `prImageName1`: 任意PR画像ファイル名1
|
| 105 |
+
- `prImageName2`: 任意PR画像ファイル名2
|
| 106 |
+
- `prImageName3`: 任意PR画像ファイル名3
|
| 107 |
+
- `prImageName4`: 任意PR画像ファイル名4
|
| 108 |
+
- `prImageName5`: 任意PR画像ファイル名5
|
| 109 |
+
- `urlX`: URL (X)
|
| 110 |
+
- `urlInstagram`: URL (Instagram)
|
| 111 |
+
- `urlOfficialWebsite`: URL (公式サイト)
|
| 112 |
+
- `urlYoutube`: URL (YouTube)
|
| 113 |
+
- `urlOther`: URL (その他)
|
| 114 |
+
"""
|
| 115 |
+
|
| 116 |
+
projectId: str
|
| 117 |
+
organization: str
|
| 118 |
+
title: str
|
| 119 |
+
genre: Literal["パフォーマンス", "飲食", "物販", "展示", "参加型", "音楽", "その他"]
|
| 120 |
+
day1: bool
|
| 121 |
+
day2: bool
|
| 122 |
+
day3: bool
|
| 123 |
+
place: str
|
| 124 |
+
description: str
|
| 125 |
+
prComment: str
|
| 126 |
+
prCommentLong: Optional[str]
|
| 127 |
+
note: Optional[str]
|
| 128 |
+
imageName: str
|
| 129 |
+
prImageName1: Optional[str]
|
| 130 |
+
prImageName2: Optional[str]
|
| 131 |
+
prImageName3: Optional[str]
|
| 132 |
+
prImageName4: Optional[str]
|
| 133 |
+
prImageName5: Optional[str]
|
| 134 |
+
urlX: Optional[str]
|
| 135 |
+
urlInstagram: Optional[str]
|
| 136 |
+
urlOfficialWebsite: Optional[str]
|
| 137 |
+
urlYoutube: Optional[str]
|
| 138 |
+
urlOther: Optional[str]
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
class ProjectIds(BaseModel):
|
| 142 |
+
"""/api/searchのレスポンス用データクラス。
|
| 143 |
+
## example:
|
| 144 |
+
```
|
| 145 |
+
{
|
| 146 |
+
"projectIds": ["62000A", "62000B", "62000C"]
|
| 147 |
+
}
|
| 148 |
+
```
|
| 149 |
+
"""
|
| 150 |
+
|
| 151 |
+
projectIds: list[str]
|
schemas/tf_token.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class TfOfField(BaseModel):
|
| 5 |
+
len: int
|
| 6 |
+
tf: dict[str, int]
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class Fields(BaseModel):
|
| 10 |
+
title: TfOfField
|
| 11 |
+
organization: TfOfField
|
| 12 |
+
reading: TfOfField
|
| 13 |
+
description: TfOfField
|
| 14 |
+
prComment: TfOfField
|
| 15 |
+
prCommentLong: TfOfField
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class Project(BaseModel):
|
| 19 |
+
projectId: str
|
| 20 |
+
fields: Fields
|
| 21 |
+
tf: dict[str, int]
|
| 22 |
+
tokens: dict[str, int]
|
scripts/1_build_dict.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import subprocess
|
| 2 |
+
import json
|
| 3 |
+
import importlib.resources
|
| 4 |
+
import os
|
| 5 |
+
import sys
|
| 6 |
+
|
| 7 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
| 8 |
+
|
| 9 |
+
from utils.logger import setup_logger
|
| 10 |
+
from utils.json import get_file_path_from_config
|
| 11 |
+
|
| 12 |
+
# --- ロギングの設定 ---
|
| 13 |
+
log = setup_logger(__name__)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def main():
|
| 17 |
+
"""
|
| 18 |
+
SudachiPyのユーザー辞書をビルドするPythonスクリプト。
|
| 19 |
+
|
| 20 |
+
Reference:
|
| 21 |
+
https://github.com/WorksApplications/SudachiPy/blob/develop/docs/tutorial.md
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
log.info("SudachiPyのユーザー辞書をビルドします。")
|
| 25 |
+
|
| 26 |
+
# 入力用のユーザー辞書CSVファイルのパス
|
| 27 |
+
user_dict_path = get_file_path_from_config(
|
| 28 |
+
"sudachi.user_dict", "resources/user_dict.csv"
|
| 29 |
+
)
|
| 30 |
+
# 出力のユーザー辞書のパス
|
| 31 |
+
output_path = get_file_path_from_config(
|
| 32 |
+
"sudachi.user_dict_generated", "data/generated/user.dic"
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
# sudachi.jsonから辞書の種類を取得
|
| 36 |
+
sudachi_config_path = get_file_path_from_config(
|
| 37 |
+
"sudachi.sudachi_config", "scripts/sudachi.json"
|
| 38 |
+
)
|
| 39 |
+
with open(sudachi_config_path, "r") as f:
|
| 40 |
+
try:
|
| 41 |
+
sudachi_dict = json.load(f)
|
| 42 |
+
sudachi_dict_size = sudachi_dict.get("systemDict", "full")
|
| 43 |
+
sudachi_dict_name = f"sudachidict_{sudachi_dict_size}"
|
| 44 |
+
except FileNotFoundError:
|
| 45 |
+
log.error(f"sudachi.jsonが見つかりません: {sudachi_config_path}")
|
| 46 |
+
sys.exit(1)
|
| 47 |
+
|
| 48 |
+
# sudachidict_<size>パッケージ内のシステム辞書のパスを取得
|
| 49 |
+
try:
|
| 50 |
+
system_dict_ref = importlib.resources.files(sudachi_dict_name).joinpath(
|
| 51 |
+
"resources", "system.dic"
|
| 52 |
+
)
|
| 53 |
+
log.info(f"システム辞書のパス: {system_dict_ref}")
|
| 54 |
+
except ModuleNotFoundError:
|
| 55 |
+
log.error(f"{sudachi_dict_name}パッケージが見つかりません。")
|
| 56 |
+
log.error(f"`pip install {sudachi_dict_name}`を試してください。")
|
| 57 |
+
sys.exit(1)
|
| 58 |
+
except Exception as e:
|
| 59 |
+
log.error(f"予期しないエラーが発生しました: {e}")
|
| 60 |
+
sys.exit(1)
|
| 61 |
+
|
| 62 |
+
# 出力ディレクトリが存在しない場合は作成
|
| 63 |
+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
| 64 |
+
|
| 65 |
+
with importlib.resources.as_file(system_dict_ref) as system_dict_path:
|
| 66 |
+
command = [
|
| 67 |
+
"sudachipy",
|
| 68 |
+
"ubuild",
|
| 69 |
+
"-o",
|
| 70 |
+
output_path, # 出力ファイルのパス
|
| 71 |
+
"-s",
|
| 72 |
+
str(system_dict_path), # システム辞書のパス
|
| 73 |
+
user_dict_path,
|
| 74 |
+
]
|
| 75 |
+
|
| 76 |
+
log.info("辞書をビルドします...")
|
| 77 |
+
log.info(f"コマンド: {' '.join(command)}")
|
| 78 |
+
|
| 79 |
+
try:
|
| 80 |
+
# コマンドを実行
|
| 81 |
+
result = subprocess.run(
|
| 82 |
+
command,
|
| 83 |
+
check=True,
|
| 84 |
+
capture_output=True,
|
| 85 |
+
text=True,
|
| 86 |
+
encoding="utf-8",
|
| 87 |
+
)
|
| 88 |
+
log.info("ビルドが正常に完了しました。")
|
| 89 |
+
log.info(f"出力先: {output_path}")
|
| 90 |
+
|
| 91 |
+
if result.stdout:
|
| 92 |
+
log.info(f"--- SudachiPyからのメッセージ ---\n{result.stdout}")
|
| 93 |
+
|
| 94 |
+
except FileNotFoundError:
|
| 95 |
+
log.error("sudachipyコマンドが見つかりません。")
|
| 96 |
+
log.error("SudachiPyがインストールされていることを確認してください。")
|
| 97 |
+
sys.exit(1)
|
| 98 |
+
except subprocess.CalledProcessError as e:
|
| 99 |
+
log.error("辞書のビルド中にエラーが発生しました。")
|
| 100 |
+
log.error(f"エラーコード: {e.returncode}")
|
| 101 |
+
if e.stderr:
|
| 102 |
+
log.error(f"--- 標準エラー出力 ---\n{e.stderr}")
|
| 103 |
+
sys.exit(1)
|
| 104 |
+
except Exception as e:
|
| 105 |
+
log.error(f"予期しないエラーが発生しました: {e}")
|
| 106 |
+
sys.exit(1)
|
| 107 |
+
sys.exit(0)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
if __name__ == "__main__":
|
| 111 |
+
main()
|
scripts/2_create_projects_data.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import pandas as pd
|
| 4 |
+
|
| 5 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
| 6 |
+
|
| 7 |
+
from utils.logger import setup_logger
|
| 8 |
+
from utils.json import get_file_path_from_config
|
| 9 |
+
from utils.text_process import remove_invisible_chars
|
| 10 |
+
from utils.json import json_dumps
|
| 11 |
+
import schemas.projects as projects_schema
|
| 12 |
+
|
| 13 |
+
# --- ロギングの設定 ---
|
| 14 |
+
log = setup_logger(__name__)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def main():
|
| 18 |
+
"""
|
| 19 |
+
CSVファイルを読み込み、Projectオブジェクトのリストを生成し、
|
| 20 |
+
project.jsonに書き出すスクリプト
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
log.info("企画データの生成を開始します。")
|
| 24 |
+
original_csv_path = get_file_path_from_config(
|
| 25 |
+
"projects.original_csv", "resources/original_projects.csv"
|
| 26 |
+
)
|
| 27 |
+
output_json_path = get_file_path_from_config(
|
| 28 |
+
"projects.output_json", "data/generated/projects.json"
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
# CSVファイルの読み込み
|
| 32 |
+
try:
|
| 33 |
+
df = pd.read_csv(original_csv_path, encoding="utf-8")
|
| 34 |
+
except FileNotFoundError:
|
| 35 |
+
log.error(f"CSVファイルが見つかりません: {original_csv_path}")
|
| 36 |
+
sys.exit(1)
|
| 37 |
+
except pd.errors.ParserError as e:
|
| 38 |
+
log.error(f"CSVファイルの解析中にエラーが発生しました: {e}")
|
| 39 |
+
sys.exit(1)
|
| 40 |
+
except Exception as e:
|
| 41 |
+
log.error(f"予期しないエラーが発生しました: {e}")
|
| 42 |
+
sys.exit(1)
|
| 43 |
+
|
| 44 |
+
# テキスト列に対して不要な文字の除去を行う
|
| 45 |
+
for col in df.select_dtypes(include=["object"]).columns:
|
| 46 |
+
df[col] = df[col].apply(
|
| 47 |
+
lambda x: remove_invisible_chars(x) if isinstance(x, str) else x
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
# 一旦全列 object にしてから欠損値を None に変換
|
| 51 |
+
df = df.astype("object").where(pd.notnull(df), None)
|
| 52 |
+
|
| 53 |
+
# Projectオブジェクトに変換
|
| 54 |
+
# 列名がキーと一致している前提
|
| 55 |
+
projects: list[projects_schema.Project] = [
|
| 56 |
+
projects_schema.Project(**row) for row in df.to_dict(orient="records")
|
| 57 |
+
]
|
| 58 |
+
|
| 59 |
+
# JSON に書き出し
|
| 60 |
+
json_dumps([item.model_dump() for item in projects], output_json_path)
|
| 61 |
+
|
| 62 |
+
log.info(f"企画データの生成が完了しました: {output_json_path}")
|
| 63 |
+
sys.exit(0)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
if __name__ == "__main__":
|
| 67 |
+
main()
|
scripts/3_build_synonyms_from_sudachi.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import json
|
| 4 |
+
from collections import defaultdict
|
| 5 |
+
from tqdm import tqdm
|
| 6 |
+
import pandas as pd
|
| 7 |
+
|
| 8 |
+
from sudachipy import dictionary, tokenizer
|
| 9 |
+
|
| 10 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
| 11 |
+
|
| 12 |
+
from utils.logger import setup_logger
|
| 13 |
+
from utils.json import get_file_path_from_config, field_getter, json_dumps
|
| 14 |
+
from utils.io import LineIteratorIO, comment_filtered_lines
|
| 15 |
+
import schemas.projects as projects_schema
|
| 16 |
+
|
| 17 |
+
# --- ロギングの設定 ---
|
| 18 |
+
log = setup_logger(__name__)
|
| 19 |
+
|
| 20 |
+
# --- 設定 ---
|
| 21 |
+
input_file = get_file_path_from_config("projects.projects_json")
|
| 22 |
+
output_file = get_file_path_from_config("sudachi.synonyms_cache")
|
| 23 |
+
sudachi_config_file = get_file_path_from_config("sudachi.sudachi_config")
|
| 24 |
+
|
| 25 |
+
try:
|
| 26 |
+
search_model = field_getter("config/search_model.json")
|
| 27 |
+
target_pos_l1 = search_model("target_pos_l1")
|
| 28 |
+
target_fields = search_model("target_fields")
|
| 29 |
+
ban_list = search_model("synonyms.banlist")
|
| 30 |
+
stopwords_file = get_file_path_from_config("sudachi.stopwords")
|
| 31 |
+
with open(stopwords_file, encoding="utf-8") as f:
|
| 32 |
+
stopwords = set(json.load(f))
|
| 33 |
+
synonyms_file = get_file_path_from_config("sudachi.synonyms")
|
| 34 |
+
except FileNotFoundError as e:
|
| 35 |
+
log.error(f"設定ファイルが見つかりません: {e}")
|
| 36 |
+
sys.exit(1)
|
| 37 |
+
except KeyError as e:
|
| 38 |
+
log.error(f"設定ファイルに必要なキーが見つかりません: {e}")
|
| 39 |
+
sys.exit(1)
|
| 40 |
+
except Exception as e:
|
| 41 |
+
log.error(f"予期しないエラーが発生しました: {e}")
|
| 42 |
+
sys.exit(1)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
# --- SudachiPy初期化 ---
|
| 46 |
+
tokenizer_obj = dictionary.Dictionary(config_path=sudachi_config_file).create()
|
| 47 |
+
mode = tokenizer.Tokenizer.SplitMode.A
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def tokenize(text: str) -> list[str]:
|
| 51 |
+
"""テキストをトークン化し、ストップワードを除去する。"""
|
| 52 |
+
if not text:
|
| 53 |
+
return []
|
| 54 |
+
tokens = []
|
| 55 |
+
for m in tokenizer_obj.tokenize(text, mode):
|
| 56 |
+
if m.normalized_form().strip() == "":
|
| 57 |
+
continue
|
| 58 |
+
pos = m.part_of_speech()
|
| 59 |
+
if pos[0] not in target_pos_l1:
|
| 60 |
+
continue
|
| 61 |
+
base = m.normalized_form().lower()
|
| 62 |
+
if base in stopwords or base in ban_list:
|
| 63 |
+
continue
|
| 64 |
+
tokens.append(base)
|
| 65 |
+
return tokens
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def get_corpus_vocab(projects: list[projects_schema.Project]) -> set[str]:
|
| 69 |
+
"""プロジェクト全体から語彙セットを構築する"""
|
| 70 |
+
vocab = set()
|
| 71 |
+
log.info("語彙セットを構築中...")
|
| 72 |
+
for project in tqdm(projects):
|
| 73 |
+
for field in target_fields:
|
| 74 |
+
text = getattr(project, field, "")
|
| 75 |
+
if not text:
|
| 76 |
+
continue
|
| 77 |
+
tokens = tokenize(str(text))
|
| 78 |
+
vocab.update(tokens)
|
| 79 |
+
return vocab
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def find_and_parse_synonyms_text() -> dict[str, list[str]]:
|
| 83 |
+
"""同義語辞書をpandasを使ってパースする"""
|
| 84 |
+
log.info("同義語辞書をパース中...")
|
| 85 |
+
log.info(f"同義語辞書のパス: {synonyms_file}")
|
| 86 |
+
|
| 87 |
+
try:
|
| 88 |
+
# ヘッダーなしのCSVとして読み込む
|
| 89 |
+
# 必要なのはグループID(0)と見出し語(8)
|
| 90 |
+
GROUP_ID_COL_IDX = 0
|
| 91 |
+
TERM_COL_IDX = 8
|
| 92 |
+
|
| 93 |
+
stream = LineIteratorIO(comment_filtered_lines(synonyms_file))
|
| 94 |
+
df = pd.read_csv(
|
| 95 |
+
stream,
|
| 96 |
+
header=None,
|
| 97 |
+
usecols=[GROUP_ID_COL_IDX, TERM_COL_IDX],
|
| 98 |
+
names=["group_id", "term"],
|
| 99 |
+
on_bad_lines="skip",
|
| 100 |
+
encoding="utf-8",
|
| 101 |
+
engine="c",
|
| 102 |
+
sep=",",
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
except Exception as e:
|
| 106 |
+
log.error(f"同義語辞書のパース中にエラーが発生しました: {e}")
|
| 107 |
+
raise e
|
| 108 |
+
|
| 109 |
+
synonym_groups = defaultdict(list)
|
| 110 |
+
# group_idでグループ化し、各グループ内の単語リストを作成
|
| 111 |
+
for group_id, group_df in df.groupby("group_id"):
|
| 112 |
+
terms = group_df["term"].tolist()
|
| 113 |
+
if len(terms) < 2:
|
| 114 |
+
continue
|
| 115 |
+
for i, term in enumerate(terms):
|
| 116 |
+
synonyms = [t for j, t in enumerate(terms) if i != j]
|
| 117 |
+
synonym_groups[term].extend(synonyms)
|
| 118 |
+
|
| 119 |
+
return synonym_groups
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def main():
|
| 123 |
+
log.info("同義語キャッシュの生成を開始します")
|
| 124 |
+
|
| 125 |
+
with open(input_file, encoding="utf-8") as f:
|
| 126 |
+
try:
|
| 127 |
+
project_dicts = json.load(f)
|
| 128 |
+
projects = [projects_schema.Project(**item) for item in project_dicts]
|
| 129 |
+
except json.JSONDecodeError as e:
|
| 130 |
+
log.error(f"JSONデコードエラー: {e}")
|
| 131 |
+
sys.exit(1)
|
| 132 |
+
except FileNotFoundError:
|
| 133 |
+
log.error(f"ファイルが見つかりません: {input_file}")
|
| 134 |
+
sys.exit(1)
|
| 135 |
+
except Exception as e:
|
| 136 |
+
log.error(f"予期しないエラーが発生しました: {e}")
|
| 137 |
+
sys.exit(1)
|
| 138 |
+
|
| 139 |
+
# 1. コーパスの語彙を構築
|
| 140 |
+
vocab = get_corpus_vocab(projects)
|
| 141 |
+
log.info(f"コーパスの語彙数: {len(vocab)}")
|
| 142 |
+
|
| 143 |
+
# 2. Sudachiの同義語辞書をパース
|
| 144 |
+
try:
|
| 145 |
+
synonym_groups = find_and_parse_synonyms_text()
|
| 146 |
+
except Exception as e:
|
| 147 |
+
log.error(e)
|
| 148 |
+
sys.exit(1)
|
| 149 |
+
|
| 150 |
+
# 3. コーパスに存在する単語に絞ってキャッシュを作成
|
| 151 |
+
synonym_cache = {}
|
| 152 |
+
log.info("同義語キャッシュを構築中...")
|
| 153 |
+
for term in tqdm(vocab):
|
| 154 |
+
if term in ban_list:
|
| 155 |
+
continue
|
| 156 |
+
if term in synonym_groups:
|
| 157 |
+
valid_synonyms = [
|
| 158 |
+
s for s in synonym_groups[term] if s in vocab and s not in ban_list
|
| 159 |
+
]
|
| 160 |
+
if valid_synonyms:
|
| 161 |
+
synonym_cache[term] = list(set(valid_synonyms)) # 重複除去
|
| 162 |
+
|
| 163 |
+
# 4. キャッシュを保存
|
| 164 |
+
json_dumps(synonym_cache, output_file)
|
| 165 |
+
|
| 166 |
+
log.info(f"同義語キャッシュの生成が完了しました: {output_file}")
|
| 167 |
+
log.info(f"キャッシュされた単語数: {len(synonym_cache)}")
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
if __name__ == "__main__":
|
| 171 |
+
main()
|
scripts/4_prepare_bm25f_meta.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import json
|
| 4 |
+
from collections import defaultdict
|
| 5 |
+
from typing import Dict, List
|
| 6 |
+
|
| 7 |
+
from sudachipy import dictionary, tokenizer
|
| 8 |
+
|
| 9 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
| 10 |
+
|
| 11 |
+
from utils.logger import setup_logger
|
| 12 |
+
from utils.json import get_file_path_from_config, field_getter, json_dumps
|
| 13 |
+
|
| 14 |
+
log = setup_logger(__name__)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def load_configs():
|
| 18 |
+
search = field_getter("config/search_model.json")
|
| 19 |
+
files = field_getter("config/files.json")
|
| 20 |
+
|
| 21 |
+
target_pos_l1: List[str] = search("target_pos_l1")
|
| 22 |
+
target_fields: List[str] = search("target_fields")
|
| 23 |
+
ban_list: List[str] = search("synonyms.banlist")
|
| 24 |
+
|
| 25 |
+
stopwords_path = files("sudachi.stopwords")
|
| 26 |
+
with open(stopwords_path, encoding="utf-8") as f:
|
| 27 |
+
stopwords = set(json.load(f))
|
| 28 |
+
|
| 29 |
+
sudachi_config_path = files("sudachi.sudachi_config")
|
| 30 |
+
|
| 31 |
+
return target_pos_l1, target_fields, set(ban_list), stopwords, sudachi_config_path
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def build_tokenizer(sudachi_config_path: str):
|
| 35 |
+
tok = dictionary.Dictionary(config_path=sudachi_config_path).create()
|
| 36 |
+
mode = tokenizer.Tokenizer.SplitMode.A
|
| 37 |
+
return tok, mode
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def tokenize(text: str, tok, mode, target_pos_l1: List[str], ban_list: set, stopwords: set) -> List[str]:
|
| 41 |
+
if not text:
|
| 42 |
+
return []
|
| 43 |
+
out: List[str] = []
|
| 44 |
+
for m in tok.tokenize(text, mode):
|
| 45 |
+
base = m.normalized_form().lower().strip()
|
| 46 |
+
if not base:
|
| 47 |
+
continue
|
| 48 |
+
pos = m.part_of_speech()
|
| 49 |
+
if pos[0] not in target_pos_l1:
|
| 50 |
+
continue
|
| 51 |
+
if base in stopwords or base in ban_list:
|
| 52 |
+
continue
|
| 53 |
+
out.append(base)
|
| 54 |
+
return out
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def main():
|
| 58 |
+
log.info("BM25Fメタデータ(bm25_meta.json)を生成します")
|
| 59 |
+
try:
|
| 60 |
+
target_pos_l1, target_fields, ban_list, stopwords, sudachi_config_path = load_configs()
|
| 61 |
+
except Exception as e:
|
| 62 |
+
log.error(f"設定の読み込みに失敗しました: {e}")
|
| 63 |
+
sys.exit(1)
|
| 64 |
+
|
| 65 |
+
projects_path = get_file_path_from_config("projects.projects_json", "data/generated/projects.json")
|
| 66 |
+
output_path = get_file_path_from_config("bm25.bm25_meta", "data/generated/bm25_meta.json")
|
| 67 |
+
|
| 68 |
+
try:
|
| 69 |
+
with open(projects_path, encoding="utf-8") as f:
|
| 70 |
+
projects = json.load(f)
|
| 71 |
+
except Exception as e:
|
| 72 |
+
log.error(f"projects.jsonの読み込みに失敗しました: {e}")
|
| 73 |
+
sys.exit(1)
|
| 74 |
+
|
| 75 |
+
tok, mode = build_tokenizer(sudachi_config_path)
|
| 76 |
+
|
| 77 |
+
N = len(projects)
|
| 78 |
+
df: Dict[str, int] = defaultdict(int)
|
| 79 |
+
field_token_lens_sum: Dict[str, int] = {f: 0 for f in target_fields}
|
| 80 |
+
|
| 81 |
+
log.info(f"ドキュメント数: {N}")
|
| 82 |
+
|
| 83 |
+
for p in projects:
|
| 84 |
+
seen_in_doc = set()
|
| 85 |
+
for field in target_fields:
|
| 86 |
+
text = p.get(field) or ""
|
| 87 |
+
toks = tokenize(str(text), tok, mode, target_pos_l1, ban_list, stopwords)
|
| 88 |
+
field_token_lens_sum[field] += len(toks)
|
| 89 |
+
for t in set(toks):
|
| 90 |
+
if t not in seen_in_doc:
|
| 91 |
+
df[t] += 1
|
| 92 |
+
seen_in_doc.add(t)
|
| 93 |
+
|
| 94 |
+
# IDF 計算(BM25で一般的な +0.5 smoothing と +1 オフセット)
|
| 95 |
+
idf: Dict[str, float] = {}
|
| 96 |
+
for term, dfi in df.items():
|
| 97 |
+
idf_val = max(0.0, ( ( (N - dfi + 0.5) / (dfi + 0.5) ) ))
|
| 98 |
+
# 数値安定化のためlog1p
|
| 99 |
+
import math
|
| 100 |
+
|
| 101 |
+
idf[term] = math.log1p(idf_val)
|
| 102 |
+
|
| 103 |
+
avg_len = {field: (field_token_lens_sum[field] / N if N > 0 else 0.0) for field in target_fields}
|
| 104 |
+
|
| 105 |
+
meta = {
|
| 106 |
+
"N": N,
|
| 107 |
+
"avg_len": avg_len,
|
| 108 |
+
"idf": idf,
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
| 112 |
+
json_dumps(meta, output_path)
|
| 113 |
+
log.info(f"bm25_meta.json を出力しました: {output_path}")
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
if __name__ == "__main__":
|
| 117 |
+
main()
|
scripts/5_prepare_tf_token.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import json
|
| 4 |
+
from collections import Counter
|
| 5 |
+
from typing import Dict, List
|
| 6 |
+
|
| 7 |
+
from sudachipy import dictionary, tokenizer
|
| 8 |
+
|
| 9 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
| 10 |
+
|
| 11 |
+
from utils.logger import setup_logger
|
| 12 |
+
from utils.json import get_file_path_from_config, field_getter, json_dumps
|
| 13 |
+
import schemas.tf_token as tf_schema
|
| 14 |
+
|
| 15 |
+
log = setup_logger(__name__)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def load_configs():
|
| 19 |
+
search = field_getter("config/search_model.json")
|
| 20 |
+
files = field_getter("config/files.json")
|
| 21 |
+
|
| 22 |
+
target_pos_l1: List[str] = search("target_pos_l1")
|
| 23 |
+
target_fields: List[str] = search("target_fields")
|
| 24 |
+
ban_list: List[str] = search("synonyms.banlist")
|
| 25 |
+
|
| 26 |
+
stopwords_path = files("sudachi.stopwords")
|
| 27 |
+
with open(stopwords_path, encoding="utf-8") as f:
|
| 28 |
+
stopwords = set(json.load(f))
|
| 29 |
+
|
| 30 |
+
sudachi_config_path = files("sudachi.sudachi_config")
|
| 31 |
+
|
| 32 |
+
return target_pos_l1, target_fields, set(ban_list), stopwords, sudachi_config_path
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def build_tokenizer(sudachi_config_path: str):
|
| 36 |
+
tok = dictionary.Dictionary(config_path=sudachi_config_path).create()
|
| 37 |
+
mode = tokenizer.Tokenizer.SplitMode.A
|
| 38 |
+
return tok, mode
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def tokenize(text: str, tok, mode, target_pos_l1: List[str], ban_list: set, stopwords: set) -> List[str]:
|
| 42 |
+
if not text:
|
| 43 |
+
return []
|
| 44 |
+
out: List[str] = []
|
| 45 |
+
for m in tok.tokenize(text, mode):
|
| 46 |
+
base = m.normalized_form().lower().strip()
|
| 47 |
+
if not base:
|
| 48 |
+
continue
|
| 49 |
+
pos = m.part_of_speech()
|
| 50 |
+
if pos[0] not in target_pos_l1:
|
| 51 |
+
continue
|
| 52 |
+
if base in stopwords or base in ban_list:
|
| 53 |
+
continue
|
| 54 |
+
out.append(base)
|
| 55 |
+
return out
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def main():
|
| 59 |
+
log.info("フィールド別TF/トークン(tf_token.json)を生成します")
|
| 60 |
+
try:
|
| 61 |
+
target_pos_l1, target_fields, ban_list, stopwords, sudachi_config_path = load_configs()
|
| 62 |
+
except Exception as e:
|
| 63 |
+
log.error(f"設定の読み込みに失敗しました: {e}")
|
| 64 |
+
sys.exit(1)
|
| 65 |
+
|
| 66 |
+
projects_path = get_file_path_from_config("projects.projects_json", "data/generated/projects.json")
|
| 67 |
+
output_path = get_file_path_from_config("bm25.tf_token", "data/generated/tf_token.json")
|
| 68 |
+
|
| 69 |
+
try:
|
| 70 |
+
with open(projects_path, encoding="utf-8") as f:
|
| 71 |
+
projects = json.load(f)
|
| 72 |
+
except Exception as e:
|
| 73 |
+
log.error(f"projects.jsonの読み込みに失敗しました: {e}")
|
| 74 |
+
sys.exit(1)
|
| 75 |
+
|
| 76 |
+
tok, mode = build_tokenizer(sudachi_config_path)
|
| 77 |
+
|
| 78 |
+
results: List[tf_schema.Project] = []
|
| 79 |
+
|
| 80 |
+
for p in projects:
|
| 81 |
+
project_id = p.get("projectId")
|
| 82 |
+
|
| 83 |
+
# 各フィールドのトークン化とTF
|
| 84 |
+
field_objs: Dict[str, tf_schema.TfOfField] = {}
|
| 85 |
+
doc_tf_counter: Counter = Counter()
|
| 86 |
+
doc_token_set: set = set()
|
| 87 |
+
|
| 88 |
+
for field in target_fields:
|
| 89 |
+
text = p.get(field) or ""
|
| 90 |
+
toks = tokenize(str(text), tok, mode, target_pos_l1, ban_list, stopwords)
|
| 91 |
+
tf = Counter(toks)
|
| 92 |
+
field_objs[field] = tf_schema.TfOfField(len=len(toks), tf=dict(tf))
|
| 93 |
+
doc_tf_counter.update(tf)
|
| 94 |
+
doc_token_set.update(tf.keys())
|
| 95 |
+
|
| 96 |
+
# スキーマ Fields へ詰める(未定義フィールドは長さ0/空dictで埋める)
|
| 97 |
+
def get_field(name: str) -> tf_schema.TfOfField:
|
| 98 |
+
return field_objs.get(name, tf_schema.TfOfField(len=0, tf={}))
|
| 99 |
+
|
| 100 |
+
fields_obj = tf_schema.Fields(
|
| 101 |
+
title=get_field("title"),
|
| 102 |
+
organization=get_field("organization"),
|
| 103 |
+
reading=get_field("reading"),
|
| 104 |
+
description=get_field("description"),
|
| 105 |
+
prComment=get_field("prComment"),
|
| 106 |
+
prCommentLong=get_field("prCommentLong"),
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
project_entry = tf_schema.Project(
|
| 110 |
+
projectId=project_id,
|
| 111 |
+
fields=fields_obj,
|
| 112 |
+
tf=dict(doc_tf_counter),
|
| 113 |
+
# tokens はユニーク語彙の存在フラグ(1)とする
|
| 114 |
+
tokens={t: 1 for t in sorted(doc_token_set)},
|
| 115 |
+
)
|
| 116 |
+
results.append(project_entry)
|
| 117 |
+
|
| 118 |
+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
| 119 |
+
json_dumps([r.model_dump() for r in results], output_path)
|
| 120 |
+
log.info(f"tf_token.json を出力しました: {output_path}")
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
if __name__ == "__main__":
|
| 124 |
+
main()
|
scripts/6_build_word_embeddings.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import json
|
| 4 |
+
from typing import Dict, List, Tuple
|
| 5 |
+
|
| 6 |
+
import numpy as np
|
| 7 |
+
|
| 8 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
| 9 |
+
|
| 10 |
+
from utils.logger import setup_logger
|
| 11 |
+
from utils.json import field_getter, json_dumps
|
| 12 |
+
|
| 13 |
+
log = setup_logger(__name__)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def load_configs():
|
| 17 |
+
files = field_getter("config/files.json")
|
| 18 |
+
|
| 19 |
+
paths = {
|
| 20 |
+
"tf_token": files("bm25.tf_token"),
|
| 21 |
+
"fasttext_vec": files("embeddings.fasttext_vec"),
|
| 22 |
+
"word_vocab": files("embeddings.word_vocab"),
|
| 23 |
+
"word_vectors": files("embeddings.word_vectors"),
|
| 24 |
+
}
|
| 25 |
+
return paths
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def read_vocab_from_tf_token(tf_token_path: str) -> Tuple[List[dict], set[str]]:
|
| 29 |
+
with open(tf_token_path, encoding="utf-8") as f:
|
| 30 |
+
docs = json.load(f)
|
| 31 |
+
vocab: set[str] = set()
|
| 32 |
+
for d in docs:
|
| 33 |
+
# doc全体tfから語彙を得る
|
| 34 |
+
for t in (d.get("tf") or {}).keys():
|
| 35 |
+
vocab.add(t)
|
| 36 |
+
return docs, vocab
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def stream_fasttext_vec(
|
| 40 |
+
vec_path: str, vocab: set[str]
|
| 41 |
+
) -> Tuple[Dict[str, int], np.ndarray]:
|
| 42 |
+
"""fastText .vec から、必要語彙のみ抽出してベクトル行列を返す。"""
|
| 43 |
+
token_to_idx: Dict[str, int] = {}
|
| 44 |
+
vectors: List[np.ndarray] = []
|
| 45 |
+
|
| 46 |
+
dim = None
|
| 47 |
+
kept = 0
|
| 48 |
+
with open(vec_path, encoding="utf-8", errors="ignore") as f:
|
| 49 |
+
header = f.readline()
|
| 50 |
+
# ヘッダ行は "<count> <dim>" のことが多い
|
| 51 |
+
try:
|
| 52 |
+
parts = header.strip().split()
|
| 53 |
+
if len(parts) >= 2 and parts[0].isdigit():
|
| 54 |
+
dim = int(parts[1])
|
| 55 |
+
except Exception:
|
| 56 |
+
pass
|
| 57 |
+
|
| 58 |
+
for line in f:
|
| 59 |
+
sp = line.rstrip().split(" ")
|
| 60 |
+
if len(sp) < 2:
|
| 61 |
+
continue
|
| 62 |
+
token = sp[0]
|
| 63 |
+
if token not in vocab:
|
| 64 |
+
continue
|
| 65 |
+
vec_vals = sp[1:]
|
| 66 |
+
if dim is None:
|
| 67 |
+
dim = len(vec_vals)
|
| 68 |
+
if len(vec_vals) != dim:
|
| 69 |
+
continue
|
| 70 |
+
try:
|
| 71 |
+
v = np.asarray([float(x) for x in vec_vals], dtype=np.float32)
|
| 72 |
+
except ValueError:
|
| 73 |
+
continue
|
| 74 |
+
# L2正規化
|
| 75 |
+
norm = np.linalg.norm(v)
|
| 76 |
+
if norm > 0:
|
| 77 |
+
v = v / norm
|
| 78 |
+
token_to_idx[token] = kept
|
| 79 |
+
vectors.append(v)
|
| 80 |
+
kept += 1
|
| 81 |
+
|
| 82 |
+
if dim is None:
|
| 83 |
+
raise RuntimeError(".vec の次元を特定できませんでした")
|
| 84 |
+
|
| 85 |
+
if not vectors:
|
| 86 |
+
log.warning("語彙に一致するベクトルが見つかりませんでした")
|
| 87 |
+
arr = np.zeros((0, dim), dtype=np.float32)
|
| 88 |
+
return token_to_idx, arr
|
| 89 |
+
|
| 90 |
+
arr = np.vstack(vectors).astype(np.float32)
|
| 91 |
+
return token_to_idx, arr
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
# top-k モードでは文書ベクトルは不要
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def main():
|
| 98 |
+
log.info(
|
| 99 |
+
"語彙/文書ベクトル(word_vocab.json, word_vectors.npz, doc_vectors.npy)を生成します"
|
| 100 |
+
)
|
| 101 |
+
try:
|
| 102 |
+
paths = load_configs()
|
| 103 |
+
except Exception as e:
|
| 104 |
+
log.error(f"設定の読み込みに失敗しました: {e}")
|
| 105 |
+
sys.exit(1)
|
| 106 |
+
|
| 107 |
+
tf_token_path = paths["tf_token"]
|
| 108 |
+
fasttext_vec_path = paths["fasttext_vec"]
|
| 109 |
+
|
| 110 |
+
if not os.path.exists(fasttext_vec_path):
|
| 111 |
+
log.warning(f".vec が見つかりません: {fasttext_vec_path}")
|
| 112 |
+
log.warning("Step 6 をスキップします")
|
| 113 |
+
sys.exit(0)
|
| 114 |
+
|
| 115 |
+
try:
|
| 116 |
+
docs, vocab = read_vocab_from_tf_token(tf_token_path)
|
| 117 |
+
except Exception as e:
|
| 118 |
+
log.error(f"tf_token.jsonの読み込みに失敗しました: {e}")
|
| 119 |
+
sys.exit(1)
|
| 120 |
+
|
| 121 |
+
log.info(f"コーパス語彙数: {len(vocab)}")
|
| 122 |
+
token_to_idx, word_vecs = stream_fasttext_vec(fasttext_vec_path, vocab)
|
| 123 |
+
log.info(f"抽出済み語彙ベクトル数: {word_vecs.shape[0]}")
|
| 124 |
+
|
| 125 |
+
# 語彙インデックスの安定化(token_to_idxは追加順次第なのでソート)
|
| 126 |
+
sorted_tokens = sorted(token_to_idx.keys())
|
| 127 |
+
remap = {t: i for i, t in enumerate(sorted_tokens)}
|
| 128 |
+
remapped_vecs = np.zeros_like(word_vecs)
|
| 129 |
+
for t, old_i in token_to_idx.items():
|
| 130 |
+
new_i = remap[t]
|
| 131 |
+
remapped_vecs[new_i] = word_vecs[old_i]
|
| 132 |
+
token_to_idx = remap
|
| 133 |
+
word_vecs = remapped_vecs
|
| 134 |
+
|
| 135 |
+
# 出力
|
| 136 |
+
os.makedirs(os.path.dirname(paths["word_vocab"]), exist_ok=True)
|
| 137 |
+
json_dumps(token_to_idx, paths["word_vocab"]) # 語→index
|
| 138 |
+
# 圧縮npz
|
| 139 |
+
np.savez_compressed(paths["word_vectors"], vectors=word_vecs)
|
| 140 |
+
|
| 141 |
+
log.info(f"word_vocab.json: {paths['word_vocab']}")
|
| 142 |
+
log.info(f"word_vectors.npz: {paths['word_vectors']}")
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
if __name__ == "__main__":
|
| 146 |
+
main()
|
scripts/build_all.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
import subprocess
|
| 4 |
+
|
| 5 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
| 6 |
+
|
| 7 |
+
from utils.logger import setup_logger
|
| 8 |
+
from utils.json import field_getter
|
| 9 |
+
|
| 10 |
+
log = setup_logger(__name__)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def run_step(cmd: list[str], allow_fail: bool = False):
|
| 14 |
+
log.info("==> %s", " ".join(cmd))
|
| 15 |
+
try:
|
| 16 |
+
subprocess.run(cmd, check=True)
|
| 17 |
+
except subprocess.CalledProcessError as e:
|
| 18 |
+
if allow_fail:
|
| 19 |
+
log.warning(f"step失敗を無視します: {cmd} (code={e.returncode})")
|
| 20 |
+
else:
|
| 21 |
+
raise
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def main():
|
| 25 |
+
files = field_getter("config/files.json")
|
| 26 |
+
|
| 27 |
+
# Step 1: Sudachi user dict (optional but recommended before tokenization)
|
| 28 |
+
run_step([sys.executable, "scripts/1_build_dict.py"], allow_fail=True)
|
| 29 |
+
|
| 30 |
+
# Step 2: projects.json
|
| 31 |
+
run_step([sys.executable, "scripts/2_create_projects_data.py"])
|
| 32 |
+
|
| 33 |
+
# Step 3: synonyms cache
|
| 34 |
+
run_step([sys.executable, "scripts/3_build_synonyms_from_sudachi.py"]) # idempotent
|
| 35 |
+
|
| 36 |
+
# Step 4: bm25 meta
|
| 37 |
+
run_step([sys.executable, "scripts/4_prepare_bm25f_meta.py"]) # needs projects.json
|
| 38 |
+
|
| 39 |
+
# Step 5: tf_token
|
| 40 |
+
run_step([sys.executable, "scripts/5_prepare_tf_token.py"]) # needs projects.json
|
| 41 |
+
|
| 42 |
+
# Step 6: embeddings (.vec がある場合のみ)
|
| 43 |
+
vec_path = files("embeddings.fasttext_vec")
|
| 44 |
+
if os.path.exists(vec_path):
|
| 45 |
+
run_step([sys.executable, "scripts/6_build_word_embeddings.py"]) # needs tf_token, bm25_meta
|
| 46 |
+
else:
|
| 47 |
+
log.info(".vec が見つからないため Step 6 をスキップします: %s", vec_path)
|
| 48 |
+
|
| 49 |
+
log.info("全ステップ完了")
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
if __name__ == "__main__":
|
| 53 |
+
main()
|
scripts/sudachi.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"systemDict": "full",
|
| 3 |
+
"userDict": [
|
| 4 |
+
"data/generated/user.dic"
|
| 5 |
+
]
|
| 6 |
+
}
|
utils/__init__.py
ADDED
|
File without changes
|
utils/io.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import io
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def comment_filtered_lines(path: str):
|
| 5 |
+
with open(path, encoding="utf-8") as f:
|
| 6 |
+
for line in f:
|
| 7 |
+
if not line.lstrip().startswith("#"):
|
| 8 |
+
yield line
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class LineIteratorIO(io.TextIOBase):
|
| 12 |
+
def __init__(self, iterator):
|
| 13 |
+
self._it = iter(iterator)
|
| 14 |
+
self._buf = ""
|
| 15 |
+
|
| 16 |
+
def readable(self):
|
| 17 |
+
return True
|
| 18 |
+
|
| 19 |
+
def read(self, size=-1):
|
| 20 |
+
# size<0 のときはEOFまで貯めて返す
|
| 21 |
+
if size is None or size < 0:
|
| 22 |
+
try:
|
| 23 |
+
for chunk in self._it:
|
| 24 |
+
self._buf += chunk
|
| 25 |
+
except StopIteration:
|
| 26 |
+
pass
|
| 27 |
+
out, self._buf = self._buf, ""
|
| 28 |
+
return out
|
| 29 |
+
|
| 30 |
+
# size 指定ありのときは、必要分だけバッファを満たす
|
| 31 |
+
while len(self._buf) < size:
|
| 32 |
+
try:
|
| 33 |
+
self._buf += next(self._it)
|
| 34 |
+
except StopIteration:
|
| 35 |
+
break
|
| 36 |
+
|
| 37 |
+
out, self._buf = self._buf[:size], self._buf[size:]
|
| 38 |
+
return out
|
utils/json.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from typing import Any
|
| 4 |
+
|
| 5 |
+
FILE_CONFIG_PATH = "config/files.json"
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def _get_nested(d: dict[str, Any], keys: list[str]) -> Any:
|
| 9 |
+
cur: Any = d
|
| 10 |
+
for k in keys:
|
| 11 |
+
if not isinstance(cur, dict) or k not in cur:
|
| 12 |
+
raise KeyError(k)
|
| 13 |
+
cur = cur[k]
|
| 14 |
+
return cur
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _get_field_from_json(
|
| 18 |
+
path: str | Path, key: str, default: str | None = None, sep: str = "."
|
| 19 |
+
) -> Any:
|
| 20 |
+
"""
|
| 21 |
+
JSONファイルからフィールドを取得する。
|
| 22 |
+
階層構造がある場合は`sep`で区切って指定する。
|
| 23 |
+
|
| 24 |
+
example: `get_file_path_from_config("projects.original_csv")`
|
| 25 |
+
"""
|
| 26 |
+
with open(path, "r", encoding="utf-8") as f:
|
| 27 |
+
try:
|
| 28 |
+
config: dict[str, Any] = json.load(f)
|
| 29 |
+
except json.JSONDecodeError as e:
|
| 30 |
+
raise ValueError(f"JSONデコードエラー: {e}")
|
| 31 |
+
except FileNotFoundError:
|
| 32 |
+
raise ValueError(f"ファイルが見つかりません: {path}")
|
| 33 |
+
|
| 34 |
+
keys = key.split(sep) if sep in key else [key]
|
| 35 |
+
|
| 36 |
+
try:
|
| 37 |
+
value = _get_nested(config, keys)
|
| 38 |
+
except KeyError:
|
| 39 |
+
if default is not None:
|
| 40 |
+
return default
|
| 41 |
+
raise KeyError(f"Key '{key}' not found in {path}")
|
| 42 |
+
|
| 43 |
+
return value
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def get_file_path_from_config(
|
| 47 |
+
key: str, default: str | None = None, sep: str = "."
|
| 48 |
+
) -> str:
|
| 49 |
+
"""
|
| 50 |
+
設定ファイルからファイルのパスを取得する。
|
| 51 |
+
階層構造がある場合は`sep`で区切って指定する。
|
| 52 |
+
|
| 53 |
+
example: `get_file_path_from_config("projects.original_csv")`
|
| 54 |
+
"""
|
| 55 |
+
with open(FILE_CONFIG_PATH, "r", encoding="utf-8") as f:
|
| 56 |
+
config: dict[str, Any] = json.load(f)
|
| 57 |
+
|
| 58 |
+
keys = key.split(sep) if sep in key else [key]
|
| 59 |
+
|
| 60 |
+
try:
|
| 61 |
+
value = _get_nested(config, keys)
|
| 62 |
+
except KeyError:
|
| 63 |
+
if default is not None:
|
| 64 |
+
return default
|
| 65 |
+
raise KeyError(f"Key '{key}' not found in {FILE_CONFIG_PATH}")
|
| 66 |
+
|
| 67 |
+
if isinstance(value, str):
|
| 68 |
+
return value
|
| 69 |
+
|
| 70 |
+
if default is not None:
|
| 71 |
+
return default
|
| 72 |
+
raise TypeError(f"Value at '{key}' is not a string: {type(value).__name__}")
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def field_getter(path: str | Path):
|
| 76 |
+
def getter(key: str, default: str | None = None, sep: str = ".") -> Any:
|
| 77 |
+
return _get_field_from_json(path, key, default, sep)
|
| 78 |
+
|
| 79 |
+
return getter
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def _write_atomic(path: Path, data: str) -> None:
|
| 83 |
+
temp = path.with_suffix(path.suffix + ".tmp")
|
| 84 |
+
temp.write_bytes(data)
|
| 85 |
+
temp.replace(path)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def json_dumps(obj: Any, path: str | Path) -> None:
|
| 89 |
+
"""
|
| 90 |
+
オブジェクトをJSON形式のファイルに書き出す。
|
| 91 |
+
"""
|
| 92 |
+
json_bytes = json.dumps(obj, ensure_ascii=False, indent=2).encode("utf-8")
|
| 93 |
+
_write_atomic(Path(path), json_bytes)
|
utils/logger.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import logging
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def setup_logger(name: str = __name__) -> logging.Logger:
|
| 6 |
+
"""ロガーを設定して返す"""
|
| 7 |
+
logging.basicConfig(level="INFO", format="%(asctime)s [%(levelname)s] %(message)s")
|
| 8 |
+
log = logging.getLogger(name)
|
| 9 |
+
log.setLevel(os.getenv("LOG_LEVEL", "INFO").upper())
|
| 10 |
+
return log
|
utils/text_process.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
|
| 3 |
+
# U+202a (LRE) やその他の目に見えない制御文字を除去するための正規表現
|
| 4 |
+
# ref: https://www.compart.com/en/unicode/category/Cf
|
| 5 |
+
INVISIBLE_CHARS_PATTERN = re.compile(r"[\u200b-\u200c\u200e-\u200f\u202a-\u202e\ufeff]")
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def remove_invisible_chars(text: str) -> str:
|
| 9 |
+
"""
|
| 10 |
+
文字列から目に見えない制御文字やフォーマット文字を除去する。
|
| 11 |
+
"""
|
| 12 |
+
if not isinstance(text, str):
|
| 13 |
+
return text
|
| 14 |
+
return INVISIBLE_CHARS_PATTERN.sub("", text)
|