xiaohy commited on
Commit
6afc963
·
verified ·
1 Parent(s): 4272ed3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +186 -168
app.py CHANGED
@@ -9,12 +9,10 @@ import gradio as gr
9
 
10
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
11
 
12
-
13
  def load_json(path):
14
  with open(os.path.join(BASE_DIR, path), 'r', encoding='utf-8') as f:
15
  return json.load(f)
16
 
17
-
18
  def clean_text(text):
19
  if not isinstance(text, str):
20
  return str(text)
@@ -23,7 +21,6 @@ def clean_text(text):
23
  text = re.sub(r'[\u200b-\u200f\u2028-\u202f\u2060-\u206f\ufeff]', '', text)
24
  return text.strip()
25
 
26
-
27
  member_data = load_json("data/member.json")
28
  non_member_data = load_json("data/non_member.json")
29
  mia_results = load_json("results/mia_results.json")
@@ -90,7 +87,6 @@ for _i in range(300):
90
  EVAL_POOL.append({'question':_q,'answer':_ans,'type_cn':TYPE_CN[_t],
91
  'baseline':bool(np.random.random()<bl_acc/100),'smooth_0.02':bool(np.random.random()<s002_acc/100),'smooth_0.2':bool(np.random.random()<s02_acc/100)})
92
 
93
-
94
  # ══════════════ 图表 ══════════════
95
 
96
  def fig_gauge(loss_val, m_mean, nm_mean, thr, m_std, nm_std):
@@ -112,7 +108,6 @@ def fig_gauge(loss_val, m_mean, nm_mean, thr, m_std, nm_std):
112
  for s in ['top','right','left']: ax.spines[s].set_visible(False)
113
  ax.set_xlabel('Loss Value', fontsize=9); plt.tight_layout(); return fig
114
 
115
-
116
  def fig_loss_dist():
117
  items = [(k,l,mia_results.get(k,{}).get('auc',0)) for k,l in [('baseline','Baseline'),('smooth_0.02',u'LS(\u03b5=0.02)'),('smooth_0.2',u'LS(\u03b5=0.2)')] if k in full_results]
118
  n = len(items)
@@ -129,7 +124,6 @@ def fig_loss_dist():
129
  ax.spines['top'].set_visible(False); ax.spines['right'].set_visible(False)
130
  plt.tight_layout(); return fig
131
 
132
-
133
  def fig_perturb_dist():
134
  base=full_results.get('baseline',{})
135
  if not base: return plt.figure()
@@ -148,7 +142,6 @@ def fig_perturb_dist():
148
  ax.spines['top'].set_visible(False); ax.spines['right'].set_visible(False)
149
  plt.tight_layout(); return fig
150
 
151
-
152
  def fig_auc_bar():
153
  data=[]
154
  for k,n,c in [('baseline','Baseline','#64748b'),(u'smooth_0.02',u'LS(\u03b5=0.02)','#3b82f6'),('smooth_0.2',u'LS(\u03b5=0.2)','#1d4ed8')]:
@@ -163,7 +156,6 @@ def fig_auc_bar():
163
  ax.legend(fontsize=9); ax.spines['top'].set_visible(False); ax.spines['right'].set_visible(False)
164
  ax.grid(axis='y',alpha=.15); plt.xticks(fontsize=10); plt.tight_layout(); return fig
165
 
166
-
167
  def fig_acc_bar():
168
  data=[]
169
  for k,n,c in [('baseline','Baseline','#64748b'),('smooth_0.02',u'LS(\u03b5=0.02)','#3b82f6'),('smooth_0.2',u'LS(\u03b5=0.2)','#1d4ed8')]:
@@ -178,7 +170,6 @@ def fig_acc_bar():
178
  ax.spines['top'].set_visible(False); ax.spines['right'].set_visible(False)
179
  ax.grid(axis='y',alpha=.15); plt.xticks(fontsize=10); plt.tight_layout(); return fig
180
 
181
-
182
  def fig_tradeoff():
183
  fig,ax=plt.subplots(figsize=(9,6.5)); pts=[]
184
  for k,n,mk,c in [('baseline','Baseline','o','#64748b'),('smooth_0.02',u'LS(\u03b5=0.02)','s','#3b82f6'),('smooth_0.2',u'LS(\u03b5=0.2)','s','#1d4ed8')]:
@@ -195,20 +186,18 @@ def fig_tradeoff():
195
  ax.spines['top'].set_visible(False); ax.spines['right'].set_visible(False)
196
  plt.tight_layout(); return fig
197
 
198
-
199
  # ══════════════ 回调 ══════════════
200
 
201
  def cb_sample(src):
202
  pool=member_data if src=="成员数据(训练集)" else non_member_data
203
  s=pool[np.random.randint(len(pool))]; m=s['metadata']
204
  tm={'calculation':'基础计算','word_problem':'应用题','concept':'概念问答','error_correction':'错题订正'}
205
- md=("| 字段 | 值 |\n|---|---|\n| 姓名 | "+clean_text(str(m.get('name','')))+
206
- " |\n| 学号 | "+clean_text(str(m.get('student_id','')))+
207
- " |\n| 班级 | "+clean_text(str(m.get('class','')))+
208
- " |\n| 成绩 | "+clean_text(str(m.get('score','')))+" 分 |\n| 类型 | "+tm.get(s.get('task_type',''),'')+" |\n")
209
  return md, clean_text(s.get('question','')), clean_text(s.get('answer',''))
210
 
211
-
212
  ATK_MAP = {
213
  u"基线模型 (Baseline)":"baseline",
214
  u"标签平滑 (\u03b5=0.02)":"smooth_0.02",
@@ -218,7 +207,6 @@ ATK_MAP = {
218
  u"输出扰动 (\u03c3=0.02)":"perturbation_0.02",
219
  }
220
 
221
-
222
  def cb_attack(idx, src, target):
223
  is_mem = src=="成员数据(训练集)"
224
  pool = member_data if is_mem else non_member_data
@@ -243,42 +231,45 @@ def cb_attack(idx, src, target):
243
  pl,pc=("训练成员","🔴") if pred else ("非训练成员","🟢")
244
  al,ac=("训练成员","🔴") if is_mem else ("非训练成员","🟢")
245
  if correct and pred and is_mem:
246
- v="⚠️ **攻击成功:隐私泄露**\n\n模型对该本过于熟悉(Loss < 阈值),攻击者成功判定为训练数据。"
247
  elif correct:
248
- v="✅ **判定正确**\n\n攻击者的判定与真实身份一致。"
249
  else:
250
- v="🛡️ **防御成功**\n\n攻击者的判定错误,防御起到了保护作用。"
251
- res=(v+"\n\n**攻击目标**: "+lbl+" | **AUC**: "+f"{auc_v:.4f}"+"\n\n"
252
  "| | 攻击者判定 | 真实身份 |\n|---|---|---|\n"
253
  "| 身份 | "+pc+" "+pl+" | "+ac+" "+al+" |\n"
254
  "| Loss | "+f"{loss:.4f}"+" | 阈值: "+f"{thr:.4f}"+" |\n")
255
- qtxt="**样本 #"+str(idx)+"**\n\n"+clean_text(sample.get('question',''))[:500]
256
  return qtxt, gauge, res
257
 
258
-
259
  EVAL_ACC={u"基线模型":bl_acc,u"标签平滑 (\u03b5=0.02)":s002_acc,u"标签平滑 (\u03b5=0.2)":s02_acc,
260
  u"输出扰动 (\u03c3=0.01)":bl_acc,u"输出扰动 (\u03c3=0.015)":bl_acc,u"输出扰动 (\u03c3=0.02)":bl_acc}
261
  EVAL_KEY={u"基线模型":"baseline",u"标签平滑 (\u03b5=0.02)":"smooth_0.02",u"标签平滑 (\u03b5=0.2)":"smooth_0.2",
262
  u"输出扰动 (\u03c3=0.01)":"baseline",u"输出扰动 (\u03c3=0.015)":"baseline",u"输出扰动 (\u03c3=0.02)":"baseline"}
263
 
264
-
265
  def cb_eval(model):
266
  k=EVAL_KEY.get(model,"baseline"); acc=EVAL_ACC.get(model,bl_acc)
267
  q=EVAL_POOL[np.random.randint(len(EVAL_POOL))]; ok=q.get(k,q.get('baseline',False))
268
  ic="✅ 正确" if ok else "❌ 错误"
269
- note="\n\n> 输出扰动不改变模型参数,准确率与基线一致。" if u"\u03c3" in model else ""
270
- return ("**"+model+"** (准确率: "+f"{acc:.1f}%"+")\n\n"
271
  "| 项目 | 内容 |\n|---|---|\n"
272
  "| 类型 | "+q['type_cn']+" |\n| 题目 | "+q['question']+" |\n"
273
  "| 正确答案 | "+q['answer']+" |\n| 判定 | "+ic+" |"+note)
274
 
275
 
276
- # ══════════════ 极致干净、高度统一的 CSS 皮肤 ══════════════
277
 
278
  CSS = """
279
- /* 1. 干净的背景与字体体系 */
280
  body {
281
- background-color: #f1f5f9 !important;
 
 
 
 
 
282
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important;
283
  }
284
  .gradio-container {
@@ -286,14 +277,14 @@ body {
286
  margin: 40px auto !important;
287
  }
288
 
289
- /* 2. 抛弃深蓝渐变,采用清爽的白色主标题面板 */
290
  .title-area {
291
  background: #ffffff;
292
- padding: 30px 40px;
293
  border-radius: 12px;
294
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
295
- margin-bottom: 20px;
296
- border-left: 6px solid #2563eb; /* 左侧科技蓝修饰线 */
297
  text-align: left;
298
  }
299
  .title-area h1 {
@@ -310,29 +301,28 @@ body {
310
  font-weight: 500;
311
  }
312
 
313
- /* 3. 核心大招:死死锁住所有标签页的大小,防止跳动 */
314
  .tabitem {
315
- background: #ffffff !important;
316
  border-radius: 0 0 12px 12px !important;
317
  border: 1px solid #e2e8f0 !important;
318
  border-top: none !important;
319
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05) !important;
320
  padding: 32px 40px !important;
321
 
322
- /* 就是这两行代码,让你的每一页大小绝对一致 */
323
  height: 760px !important;
324
  max-height: 760px !important;
325
  overflow-y: auto !important;
326
  overflow-x: hidden !important;
327
  }
328
 
329
- /* 美化内页的滚动条,让它看起来更高级 */
330
  .tabitem::-webkit-scrollbar { width: 6px; }
331
  .tabitem::-webkit-scrollbar-track { background: transparent; }
332
  .tabitem::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
333
  .tabitem::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
334
 
335
- /* 4. Tab 导航栏的极简设计 */
336
  .tab-nav {
337
  border-bottom: none !important;
338
  gap: 4px !important;
@@ -343,7 +333,7 @@ body {
343
  padding: 12px 24px !important;
344
  font-weight: 600 !important;
345
  color: #64748b !important;
346
- background: #f1f5f9 !important;
347
  border: 1px solid #e2e8f0 !important;
348
  border-bottom: none !important;
349
  border-radius: 10px 10px 0 0 !important;
@@ -361,7 +351,7 @@ body {
361
  box-shadow: 0 -4px 6px -2px rgba(0,0,0,0.02) !important;
362
  }
363
 
364
- /* 5. 内部排版(去掉默认的黑粗体,改为高级灰黑) */
365
  .prose h2 {
366
  font-size: 1.3rem !important;
367
  color: #0f172a !important;
@@ -404,7 +394,7 @@ body {
404
  .prose tr:last-child td { border-bottom: none !important; }
405
  .prose tr:hover td { background: #f0f9ff !important; }
406
 
407
- /* 7. 纯色扁平化高级按钮 */
408
  button.primary {
409
  background: #2563eb !important;
410
  color: white !important;
@@ -421,7 +411,7 @@ button.primary:hover {
421
  box-shadow: 0 6px 10px -1px rgba(37, 99, 235, 0.3) !important;
422
  }
423
 
424
- /* 8. 提示/警报框 */
425
  .prose blockquote {
426
  border-left: 4px solid #3b82f6 !important;
427
  background: #eff6ff !important;
@@ -432,165 +422,193 @@ button.primary:hover {
432
  margin: 1.5em 0 !important;
433
  }
434
 
435
- /* 隐藏底部水印 */
 
 
 
 
 
 
 
 
 
 
 
436
  footer { display: none !important; }
437
  """
438
 
439
  with gr.Blocks(title="MIA攻防研究", theme=gr.themes.Base(), css=CSS) as demo:
440
 
441
  gr.HTML("""<div class="title-area">
442
- <h1>教育大模型中的成员推理攻击及其防御研究</h1>
443
- <p>Membership Inference Attack & Defense on Educational LLM</p>
444
  </div>""")
445
 
446
- # ═══════ Tab 1 ═══════
447
- with gr.Tab("实验总览"):
448
- gr.Markdown(
449
- "## 研究背景与目标\n\n"
450
- "大语言模型在教育领域的应用日益广泛(如AI数学辅导),模型训练不可避免地接触学生敏感数据。"
451
- "**成员推理攻击 (MIA)** 可判断某条数据是否参与了训练,构成隐私威胁。\n\n"
452
- "本研究基于 **" + model_name + "** 微调的数学辅导模型,验证MIA风险的存在性,"
453
- "并探索 **标签平滑**训练期 **输出扰动**(推理期)两类防御策略的有效性及其对模型效用的影响。\n\n---")
454
-
455
- gr.Markdown("## 实验核心指标\n")
456
- gr.Markdown(
457
- "| 策略 | AUC | 准确率 | 说明 |\n|---|---|---|---|\n"
458
- "| **基线(无防御)** | **" + f"{bl_auc:.4f}" + "** | " + f"{bl_acc:.1f}%" + " | 攻击风险基准 |\n"
459
- "| " + u"LS(\u03b5=0.02)" + " | " + f"{s002_auc:.4f}" + " | " + f"{s002_acc:.1f}%" + " | 训练期防御 |\n"
460
- "| " + u"LS(\u03b5=0.2)" + " | " + f"{s02_auc:.4f}" + " | " + f"{s02_acc:.1f}%" + " | 训练期防御 |\n"
461
- "| " + u"OP(\u03c3=0.01)" + " | " + f"{op001_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 推理期防御 |\n"
462
- "| " + u"OP(\u03c3=0.015)" + " | " + f"{op0015_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 推理期防御 |\n"
463
- "| " + u"OP(\u03c3=0.02)" + " | " + f"{op002_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 推理期防御 |\n\n"
464
- "> AUC越接近0.5 = 防御越有效 | 准确率越高 = 模型效用越好\n\n---")
465
-
466
- gr.Markdown(
467
- "## 实验流程\n\n"
468
- "| 阶段 | 内容 | 方法 |\n|---|---|---|\n"
469
- "| 1. 数据准备 | 2000条数学辅导对话 | 模板化生成,含姓名/学号/成绩 |\n"
470
- "| 2. 基线训练 | " + model_name + " + LoRA | 标准微调(r=8, alpha=16, 10 epochs) |\n"
471
- "| 3. 防御训练 | " + u"\u03b5=0.02 / \u03b5=0.2" + " | 两组标签平滑参数分别训练 |\n"
472
- "| 4. 攻击测试 | 3个模型 + 3组扰动 | Loss阈值判定,AUC评估 |\n"
473
- "| 5. 效用评估 | 300道学题 | 6种配置分别测试准确率 |\n"
474
- "| 6. 综合分析 | 隐私-效用权衡 | 定量对比可视化 |\n")
475
-
476
- # ═══════ Tab 2 ═══════
477
- with gr.Tab("数据与模型"):
478
- gr.Markdown(
479
- "## 实验数据集\n\n"
480
- "| 数据组 | 数量 | 用途 | 说明 |\n|---|---|---|---|\n"
481
- "| 成员数据 | 1000条 | 模型训练 | 模型会\"记住\",Loss偏低 |\n"
482
- "| 非成员数据 | 1000条 | 攻击对照 | 模型\"没见过\",Loss偏高 |\n\n"
483
- "> 两组数据格式完全相同(均含隐私字段),这是MIA实验的标准设置——攻击者无法从格式区分\n\n"
484
- "| 任务类�� | 数量 | 占比 |\n|---|---|---|\n"
485
- "| 基础计算 | 800 | 40% |\n| 应用题 | 600 | 30% |\n| 概念问答 | 400 | 20% |\n| 错题订正 | 200 | 10% |\n")
486
- gr.Markdown("### 数据样例浏览")
 
 
 
 
 
487
  with gr.Row(equal_height=True):
488
  with gr.Column(scale=2):
489
- d_src = gr.Radio(["成员数据(训练集)","非成员数据(测试集)"], value="成员数据(训练集)", label="数据来源")
490
- d_btn = gr.Button("随机提取样本", variant="primary")
491
  d_meta = gr.Markdown()
492
  with gr.Column(scale=3):
493
- d_q = gr.Textbox(label="学生提问", lines=4, interactive=False)
494
- d_a = gr.Textbox(label="标准回答", lines=4, interactive=False)
495
  d_btn.click(cb_sample, [d_src], [d_meta, d_q, d_a])
496
 
497
- # ═══════ Tab 3 ═══════
498
- with gr.Tab("攻击与防御验证"):
499
- gr.Markdown("## 成员推理攻击交互演示\n\n"
500
- "选择攻击目标数据源,系统实时计算Loss并判定成员身份通过切换不同目标形成对照实验。")
501
  with gr.Row(equal_height=True):
502
  with gr.Column(scale=2):
503
  a_target = gr.Radio([u"基线模型 (Baseline)",u"标签平滑 (\u03b5=0.02)",u"标签平滑 (\u03b5=0.2)",
504
  u"输出扰动 (\u03c3=0.01)",u"输出扰动 (\u03c3=0.015)",u"输出扰动 (\u03c3=0.02)"],
505
- value=u"基线模型 (Baseline)", label="攻击目标")
506
  a_src = gr.Radio(["成员数据(训练集)","非成员数据(测试集)"], value="成员数据(训练集)", label="数据来源")
507
- a_idx = gr.Slider(0, 999, step=1, value=12, label="样本 ID")
508
- a_btn = gr.Button("执行成员推理攻击", variant="primary", size="lg")
509
  a_qtxt = gr.Markdown()
510
  with gr.Column(scale=3):
511
- a_gauge = gr.Plot(label="Loss位置判定")
512
  a_res = gr.Markdown()
513
  a_btn.click(cb_attack, [a_idx, a_src, a_target], [a_qtxt, a_gauge, a_res])
514
 
515
- # ═══════ Tab 4 ═══════
516
- with gr.Tab("防御效果分析"):
517
- gr.Markdown("## MIA攻击AUC对比\n\n> 柱子越矮 = AUC越低 = 攻击越难成功 = 防御越有效")
518
- gr.Plot(value=fig_auc_bar())
519
-
520
- gr.Markdown("## Loss分布对比\n### 三个模型(训练期防御效果)\n\n> 蓝色=成员,红色=非成员。两色重叠越多 = 攻击者越难区分")
521
- gr.Plot(value=fig_loss_dist())
522
- gr.Markdown("### 输出扰动效果(推理期防御)\n\n> 在基线模型Loss上加噪声,随噪声增大分布更加重叠")
523
- gr.Plot(value=fig_perturb_dist())
524
-
525
- gr.Markdown(
526
- "## 完整实验数据\n\n"
527
- "| 策略 | 类型 | AUC | 准确率 | AUC变化 |\n|---|---|---|---|---|\n"
528
- "| 基线 | — | " + f"{bl_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | — |\n"
529
- "| " + u"LS(\u03b5=0.02)" + " | 训练期 | " + f"{s002_auc:.4f}" + " | " + f"{s002_acc:.1f}%" + " | " + f"{s002_auc-bl_auc:+.4f}" + " |\n"
530
- "| " + u"LS(\u03b5=0.2)" + " | 训练期 | " + f"{s02_auc:.4f}" + " | " + f"{s02_acc:.1f}%" + " | " + f"{s02_auc-bl_auc:+.4f}" + " |\n"
531
- "| " + u"OP(\u03c3=0.01)" + " | 推理期 | " + f"{op001_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | " + f"{op001_auc-bl_auc:+.4f}" + " |\n"
532
- "| " + u"OP(\u03c3=0.015)" + " | 推理期 | " + f"{op0015_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | " + f"{op0015_auc-bl_auc:+.4f}" + " |\n"
533
- "| " + u"OP(\u03c3=0.02)" + " | 推理期 | " + f"{op002_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | " + f"{op002_auc-bl_auc:+.4f}" + " |\n\n"
534
- "## 防御机制说明\n\n"
535
- "| 维度 | 标签平滑 | 输出扰动 |\n|---|---|---|\n"
536
- "| 阶段 | 训练期 | 推理期 |\n"
537
- "| 原理 | 软化标签降低记忆 | Loss加噪遮蔽信号 |\n"
538
- "| 需重训 | | |\n"
539
- "| 效用影响 | 取决于参数 | |\n"
540
- "| 部署 | 训练时介入 | 即插即用 |\n\n"
541
- "**标签平滑**: y_smooth = (1 - " + u"\u03b5" + ") * y_onehot + " + u"\u03b5" + " / V\n\n"
542
- "**输出扰动**: L_perturbed = L_original + N(0, " + u"\u03c3" + u"\u00b2" + ")\n")
543
-
544
- for fn, cap in [("fig1_loss_distribution_comparison.png","Loss分布对比"),
545
- ("fig2_privacy_utility_tradeoff_fixed.png","隐私-效用权衡"),
546
- ("fig3_defense_comparison_bar.png","防御策略AUC对比")]:
547
- p = os.path.join(BASE_DIR,"figures",fn)
548
- if os.path.exists(p):
549
- gr.Markdown("### "+cap); gr.Image(value=p, show_label=False, height=420)
550
-
551
- # ═══════ Tab 5 ═══════
552
- with gr.Tab("效用评估"):
553
- gr.Markdown("## 模型效用测试\n\n> 基于300道数学测试题评估各策略对模型实际能力的影响")
 
 
 
554
  with gr.Row(equal_height=True):
555
  with gr.Column(): gr.Plot(value=fig_acc_bar())
556
  with gr.Column(): gr.Plot(value=fig_tradeoff())
557
- gr.Markdown("### 在线效用演示\n\n从测试题库中随机抽取,查看不同模型/策略的作答情况。")
 
558
  with gr.Row(equal_height=True):
559
  with gr.Column(scale=1):
560
  e_model = gr.Radio([u"基线模型",u"标签平滑 (\u03b5=0.02)",u"标签平滑 (\u03b5=0.2)",
561
- u"输出扰动 (\u03c3=0.01)",u"输出扰动 (\u03c3=0.015)",u"输出扰动 (\u03c3=0.02)"], value=u"基线模型", label="选择模型")
562
- e_btn = gr.Button("随机抽题测试", variant="primary")
563
  with gr.Column(scale=2):
564
  e_res = gr.Markdown()
565
  e_btn.click(cb_eval, [e_model], [e_res])
566
 
567
- # ═══════ Tab 6 ═══════
568
- with gr.Tab("研究结论"):
569
- gr.Markdown(
570
- "## 核心研究发现\n\n---\n\n"
571
- "### 一、教育大模型存在可量化的MIA风险\n\n"
572
- "基线模型 AUC = **" + f"{bl_auc:.4f}" + "** > 0.5,成员平均Loss (" + f"{bl_m_mean:.4f}"
573
- + ") < 非成员 (" + f"{bl_nm_mean:.4f}" + "),模型对训练数据存在可利用的记忆效应。\n\n---\n\n"
574
- "### 二、标签平滑(训练期防御)\n\n"
575
- "| 参数 | AUC | 准确率 | 分析 |\n|---|---|---|---|\n"
576
- "| 基线 | " + f"{bl_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 无防御 |\n"
577
- "| " + u"\u03b5=0.02" + " | " + f"{s002_auc:.4f}" + " | " + f"{s002_acc:.1f}%" + " | 正则化提升泛化 |\n"
578
- "| " + u"\u03b5=0.2" + " | " + f"{s02_auc:.4f}" + " | " + f"{s02_acc:.1f}%" + " | 防御增强 |\n\n---\n\n"
579
- "### 三、输出扰动(推理期防御)\n\n"
580
- "| 参数 | AUC | AUC降幅 | 准确率 |\n|---|---|---|---|\n"
581
- "| " + u"\u03c3=0.01" + " | " + f"{op001_auc:.4f}" + " | " + f"{bl_auc-op001_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " |\n"
582
- "| " + u"\u03c3=0.015" + " | " + f"{op0015_auc:.4f}" + " | " + f"{bl_auc-op0015_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " |\n"
583
- "| " + u"OP(\u03c3=0.02)" + " | " + f"{op002_auc:.4f}" + " | " + f"{bl_auc-op002_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " |\n\n"
584
- "零效用损失,适合已部署系统的后期加固。\n\n---\n\n"
585
- "### 四、隐私-效用权衡总结\n\n"
586
- "| 策略 | AUC | 准确率 | 隐私 | 效用 |\n|---|---|---|---|---|\n"
587
- "| 基线 | " + f"{bl_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 风险最高 | 基准 |\n"
588
- "| " + u"LS(\u03b5=0.02)" + " | " + f"{s002_auc:.4f}" + " | " + f"{s002_acc:.1f}%" + " | 降低 | 提升 |\n"
589
- "| " + u"LS(\u03b5=0.2)" + " | " + f"{s02_auc:.4f}" + " | " + f"{s02_acc:.1f}%" + " | 显著降低 | 可接受 |\n"
590
- "| " + u"OP(\u03c3=0.02)" + " | " + f"{op002_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 显著降低 | 不变 |\n\n"
591
- "两类策略机制互补:标签平滑从训练阶段降低记忆,输出扰动从推理阶段遮蔽信号。可根据实际需求灵活选择。\n")
592
-
593
- gr.HTML("<div style='text-align:center;color:#94a3b8;font-size:.82rem;padding:16px 0 8px'>"
594
- "</div>")
 
 
 
 
 
 
 
 
595
 
596
  demo.launch()
 
9
 
10
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
11
 
 
12
  def load_json(path):
13
  with open(os.path.join(BASE_DIR, path), 'r', encoding='utf-8') as f:
14
  return json.load(f)
15
 
 
16
  def clean_text(text):
17
  if not isinstance(text, str):
18
  return str(text)
 
21
  text = re.sub(r'[\u200b-\u200f\u2028-\u202f\u2060-\u206f\ufeff]', '', text)
22
  return text.strip()
23
 
 
24
  member_data = load_json("data/member.json")
25
  non_member_data = load_json("data/non_member.json")
26
  mia_results = load_json("results/mia_results.json")
 
87
  EVAL_POOL.append({'question':_q,'answer':_ans,'type_cn':TYPE_CN[_t],
88
  'baseline':bool(np.random.random()<bl_acc/100),'smooth_0.02':bool(np.random.random()<s002_acc/100),'smooth_0.2':bool(np.random.random()<s02_acc/100)})
89
 
 
90
  # ══════════════ 图表 ══════════════
91
 
92
  def fig_gauge(loss_val, m_mean, nm_mean, thr, m_std, nm_std):
 
108
  for s in ['top','right','left']: ax.spines[s].set_visible(False)
109
  ax.set_xlabel('Loss Value', fontsize=9); plt.tight_layout(); return fig
110
 
 
111
  def fig_loss_dist():
112
  items = [(k,l,mia_results.get(k,{}).get('auc',0)) for k,l in [('baseline','Baseline'),('smooth_0.02',u'LS(\u03b5=0.02)'),('smooth_0.2',u'LS(\u03b5=0.2)')] if k in full_results]
113
  n = len(items)
 
124
  ax.spines['top'].set_visible(False); ax.spines['right'].set_visible(False)
125
  plt.tight_layout(); return fig
126
 
 
127
  def fig_perturb_dist():
128
  base=full_results.get('baseline',{})
129
  if not base: return plt.figure()
 
142
  ax.spines['top'].set_visible(False); ax.spines['right'].set_visible(False)
143
  plt.tight_layout(); return fig
144
 
 
145
  def fig_auc_bar():
146
  data=[]
147
  for k,n,c in [('baseline','Baseline','#64748b'),(u'smooth_0.02',u'LS(\u03b5=0.02)','#3b82f6'),('smooth_0.2',u'LS(\u03b5=0.2)','#1d4ed8')]:
 
156
  ax.legend(fontsize=9); ax.spines['top'].set_visible(False); ax.spines['right'].set_visible(False)
157
  ax.grid(axis='y',alpha=.15); plt.xticks(fontsize=10); plt.tight_layout(); return fig
158
 
 
159
  def fig_acc_bar():
160
  data=[]
161
  for k,n,c in [('baseline','Baseline','#64748b'),('smooth_0.02',u'LS(\u03b5=0.02)','#3b82f6'),('smooth_0.2',u'LS(\u03b5=0.2)','#1d4ed8')]:
 
170
  ax.spines['top'].set_visible(False); ax.spines['right'].set_visible(False)
171
  ax.grid(axis='y',alpha=.15); plt.xticks(fontsize=10); plt.tight_layout(); return fig
172
 
 
173
  def fig_tradeoff():
174
  fig,ax=plt.subplots(figsize=(9,6.5)); pts=[]
175
  for k,n,mk,c in [('baseline','Baseline','o','#64748b'),('smooth_0.02',u'LS(\u03b5=0.02)','s','#3b82f6'),('smooth_0.2',u'LS(\u03b5=0.2)','s','#1d4ed8')]:
 
186
  ax.spines['top'].set_visible(False); ax.spines['right'].set_visible(False)
187
  plt.tight_layout(); return fig
188
 
 
189
  # ══════════════ 回调 ══════════════
190
 
191
  def cb_sample(src):
192
  pool=member_data if src=="成员数据(训练集)" else non_member_data
193
  s=pool[np.random.randint(len(pool))]; m=s['metadata']
194
  tm={'calculation':'基础计算','word_problem':'应用题','concept':'概念问答','error_correction':'错题订正'}
195
+ md=("| 字段 | 值 |\n|---|---|\n| 👤 姓名 | "+clean_text(str(m.get('name','')))+
196
+ " |\n| 🆔 学号 | "+clean_text(str(m.get('student_id','')))+
197
+ " |\n| 🏫 班级 | "+clean_text(str(m.get('class','')))+
198
+ " |\n| 💯 成绩 | "+clean_text(str(m.get('score','')))+" 分 |\n| 🔖 类型 | "+tm.get(s.get('task_type',''),'')+" |\n")
199
  return md, clean_text(s.get('question','')), clean_text(s.get('answer',''))
200
 
 
201
  ATK_MAP = {
202
  u"基线模型 (Baseline)":"baseline",
203
  u"标签平滑 (\u03b5=0.02)":"smooth_0.02",
 
207
  u"输出扰动 (\u03c3=0.02)":"perturbation_0.02",
208
  }
209
 
 
210
  def cb_attack(idx, src, target):
211
  is_mem = src=="成员数据(训练集)"
212
  pool = member_data if is_mem else non_member_data
 
231
  pl,pc=("训练成员","🔴") if pred else ("非训练成员","🟢")
232
  al,ac=("训练成员","🔴") if is_mem else ("非训练成员","🟢")
233
  if correct and pred and is_mem:
234
+ v="⚠️ **攻击成功:隐私泄露**\n\n> 模型对该���本过于熟悉(Loss < 阈值),攻击者成功判定为训练数据。"
235
  elif correct:
236
+ v="✅ **判定正确**\n\n> 攻击者的判定与真实身份一致。"
237
  else:
238
+ v="🛡️ **防御成功**\n\n> 攻击者的判定错误,防御起到了保护作用。"
239
+ res=(v+"\n\n**🎯 攻击目标**: "+lbl+" | **📊 AUC**: "+f"{auc_v:.4f}"+"\n\n"
240
  "| | 攻击者判定 | 真实身份 |\n|---|---|---|\n"
241
  "| 身份 | "+pc+" "+pl+" | "+ac+" "+al+" |\n"
242
  "| Loss | "+f"{loss:.4f}"+" | 阈值: "+f"{thr:.4f}"+" |\n")
243
+ qtxt="**📝 样本题号 #"+str(idx)+"**\n\n"+clean_text(sample.get('question',''))[:500]
244
  return qtxt, gauge, res
245
 
 
246
  EVAL_ACC={u"基线模型":bl_acc,u"标签平滑 (\u03b5=0.02)":s002_acc,u"标签平滑 (\u03b5=0.2)":s02_acc,
247
  u"输出扰动 (\u03c3=0.01)":bl_acc,u"输出扰动 (\u03c3=0.015)":bl_acc,u"输出扰动 (\u03c3=0.02)":bl_acc}
248
  EVAL_KEY={u"基线模型":"baseline",u"标签平滑 (\u03b5=0.02)":"smooth_0.02",u"标签平滑 (\u03b5=0.2)":"smooth_0.2",
249
  u"输出扰动 (\u03c3=0.01)":"baseline",u"输出扰动 (\u03c3=0.015)":"baseline",u"输出扰动 (\u03c3=0.02)":"baseline"}
250
 
 
251
  def cb_eval(model):
252
  k=EVAL_KEY.get(model,"baseline"); acc=EVAL_ACC.get(model,bl_acc)
253
  q=EVAL_POOL[np.random.randint(len(EVAL_POOL))]; ok=q.get(k,q.get('baseline',False))
254
  ic="✅ 正确" if ok else "❌ 错误"
255
+ note="\n\n> 💡 输出扰动不改变模型参数,准确率与基线一致。" if u"\u03c3" in model else ""
256
+ return ("**🖥️ 模型**: "+model+" (准确率: "+f"{acc:.1f}%"+")\n\n"
257
  "| 项目 | 内容 |\n|---|---|\n"
258
  "| 类型 | "+q['type_cn']+" |\n| 题目 | "+q['question']+" |\n"
259
  "| 正确答案 | "+q['answer']+" |\n| 判定 | "+ic+" |"+note)
260
 
261
 
262
+ # ══════════════ 界面美化 CSS ══════════════
263
 
264
  CSS = """
265
+ /* 1. 带有十字准星网格全局背景 */
266
  body {
267
+ background-color: #f8fafc !important;
268
+ background-image:
269
+ linear-gradient(#e2e8f0 1px, transparent 1px),
270
+ linear-gradient(90deg, #e2e8f0 1px, transparent 1px) !important;
271
+ background-size: 20px 20px !important;
272
+ background-position: center center !important;
273
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important;
274
  }
275
  .gradio-container {
 
277
  margin: 40px auto !important;
278
  }
279
 
280
+ /* 2. 科技感悬浮 Title 面板 */
281
  .title-area {
282
  background: #ffffff;
283
+ padding: 28px 40px;
284
  border-radius: 12px;
285
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
286
+ margin-bottom: 24px;
287
+ border-left: 6px solid #2563eb;
288
  text-align: left;
289
  }
290
  .title-area h1 {
 
301
  font-weight: 500;
302
  }
303
 
304
+ /* 3. 死死锁住所有标签页的大小,防止跳动 (760px 高度) */
305
  .tabitem {
306
+ background: rgba(255, 255, 255, 0.98) !important;
307
  border-radius: 0 0 12px 12px !important;
308
  border: 1px solid #e2e8f0 !important;
309
  border-top: none !important;
310
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05) !important;
311
  padding: 32px 40px !important;
312
 
 
313
  height: 760px !important;
314
  max-height: 760px !important;
315
  overflow-y: auto !important;
316
  overflow-x: hidden !important;
317
  }
318
 
319
+ /* 优雅隐藏式滚动条 */
320
  .tabitem::-webkit-scrollbar { width: 6px; }
321
  .tabitem::-webkit-scrollbar-track { background: transparent; }
322
  .tabitem::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
323
  .tabitem::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
324
 
325
+ /* 4. Tab 导航栏拟设计 */
326
  .tab-nav {
327
  border-bottom: none !important;
328
  gap: 4px !important;
 
333
  padding: 12px 24px !important;
334
  font-weight: 600 !important;
335
  color: #64748b !important;
336
+ background: #e2e8f0 !important;
337
  border: 1px solid #e2e8f0 !important;
338
  border-bottom: none !important;
339
  border-radius: 10px 10px 0 0 !important;
 
351
  box-shadow: 0 -4px 6px -2px rgba(0,0,0,0.02) !important;
352
  }
353
 
354
+ /* 5. 内部排版优化 */
355
  .prose h2 {
356
  font-size: 1.3rem !important;
357
  color: #0f172a !important;
 
394
  .prose tr:last-child td { border-bottom: none !important; }
395
  .prose tr:hover td { background: #f0f9ff !important; }
396
 
397
+ /* 7. 纯色科技感按钮 */
398
  button.primary {
399
  background: #2563eb !important;
400
  color: white !important;
 
411
  box-shadow: 0 6px 10px -1px rgba(37, 99, 235, 0.3) !important;
412
  }
413
 
414
+ /* 8. 提示框 */
415
  .prose blockquote {
416
  border-left: 4px solid #3b82f6 !important;
417
  background: #eff6ff !important;
 
422
  margin: 1.5em 0 !important;
423
  }
424
 
425
+ /* 9. Accordion 折叠面板美化 */
426
+ .wrap.svelte-182y6v9 {
427
+ border: 1px solid #e2e8f0 !important;
428
+ border-radius: 8px !important;
429
+ background: #f8fafc !important;
430
+ box-shadow: none !important;
431
+ }
432
+ .label.svelte-182y6v9 {
433
+ font-weight: 600 !important;
434
+ color: #0f172a !important;
435
+ }
436
+
437
  footer { display: none !important; }
438
  """
439
 
440
  with gr.Blocks(title="MIA攻防研究", theme=gr.themes.Base(), css=CSS) as demo:
441
 
442
  gr.HTML("""<div class="title-area">
443
+ <h1>🎓 教育大模型中的成员推理攻击及其防御研究</h1>
444
+ <p>Membership Inference Attack & Defense on Educational LLM Dashboard</p>
445
  </div>""")
446
 
447
+ # ═══════ Tab 1: 实验总览 (引入折叠面板展开) ═══════
448
+ with gr.Tab("📊 实验总览"):
449
+ gr.Markdown("## 📌 研究背景与目标\n\n大语言模型在教育领域的应用日益广泛(如AI数学辅导),模型训练不可避免地接触学生敏感数据。**成员推理攻击 (MIA)** 可判断某条数据是否参与了训练,构成隐私威胁。\n\n本研究基于 **" + model_name + "** 微调的数学辅导模型,验证MIA风险的存在性,并探索 **标签平滑**(训练期)与 **输出扰动**(推理期)两类防御策略的有效性及其对模型效用的影响。")
450
+
451
+ with gr.Accordion("📈 展开查看:实验核心指标", open=True):
452
+ gr.Markdown(
453
+ "| 🛡️ 策略配置 | 📊 AUC | 🎯 准确率 | 💡 说明 |\n|---|---|---|---|\n"
454
+ "| **基线无防御** | **" + f"{bl_auc:.4f}" + "** | " + f"{bl_acc:.1f}%" + " | 攻击风险基准 |\n"
455
+ "| " + u"LS(\u03b5=0.02)" + " | " + f"{s002_auc:.4f}" + " | " + f"{s002_acc:.1f}%" + " | 训练期防御 |\n"
456
+ "| " + u"LS(\u03b5=0.2)" + " | " + f"{s02_auc:.4f}" + " | " + f"{s02_acc:.1f}%" + " | 训练期防御 |\n"
457
+ "| " + u"OP(\u03c3=0.01)" + " | " + f"{op001_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 推理期防御 |\n"
458
+ "| " + u"OP(\u03c3=0.015)" + " | " + f"{op0015_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 推理期防御 |\n"
459
+ "| " + u"OP(\u03c3=0.02)" + " | " + f"{op002_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 推理期防御 |\n\n"
460
+ "> 💡 **指标提示**: AUC越接近0.5 = 防御越有效;准确率越高 = 模型效用越好。"
461
+ )
462
+
463
+ with gr.Accordion("🚀 展开查看:实验流程规划", open=False):
464
+ gr.Markdown(
465
+ "| 阶段 | 内容 | 方法 |\n|---|---|---|\n"
466
+ "| 1. 数据准备 | 2000条数学辅导对话 | 模板化生成,含姓名/学号/成绩 |\n"
467
+ "| 2. 基线训练 | " + model_name + " + LoRA | 标准微调(r=8, alpha=16, 10 epochs) |\n"
468
+ "| 3. 防御训练 | " + u"\u03b5=0.02 / \u03b5=0.2" + " | 两组标签平滑参数分别训练 |\n"
469
+ "| 4. 攻击测试 | 3个模型 + 3组扰动 | Loss阈值判定,AUC评估 |\n"
470
+ "| 5. 效用评估 | 300道数学 | 6种配置分别测试准确率 |\n"
471
+ "| 6. 综合分析 | 隐私-效用权衡 | 定量对比与可视化 |\n"
472
+ )
473
+
474
+ # ═══════ Tab 2:据与模型 ═══════
475
+ with gr.Tab("📁 数据模型"):
476
+ gr.Markdown("## 📦 实验数据集概况")
477
+
478
+ with gr.Row():
479
+ with gr.Column(scale=1):
480
+ gr.Markdown(
481
+ "| 数据组 | 数量 | 用途 | 说明 |\n|---|---|---|---|\n"
482
+ "| 🔴 成员数据 | 1000条 | 模型训练 | 模型会\"记住\",Loss偏低 |\n"
483
+ "| 🟢 非成员数据 | 1000条 | 攻击对照 | 模型\"没见过\",Loss偏高 |\n\n"
484
+ "> ⚠️ 两组数据格式完全相同(均含隐私字段),这是MIA实验的标准设置——攻击者无法从格式区分"
485
+ )
486
+ with gr.Column(scale=1):
487
+ gr.Markdown(
488
+ "| 任务类别 | 数量 | 占比 |\n|---|---|---|\n"
489
+ "| 🧮 基础计算 | 800 | 40% |\n| 📝 应用题 | 600 | 30% |\n| 🧠 概念问答 | 400 | 20% |\n| ✍️ 错题订正 | 200 | 10% |\n"
490
+ )
491
+
492
+ gr.Markdown("### 🔍 数据样例浏览提取")
493
  with gr.Row(equal_height=True):
494
  with gr.Column(scale=2):
495
+ d_src = gr.Radio(["成员数据(训练集)","非成员数据(测试集)"], value="成员数据(训练集)", label="选择靶向数据来源")
496
+ d_btn = gr.Button("🎲 随机提取样本", variant="primary")
497
  d_meta = gr.Markdown()
498
  with gr.Column(scale=3):
499
+ d_q = gr.Textbox(label="🧑‍🎓 学生提问 (Prompt)", lines=4, interactive=False)
500
+ d_a = gr.Textbox(label="🤖 标准回答 (Ground Truth)", lines=4, interactive=False)
501
  d_btn.click(cb_sample, [d_src], [d_meta, d_q, d_a])
502
 
503
+ # ═══════ Tab 3: 攻击与防御验证 ═══════
504
+ with gr.Tab("🎯 攻击验证"):
505
+ gr.Markdown("## 🕵️ 成员推理攻击交互演示\n\n"
506
+ "配置攻击目标实体与数据源,系统将执行 Loss 计算并映射攻击边界,以此判定数据归属。")
507
  with gr.Row(equal_height=True):
508
  with gr.Column(scale=2):
509
  a_target = gr.Radio([u"基线模型 (Baseline)",u"标签平滑 (\u03b5=0.02)",u"标签平滑 (\u03b5=0.2)",
510
  u"输出扰动 (\u03c3=0.01)",u"输出扰动 (\u03c3=0.015)",u"输出扰动 (\u03c3=0.02)"],
511
+ value=u"基线模型 (Baseline)", label="选择攻击目标")
512
  a_src = gr.Radio(["成员数据(训练集)","非成员数据(测试集)"], value="成员数据(训练集)", label="数据来源")
513
+ a_idx = gr.Slider(0, 999, step=1, value=12, label="定位样本 ID")
514
+ a_btn = gr.Button("执行成员推理攻击", variant="primary", size="lg")
515
  a_qtxt = gr.Markdown()
516
  with gr.Column(scale=3):
517
+ a_gauge = gr.Plot(label="Loss位置判定 (Decision Boundary)")
518
  a_res = gr.Markdown()
519
  a_btn.click(cb_attack, [a_idx, a_src, a_target], [a_qtxt, a_gauge, a_res])
520
 
521
+ # ═══════ Tab 4: 防御效果分析 ═══════
522
+ with gr.Tab("🛡️ 防御分析"):
523
+ with gr.Accordion("📊 展开查看:防御对比直方图", open=False):
524
+ gr.Markdown("### MIA攻击AUC对比\n\n> 柱子��矮 = AUC越低 = 攻击越难成功 = 防御越有效")
525
+ gr.Plot(value=fig_auc_bar())
526
+
527
+ gr.Markdown("### Loss分布对比\n#### 三个模型(训练期防御效果)\n\n> 蓝色=成员,红色=非成员。两色重叠越多 = 攻击者越难区分")
528
+ gr.Plot(value=fig_loss_dist())
529
+ gr.Markdown("#### 输出扰动效果(推理期防御)\n\n> 在基线模型Loss上加噪声,随噪声增大分布更加重叠")
530
+ gr.Plot(value=fig_perturb_dist())
531
+
532
+ with gr.Accordion("⚙️ 展开查看:完整实验数据与机制说明", open=True):
533
+ gr.Markdown(
534
+ "### 完整实验数据表\n\n"
535
+ "| 策略 | 类型 | AUC | 准确率 | AUC变化 |\n|---|---|---|---|---|\n"
536
+ "| 基线 | | " + f"{bl_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | |\n"
537
+ "| " + u"LS(\u03b5=0.02)" + " | 训练期 | " + f"{s002_auc:.4f}" + " | " + f"{s002_acc:.1f}%" + " | " + f"{s002_auc-bl_auc:+.4f}" + " |\n"
538
+ "| " + u"LS(\u03b5=0.2)" + " | 训练期 | " + f"{s02_auc:.4f}" + " | " + f"{s02_acc:.1f}%" + " | " + f"{s02_auc-bl_auc:+.4f}" + " |\n"
539
+ "| " + u"OP(\u03c3=0.01)" + " | 推理期 | " + f"{op001_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | " + f"{op001_auc-bl_auc:+.4f}" + " |\n"
540
+ "| " + u"OP(\u03c3=0.015)" + " | 推理期 | " + f"{op0015_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | " + f"{op0015_auc-bl_auc:+.4f}" + " |\n"
541
+ "| " + u"OP(\u03c3=0.02)" + " | 推理期 | " + f"{op002_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | " + f"{op002_auc-bl_auc:+.4f}" + " |\n\n"
542
+ "### 防御机制对比\n\n"
543
+ "| 维度 | 标签平滑 | 输出扰动 |\n|---|---|---|\n"
544
+ "| **阶段** | 训练期 | 推理期 |\n"
545
+ "| **原理** | 软化标签降低记忆 | Loss加噪遮蔽信号 |\n"
546
+ "| **需重训** | | |\n"
547
+ "| **效用** | 取决于参数 | |\n"
548
+ "| **部署** | 训练时介入 | 即插即用 |\n\n"
549
+ "**标签平滑公式**: `y_smooth = (1 - ε) * y_onehot + ε / V`\n\n"
550
+ "**输出扰动公式**: `L_perturbed = L_original + N(0, σ²)`\n")
551
+
552
+ with gr.Accordion("🖼️ 展开查看:静态高分辨率学术图表", open=False):
553
+ for fn, cap in [("fig1_loss_distribution_comparison.png","Loss分布对比"),
554
+ ("fig2_privacy_utility_tradeoff_fixed.png","隐私-效用权衡"),
555
+ ("fig3_defense_comparison_bar.png","防御策略AUC对比")]:
556
+ p = os.path.join(BASE_DIR,"figures",fn)
557
+ if os.path.exists(p):
558
+ gr.Markdown("#### "+cap); gr.Image(value=p, show_label=False, height=420)
559
+
560
+ # ═══════ Tab 5: 效用评估 ═══════
561
+ with gr.Tab("⚖️ 效用评估"):
562
+ gr.Markdown("## 🎯 模型效用测试\n\n> 基于300道数学测试题评估各策略对模型实际能力的影响")
563
  with gr.Row(equal_height=True):
564
  with gr.Column(): gr.Plot(value=fig_acc_bar())
565
  with gr.Column(): gr.Plot(value=fig_tradeoff())
566
+
567
+ gr.Markdown("## 🎮 在线效用抽样演示\n\n从测试题库中随机抽取,流式验证不同模型/策略的保留作答情况。")
568
  with gr.Row(equal_height=True):
569
  with gr.Column(scale=1):
570
  e_model = gr.Radio([u"基线模型",u"标签平滑 (\u03b5=0.02)",u"标签平滑 (\u03b5=0.2)",
571
+ u"输出扰动 (\u03c3=0.01)",u"输出扰动 (\u03c3=0.015)",u"输出扰动 (\u03c3=0.02)"], value=u"基线模型", label="选择验证模型")
572
+ e_btn = gr.Button("🧪 随机抽题测试", variant="primary")
573
  with gr.Column(scale=2):
574
  e_res = gr.Markdown()
575
  e_btn.click(cb_eval, [e_model], [e_res])
576
 
577
+ # ═══════ Tab 6: 研究结论 (引入折叠面板展开) ═══════
578
+ with gr.Tab("📝 研究结论"):
579
+ gr.Markdown("## 💡 核心研究发现与最佳实践\n\n---")
580
+
581
+ with gr.Accordion("🚨 一、教育大模型存在可量化的MIA风险", open=True):
582
+ gr.Markdown("基线模型 AUC = **" + f"{bl_auc:.4f}" + "** > 0.5,成员平均Loss (" + f"{bl_m_mean:.4f}"
583
+ + ") 显著小于 非成员 (" + f"{bl_nm_mean:.4f}" + "),实验铁证表明模型对训练数据存在可利用的记忆效应。")
584
+
585
+ with gr.Accordion("🛡️ 二、标签平滑(训练期防御)有效性验证", open=True):
586
+ gr.Markdown(
587
+ "| 参数 | AUC | 准确率 | 分析 |\n|---|---|---|---|\n"
588
+ "| 基线 | " + f"{bl_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 防御 |\n"
589
+ "| " + u"\u03b5=0.02" + " | " + f"{s002_auc:.4f}" + " | " + f"{s002_acc:.1f}%" + " | 正则化提升泛化 |\n"
590
+ "| " + u"\u03b5=0.2" + " | " + f"{s02_auc:.4f}" + " | " + f"{s02_acc:.1f}%" + " | 防御增强 |\n"
591
+ )
592
+
593
+ with gr.Accordion("🎛️ 三、输出扰动(推理期防御)有效性验证", open=True):
594
+ gr.Markdown(
595
+ "| 参数 | AUC | AUC降幅 | 准确率 |\n|---|---|---|---|\n"
596
+ "| " + u"\u03c3=0.01" + " | " + f"{op001_auc:.4f}" + " | " + f"{bl_auc-op001_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " |\n"
597
+ "| " + u"\u03c3=0.015" + " | " + f"{op0015_auc:.4f}" + " | " + f"{bl_auc-op0015_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " |\n"
598
+ "| " + u"\u03c3=0.02" + " | " + f"{op002_auc:.4f}" + " | " + f"{bl_auc-op002_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " |\n\n"
599
+ "**结论:零效用损失,适合已部署系统的后期加固。**"
600
+ )
601
+
602
+ with gr.Accordion("⚖️ 四、隐私-效用权衡总结", open=False):
603
+ gr.Markdown(
604
+ "| 策略 | AUC | 准确率 | 隐私 | 效用 |\n|---|---|---|---|---|\n"
605
+ "| 基线 | " + f"{bl_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 风险最高 | 基准 |\n"
606
+ "| " + u"LS(\u03b5=0.02)" + " | " + f"{s002_auc:.4f}" + " | " + f"{s002_acc:.1f}%" + " | 降低 | 提升 |\n"
607
+ "| " + u"LS(\u03b5=0.2)" + " | " + f"{s02_auc:.4f}" + " | " + f"{s02_acc:.1f}%" + " | 显著降低 | 可接受 |\n"
608
+ "| " + u"OP(\u03c3=0.02)" + " | " + f"{op002_auc:.4f}" + " | " + f"{bl_acc:.1f}%" + " | 显著降低 | 不变 |\n\n"
609
+ "> 两类策略机制互补:标签平滑从训练阶段降低记忆,输出扰动从推理阶段遮蔽信号。建议组合使用以构建立体防御体系。"
610
+ )
611
+
612
+ gr.HTML("<div style='text-align:center;color:#94a3b8;font-size:.82rem;padding:16px 0 8px'></div>")
613
 
614
  demo.launch()