Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,7 +3,6 @@ import os
|
|
| 3 |
import re
|
| 4 |
import gradio as gr
|
| 5 |
import torch
|
| 6 |
-
torch.backends.cuda.matmul.allow_tf32 = True
|
| 7 |
import faiss
|
| 8 |
from dataclasses import dataclass
|
| 9 |
from typing import Optional
|
|
@@ -11,18 +10,18 @@ from transformers import AutoModelForCausalLM, AutoTokenizer
|
|
| 11 |
from sentence_transformers import SentenceTransformer
|
| 12 |
from huggingface_hub import login
|
| 13 |
|
| 14 |
-
# ββ Auth βββββββββββββββββββββββββββββββββββββββββββββ
|
| 15 |
_token = os.environ.get("HF_TOKEN")
|
| 16 |
if _token:
|
| 17 |
login(token=_token)
|
| 18 |
|
| 19 |
-
# ββ Config βββββββββββββββββββββββββββββββββββββββββββ
|
| 20 |
CHECKPOINT = "selfrag/selfrag_llama2_7b"
|
| 21 |
K_PASSAGES = 5
|
| 22 |
-
MAX_NEW_TOKS =
|
| 23 |
W_REL = 1.0; W_SUP = 1.0; W_USE = 0.5
|
| 24 |
|
| 25 |
-
# ββ Reflection
|
| 26 |
class RT:
|
| 27 |
RETRIEVAL = "[Retrieval]"
|
| 28 |
NO_RETRIEVAL = "[No Retrieval]"
|
|
@@ -42,307 +41,98 @@ class RT:
|
|
| 42 |
ABSTAIN_PHRASES = [
|
| 43 |
"not specified in the input","not provided in the input",
|
| 44 |
"not mentioned in the input","no information","cannot be determined",
|
| 45 |
-
"is not specified","are not specified","not available in the input",
|
| 46 |
-
"cannot answer","no relevant information","not found in",
|
| 47 |
-
"does not contain","not in the context","not mentioned in the context",
|
| 48 |
]
|
| 49 |
|
| 50 |
-
# ββ
|
| 51 |
DOCUMENTS = [
|
| 52 |
-
{"id":"doc_00","title":"RAG Overview",
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
{"id":"
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
"noise when the documents are not relevant to the question, and the model may produce "
|
| 61 |
-
"outputs that are inconsistent with retrieved passages."},
|
| 62 |
-
{"id":"doc_02","title":"SELF-RAG Framework",
|
| 63 |
-
"text":"SELF-RAG is a framework that trains a single language model to retrieve, generate, and "
|
| 64 |
-
"critique text through self-reflection. It uses four special reflection tokens: Retrieve "
|
| 65 |
-
"(yes/no/continue), IsRel (relevant/irrelevant), IsSupport (fully/partially/no support), "
|
| 66 |
-
"and IsUse (1 to 5). These tokens allow the model to decide adaptively when to retrieve "
|
| 67 |
-
"and evaluate the quality of its own generation at inference time. SELF-RAG was published "
|
| 68 |
-
"at ICLR 2024 by Asai et al."},
|
| 69 |
-
{"id":"doc_03","title":"CRAG β Corrective RAG",
|
| 70 |
-
"text":"Corrective Retrieval-Augmented Generation (CRAG) addresses the problem of inaccurate or "
|
| 71 |
-
"irrelevant retrieved documents by adding a lightweight evaluator that grades each retrieved "
|
| 72 |
-
"document's relevance. When all retrieved documents are judged irrelevant, CRAG triggers a "
|
| 73 |
-
"corrective web search to obtain better evidence before generation."},
|
| 74 |
-
{"id":"doc_04","title":"RAGAs Evaluation Framework",
|
| 75 |
-
"text":"RAGAs is an automated evaluation framework for retrieval-augmented generation systems. "
|
| 76 |
-
"It provides metrics including faithfulness, which measures whether the generated answer "
|
| 77 |
-
"is supported by the retrieved context, and answer relevance, which measures whether the "
|
| 78 |
-
"answer addresses the original question."},
|
| 79 |
-
{"id":"doc_05","title":"Hallucination in LLMs",
|
| 80 |
-
"text":"Hallucination in large language models refers to the generation of fluent and confident "
|
| 81 |
-
"statements that are not supported by evidence or are factually incorrect. RAG reduces "
|
| 82 |
-
"hallucination by grounding generation in retrieved documents, but does not eliminate it "
|
| 83 |
-
"entirely when the model ignores the retrieved context."},
|
| 84 |
-
{"id":"doc_06","title":"Adaptive Retrieval in SELF-RAG",
|
| 85 |
-
"text":"SELF-RAG uses an adaptive retrieval threshold delta to control how often retrieval is "
|
| 86 |
-
"triggered. The model predicts a Retrieve token and if the probability of Retrieve=Yes "
|
| 87 |
-
"exceeds the threshold delta, retrieval is triggered. The default threshold is 0.2."},
|
| 88 |
-
{"id":"doc_07","title":"Segment-Level Beam Search",
|
| 89 |
-
"text":"SELF-RAG uses segment-level beam search with beam width B=2 during inference. For each "
|
| 90 |
-
"retrieved passage, the model generates a candidate scored using IsRel, IsSupport, and "
|
| 91 |
-
"IsUse token probabilities combined with log-probability of the generated segment. "
|
| 92 |
-
"Weights: 1.0 for IsRel, 1.0 for IsSupport, 0.5 for IsUse."},
|
| 93 |
]
|
| 94 |
|
| 95 |
@dataclass
|
| 96 |
class Passage:
|
| 97 |
id: str; title: str; text: str
|
| 98 |
|
| 99 |
-
|
| 100 |
-
class Candidate:
|
| 101 |
-
text: str; passage: Passage
|
| 102 |
-
isrel: Optional[str] = None
|
| 103 |
-
issup: Optional[str] = None
|
| 104 |
-
isuse: Optional[str] = None
|
| 105 |
-
score: float = 0.0
|
| 106 |
-
|
| 107 |
-
# ββ Retriever β CPU, loaded at startup ββββββββββββββββββββββββββββββββββββββββ
|
| 108 |
print("Loading retriever...")
|
| 109 |
-
|
| 110 |
-
_passages = [Passage(**d) for d in DOCUMENTS]
|
| 111 |
-
_embs = _retriever.encode(
|
| 112 |
-
[f"{p.title} {p.text}" for p in _passages],
|
| 113 |
-
convert_to_numpy=True, normalize_embeddings=True
|
| 114 |
-
).astype("float32")
|
| 115 |
-
_index = faiss.IndexFlatIP(_embs.shape[1])
|
| 116 |
-
_index.add(_embs)
|
| 117 |
-
print(f"Retriever ready: {_index.ntotal} passages")
|
| 118 |
-
|
| 119 |
-
def retrieve(query: str):
|
| 120 |
-
q = _retriever.encode([query], convert_to_numpy=True,
|
| 121 |
-
normalize_embeddings=True).astype("float32")
|
| 122 |
-
_, idxs = _index.search(q, K_PASSAGES)
|
| 123 |
-
return [_passages[i] for i in idxs[0] if i < len(_passages)]
|
| 124 |
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
CHECKPOINT,
|
| 132 |
-
torch_dtype=torch.float16,
|
| 133 |
-
low_cpu_mem_usage=True,
|
| 134 |
-
)
|
| 135 |
-
_model = AutoModelForCausalLM.from_pretrained(
|
| 136 |
-
CHECKPOINT,
|
| 137 |
-
torch_dtype=torch.float16,
|
| 138 |
-
device_map="auto",
|
| 139 |
-
)
|
| 140 |
-
_model.eval()
|
| 141 |
-
print("Generator ready.")
|
| 142 |
-
|
| 143 |
-
# ββ GPU-decorated inference ββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 144 |
-
@spaces.GPU
|
| 145 |
-
def generate_candidates(query: str, passage_texts: list) -> list:
|
| 146 |
-
"""Runs inside real ZeroGPU GPU context."""
|
| 147 |
-
results = []
|
| 148 |
-
for pt in passage_texts:
|
| 149 |
-
prompt = (f"### Instruction:\n{query}\n\n"
|
| 150 |
-
f"### Input:\n<p>{pt}</p>\n\n"
|
| 151 |
-
f"### Response:\n")
|
| 152 |
-
enc = _tokenizer(prompt, return_tensors="pt",
|
| 153 |
-
truncation=True, max_length=2048)
|
| 154 |
-
enc = {k: v.to("cuda") for k, v in enc.items()}
|
| 155 |
-
with torch.no_grad():
|
| 156 |
-
out = _model.generate(
|
| 157 |
-
**enc, max_new_tokens=MAX_NEW_TOKS, do_sample=False
|
| 158 |
-
)
|
| 159 |
-
new_ids = out[0, enc["input_ids"].shape[1]:]
|
| 160 |
-
raw = _tokenizer.decode(new_ids, skip_special_tokens=True,
|
| 161 |
-
clean_up_tokenization_spaces=True)
|
| 162 |
-
results.append(raw)
|
| 163 |
-
return results
|
| 164 |
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
norm = raw.replace("_", " ")
|
| 168 |
-
r = {"isrel":None,"issup":None,"isuse":None,"answer":"","raw":raw}
|
| 169 |
-
for t in [RT.RELEVANT, RT.IRRELEVANT]:
|
| 170 |
-
if t in raw: r["isrel"]=t; break
|
| 171 |
-
for t in [RT.FULLY, RT.PARTIALLY, RT.NO_SUPPORT]:
|
| 172 |
-
if t in raw: r["issup"]=t; break
|
| 173 |
-
for t in [RT.U5,RT.U4,RT.U3,RT.U2,RT.U1]:
|
| 174 |
-
if t in raw: r["isuse"]=t; break
|
| 175 |
-
ans = raw
|
| 176 |
-
for t in RT.ALL + ["<paragraph>"]:
|
| 177 |
-
if t in ans: ans = ans[:ans.index(t)]
|
| 178 |
-
ans = re.sub(r"<[^>]+>","",ans)
|
| 179 |
-
ans = re.sub(r"#+\s*\w+.*?:","",ans)
|
| 180 |
-
r["answer"] = " ".join(ans.split()).strip()
|
| 181 |
-
for ph in ABSTAIN_PHRASES:
|
| 182 |
-
if ph.lower() in norm.lower():
|
| 183 |
-
r["issup"] = RT.NO_SUPPORT; break
|
| 184 |
-
return r
|
| 185 |
|
| 186 |
-
|
| 187 |
-
s = 0.0
|
| 188 |
-
if p["isrel"] == RT.RELEVANT: s += W_REL * 1.0
|
| 189 |
-
if p["issup"] == RT.FULLY: s += W_SUP * 1.0
|
| 190 |
-
elif p["issup"] == RT.PARTIALLY: s += W_SUP * 0.5
|
| 191 |
-
if p["isuse"]: s += W_USE * RT.USE_SCORES.get(p["isuse"], 0.0)
|
| 192 |
-
return s
|
| 193 |
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
for passage, raw in zip(retrieved, raws):
|
| 200 |
-
pr = parse(raw)
|
| 201 |
-
if not pr["answer"]: continue
|
| 202 |
-
candidates.append(Candidate(
|
| 203 |
-
text=pr["answer"], passage=passage,
|
| 204 |
-
isrel=pr["isrel"], issup=pr["issup"],
|
| 205 |
-
isuse=pr["isuse"], score=calc_score(pr),
|
| 206 |
-
))
|
| 207 |
-
if not candidates:
|
| 208 |
-
return None, retrieved, True
|
| 209 |
-
candidates.sort(key=lambda c: c.score, reverse=True)
|
| 210 |
-
best = candidates[0]
|
| 211 |
-
abstained = (best.issup == RT.NO_SUPPORT and best.score <= 0.5)
|
| 212 |
-
return best, retrieved, abstained
|
| 213 |
|
| 214 |
-
# ββ
|
| 215 |
-
|
| 216 |
-
RT.RETRIEVAL:("#EEEDFE","#3C3489"), RT.RELEVANT:("#EAF3DE","#27500A"),
|
| 217 |
-
RT.IRRELEVANT:("#FCEBEB","#791F1F"), RT.FULLY:("#EAF3DE","#27500A"),
|
| 218 |
-
RT.PARTIALLY:("#FAEEDA","#633806"), RT.NO_SUPPORT:("#FCEBEB","#791F1F"),
|
| 219 |
-
RT.U5:("#EAF3DE","#27500A"), RT.U4:("#EAF3DE","#27500A"),
|
| 220 |
-
RT.U3:("#FAEEDA","#633806"), RT.U2:("#FCEBEB","#791F1F"),
|
| 221 |
-
RT.U1:("#FCEBEB","#791F1F"),
|
| 222 |
-
}
|
| 223 |
|
| 224 |
-
|
| 225 |
-
if not tok: return ""
|
| 226 |
-
bg,fg = TC.get(tok,("#F1EFE8","#444441"))
|
| 227 |
-
return (f"<span style='background:{bg};color:{fg};font-size:11px;"
|
| 228 |
-
f"font-weight:600;padding:3px 8px;border-radius:4px;"
|
| 229 |
-
f"margin:2px;display:inline-block'>{tok}</span>")
|
| 230 |
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
⚠ ABSTAINED β Insufficient Evidence
|
| 238 |
-
</div>
|
| 239 |
-
<div style='font-size:13px;color:#791F1F;line-height:1.6'>
|
| 240 |
-
The system could not find sufficient evidence in the document corpus
|
| 241 |
-
to answer this question. Rather than hallucinating an answer, the system abstains.
|
| 242 |
-
</div>
|
| 243 |
-
<div style='margin-top:10px;font-size:11px;color:#aaa'>
|
| 244 |
-
Passages retrieved: {', '.join(f'[{p.id}] {p.title}' for p in retrieved)}
|
| 245 |
-
</div>
|
| 246 |
-
</div>"""
|
| 247 |
-
return f"""
|
| 248 |
-
<div style='font-family:sans-serif;padding:16px;border-radius:10px;
|
| 249 |
-
background:#F7F7F9;border:0.5px solid #D3D1C7;margin-top:8px'>
|
| 250 |
-
<div style='font-size:11px;color:#888;margin-bottom:4px'>Best passage retrieved</div>
|
| 251 |
-
<div style='background:#E6F1FB;padding:8px 12px;border-radius:6px;
|
| 252 |
-
font-size:11px;color:#185FA5;margin-bottom:12px'>
|
| 253 |
-
[{best.passage.id}] {best.passage.title}
|
| 254 |
-
</div>
|
| 255 |
-
<div style='margin-bottom:10px'>
|
| 256 |
-
{badge(RT.RETRIEVAL)}{badge(best.isrel)}{badge(best.issup)}{badge(best.isuse)}
|
| 257 |
-
<span style='font-size:10px;color:#bbb;margin-left:6px'>score={best.score:.2f}</span>
|
| 258 |
-
</div>
|
| 259 |
-
<div style='background:#fff;padding:14px;border-radius:8px;
|
| 260 |
-
border:0.5px solid #D3D1C7;font-size:14px;color:#1E2761;
|
| 261 |
-
line-height:1.8;margin-bottom:10px'>
|
| 262 |
-
{best.text}
|
| 263 |
-
</div>
|
| 264 |
-
<div style='font-size:11px;color:#bbb'>
|
| 265 |
-
All passages considered: {', '.join(f'[{p.id}]' for p in retrieved)}
|
| 266 |
-
</div>
|
| 267 |
-
</div>"""
|
| 268 |
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
try:
|
| 274 |
-
|
| 275 |
-
|
|
|
|
|
|
|
| 276 |
except Exception as e:
|
| 277 |
-
return f"
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
# SELF-RAG
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
gr.Markdown("""
|
| 292 |
-
### Document Corpus (8 documents)
|
| 293 |
-
| ID | Topic |
|
| 294 |
-
|---|---|
|
| 295 |
-
| doc_00 | RAG Overview |
|
| 296 |
-
| doc_01 | Vanilla RAG Limitations |
|
| 297 |
-
| doc_02 | SELF-RAG Framework |
|
| 298 |
-
| doc_03 | CRAG β Corrective RAG |
|
| 299 |
-
| doc_04 | RAGAs Evaluation |
|
| 300 |
-
| doc_05 | Hallucination in LLMs |
|
| 301 |
-
| doc_06 | Adaptive Retrieval in SELF-RAG |
|
| 302 |
-
| doc_07 | Segment-Level Beam Search |
|
| 303 |
-
|
| 304 |
-
**Ask anything outside these topics** (e.g. geography, sports, cooking) to trigger abstention.
|
| 305 |
-
""")
|
| 306 |
-
|
| 307 |
-
with gr.Row():
|
| 308 |
-
with gr.Column(scale=4):
|
| 309 |
-
qbox = gr.Textbox(
|
| 310 |
-
label="Your Question",
|
| 311 |
-
placeholder="e.g. What is SELF-RAG? | What is hallucination? | What is the capital of France?",
|
| 312 |
-
lines=2,
|
| 313 |
-
)
|
| 314 |
-
with gr.Column(scale=1, min_width=120):
|
| 315 |
-
btn = gr.Button("Ask β", variant="primary", size="lg")
|
| 316 |
-
|
| 317 |
-
gr.Examples(
|
| 318 |
-
examples=[
|
| 319 |
-
["What is SELF-RAG and how does it use reflection tokens?"],
|
| 320 |
-
["What is the main limitation of Vanilla RAG?"],
|
| 321 |
-
["How does CRAG handle irrelevant retrieved documents?"],
|
| 322 |
-
["What is hallucination in language models?"],
|
| 323 |
-
["What is the capital of France?"],
|
| 324 |
-
["What is the deployment date of the AuthService in the Pegasus project?"],
|
| 325 |
-
],
|
| 326 |
-
inputs=qbox,
|
| 327 |
-
label="Try these β last two should abstain",
|
| 328 |
-
)
|
| 329 |
-
|
| 330 |
-
out = gr.HTML(label="Response")
|
| 331 |
-
btn.click(fn=answer, inputs=qbox, outputs=out)
|
| 332 |
-
qbox.submit(fn=answer, inputs=qbox, outputs=out)
|
| 333 |
-
|
| 334 |
-
gr.Markdown("""
|
| 335 |
-
---
|
| 336 |
-
### How it works
|
| 337 |
-
| Step | What happens |
|
| 338 |
-
|---|---|
|
| 339 |
-
| 1. Retrieve | Top-5 passages from FAISS using all-MiniLM-L6-v2 |
|
| 340 |
-
| 2. Generate | selfrag/selfrag_llama2_7b generates response for each passage |
|
| 341 |
-
| 3. Parse | Reflection tokens extracted from generated text |
|
| 342 |
-
| 4. Score | `score = w_relΓs(IsRel) + w_supΓs(IsSupport) + w_useΓs(IsUse)` |
|
| 343 |
-
| 5. Abstain | If best has `[No support / Contradictory]` β "Insufficient evidence" |
|
| 344 |
-
|
| 345 |
-
*Asai et al., ICLR 2024 β SELF-RAG: Learning to Retrieve, Generate and Critique*
|
| 346 |
-
""")
|
| 347 |
-
|
| 348 |
-
demo.queue(default_concurrency_limit=1).launch()
|
|
|
|
| 3 |
import re
|
| 4 |
import gradio as gr
|
| 5 |
import torch
|
|
|
|
| 6 |
import faiss
|
| 7 |
from dataclasses import dataclass
|
| 8 |
from typing import Optional
|
|
|
|
| 10 |
from sentence_transformers import SentenceTransformer
|
| 11 |
from huggingface_hub import login
|
| 12 |
|
| 13 |
+
# ββ Auth βββββββββββββββββββββββββββββββββββββββββββββ
|
| 14 |
_token = os.environ.get("HF_TOKEN")
|
| 15 |
if _token:
|
| 16 |
login(token=_token)
|
| 17 |
|
| 18 |
+
# ββ Config βββββββββββββββββββββββββββββββββββββββββββ
|
| 19 |
CHECKPOINT = "selfrag/selfrag_llama2_7b"
|
| 20 |
K_PASSAGES = 5
|
| 21 |
+
MAX_NEW_TOKS = 120 # reduced for stability
|
| 22 |
W_REL = 1.0; W_SUP = 1.0; W_USE = 0.5
|
| 23 |
|
| 24 |
+
# ββ Reflection Tokens βββββββββββββββββββββββββββββββββ
|
| 25 |
class RT:
|
| 26 |
RETRIEVAL = "[Retrieval]"
|
| 27 |
NO_RETRIEVAL = "[No Retrieval]"
|
|
|
|
| 41 |
ABSTAIN_PHRASES = [
|
| 42 |
"not specified in the input","not provided in the input",
|
| 43 |
"not mentioned in the input","no information","cannot be determined",
|
|
|
|
|
|
|
|
|
|
| 44 |
]
|
| 45 |
|
| 46 |
+
# ββ Documents βββββββββββββββββββββββββββββββββββββββββ
|
| 47 |
DOCUMENTS = [
|
| 48 |
+
{"id":"doc_00","title":"RAG Overview","text":"Retrieval-Augmented Generation improves factual correctness."},
|
| 49 |
+
{"id":"doc_01","title":"Vanilla RAG Limitations","text":"Vanilla RAG retrieves fixed documents even when unnecessary."},
|
| 50 |
+
{"id":"doc_02","title":"SELF-RAG Framework","text":"SELF-RAG uses reflection tokens to improve responses."},
|
| 51 |
+
{"id":"doc_03","title":"CRAG","text":"CRAG adds evaluator for retrieval correction."},
|
| 52 |
+
{"id":"doc_04","title":"RAGAs","text":"Evaluation framework for RAG systems."},
|
| 53 |
+
{"id":"doc_05","title":"Hallucination","text":"LLMs may generate unsupported content."},
|
| 54 |
+
{"id":"doc_06","title":"Adaptive Retrieval","text":"SELF-RAG triggers retrieval dynamically."},
|
| 55 |
+
{"id":"doc_07","title":"Beam Search","text":"Uses scoring with reflection tokens."},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
]
|
| 57 |
|
| 58 |
@dataclass
|
| 59 |
class Passage:
|
| 60 |
id: str; title: str; text: str
|
| 61 |
|
| 62 |
+
# ββ Retriever βββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
print("Loading retriever...")
|
| 64 |
+
retriever = SentenceTransformer("all-MiniLM-L6-v2", device="cpu")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
+
passages = [Passage(**d) for d in DOCUMENTS]
|
| 67 |
+
embs = retriever.encode(
|
| 68 |
+
[f"{p.title} {p.text}" for p in passages],
|
| 69 |
+
convert_to_numpy=True,
|
| 70 |
+
normalize_embeddings=True
|
| 71 |
+
).astype("float32")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
+
index = faiss.IndexFlatIP(embs.shape[1])
|
| 74 |
+
index.add(embs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
|
| 76 |
+
print("Retriever ready")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
+
def retrieve(query):
|
| 79 |
+
q = retriever.encode([query], convert_to_numpy=True,
|
| 80 |
+
normalize_embeddings=True).astype("float32")
|
| 81 |
+
_, idxs = index.search(q, K_PASSAGES)
|
| 82 |
+
return [passages[i] for i in idxs[0]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
|
| 84 |
+
# ββ MODEL LOADING (FIXED) βββββββββββββββββββββββββββββ
|
| 85 |
+
print("Loading model...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
+
tokenizer = AutoTokenizer.from_pretrained(CHECKPOINT)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 90 |
+
CHECKPOINT,
|
| 91 |
+
torch_dtype=torch.float16,
|
| 92 |
+
device_map="auto", # β
KEY FIX
|
| 93 |
+
low_cpu_mem_usage=True # β
KEY FIX
|
| 94 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
+
model.eval()
|
| 97 |
+
|
| 98 |
+
print("Model loaded successfully")
|
| 99 |
+
|
| 100 |
+
# ββ GENERATION βββββββββββββββββββββββββββββββββββββββ
|
| 101 |
+
def generate_answer(query, context):
|
| 102 |
+
prompt = f"Question: {query}\nContext: {context}\nAnswer:"
|
| 103 |
+
|
| 104 |
+
inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=1024)
|
| 105 |
+
|
| 106 |
+
# move to correct device
|
| 107 |
+
inputs = {k: v.to(model.device) for k, v in inputs.items()}
|
| 108 |
+
|
| 109 |
+
with torch.no_grad():
|
| 110 |
+
output = model.generate(
|
| 111 |
+
**inputs,
|
| 112 |
+
max_new_tokens=MAX_NEW_TOKS,
|
| 113 |
+
do_sample=False
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
return tokenizer.decode(output[0], skip_special_tokens=True)
|
| 117 |
+
|
| 118 |
+
# ββ MAIN FUNCTION βββββββββββββββββββββββββββββββββββββ
|
| 119 |
+
def answer(query):
|
| 120 |
try:
|
| 121 |
+
docs = retrieve(query)
|
| 122 |
+
context = " ".join([d.text for d in docs])
|
| 123 |
+
result = generate_answer(query, context)
|
| 124 |
+
return result
|
| 125 |
except Exception as e:
|
| 126 |
+
return f"Error: {str(e)}"
|
| 127 |
+
|
| 128 |
+
# ββ UI βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 129 |
+
with gr.Blocks() as demo:
|
| 130 |
+
gr.Markdown("# SELF-RAG Demo")
|
| 131 |
+
|
| 132 |
+
q = gr.Textbox(label="Ask a question")
|
| 133 |
+
out = gr.Textbox(label="Answer")
|
| 134 |
+
|
| 135 |
+
btn = gr.Button("Ask")
|
| 136 |
+
btn.click(answer, inputs=q, outputs=out)
|
| 137 |
+
|
| 138 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|