jeffliulab commited on
Commit
86b0757
·
verified ·
1 Parent(s): 897cfc7

v1.3: add Stage 1 universal abstract image display + v3 judge methodology link

Browse files
Files changed (1) hide show
  1. app.py +93 -22
app.py CHANGED
@@ -1,18 +1,20 @@
1
  """
2
- VisInject — HF Space Demo
3
- ==========================
4
  Stage 2 (AnyAttack fusion) only. Stripped-down, CPU-only Gradio app.
5
 
6
  How it works:
7
  1. Pick an attack prompt (7 options) from the dropdown
8
- 2. Upload a clean image
9
- 3. The app loads:
 
 
 
 
10
  • CLIP ViT-B/32 (cached after first call)
11
  • AnyAttack Decoder, fetched from `jiamingzz/anyattack` on HF
12
- Precomputed universal adversarial image for the chosen prompt,
13
- fetched from `jeffliulab/visinject` (Dataset) on HF
14
- 4. CLIP encodes universal → 512-d embedding → Decoder → bounded noise
15
- (eps = 16/255) → noise + clean → adversarial image
16
  5. Returns the adv image + PSNR
17
 
18
  This Space cannot run Stage 1 (multi-VLM PGD optimization) or Stage 3 (VLM
@@ -20,7 +22,7 @@ inference verification): both need GPU + multiple VLMs loaded simultaneously,
20
  which exceeds the free-tier 16 GB RAM / CPU-only budget.
21
 
22
  Source code, full pipeline, and HPC scripts:
23
- https://github.com/jeffliulab/VisInject (replace with actual URL)
24
  """
25
 
26
  import glob
@@ -123,7 +125,7 @@ def _get_universal_path(tag: str) -> str:
123
  return matches[0]
124
 
125
 
126
- # ── Stage 2 fusion ────────────────────────────────────────────────
127
 
128
  def _format_prompt_choice(tag: str, phrase: str) -> str:
129
  return f"{tag} — \"{phrase}\""
@@ -133,6 +135,32 @@ def _choice_to_tag(choice: str) -> str:
133
  return choice.split(" — ", 1)[0].strip()
134
 
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  def run_fusion(prompt_choice: str, clean_image_path: str):
137
  """Run Stage 2 fusion. Returns (adv_path, info_text, explanation)."""
138
  if clean_image_path is None:
@@ -193,11 +221,11 @@ def build_ui():
193
  """
194
  # VisInject — Adversarial Prompt Injection Demo
195
 
196
- Pick an **attack prompt**, upload a **clean image**, and the app will fuse a
197
- precomputed universal adversarial image into yours via CLIP ViT-B/32 + the
198
- AnyAttack Decoder.
199
 
200
- The output is visually indistinguishable from your original (PSNR ≈ 25 dB),
201
  but Vision-Language Models read it as containing the target phrase.
202
 
203
  **Limitations**: this demo runs only **Stage 2** (fusion). It cannot retrain
@@ -211,21 +239,41 @@ image download to the Space cache. Subsequent calls are 2–5 s.
211
  )
212
 
213
  with gr.Tab("Generate adversarial image"):
 
 
 
 
 
 
 
 
 
214
  with gr.Row():
215
  with gr.Column():
216
- prompt_dd = gr.Dropdown(
217
- choices=choices,
218
- value=choices[0],
219
- label="Attack prompt",
220
- info="Select the target phrase to inject",
 
 
 
 
 
 
221
  )
 
 
 
 
222
  clean_img = gr.Image(
223
- label="Clean image",
224
  type="filepath",
225
  sources=["upload", "clipboard"],
226
  )
227
  go_btn = gr.Button(
228
- "Generate adversarial image", variant="primary"
 
229
  )
230
  with gr.Column():
231
  adv_img = gr.Image(
@@ -237,6 +285,20 @@ image download to the Space cache. Subsequent calls are 2–5 s.
237
  label="What next?", lines=4, interactive=False
238
  )
239
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  go_btn.click(
241
  fn=run_fusion,
242
  inputs=[prompt_dd, clean_img],
@@ -249,9 +311,18 @@ image download to the Space cache. Subsequent calls are 2–5 s.
249
  ## About
250
 
251
  - **Code**: [github.com/jeffliulab/VisInject](https://github.com/jeffliulab/VisInject)
252
- - **Experimental data** (147 response_pairs, 21 universal images, 147 adv images): [datasets/jeffliulab/visinject](https://huggingface.co/datasets/jeffliulab/visinject)
253
  - **Decoder weights**: [`jiamingzz/anyattack`](https://huggingface.co/jiamingzz/anyattack) — from Zhang et al., *AnyAttack: Towards Large-scale Self-supervised Adversarial Attacks on Vision-language Models*, CVPR 2025.
254
 
 
 
 
 
 
 
 
 
 
255
  VisInject is released for **defensive security research**. Do not use it to target production systems without authorization.
256
  """
257
  )
 
1
  """
2
+ VisInject — HF Space Demo (v1.3)
3
+ =================================
4
  Stage 2 (AnyAttack fusion) only. Stripped-down, CPU-only Gradio app.
5
 
6
  How it works:
7
  1. Pick an attack prompt (7 options) from the dropdown
8
+ 2. The app immediately displays the corresponding **Stage 1 universal
9
+ adversarial image** the abstract noise-like image that encodes the
10
+ target phrase in CLIP feature space (offline-trained on HPC, fetched
11
+ from HF Dataset jeffliulab/visinject).
12
+ 3. Upload a clean image
13
+ 4. The app:
14
  • CLIP ViT-B/32 (cached after first call)
15
  • AnyAttack Decoder, fetched from `jiamingzz/anyattack` on HF
16
+ Encodes universal image 512-d embedding → Decoder → bounded noise
17
+ (eps = 16/255) noise + clean → adversarial image
 
 
18
  5. Returns the adv image + PSNR
19
 
20
  This Space cannot run Stage 1 (multi-VLM PGD optimization) or Stage 3 (VLM
 
22
  which exceeds the free-tier 16 GB RAM / CPU-only budget.
23
 
24
  Source code, full pipeline, and HPC scripts:
25
+ https://github.com/jeffliulab/VisInject
26
  """
27
 
28
  import glob
 
125
  return matches[0]
126
 
127
 
128
+ # ── UI helpers ────────────────────────────────────────────────────
129
 
130
  def _format_prompt_choice(tag: str, phrase: str) -> str:
131
  return f"{tag} — \"{phrase}\""
 
135
  return choice.split(" — ", 1)[0].strip()
136
 
137
 
138
+ def show_universal_image(prompt_choice: str):
139
+ """Triggered on Prompt dropdown change. Returns (universal_path, info_text)."""
140
+ if not prompt_choice:
141
+ return None, ""
142
+ tag = _choice_to_tag(prompt_choice)
143
+ target_phrase = dict(PROMPTS).get(tag, "")
144
+ try:
145
+ universal_path = _get_universal_path(tag)
146
+ except Exception as e:
147
+ return None, f"⚠️ Failed to fetch universal image for '{tag}': {e}"
148
+
149
+ info = (
150
+ f"Stage 1 product: universal_{tag}_2m → {os.path.basename(universal_path)}\n"
151
+ f"Target phrase encoded in CLIP-feature space: \"{target_phrase}\"\n"
152
+ f"\n"
153
+ f"This abstract image was obtained by running PGD optimisation jointly\n"
154
+ f"on Qwen2.5-VL-3B + BLIP-2-OPT-2.7B (the 2-model ensemble) until each\n"
155
+ f"target VLM emitted the target phrase when seeing this image. The\n"
156
+ f"signal lives in CLIP feature space — Stage 2 (next step) decodes it\n"
157
+ f"into bounded noise that can be added to ANY clean photo."
158
+ )
159
+ return universal_path, info
160
+
161
+
162
+ # ── Stage 2 fusion ────────────────────────────────────────────────
163
+
164
  def run_fusion(prompt_choice: str, clean_image_path: str):
165
  """Run Stage 2 fusion. Returns (adv_path, info_text, explanation)."""
166
  if clean_image_path is None:
 
221
  """
222
  # VisInject — Adversarial Prompt Injection Demo
223
 
224
+ Pick an **attack prompt**, see the **Stage 1 universal abstract image** that
225
+ encodes it, then upload a **clean image** and the app fuses the two via
226
+ CLIP ViT-B/32 + the AnyAttack Decoder.
227
 
228
+ The output is visually indistinguishable from your clean image (PSNR ≈ 25 dB),
229
  but Vision-Language Models read it as containing the target phrase.
230
 
231
  **Limitations**: this demo runs only **Stage 2** (fusion). It cannot retrain
 
239
  )
240
 
241
  with gr.Tab("Generate adversarial image"):
242
+ # Step 1: Prompt selection
243
+ prompt_dd = gr.Dropdown(
244
+ choices=choices,
245
+ value=choices[0],
246
+ label="Step 1 — Pick an attack prompt",
247
+ info="The target phrase the attacker wants the VLM to emit",
248
+ )
249
+
250
+ # Step 2: Stage 1 universal image (auto-displayed when prompt changes)
251
  with gr.Row():
252
  with gr.Column():
253
+ universal_img = gr.Image(
254
+ label="Stage 1 — Universal Adversarial Image (abstract; encodes the target in CLIP space)",
255
+ type="filepath",
256
+ interactive=False,
257
+ height=300,
258
+ )
259
+ with gr.Column():
260
+ universal_info = gr.Textbox(
261
+ label="Stage 1 — info",
262
+ lines=8,
263
+ interactive=False,
264
  )
265
+
266
+ # Step 3: Clean image upload + Stage 2 fusion
267
+ with gr.Row():
268
+ with gr.Column():
269
  clean_img = gr.Image(
270
+ label="Step 3 — Upload a clean image",
271
  type="filepath",
272
  sources=["upload", "clipboard"],
273
  )
274
  go_btn = gr.Button(
275
+ "Step 4 — Run Stage 2 fusion → adversarial image",
276
+ variant="primary",
277
  )
278
  with gr.Column():
279
  adv_img = gr.Image(
 
285
  label="What next?", lines=4, interactive=False
286
  )
287
 
288
+ # Wire up: prompt change → show universal image
289
+ prompt_dd.change(
290
+ fn=show_universal_image,
291
+ inputs=[prompt_dd],
292
+ outputs=[universal_img, universal_info],
293
+ )
294
+ # Load default universal image on Space startup
295
+ demo.load(
296
+ fn=show_universal_image,
297
+ inputs=[prompt_dd],
298
+ outputs=[universal_img, universal_info],
299
+ )
300
+
301
+ # Wire up: button click → Stage 2 fusion
302
  go_btn.click(
303
  fn=run_fusion,
304
  inputs=[prompt_dd, clean_img],
 
311
  ## About
312
 
313
  - **Code**: [github.com/jeffliulab/VisInject](https://github.com/jeffliulab/VisInject)
314
+ - **Experimental data** (147 response_pairs, 21 universal images, 147 adv images, v3 dual-axis judge results): [datasets/jeffliulab/visinject](https://huggingface.co/datasets/jeffliulab/visinject)
315
  - **Decoder weights**: [`jiamingzz/anyattack`](https://huggingface.co/jiamingzz/anyattack) — from Zhang et al., *AnyAttack: Towards Large-scale Self-supervised Adversarial Attacks on Vision-language Models*, CVPR 2025.
316
 
317
+ ### v1.3 Methodology
318
+ Attack success is now scored by a **dual-axis LLM judge** (DeepSeek-V4-Pro,
319
+ thinking mode, calibrated against Claude Opus 4.7 with Cohen's κ = 0.79 on
320
+ injection axis). Both axes — **Influence** (did the response change?) and
321
+ **Precise Injection** (did the target concept come through?) — are reported
322
+ separately. See the [paper](https://github.com/jeffliulab/VisInject/blob/main/report/pdf/main.pdf)
323
+ §3.4 for full methodology and the dataset README for reproducibility manifest
324
+ (cache replay path: no API key required to reproduce paper numbers).
325
+
326
  VisInject is released for **defensive security research**. Do not use it to target production systems without authorization.
327
  """
328
  )