fixed contrastive search error
Browse files
app.py
CHANGED
|
@@ -18,7 +18,7 @@ from transformers import (
|
|
| 18 |
CLIPModel, CLIPProcessor, BitsAndBytesConfig
|
| 19 |
)
|
| 20 |
|
| 21 |
-
app = FastAPI(title="XAI Auditor:
|
| 22 |
|
| 23 |
app.add_middleware(
|
| 24 |
CORSMiddleware,
|
|
@@ -38,7 +38,7 @@ async def startup_event():
|
|
| 38 |
token = os.getenv("HF_Token")
|
| 39 |
if token: login(token=token)
|
| 40 |
|
| 41 |
-
print("Pinning 8-bit quantized models to memory...")
|
| 42 |
local_dir = snapshot_download(repo_id="SaniaE/Image_Captioning_Ensemble", token=token, local_dir="weights")
|
| 43 |
|
| 44 |
quantization_config = BitsAndBytesConfig(
|
|
@@ -56,7 +56,7 @@ async def startup_event():
|
|
| 56 |
"processor": BlipProcessor.from_pretrained("Salesforce/blip-image-captioning-large")
|
| 57 |
}
|
| 58 |
|
| 59 |
-
# 2. Load Compressed ViT Track (
|
| 60 |
MODELS["vit"] = {
|
| 61 |
"model": AutoModelForCausalLM.from_pretrained(
|
| 62 |
os.path.join(local_dir, "vit"),
|
|
@@ -77,19 +77,19 @@ async def startup_event():
|
|
| 77 |
"processor": CLIPProcessor.from_pretrained(os.path.join(local_dir, "clip/clip_processor"))
|
| 78 |
}
|
| 79 |
|
| 80 |
-
print("
|
| 81 |
|
| 82 |
-
# --- High-Speed
|
| 83 |
|
| 84 |
-
def _generate_balanced_4_track(image,
|
| 85 |
"""
|
| 86 |
-
|
| 87 |
-
|
| 88 |
"""
|
| 89 |
captions = []
|
| 90 |
|
| 91 |
with torch.inference_mode():
|
| 92 |
-
# Track A:
|
| 93 |
b_data = MODELS["blip"]
|
| 94 |
b_inputs = b_data["processor"](images=image, return_tensors="pt")
|
| 95 |
b_pixels = b_inputs.pixel_values.to(DEVICE)
|
|
@@ -98,16 +98,15 @@ def _generate_balanced_4_track(image, temp, max_len=15):
|
|
| 98 |
b_ids = b_data["model"].generate(
|
| 99 |
pixel_values=batched_b_pixels,
|
| 100 |
max_new_tokens=max_len,
|
| 101 |
-
do_sample=False,
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
use_cache=True
|
| 106 |
)
|
| 107 |
b_caps = b_data["processor"].batch_decode(b_ids, skip_special_tokens=True)
|
| 108 |
captions.extend([cap.strip() for cap in b_caps])
|
| 109 |
|
| 110 |
-
# Track B:
|
| 111 |
v_data = MODELS["vit"]
|
| 112 |
i_proc, t_proc = v_data["processor"]
|
| 113 |
v_inputs = i_proc(images=image, return_tensors="pt")
|
|
@@ -123,9 +122,8 @@ def _generate_balanced_4_track(image, temp, max_len=15):
|
|
| 123 |
pixel_values=batched_v_pixels,
|
| 124 |
attention_mask=batched_mask,
|
| 125 |
max_new_tokens=max_len,
|
| 126 |
-
do_sample=False,
|
| 127 |
-
|
| 128 |
-
top_k=4, # Narrow contrastive candidate pool
|
| 129 |
early_stopping=True,
|
| 130 |
use_cache=True
|
| 131 |
)
|
|
@@ -133,16 +131,17 @@ def _generate_balanced_4_track(image, temp, max_len=15):
|
|
| 133 |
captions.extend([cap.strip() for cap in v_caps])
|
| 134 |
|
| 135 |
return captions
|
|
|
|
| 136 |
# --- Endpoints ---
|
| 137 |
|
| 138 |
@app.post("/generate")
|
| 139 |
-
async def generate_captions(file: UploadFile = File(...)
|
| 140 |
"""Generates 4 diverse captions split evenly across architectures for UI balance."""
|
| 141 |
start_time = time.perf_counter()
|
| 142 |
image = Image.open(file.file).convert("RGB")
|
| 143 |
|
| 144 |
-
#
|
| 145 |
-
captions = await asyncio.to_thread(_generate_balanced_4_track, image,
|
| 146 |
|
| 147 |
elapsed_time = time.perf_counter() - start_time
|
| 148 |
print(f"[BENCHMARK] /generate 4-caption turnaround: {elapsed_time:.4f}s")
|
|
@@ -198,37 +197,4 @@ async def internal_debate_audit(file: UploadFile = File(...), user_prompt: str =
|
|
| 198 |
image_bytes = await file.read()
|
| 199 |
image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
|
| 200 |
|
| 201 |
-
# Extract
|
| 202 |
-
blip_caption = (await asyncio.to_thread(_generate_balanced_4_track, image, 1.0, 15))[0]
|
| 203 |
-
|
| 204 |
-
clip_m = MODELS["clip"]["model"]
|
| 205 |
-
clip_p = MODELS["clip"]["processor"]
|
| 206 |
-
clip_dtype = torch.float16 if DEVICE == "cuda" else torch.float32
|
| 207 |
-
|
| 208 |
-
image_inputs = clip_p(images=image, return_tensors="pt")
|
| 209 |
-
text_inputs = clip_p(text=[user_prompt, blip_caption], return_tensors="pt", padding=True)
|
| 210 |
-
|
| 211 |
-
with torch.inference_mode():
|
| 212 |
-
img_pixels = image_inputs.pixel_values.to(device=DEVICE, dtype=clip_dtype)
|
| 213 |
-
txt_ids = text_inputs.input_ids.to(DEVICE)
|
| 214 |
-
txt_mask = text_inputs.attention_mask.to(DEVICE)
|
| 215 |
-
|
| 216 |
-
image_features = clip_m.get_image_features(pixel_values=img_pixels)
|
| 217 |
-
image_features = image_features / image_features.norm(dim=-1, keepdim=True)
|
| 218 |
-
|
| 219 |
-
text_features = clip_m.get_text_features(input_ids=txt_ids, attention_mask=txt_mask)
|
| 220 |
-
text_features = text_features / text_features.norm(dim=-1, keepdim=True)
|
| 221 |
-
|
| 222 |
-
logits_per_image = (image_features @ text_features.T) * clip_m.logit_scale.exp()
|
| 223 |
-
probs = F.softmax(logits_per_image, dim=-1).cpu().to(torch.float32).numpy()[0]
|
| 224 |
-
|
| 225 |
-
u_score, m_score = float(probs[0]), float(probs[1])
|
| 226 |
-
verdict = "Model Bias Detected." if abs(u_score - m_score) >= 0.15 else "Consensus: High Alignment."
|
| 227 |
-
if u_score < 0.35: verdict = "Perspective Divergence: Intent not grounded in image."
|
| 228 |
-
|
| 229 |
-
return {
|
| 230 |
-
"perspectives": {"user": user_prompt, "ai": blip_caption},
|
| 231 |
-
"audit_scores": {"intent_grounding": round(u_score, 4), "ai_grounding": round(m_score, 4)},
|
| 232 |
-
"verdict": verdict,
|
| 233 |
-
"metadata": {"processing_time_sec": round(time.perf_counter() - start_time, 4)}
|
| 234 |
-
}
|
|
|
|
| 18 |
CLIPModel, CLIPProcessor, BitsAndBytesConfig
|
| 19 |
)
|
| 20 |
|
| 21 |
+
app = FastAPI(title="XAI Auditor: Symmetrical Deterministic Ensemble")
|
| 22 |
|
| 23 |
app.add_middleware(
|
| 24 |
CORSMiddleware,
|
|
|
|
| 38 |
token = os.getenv("HF_Token")
|
| 39 |
if token: login(token=token)
|
| 40 |
|
| 41 |
+
print("Pinning 8-bit quantized models to memory space...")
|
| 42 |
local_dir = snapshot_download(repo_id="SaniaE/Image_Captioning_Ensemble", token=token, local_dir="weights")
|
| 43 |
|
| 44 |
quantization_config = BitsAndBytesConfig(
|
|
|
|
| 56 |
"processor": BlipProcessor.from_pretrained("Salesforce/blip-image-captioning-large")
|
| 57 |
}
|
| 58 |
|
| 59 |
+
# 2. Load Compressed ViT Track (Fixed Shape Mappings)
|
| 60 |
MODELS["vit"] = {
|
| 61 |
"model": AutoModelForCausalLM.from_pretrained(
|
| 62 |
os.path.join(local_dir, "vit"),
|
|
|
|
| 77 |
"processor": CLIPProcessor.from_pretrained(os.path.join(local_dir, "clip/clip_processor"))
|
| 78 |
}
|
| 79 |
|
| 80 |
+
print("All system weights safely pinned. Matrix crash protections active.")
|
| 81 |
|
| 82 |
+
# --- High-Speed Fixed Matrix Execution Engine ---
|
| 83 |
|
| 84 |
+
def _generate_balanced_4_track(image, max_len=15):
|
| 85 |
"""
|
| 86 |
+
Generates exactly 4 captions (2 BLIP, 2 ViT) using safe, high-speed
|
| 87 |
+
deterministic decoding loops that eliminate padding math overhead.
|
| 88 |
"""
|
| 89 |
captions = []
|
| 90 |
|
| 91 |
with torch.inference_mode():
|
| 92 |
+
# Track A: Batched BLIP Pass (2 Parallel Paths)
|
| 93 |
b_data = MODELS["blip"]
|
| 94 |
b_inputs = b_data["processor"](images=image, return_tensors="pt")
|
| 95 |
b_pixels = b_inputs.pixel_values.to(DEVICE)
|
|
|
|
| 98 |
b_ids = b_data["model"].generate(
|
| 99 |
pixel_values=batched_b_pixels,
|
| 100 |
max_new_tokens=max_len,
|
| 101 |
+
do_sample=False, # Purely deterministic execution
|
| 102 |
+
num_beams=2, # 2 separate beams for internal diversity split
|
| 103 |
+
early_stopping=True, # Break out the millisecond end token drops
|
| 104 |
+
use_cache=True # Leverage cached keys to prevent recomputation
|
|
|
|
| 105 |
)
|
| 106 |
b_caps = b_data["processor"].batch_decode(b_ids, skip_special_tokens=True)
|
| 107 |
captions.extend([cap.strip() for cap in b_caps])
|
| 108 |
|
| 109 |
+
# Track B: Batched ViT Pass (2 Parallel Paths - Safe Matrix Sizing)
|
| 110 |
v_data = MODELS["vit"]
|
| 111 |
i_proc, t_proc = v_data["processor"]
|
| 112 |
v_inputs = i_proc(images=image, return_tensors="pt")
|
|
|
|
| 122 |
pixel_values=batched_v_pixels,
|
| 123 |
attention_mask=batched_mask,
|
| 124 |
max_new_tokens=max_len,
|
| 125 |
+
do_sample=False, # Swapped off sample/contrastive to prevent shape mismatch
|
| 126 |
+
num_beams=2, # Safe, standardized beam expansion tracking
|
|
|
|
| 127 |
early_stopping=True,
|
| 128 |
use_cache=True
|
| 129 |
)
|
|
|
|
| 131 |
captions.extend([cap.strip() for cap in v_caps])
|
| 132 |
|
| 133 |
return captions
|
| 134 |
+
|
| 135 |
# --- Endpoints ---
|
| 136 |
|
| 137 |
@app.post("/generate")
|
| 138 |
+
async def generate_captions(file: UploadFile = File(...)):
|
| 139 |
"""Generates 4 diverse captions split evenly across architectures for UI balance."""
|
| 140 |
start_time = time.perf_counter()
|
| 141 |
image = Image.open(file.file).convert("RGB")
|
| 142 |
|
| 143 |
+
# Fire off the optimized, crash-safe execution matrix
|
| 144 |
+
captions = await asyncio.to_thread(_generate_balanced_4_track, image, 15)
|
| 145 |
|
| 146 |
elapsed_time = time.perf_counter() - start_time
|
| 147 |
print(f"[BENCHMARK] /generate 4-caption turnaround: {elapsed_time:.4f}s")
|
|
|
|
| 197 |
image_bytes = await file.read()
|
| 198 |
image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
|
| 199 |
|
| 200 |
+
# Extract
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|