Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
| 1 |
# ================================================================
|
| 2 |
-
# 教育大模型MIA攻防研究 - Gradio演示系统
|
| 3 |
-
# 1. 严格基于
|
| 4 |
-
# 2.
|
| 5 |
-
# 3.
|
| 6 |
-
# 4. 效用评估图表完璧归赵并放大尺寸
|
| 7 |
# ================================================================
|
| 8 |
|
| 9 |
import os
|
|
@@ -27,7 +26,8 @@ def load_json(path):
|
|
| 27 |
return json.load(f)
|
| 28 |
|
| 29 |
def clean_text(text):
|
| 30 |
-
if not isinstance(text, str):
|
|
|
|
| 31 |
text = re.sub(r'[\U00010000-\U0010ffff]', '', text)
|
| 32 |
text = re.sub(r'[\ufff0-\uffff]', '', text)
|
| 33 |
text = re.sub(r'[\u200b-\u200f\u2028-\u202f\u2060-\u206f\ufeff]', '', text)
|
|
@@ -89,7 +89,8 @@ def apply_light_style(fig, ax_or_axes):
|
|
| 89 |
for ax in axes:
|
| 90 |
ax.set_facecolor(COLORS['panel'])
|
| 91 |
for spine in ax.spines.values():
|
| 92 |
-
spine.set_color(COLORS['grid'])
|
|
|
|
| 93 |
ax.tick_params(colors=COLORS['text_dim'], labelsize=10, width=1)
|
| 94 |
ax.xaxis.label.set_color(COLORS['text'])
|
| 95 |
ax.yaxis.label.set_color(COLORS['text'])
|
|
@@ -99,7 +100,7 @@ def apply_light_style(fig, ax_or_axes):
|
|
| 99 |
ax.set_axisbelow(True)
|
| 100 |
|
| 101 |
# ================================================================
|
| 102 |
-
#
|
| 103 |
# ================================================================
|
| 104 |
LS_KEYS = ["baseline", "smooth_eps_0.02", "smooth_eps_0.05", "smooth_eps_0.1", "smooth_eps_0.2"]
|
| 105 |
LS_LABELS_PLOT = ["Baseline", "LS(ε=0.02)", "LS(ε=0.05)", "LS(ε=0.1)", "LS(ε=0.2)"]
|
|
@@ -157,7 +158,7 @@ for _i in range(300):
|
|
| 157 |
EVAL_POOL.append(item)
|
| 158 |
|
| 159 |
# ================================================================
|
| 160 |
-
# 图表绘制函数 (
|
| 161 |
# ================================================================
|
| 162 |
def fig_gauge(loss_val, m_mean, nm_mean, thr, m_std, nm_std):
|
| 163 |
fig, ax = plt.subplots(figsize=(10, 2.6)); fig.patch.set_facecolor(COLORS['bg']); ax.set_facecolor(COLORS['panel'])
|
|
@@ -220,7 +221,7 @@ def fig_radar():
|
|
| 220 |
plt.tight_layout()
|
| 221 |
return fig
|
| 222 |
|
| 223 |
-
# 🌟
|
| 224 |
def fig_d3_dist_compare():
|
| 225 |
configs = [
|
| 226 |
("Baseline (No Defense)", "baseline", COLORS['danger'], None),
|
|
@@ -332,7 +333,7 @@ def fig_loss_gap_waterfall():
|
|
| 332 |
ax.set_ylabel('Loss Gap', fontsize=12, fontweight='medium'); ax.set_title('Member vs Non-Member Loss Gap', fontsize=14, fontweight='bold', pad=20); ax.set_xticks(range(len(names))); ax.set_xticklabels(names, rotation=30, ha='right', fontsize=11); ax.annotate('Smaller gap = Better Privacy', xy=(8, gaps[0]*0.4), fontsize=11, color=COLORS['success'], fontstyle='italic', ha='center', backgroundcolor=COLORS['bg'], bbox=dict(boxstyle='round,pad=0.4', facecolor=COLORS['panel'], edgecolor=COLORS['success'], alpha=0.8)); plt.tight_layout()
|
| 333 |
return fig
|
| 334 |
|
| 335 |
-
# 🌟
|
| 336 |
def fig_acc_bar():
|
| 337 |
names, vals, clrs = [], [], []; ls_c = [COLORS['baseline']] + COLORS['ls_colors']
|
| 338 |
for i, (k, l) in enumerate(zip(LS_KEYS, LS_LABELS_PLOT)):
|
|
@@ -345,34 +346,43 @@ def fig_acc_bar():
|
|
| 345 |
ax.set_ylim(0, 105); ax.set_xticks(range(len(names))); ax.set_xticklabels(names, rotation=35, ha='right', fontsize=12); plt.tight_layout()
|
| 346 |
return fig
|
| 347 |
|
|
|
|
| 348 |
def fig_tradeoff():
|
| 349 |
fig, ax = plt.subplots(figsize=(12, 7)); apply_light_style(fig, ax); markers_ls = ['o', 's', 's', 's', 's']; ls_c = [COLORS['baseline']] + COLORS['ls_colors']
|
| 350 |
for i, (k, l) in enumerate(zip(LS_KEYS, LS_LABELS_PLOT)):
|
| 351 |
if k in mia_results and k in utility_results: ax.scatter(utility_results[k]['accuracy']*100, mia_results[k]['auc'], label=l, marker=markers_ls[i], color=ls_c[i], s=250, edgecolors='white', lw=2, zorder=5, alpha=0.9)
|
| 352 |
op_markers = ['^', 'D', 'v', 'P', 'X', 'h']
|
| 353 |
for i, (k, l) in enumerate(zip(OP_KEYS, OP_LABELS_PLOT)):
|
| 354 |
-
|
|
|
|
|
|
|
| 355 |
ax.axhline(0.5, color=COLORS['text_dim'], ls='--', alpha=0.6, label='Random (AUC=0.5)')
|
| 356 |
ax.annotate('IDEAL ZONE\nHigh Utility, Low Risk', xy=(85, 0.51), fontsize=11, fontweight='bold', color=COLORS['success'], alpha=0.7, ha='center', backgroundcolor=COLORS['bg'])
|
| 357 |
ax.annotate('HIGH RISK ZONE\nLow Utility, High Risk', xy=(62, 0.61), fontsize=11, fontweight='bold', color=COLORS['danger'], alpha=0.7, ha='center', backgroundcolor=COLORS['bg'])
|
| 358 |
ax.set_xlabel('Model Utility (Accuracy %)', fontsize=12, fontweight='medium'); ax.set_ylabel('Privacy Risk (MIA AUC)', fontsize=12, fontweight='medium')
|
| 359 |
ax.set_title('Privacy-Utility Trade-off Analysis', fontsize=15, fontweight='bold', pad=20)
|
| 360 |
-
|
|
|
|
| 361 |
return fig
|
| 362 |
|
|
|
|
| 363 |
def fig_auc_trend():
|
| 364 |
fig, axes = plt.subplots(1, 2, figsize=(16, 6.5)); apply_light_style(fig, axes); ax = axes[0]; eps_vals = [0.0, 0.02, 0.05, 0.1, 0.2]; auc_vals = [gm(k, 'auc') for k in LS_KEYS]; acc_vals = [gu(k) for k in LS_KEYS]
|
| 365 |
ax2 = ax.twinx(); line1 = ax.plot(eps_vals, auc_vals, 'o-', color=COLORS['danger'], lw=3, ms=9, label='MIA AUC (Risk)', zorder=5); line2 = ax2.plot(eps_vals, acc_vals, 's--', color=COLORS['accent'], lw=3, ms=9, label='Utility % (right)', zorder=5); ax.axhline(0.5, color=COLORS['text_dim'], ls=':', alpha=0.5)
|
| 366 |
ax.fill_between(eps_vals, auc_vals, 0.5, alpha=0.08, color=COLORS['danger'])
|
| 367 |
-
ax.set_xlabel('Label Smoothing ε', fontsize=12, fontweight='medium'); ax.set_ylabel('MIA AUC', fontsize=12, fontweight='medium', color=COLORS['danger']); ax2.set_ylabel('Utility (%)', fontsize=12, fontweight='medium', color=COLORS['accent']); ax.set_title('Label Smoothing Trends', fontsize=14, fontweight='bold', pad=15); ax.tick_params(axis='y', labelcolor=COLORS['danger']); ax2.tick_params(axis='y', labelcolor=COLORS['accent']); ax2.spines['right'].set_color(COLORS['accent']); ax2.spines['left'].set_color(COLORS['danger']); lines = line1 + line2; labels = [l.get_label() for l in lines]
|
|
|
|
|
|
|
| 368 |
|
| 369 |
ax = axes[1]; sig_vals = OP_SIGMAS; auc_op = [gm(k, 'auc') for k in OP_KEYS]; ax.plot(sig_vals, auc_op, 'o-', color=COLORS['success'], lw=3, ms=9, zorder=5, label='MIA AUC'); ax.axhline(bl_auc, color=COLORS['danger'], ls='--', lw=2, alpha=0.6, label=f'Baseline ({bl_auc:.4f})'); ax.axhline(0.5, color=COLORS['text_dim'], ls=':', alpha=0.5, label='Random (0.5)'); ax.fill_between(sig_vals, auc_op, bl_auc, alpha=0.2, color=COLORS['success'], label='AUC Reduction')
|
| 370 |
ax2r = ax.twinx(); ax2r.axhline(bl_acc, color=COLORS['success'], ls='-', lw=2.5, alpha=0.8); ax2r.set_ylabel(f'Utility = {bl_acc:.1f}% (unchanged)', fontsize=12, fontweight='medium', color=COLORS['success']); ax2r.set_ylim(0,100); ax2r.tick_params(axis='y', labelcolor=COLORS['success']); ax2r.spines['right'].set_color(COLORS['success'])
|
| 371 |
-
ax.set_xlabel('Perturbation σ', fontsize=12, fontweight='medium'); ax.set_ylabel('MIA AUC', fontsize=12, fontweight='medium'); ax.set_title('Output Perturbation Trends', fontsize=14, fontweight='bold', pad=15)
|
|
|
|
|
|
|
| 372 |
return fig
|
| 373 |
|
| 374 |
# ================================================================
|
| 375 |
-
# 回调函数
|
| 376 |
# ================================================================
|
| 377 |
def cb_sample(src):
|
| 378 |
pool = member_data if "训练集" in src else non_member_data
|
|
@@ -560,8 +570,6 @@ button.primary:hover { background-color: #0062CC !important; box-shadow: 0 4px 1
|
|
| 560 |
.card-wrap { background: var(--card-bg) !important; border: 1px solid var(--border-color) !important; border-radius: 14px !important; padding: 24px !important; box-shadow: 0 2px 8px rgba(0,0,0,0.04) !important; }
|
| 561 |
.block.svelte-12cmxck { border-radius: 12px !important; border-color: var(--border-color) !important; }
|
| 562 |
.input-label { color: var(--text-gray) !important; font-weight: 500 !important; }
|
| 563 |
-
.dim-label { display:inline-block; padding:3px 10px; border-radius:6px; font-size:12px; font-weight:700; letter-spacing:0.05em; margin-right:8px; }
|
| 564 |
-
.dim1 { background:#FEF3F2; color:#B42318; } .dim2 { background:#FFFAEB; color:#B54708; } .dim3 { background:#F4F3FF; color:#5925DC; } .dim4 { background:#EFF8FF; color:#175CD3; } .dim5 { background:#F0FDF9; color:#107569; }
|
| 565 |
footer { display: none !important; }
|
| 566 |
"""
|
| 567 |
|
|
@@ -731,6 +739,7 @@ with gr.Blocks(title="MIA攻防研究", theme=gr.themes.Soft(), css=CSS) as demo
|
|
| 731 |
> **LS 的防御本质:** 随着 ε 增大,两座山峰趋于完美重合,均值差距缩小到了 {gm('smooth_eps_0.2','loss_gap'):.4f}。这是从物理上抹除了模型记忆。
|
| 732 |
> **OP 的防御本质:** 均值差距未变,但高斯噪声导致分布变得极其扁平宽阔,红蓝区域被完全搅混,蒙蔽了攻击者的双眼。
|
| 733 |
""")
|
|
|
|
| 734 |
gr.Plot(value=fig_d3_dist_compare())
|
| 735 |
gr.Plot(value=fig_loss_gap_waterfall())
|
| 736 |
|
|
@@ -808,7 +817,7 @@ with gr.Blocks(title="MIA攻防研究", theme=gr.themes.Soft(), css=CSS) as demo
|
|
| 808 |
|
| 809 |
with gr.Tab("📝 研究结论"):
|
| 810 |
gr.Markdown(f"""\
|
| 811 |
-
## 核心研究发现
|
| 812 |
|
| 813 |
---
|
| 814 |
|
|
@@ -823,7 +832,7 @@ with gr.Blocks(title="MIA攻防研究", theme=gr.themes.Soft(), css=CSS) as demo
|
|
| 823 |
| ε=0.1 | {gm('smooth_eps_0.1','auc'):.4f} | {bl_auc-gm('smooth_eps_0.1','auc'):.4f} | {gu('smooth_eps_0.1'):.1f}% | {gu('smooth_eps_0.1')-bl_acc:+.1f}% |
|
| 824 |
| ε=0.2 | {gm('smooth_eps_0.2','auc'):.4f} | {bl_auc-gm('smooth_eps_0.2','auc'):.4f} | {gu('smooth_eps_0.2'):.1f}% | {gu('smooth_eps_0.2')-bl_acc:+.1f}% |
|
| 825 |
|
| 826 |
-
**
|
| 827 |
|
| 828 |
### 🎭 结论三:输出扰动是有效的推理期防御 (零效用损耗)
|
| 829 |
| σ 参数 | AUC | AUC降幅 | 效用 |
|
|
@@ -839,10 +848,10 @@ with gr.Blocks(title="MIA攻防研究", theme=gr.themes.Soft(), css=CSS) as demo
|
|
| 839 |
|
| 840 |
### 💡 结论四:最佳实践建议
|
| 841 |
|
| 842 |
-
> **推荐
|
| 843 |
>
|
| 844 |
-
> - **训练期 (治本):** 标签平滑从源头降低
|
| 845 |
-
> - **推理期 (治标):** 输出扰动
|
| 846 |
> - **两者机制互补,可叠加使用。**
|
| 847 |
|
| 848 |
""")
|
|
|
|
| 1 |
# ================================================================
|
| 2 |
+
# 教育大模型MIA攻防研究 - Gradio演示系统 终极防重叠版
|
| 3 |
+
# 1. 严格基于你发来的完整代码底座,一字不漏
|
| 4 |
+
# 2. 修复:散点图(Trade-off)图例移至左下角,增加点位透明度防遮挡
|
| 5 |
+
# 3. 修复:折线图(Trend)图例移至下方空白处,不再遮挡数据线和阴影
|
|
|
|
| 6 |
# ================================================================
|
| 7 |
|
| 8 |
import os
|
|
|
|
| 26 |
return json.load(f)
|
| 27 |
|
| 28 |
def clean_text(text):
|
| 29 |
+
if not isinstance(text, str):
|
| 30 |
+
return str(text)
|
| 31 |
text = re.sub(r'[\U00010000-\U0010ffff]', '', text)
|
| 32 |
text = re.sub(r'[\ufff0-\uffff]', '', text)
|
| 33 |
text = re.sub(r'[\u200b-\u200f\u2028-\u202f\u2060-\u206f\ufeff]', '', text)
|
|
|
|
| 89 |
for ax in axes:
|
| 90 |
ax.set_facecolor(COLORS['panel'])
|
| 91 |
for spine in ax.spines.values():
|
| 92 |
+
spine.set_color(COLORS['grid'])
|
| 93 |
+
spine.set_linewidth(1)
|
| 94 |
ax.tick_params(colors=COLORS['text_dim'], labelsize=10, width=1)
|
| 95 |
ax.xaxis.label.set_color(COLORS['text'])
|
| 96 |
ax.yaxis.label.set_color(COLORS['text'])
|
|
|
|
| 100 |
ax.set_axisbelow(True)
|
| 101 |
|
| 102 |
# ================================================================
|
| 103 |
+
# 使用标准 Unicode ε 和 σ
|
| 104 |
# ================================================================
|
| 105 |
LS_KEYS = ["baseline", "smooth_eps_0.02", "smooth_eps_0.05", "smooth_eps_0.1", "smooth_eps_0.2"]
|
| 106 |
LS_LABELS_PLOT = ["Baseline", "LS(ε=0.02)", "LS(ε=0.05)", "LS(ε=0.1)", "LS(ε=0.2)"]
|
|
|
|
| 158 |
EVAL_POOL.append(item)
|
| 159 |
|
| 160 |
# ================================================================
|
| 161 |
+
# 图表绘制函数 (保留你的原貌,只做防重叠修复)
|
| 162 |
# ================================================================
|
| 163 |
def fig_gauge(loss_val, m_mean, nm_mean, thr, m_std, nm_std):
|
| 164 |
fig, ax = plt.subplots(figsize=(10, 2.6)); fig.patch.set_facecolor(COLORS['bg']); ax.set_facecolor(COLORS['panel'])
|
|
|
|
| 221 |
plt.tight_layout()
|
| 222 |
return fig
|
| 223 |
|
| 224 |
+
# 🌟 修复:这是我专门加的 3联 Loss 直方图横向对比
|
| 225 |
def fig_d3_dist_compare():
|
| 226 |
configs = [
|
| 227 |
("Baseline (No Defense)", "baseline", COLORS['danger'], None),
|
|
|
|
| 333 |
ax.set_ylabel('Loss Gap', fontsize=12, fontweight='medium'); ax.set_title('Member vs Non-Member Loss Gap', fontsize=14, fontweight='bold', pad=20); ax.set_xticks(range(len(names))); ax.set_xticklabels(names, rotation=30, ha='right', fontsize=11); ax.annotate('Smaller gap = Better Privacy', xy=(8, gaps[0]*0.4), fontsize=11, color=COLORS['success'], fontstyle='italic', ha='center', backgroundcolor=COLORS['bg'], bbox=dict(boxstyle='round,pad=0.4', facecolor=COLORS['panel'], edgecolor=COLORS['success'], alpha=0.8)); plt.tight_layout()
|
| 334 |
return fig
|
| 335 |
|
| 336 |
+
# 🌟 效用页面柱状图
|
| 337 |
def fig_acc_bar():
|
| 338 |
names, vals, clrs = [], [], []; ls_c = [COLORS['baseline']] + COLORS['ls_colors']
|
| 339 |
for i, (k, l) in enumerate(zip(LS_KEYS, LS_LABELS_PLOT)):
|
|
|
|
| 346 |
ax.set_ylim(0, 105); ax.set_xticks(range(len(names))); ax.set_xticklabels(names, rotation=35, ha='right', fontsize=12); plt.tight_layout()
|
| 347 |
return fig
|
| 348 |
|
| 349 |
+
# 🌟 修复:散点图增加透明度防遮挡,并且图例移至左下角空白处
|
| 350 |
def fig_tradeoff():
|
| 351 |
fig, ax = plt.subplots(figsize=(12, 7)); apply_light_style(fig, ax); markers_ls = ['o', 's', 's', 's', 's']; ls_c = [COLORS['baseline']] + COLORS['ls_colors']
|
| 352 |
for i, (k, l) in enumerate(zip(LS_KEYS, LS_LABELS_PLOT)):
|
| 353 |
if k in mia_results and k in utility_results: ax.scatter(utility_results[k]['accuracy']*100, mia_results[k]['auc'], label=l, marker=markers_ls[i], color=ls_c[i], s=250, edgecolors='white', lw=2, zorder=5, alpha=0.9)
|
| 354 |
op_markers = ['^', 'D', 'v', 'P', 'X', 'h']
|
| 355 |
for i, (k, l) in enumerate(zip(OP_KEYS, OP_LABELS_PLOT)):
|
| 356 |
+
# 增加透明度 alpha=0.75 防止叠放时遮盖
|
| 357 |
+
if k in perturb_results: ax.scatter(bl_acc, perturb_results[k]['auc'], label=l, marker=op_markers[i], color=COLORS['op_colors'][i], s=200, edgecolors='white', lw=1.5, zorder=6, alpha=0.75)
|
| 358 |
+
|
| 359 |
ax.axhline(0.5, color=COLORS['text_dim'], ls='--', alpha=0.6, label='Random (AUC=0.5)')
|
| 360 |
ax.annotate('IDEAL ZONE\nHigh Utility, Low Risk', xy=(85, 0.51), fontsize=11, fontweight='bold', color=COLORS['success'], alpha=0.7, ha='center', backgroundcolor=COLORS['bg'])
|
| 361 |
ax.annotate('HIGH RISK ZONE\nLow Utility, High Risk', xy=(62, 0.61), fontsize=11, fontweight='bold', color=COLORS['danger'], alpha=0.7, ha='center', backgroundcolor=COLORS['bg'])
|
| 362 |
ax.set_xlabel('Model Utility (Accuracy %)', fontsize=12, fontweight='medium'); ax.set_ylabel('Privacy Risk (MIA AUC)', fontsize=12, fontweight='medium')
|
| 363 |
ax.set_title('Privacy-Utility Trade-off Analysis', fontsize=15, fontweight='bold', pad=20)
|
| 364 |
+
# 图例放到绝对安全的左下角空白区域
|
| 365 |
+
ax.legend(fontsize=11, loc='lower left', ncol=2, facecolor=COLORS['bg'], edgecolor='none'); plt.tight_layout()
|
| 366 |
return fig
|
| 367 |
|
| 368 |
+
# 🌟 修复:趋势图图例移位,避免遮挡数据线和阴影
|
| 369 |
def fig_auc_trend():
|
| 370 |
fig, axes = plt.subplots(1, 2, figsize=(16, 6.5)); apply_light_style(fig, axes); ax = axes[0]; eps_vals = [0.0, 0.02, 0.05, 0.1, 0.2]; auc_vals = [gm(k, 'auc') for k in LS_KEYS]; acc_vals = [gu(k) for k in LS_KEYS]
|
| 371 |
ax2 = ax.twinx(); line1 = ax.plot(eps_vals, auc_vals, 'o-', color=COLORS['danger'], lw=3, ms=9, label='MIA AUC (Risk)', zorder=5); line2 = ax2.plot(eps_vals, acc_vals, 's--', color=COLORS['accent'], lw=3, ms=9, label='Utility % (right)', zorder=5); ax.axhline(0.5, color=COLORS['text_dim'], ls=':', alpha=0.5)
|
| 372 |
ax.fill_between(eps_vals, auc_vals, 0.5, alpha=0.08, color=COLORS['danger'])
|
| 373 |
+
ax.set_xlabel('Label Smoothing ε', fontsize=12, fontweight='medium'); ax.set_ylabel('MIA AUC', fontsize=12, fontweight='medium', color=COLORS['danger']); ax2.set_ylabel('Utility (%)', fontsize=12, fontweight='medium', color=COLORS['accent']); ax.set_title('Label Smoothing Trends', fontsize=14, fontweight='bold', pad=15); ax.tick_params(axis='y', labelcolor=COLORS['danger']); ax2.tick_params(axis='y', labelcolor=COLORS['accent']); ax2.spines['right'].set_color(COLORS['accent']); ax2.spines['left'].set_color(COLORS['danger']); lines = line1 + line2; labels = [l.get_label() for l in lines]
|
| 374 |
+
# 图例移至右下角安全区
|
| 375 |
+
ax.legend(lines, labels, fontsize=10, facecolor=COLORS['bg'], edgecolor='none', loc='lower right')
|
| 376 |
|
| 377 |
ax = axes[1]; sig_vals = OP_SIGMAS; auc_op = [gm(k, 'auc') for k in OP_KEYS]; ax.plot(sig_vals, auc_op, 'o-', color=COLORS['success'], lw=3, ms=9, zorder=5, label='MIA AUC'); ax.axhline(bl_auc, color=COLORS['danger'], ls='--', lw=2, alpha=0.6, label=f'Baseline ({bl_auc:.4f})'); ax.axhline(0.5, color=COLORS['text_dim'], ls=':', alpha=0.5, label='Random (0.5)'); ax.fill_between(sig_vals, auc_op, bl_auc, alpha=0.2, color=COLORS['success'], label='AUC Reduction')
|
| 378 |
ax2r = ax.twinx(); ax2r.axhline(bl_acc, color=COLORS['success'], ls='-', lw=2.5, alpha=0.8); ax2r.set_ylabel(f'Utility = {bl_acc:.1f}% (unchanged)', fontsize=12, fontweight='medium', color=COLORS['success']); ax2r.set_ylim(0,100); ax2r.tick_params(axis='y', labelcolor=COLORS['success']); ax2r.spines['right'].set_color(COLORS['success'])
|
| 379 |
+
ax.set_xlabel('Perturbation σ', fontsize=12, fontweight='medium'); ax.set_ylabel('MIA AUC', fontsize=12, fontweight='medium'); ax.set_title('Output Perturbation Trends', fontsize=14, fontweight='bold', pad=15)
|
| 380 |
+
# 图例移至左下角安全区
|
| 381 |
+
ax.legend(fontsize=10, facecolor=COLORS['bg'], edgecolor='none', loc='lower left'); plt.tight_layout()
|
| 382 |
return fig
|
| 383 |
|
| 384 |
# ================================================================
|
| 385 |
+
# 回调函数 (维持你原有的所有核心逻辑不变)
|
| 386 |
# ================================================================
|
| 387 |
def cb_sample(src):
|
| 388 |
pool = member_data if "训练集" in src else non_member_data
|
|
|
|
| 570 |
.card-wrap { background: var(--card-bg) !important; border: 1px solid var(--border-color) !important; border-radius: 14px !important; padding: 24px !important; box-shadow: 0 2px 8px rgba(0,0,0,0.04) !important; }
|
| 571 |
.block.svelte-12cmxck { border-radius: 12px !important; border-color: var(--border-color) !important; }
|
| 572 |
.input-label { color: var(--text-gray) !important; font-weight: 500 !important; }
|
|
|
|
|
|
|
| 573 |
footer { display: none !important; }
|
| 574 |
"""
|
| 575 |
|
|
|
|
| 739 |
> **LS 的防御本质:** 随着 ε 增大,两座山峰趋于完美重合,均值差距缩小到了 {gm('smooth_eps_0.2','loss_gap'):.4f}。这是从物理上抹除了模型记忆。
|
| 740 |
> **OP 的防御本质:** 均值差距未变,但高斯噪声导致分布变得极其扁平宽阔,红蓝区域被完全搅混,蒙蔽了攻击者的双眼。
|
| 741 |
""")
|
| 742 |
+
# 🌟 调用专门添加的三联横向对比直方图
|
| 743 |
gr.Plot(value=fig_d3_dist_compare())
|
| 744 |
gr.Plot(value=fig_loss_gap_waterfall())
|
| 745 |
|
|
|
|
| 817 |
|
| 818 |
with gr.Tab("📝 研究结论"):
|
| 819 |
gr.Markdown(f"""\
|
| 820 |
+
## 核心研究发现
|
| 821 |
|
| 822 |
---
|
| 823 |
|
|
|
|
| 832 |
| ε=0.1 | {gm('smooth_eps_0.1','auc'):.4f} | {bl_auc-gm('smooth_eps_0.1','auc'):.4f} | {gu('smooth_eps_0.1'):.1f}% | {gu('smooth_eps_0.1')-bl_acc:+.1f}% |
|
| 833 |
| ε=0.2 | {gm('smooth_eps_0.2','auc'):.4f} | {bl_auc-gm('smooth_eps_0.2','auc'):.4f} | {gu('smooth_eps_0.2'):.1f}% | {gu('smooth_eps_0.2')-bl_acc:+.1f}% |
|
| 834 |
|
| 835 |
+
**重要发现:ε≥0.05时,隐私保护和模型效用同时提升。标签平滑的正则化效应防止了过拟合。**
|
| 836 |
|
| 837 |
### 🎭 结论三:输出扰动是有效的推理期防御 (零效用损耗)
|
| 838 |
| σ 参数 | AUC | AUC降幅 | 效用 |
|
|
|
|
| 848 |
|
| 849 |
### 💡 结论四:最佳实践建议
|
| 850 |
|
| 851 |
+
> **推荐组合方案: LS(ε=0.1) + OP(σ=0.02)**
|
| 852 |
>
|
| 853 |
+
> - **训练期 (治本):** 标签平滑从源头降低记忆,缩小Loss差距
|
| 854 |
+
> - **推理期 (治标):** 输出扰动遮蔽残余信号,进一步降低AUC
|
| 855 |
> - **两者机制互补,可叠加使用。**
|
| 856 |
|
| 857 |
""")
|