Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>PHI Exposure Guard</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=DM+Mono:wght@400;500&family=Crimson+Pro:ital,wght@0,300;0,400;0,500;1,300;1,400&display=swap" rel="stylesheet" /> | |
| <style> | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| :root { | |
| --bg: #1a1018; | |
| --surface: #221520; | |
| --surface2: #2a1c28; | |
| --border: #3a2538; | |
| --border2: #4a3048; | |
| --accent: #c4785a; | |
| --accent-hover:#d4886a; | |
| --accent-dim: rgba(196,120,90,0.12); | |
| --accent-dim2: rgba(196,120,90,0.06); | |
| --teal: #7ab8a8; | |
| --teal-dim: rgba(122,184,168,0.1); | |
| --red: #c45a5a; | |
| --red-dim: rgba(196,90,90,0.1); | |
| --amber: #c4a05a; | |
| --amber-dim: rgba(196,160,90,0.1); | |
| --text: #e8ddd8; | |
| --muted: #8a7880; | |
| --heading: #f4ede8; | |
| --code-bg: #110d10; | |
| --code-text: #c4b4b8; | |
| } | |
| html { scroll-behavior: smooth; } | |
| body { | |
| background: var(--bg); | |
| color: var(--text); | |
| font-family: 'Crimson Pro', serif; | |
| font-weight: 300; | |
| line-height: 1.8; | |
| min-height: 100vh; | |
| } | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| inset: 0; | |
| background: | |
| radial-gradient(ellipse 80% 50% at 20% 20%, rgba(196,120,90,0.04) 0%, transparent 60%), | |
| radial-gradient(ellipse 60% 40% at 80% 80%, rgba(122,184,168,0.03) 0%, transparent 60%); | |
| pointer-events: none; | |
| z-index: 0; | |
| } | |
| .wrap { | |
| max-width: 900px; | |
| margin: 0 auto; | |
| padding: 0 32px; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .topbar { | |
| position: fixed; | |
| top: 0; left: 0; right: 0; | |
| height: 52px; | |
| background: rgba(26,16,24,0.94); | |
| backdrop-filter: blur(12px); | |
| border-bottom: 1px solid var(--border); | |
| display: flex; | |
| align-items: center; | |
| padding: 0 32px; | |
| z-index: 100; | |
| gap: 28px; | |
| } | |
| .topbar-logo { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 11px; | |
| color: var(--accent); | |
| letter-spacing: 0.08em; | |
| text-transform: uppercase; | |
| display: flex; | |
| align-items: center; | |
| gap: 7px; | |
| } | |
| .logo-dot { | |
| width: 6px; height: 6px; | |
| border-radius: 50%; | |
| background: var(--accent); | |
| animation: blink 2.5s infinite; | |
| } | |
| @keyframes blink { 0%,100%{opacity:1} 50%{opacity:0.2} } | |
| .topbar-links { display: flex; gap: 24px; margin-left: auto; } | |
| .topbar-links a { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 10px; | |
| letter-spacing: 0.06em; | |
| text-transform: uppercase; | |
| color: var(--muted); | |
| text-decoration: none; | |
| transition: color 0.15s; | |
| } | |
| .topbar-links a:hover { color: var(--accent); } | |
| .hero { | |
| padding: 120px 0 72px; | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .tag-row { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 30px; } | |
| .tag { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 9px; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| padding: 3px 10px; | |
| border-radius: 2px; | |
| border: 1px solid currentColor; | |
| } | |
| .tag-rose { color: var(--accent); background: var(--accent-dim2); } | |
| .tag-teal { color: var(--teal); background: var(--teal-dim); } | |
| .tag-red { color: var(--red); background: var(--red-dim); } | |
| h1 { | |
| font-family: 'Instrument Serif', serif; | |
| font-size: clamp(2.4rem, 5.5vw, 3.8rem); | |
| color: var(--heading); | |
| line-height: 1.1; | |
| margin-bottom: 22px; | |
| font-weight: 400; | |
| letter-spacing: -0.01em; | |
| } | |
| h1 em { font-style: italic; color: var(--accent); } | |
| .hero-sub { | |
| font-family: 'Crimson Pro', serif; | |
| font-size: 1.1rem; | |
| color: var(--muted); | |
| max-width: 580px; | |
| margin-bottom: 44px; | |
| line-height: 1.85; | |
| font-weight: 300; | |
| } | |
| .btn-row { display: flex; flex-wrap: wrap; gap: 9px; } | |
| .btn { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| padding: 9px 20px; | |
| border-radius: 2px; | |
| font-family: 'DM Mono', monospace; | |
| font-size: 10px; | |
| font-weight: 500; | |
| letter-spacing: 0.05em; | |
| text-transform: uppercase; | |
| text-decoration: none; | |
| transition: all 0.16s; | |
| cursor: pointer; | |
| border: none; | |
| } | |
| .btn-primary { background: var(--accent); color: #1a1018; } | |
| .btn-primary:hover { background: var(--accent-hover); transform: translateY(-1px); box-shadow: 0 4px 20px rgba(196,120,90,0.3); } | |
| .btn-ghost { border: 1px solid var(--border2); color: var(--muted); background: transparent; } | |
| .btn-ghost:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-1px); } | |
| .stat-strip { | |
| display: grid; | |
| grid-template-columns: repeat(4,1fr); | |
| gap: 1px; | |
| background: var(--border); | |
| border: 1px solid var(--border); | |
| border-radius: 3px; | |
| overflow: hidden; | |
| margin: 52px 0 0; | |
| } | |
| @media(max-width:580px){ .stat-strip { grid-template-columns: repeat(2,1fr); } } | |
| .stat-cell { background: var(--surface); padding: 28px 20px; text-align: center; } | |
| .stat-val { | |
| font-family: 'Instrument Serif', serif; | |
| font-size: 2.3rem; | |
| color: var(--accent); | |
| line-height: 1; | |
| display: block; | |
| margin-bottom: 8px; | |
| letter-spacing: -0.02em; | |
| } | |
| .stat-label { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 8.5px; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| color: var(--muted); | |
| } | |
| section { padding: 68px 0; border-bottom: 1px solid var(--border); } | |
| .sec-label { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 9px; | |
| letter-spacing: 0.14em; | |
| text-transform: uppercase; | |
| color: var(--accent); | |
| display: block; | |
| margin-bottom: 10px; | |
| } | |
| h2 { | |
| font-family: 'Instrument Serif', serif; | |
| font-size: 1.95rem; | |
| color: var(--heading); | |
| font-weight: 400; | |
| margin-bottom: 18px; | |
| line-height: 1.2; | |
| } | |
| p { margin-bottom: 14px; font-size: 1rem; font-family: 'Crimson Pro', serif; font-weight: 300; } | |
| .two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 28px; } | |
| @media(max-width:560px){ .two-col { grid-template-columns: 1fr; } } | |
| .vs-card { border: 1px solid var(--border); border-radius: 3px; padding: 24px; background: var(--surface); } | |
| .vs-card.bad { border-top: 2px solid var(--red); } | |
| .vs-card.good { border-top: 2px solid var(--teal); } | |
| .vs-label { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 9px; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| margin-bottom: 10px; | |
| font-weight: 500; | |
| } | |
| .vs-card.bad .vs-label { color: var(--red); } | |
| .vs-card.good .vs-label { color: var(--teal); } | |
| .vs-card p { color: var(--muted); font-size: 0.92rem; margin: 0; line-height: 1.7; } | |
| .pipeline { | |
| display: flex; | |
| align-items: stretch; | |
| margin: 28px 0; | |
| border: 1px solid var(--border); | |
| border-radius: 3px; | |
| overflow: hidden; | |
| } | |
| .pipe-step { | |
| flex: 1; | |
| text-align: center; | |
| padding: 16px 8px; | |
| background: var(--surface); | |
| font-family: 'DM Mono', monospace; | |
| font-size: 10px; | |
| letter-spacing: 0.06em; | |
| text-transform: uppercase; | |
| color: var(--muted); | |
| cursor: default; | |
| transition: all 0.18s; | |
| border-right: 1px solid var(--border); | |
| } | |
| .pipe-step:last-child { border-right: none; } | |
| .pipe-step:hover { background: var(--accent-dim); color: var(--accent); } | |
| .pipe-step.danger:hover { background: var(--red-dim); color: var(--red); } | |
| .pipe-hint { display: block; font-size: 8px; margin-top: 5px; opacity: 0; transition: opacity 0.18s; color: var(--muted); } | |
| .pipe-step:hover .pipe-hint { opacity: 1; } | |
| .pipe-step.danger:hover .pipe-hint { color: var(--red); } | |
| .tab-bar { | |
| display: flex; | |
| gap: 2px; | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| border-radius: 3px 3px 0 0; | |
| padding: 4px; | |
| flex-wrap: wrap; | |
| } | |
| .tab-btn { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 9.5px; | |
| letter-spacing: 0.05em; | |
| text-transform: uppercase; | |
| padding: 7px 13px; | |
| border-radius: 2px; | |
| border: none; | |
| background: transparent; | |
| color: var(--muted); | |
| cursor: pointer; | |
| transition: all 0.14s; | |
| } | |
| .tab-btn.active { background: var(--surface); color: var(--accent); box-shadow: 0 1px 4px rgba(0,0,0,0.3); } | |
| .tab-btn:hover:not(.active) { color: var(--text); } | |
| .tab-panel { | |
| display: none; | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-top: none; | |
| border-radius: 0 0 3px 3px; | |
| padding: 28px; | |
| } | |
| .tab-panel.active { display: block; } | |
| .chart-img { | |
| width: 100%; | |
| display: block; | |
| border-radius: 2px; | |
| margin-top: 14px; | |
| border: 1px solid var(--border); | |
| } | |
| .chart-caption { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 8.5px; | |
| letter-spacing: 0.07em; | |
| text-transform: uppercase; | |
| color: var(--muted); | |
| margin-top: 8px; | |
| text-align: center; | |
| } | |
| .panel-text { | |
| font-family: 'Crimson Pro', serif; | |
| font-size: 0.95rem; | |
| color: var(--muted); | |
| margin-bottom: 4px; | |
| line-height: 1.8; | |
| font-weight: 300; | |
| } | |
| .panel-text strong { color: var(--accent); font-weight: 500; } | |
| .table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 3px; margin-top: 20px; } | |
| table { width: 100%; border-collapse: collapse; font-family: 'DM Mono', monospace; font-size: 0.78rem; } | |
| th { | |
| text-align: left; | |
| padding: 10px 16px; | |
| background: var(--surface2); | |
| color: var(--muted); | |
| font-size: 8px; | |
| letter-spacing: 0.12em; | |
| text-transform: uppercase; | |
| border-bottom: 1px solid var(--border); | |
| } | |
| td { padding: 11px 16px; border-bottom: 1px solid var(--surface2); color: var(--text); } | |
| tr:last-child td { border-bottom: none; } | |
| tr.win td { color: var(--accent); background: var(--accent-dim); font-weight: 500; } | |
| tr:not(.win):hover td { background: var(--surface2); } | |
| .arch-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; margin-top: 28px; } | |
| @media(max-width:620px){ .arch-grid { grid-template-columns: 1fr 1fr; } } | |
| .arch-card { | |
| border: 1px solid var(--border); | |
| border-radius: 3px; | |
| padding: 20px; | |
| background: var(--surface); | |
| transition: all 0.16s; | |
| } | |
| .arch-card:hover { border-color: var(--accent); background: var(--accent-dim2); transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,0.3); } | |
| .arch-tag { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 8px; | |
| letter-spacing: 0.1em; | |
| text-transform: uppercase; | |
| color: var(--accent); | |
| background: var(--accent-dim); | |
| padding: 2px 7px; | |
| border-radius: 2px; | |
| display: inline-block; | |
| margin-bottom: 10px; | |
| } | |
| .arch-card strong { display: block; font-family: 'Crimson Pro', serif; font-size: 0.95rem; color: var(--heading); font-weight: 500; margin-bottom: 5px; } | |
| .arch-card span { font-family: 'Crimson Pro', serif; font-size: 0.82rem; color: var(--muted); line-height: 1.6; font-weight: 300; } | |
| pre { | |
| background: var(--code-bg); | |
| color: var(--code-text); | |
| border-radius: 3px; | |
| padding: 22px 26px; | |
| font-family: 'DM Mono', monospace; | |
| font-size: 0.82rem; | |
| overflow-x: auto; | |
| margin-top: 16px; | |
| line-height: 1.8; | |
| border: 1px solid var(--border); | |
| } | |
| .kw { color: var(--accent); } | |
| .cm { color: #5a4050; } | |
| code { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 0.82em; | |
| background: var(--accent-dim); | |
| color: var(--accent); | |
| padding: 2px 6px; | |
| border-radius: 2px; | |
| } | |
| .demo-wrap { margin-top: 20px; border: 1px solid var(--border); border-radius: 3px; overflow: hidden; box-shadow: 0 4px 24px rgba(0,0,0,0.4); } | |
| .demo-wrap iframe { width: 100%; height: 680px; display: block; border: none; } | |
| .notice { | |
| border: 1px solid var(--border); | |
| border-left: 3px solid var(--amber); | |
| border-radius: 0 3px 3px 0; | |
| background: var(--amber-dim); | |
| padding: 14px 18px; | |
| font-family: 'Crimson Pro', serif; | |
| font-size: 0.9rem; | |
| color: var(--amber); | |
| margin-top: 16px; | |
| line-height: 1.7; | |
| font-weight: 300; | |
| } | |
| footer { | |
| padding: 36px 0 44px; | |
| display: flex; | |
| flex-wrap: wrap; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 14px; | |
| } | |
| .foot-note { font-family: 'DM Mono', monospace; font-size: 9px; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted); } | |
| .foot-links { display: flex; gap: 20px; } | |
| .foot-links a { font-family: 'DM Mono', monospace; font-size: 10px; letter-spacing: 0.05em; color: var(--muted); text-decoration: none; transition: color 0.14s; } | |
| .foot-links a:hover { color: var(--accent); } | |
| .reveal { opacity: 0; transform: translateY(18px); transition: opacity 0.5s ease, transform 0.5s ease; } | |
| .reveal.visible { opacity: 1; transform: none; } | |
| </style> | |
| </head> | |
| <body> | |
| <nav class="topbar"> | |
| <span class="topbar-logo"><span class="logo-dot"></span>phi-exposure-guard</span> | |
| <div class="topbar-links"> | |
| <a href="#results">Results</a> | |
| <a href="#architecture">Architecture</a> | |
| <a href="#demo">Demo</a> | |
| <a href="https://github.com/azithteja91/phi-exposure-guard" target="_blank">GitHub</a> | |
| </div> | |
| </nav> | |
| <div class="wrap"> | |
| <div class="hero"> | |
| <div class="tag-row"> | |
| <span class="tag tag-rose">Healthcare AI</span> | |
| <span class="tag tag-teal">Privacy Research</span> | |
| <span class="tag tag-red">Patent Pending</span> | |
| </div> | |
| <h1>PHI Exposure Guard<br><em>Stateful De-Identification</em><br>for Streaming Data</h1> | |
| <p class="hero-sub"> | |
| Re-identification risk builds across events, modalities, and time. Most de-identification | |
| pipelines treat each record as isolated and never see it coming. This system tracks | |
| cumulative exposure continuously and adjusts masking strength before it's too late. | |
| </p> | |
| <div class="btn-row"> | |
| <a class="btn btn-primary" href="https://colab.research.google.com/github/azithteja91/phi-exposure-guard/blob/main/notebooks/demo_colab.ipynb" target="_blank">▶ Run on Colab</a> | |
| <a class="btn btn-ghost" href="https://vkatg-dcpg-scorer-demo.hf.space" target="_blank">⚡ Live Scorer</a> | |
| <a class="btn btn-ghost" href="https://huggingface.co/datasets/vkatg/streaming-phi-deidentification-benchmark" target="_blank">⬡ Dataset</a> | |
| <a class="btn btn-ghost" href="https://github.com/azithteja91/phi-exposure-guard" target="_blank">↗ GitHub</a> | |
| <a class="btn btn-ghost" href="https://doi.org/10.5281/zenodo.18865882" target="_blank">DOI</a> | |
| </div> | |
| <div class="stat-strip"> | |
| <div class="stat-cell"> | |
| <span class="stat-val">-0.917</span> | |
| <span class="stat-label">Delta-AUROC (n=10)</span> | |
| </div> | |
| <div class="stat-cell"> | |
| <span class="stat-val">17ms</span> | |
| <span class="stat-label">Multi-run latency</span> | |
| </div> | |
| <div class="stat-cell"> | |
| <span class="stat-val">0.991</span> | |
| <span class="stat-label">Privacy @ High Risk</span> | |
| </div> | |
| <div class="stat-cell"> | |
| <span class="stat-val">0.881</span> | |
| <span class="stat-label">Risk model Pearson r</span> | |
| </div> | |
| </div> | |
| </div> | |
| <section class="reveal"> | |
| <span class="sec-label">Motivation</span> | |
| <h2>Static masking is the wrong model</h2> | |
| <p>Standard pipelines treat every record independently. Detect PHI, remove it, move on. That works for single documents. It breaks in streaming systems where the same patient shows up across hundreds of events over time: clinical notes, ASR transcripts, imaging metadata, waveform headers.</p> | |
| <p>A name fragment, a voice signature, a face region in an image proxy. None of these are identifying alone. Accumulated across a session, they are.</p> | |
| <div class="two-col"> | |
| <div class="vs-card bad"> | |
| <div class="vs-label">Standard approach</div> | |
| <p>Per-document masking. No memory of prior events. Risk accumulates invisibly across the stream and no threshold ever fires.</p> | |
| </div> | |
| <div class="vs-card good"> | |
| <div class="vs-label">This system</div> | |
| <p>Subject-level exposure state persisted across events. Rolling risk across modalities and time. Masking strength scales with actual accumulated risk.</p> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="reveal"> | |
| <span class="sec-label">System</span> | |
| <h2>Five masking tiers, one adaptive controller</h2> | |
| <p>Instead of a single policy applied to everything, the controller picks from five tiers based on a continuously updated risk score. Hover each tier to see the threshold.</p> | |
| <div class="pipeline"> | |
| <div class="pipe-step">Raw<span class="pipe-hint">r < 0.40</span></div> | |
| <div class="pipe-step">Weak<span class="pipe-hint">r < 0.40</span></div> | |
| <div class="pipe-step">Synthetic<span class="pipe-hint">0.40 ≤ r < 0.60</span></div> | |
| <div class="pipe-step">Pseudo<span class="pipe-hint">0.60 ≤ r < 0.80</span></div> | |
| <div class="pipe-step danger">Redact<span class="pipe-hint">r ≥ 0.80</span></div> | |
| </div> | |
| <p>The exposure state lives in a <strong style="color:var(--accent)">Dynamic Contextual Privacy Graph (DCPG)</strong>, a per-patient, per-modality graph that accumulates PHI unit counts, tracks cross-modal semantic links (cosine similarity > 0.30), and applies recency weighting to the entropy calculation. Policy decisions are made by a PPO agent with an LSTM backbone, pre-trained over 200 stratified episodes.</p> | |
| <img src="phi_graph_structure.png" alt="DCPG graph structure" class="chart-img" /> | |
| <p class="chart-caption">DCPG node and edge structure. Each patient is a subgraph; nodes = PHI per modality; edges = co-occurrence and cross-modal semantic links.</p> | |
| </section> | |
| <section id="results" class="reveal"> | |
| <span class="sec-label">Results — March 13 2026 · 34 live events · n=10 runs</span> | |
| <h2>What the numbers look like</h2> | |
| <div class="tab-bar"> | |
| <button class="tab-btn active" onclick="switchTab(this,'tab-auroc')">AUROC Reduction</button> | |
| <button class="tab-btn" onclick="switchTab(this,'tab-pareto')">Privacy vs Utility</button> | |
| <button class="tab-btn" onclick="switchTab(this,'tab-risk')">Risk Timeline</button> | |
| <button class="tab-btn" onclick="switchTab(this,'tab-adversarial')">Adversarial</button> | |
| <button class="tab-btn" onclick="switchTab(this,'tab-latency')">Latency</button> | |
| <button class="tab-btn" onclick="switchTab(this,'tab-rl')">RL Training</button> | |
| <button class="tab-btn" onclick="switchTab(this,'tab-crdt')">CRDT</button> | |
| </div> | |
| <div id="tab-auroc" class="tab-panel active"> | |
| <p class="panel-text">Delta-AUROC measures how much masking degrades an adversarial re-identification classifier. Negative means the adversary is losing signal. <strong>Multi-run mean: -0.9167 +/- 0.0000 (95% CI, n=10).</strong> Same result across all 10 jittered runs. By the end of the stream the masked output carries almost no re-identification signal.</p> | |
| <img class="chart-img" src="delta_auroc_annotated.png" alt="Delta AUROC" /> | |
| <p class="chart-caption">AUROC reduction over the 34-event stream. Peak protection at event 11: delta = -1.000.</p> | |
| <img class="chart-img" src="statistical_robustness.png" alt="Statistical robustness" style="margin-top:16px" /> | |
| <p class="chart-caption">Multi-run robustness across 10 jittered replications. Zero variance in final delta-AUROC.</p> | |
| </div> | |
| <div id="tab-pareto" class="tab-panel"> | |
| <p class="panel-text">Every static policy fails at least one side. Redact everything and utility collapses. Apply weak masking and privacy breaks at high risk. <strong>Adaptive is the only policy that clears the 0.85 privacy floor while keeping utility above 0.50</strong> on the bursty workload.</p> | |
| <img class="chart-img" src="pareto_frontier_annotated.png" alt="Pareto frontier" /> | |
| <p class="chart-caption">Privacy @ High Risk vs Utility @ Low Risk. Adaptive uniquely satisfies both constraints on the bursty workload.</p> | |
| <div class="table-wrap"> | |
| <table> | |
| <thead><tr><th>Policy</th><th>Privacy @ High Risk</th><th>Utility @ Low Risk</th><th>Consent Violations</th><th>Latency (ms)</th></tr></thead> | |
| <tbody> | |
| <tr><td>Always-Raw</td><td>0.000</td><td>1.000</td><td>0</td><td>0.5</td></tr> | |
| <tr><td>Always-Weak</td><td>0.004</td><td>0.847</td><td>0</td><td>1.0</td></tr> | |
| <tr><td>Always-Synthetic</td><td>0.564</td><td>0.676</td><td>0</td><td>2.0</td></tr> | |
| <tr><td>Always-Pseudo</td><td>0.855</td><td>0.440</td><td>0</td><td>1.5</td></tr> | |
| <tr><td>Always-Redact</td><td>1.000</td><td>0.000</td><td>17</td><td>1.0</td></tr> | |
| <tr class="win"><td>Adaptive ★</td><td>0.991</td><td>0.847</td><td>10</td><td>1.09</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <img class="chart-img" src="adaptive_vs_static_delta.png" alt="Delta vs static" style="margin-top:16px" /> | |
| <p class="chart-caption">Delta vs each static baseline. Green = adaptive wins on privacy, blue = adaptive wins on utility.</p> | |
| <img class="chart-img" src="risk_conditional_scores.png" alt="Risk conditional" style="margin-top:16px" /> | |
| <p class="chart-caption">Effective privacy and utility conditioned on risk level. Static policies cannot adapt across the full risk range.</p> | |
| </div> | |
| <div id="tab-risk" class="tab-panel"> | |
| <p class="panel-text">Risk starts around 0.5 and climbs toward 0.97 by event 33 as PHI accumulates across patients and modalities. The policy switch timeline shows which tier is active at each event, with threshold crossings and consent-cap events annotated.</p> | |
| <img class="chart-img" src="adaptive_risk_timeline.png" alt="Risk timeline" /> | |
| <p class="chart-caption">Cumulative risk score across all 34 live events.</p> | |
| <img class="chart-img" src="policy_switch_annotated.png" alt="Policy switches" style="margin-top:16px" /> | |
| <p class="chart-caption">Policy tier per event. Consent-cap events show where redact decisions are downgraded to pseudo for patient A.</p> | |
| <img class="chart-img" src="risk_model_validation.png" alt="Risk model validation" style="margin-top:16px" /> | |
| <p class="chart-caption">Risk model validation: exposure-entropy score vs combinatorial reconstruction probability. Pearson r = 0.881.</p> | |
| </div> | |
| <div id="tab-adversarial" class="tab-panel"> | |
| <p class="panel-text">The obvious evasion: keep every event just below the lowest threshold. We modeled this formally. The attacker spaces PHI at risk 0.34-0.39 and sends a cross-modal probe every 5th event. The system detects it via cosine similarity, applies a +0.15 nudge, and escalates to pseudo on every probe event. <strong>Always-Weak never responds.</strong></p> | |
| <img class="chart-img" src="adversarial_algorithm.png" alt="Adversarial algorithm" /> | |
| <p class="chart-caption">Formal adversarial model: sub-threshold PHI probing with cross-modal exploitation.</p> | |
| <img class="chart-img" src="adversarial_workload_detail.png" alt="Adversarial workload" style="margin-top:16px" /> | |
| <p class="chart-caption">Adaptive escalates on every probe event. Always-Weak stays flat throughout.</p> | |
| <img class="chart-img" src="messy_workload_analysis.png" alt="Messy workloads" style="margin-top:16px" /> | |
| <p class="chart-caption">Modality-imbalanced and alternating-burst workloads. Adaptive maintains the privacy floor across all three.</p> | |
| </div> | |
| <div id="tab-latency" class="tab-panel"> | |
| <p class="panel-text">Canonical multi-run latency is <strong>17.2-17.9 ms</strong> across pseudo, redact, and synthetic, well inside the 50 ms real-time threshold. Latency stays flat with respect to risk score, so the controller is not doing more work as exposure accumulates.</p> | |
| <img class="chart-img" src="latency_by_policy.png" alt="Latency by policy" /> | |
| <p class="chart-caption">Per-policy latency. Multi-run means shown in red. Single-run warmup outliers excluded from canonical figure.</p> | |
| <img class="chart-img" src="latency_histogram.png" alt="Latency histogram" style="margin-top:16px" /> | |
| <p class="chart-caption">Overall latency distribution across all 34 live events. One warmup outlier visible near 1100ms.</p> | |
| </div> | |
| <div id="tab-rl" class="tab-panel"> | |
| <p class="panel-text">200 stratified pre-training episodes before live deployment. By the final epoch, pseudo and redact make up a larger share of decisions, which is convergence toward risk-appropriate choices. Live-loop rewards settle into 0.62-0.67 after event 5.</p> | |
| <img class="chart-img" src="rl_training_stability.png" alt="RL training stability" /> | |
| <p class="chart-caption">PPO training stability: reward with rolling average and policy distribution across training epochs.</p> | |
| <img class="chart-img" src="ppo_live_reward_curve.png" alt="Live reward" style="margin-top:16px" /> | |
| <p class="chart-caption">Per-event RL reward during the 34-event live loop.</p> | |
| <img class="chart-img" src="ppo_reward_fix.png" alt="Reward fix" style="margin-top:16px" /> | |
| <p class="chart-caption">Reward function fix: old alpha*(1-risk) term penalized the agent for high-risk environments. New risk-normalized term stays stable.</p> | |
| </div> | |
| <div id="tab-crdt" class="tab-panel"> | |
| <p class="panel-text">The DCPG state can be merged across edge devices using a CRDT. Two devices with overlapping patient observations converge to the same result regardless of update ordering.</p> | |
| <img class="chart-img" src="crdt_vs_sqlite_risk.png" alt="CRDT vs SQLite" /> | |
| <p class="chart-caption">CRDT-backed risk vs centralized SQLite DCPG over the live run. Both converge toward 0.97 by event 33.</p> | |
| <img class="chart-img" src="multimodal_phi_correlation.png" alt="PHI correlation" style="margin-top:16px" /> | |
| <p class="chart-caption">Cross-modal PHI correlation matrix. Off-diagonal mean r = 0.081, validating the 0.30 cosine similarity threshold.</p> | |
| </div> | |
| </section> | |
| <section id="architecture" class="reveal"> | |
| <span class="sec-label">Architecture</span> | |
| <h2>Modular pipeline</h2> | |
| <p>PHI detection feeds the DCPG, which produces a risk score, which the PPO agent uses to select a policy, which the consent layer caps, which the masking CMO executes, all logged to a signed audit trail.</p> | |
| <div class="arch-grid"> | |
| <div class="arch-card"><span class="arch-tag">dcpg</span><strong>DCPG</strong><span>Per-patient, per-modality graph. PHI accumulation, cross-modal links, recency-weighted entropy.</span></div> | |
| <div class="arch-card"><span class="arch-tag">ctrl</span><strong>Controller</strong><span>Risk-threshold policy selection, consent cap enforcement, retokenization trigger at r ≥ 0.68.</span></div> | |
| <div class="arch-card"><span class="arch-tag">rl</span><strong>PPO Agent</strong><span>LSTM policy network, 128-dim hidden, 2 layers, 14-dim state. Pre-trained 200 episodes.</span></div> | |
| <div class="arch-card"><span class="arch-tag">cmo</span><strong>Masking CMOs</strong><span>Five-tier composable masking operators with DAG execution and fallback handling.</span></div> | |
| <div class="arch-card"><span class="arch-tag">crdt</span><strong>Federated Graph</strong><span>CRDT merge for distributed edge deployments. Convergence guaranteed regardless of ordering.</span></div> | |
| <div class="arch-card"><span class="arch-tag">audit</span><strong>Audit Signing</strong><span>Cryptographic signing and FHIR export of all masking decisions with full provenance.</span></div> | |
| </div> | |
| </section> | |
| <section id="demo" class="reveal"> | |
| <span class="sec-label">Live Demo</span> | |
| <h2>Try the scorer</h2> | |
| <p>Add events for a patient across modalities and watch risk accumulate in real time. State is shared across all users. Submit events for <code>patient_001</code> and the next visitor picks up where you left off.</p> | |
| <p>Suggested sequence: start with <code>text</code>, then <code>asr</code> with overlapping name and date strings. Watch <code>cross_modal_matches</code> fire on the second event. Keep going until <code>trigger: true</code>.</p> | |
| <div class="demo-wrap"> | |
| <iframe src="https://vkatg-dcpg-scorer-demo.hf.space" title="DCPG Live Scorer"></iframe> | |
| </div> | |
| </section> | |
| <section class="reveal"> | |
| <span class="sec-label">Quick Start</span> | |
| <h2>Run it yourself</h2> | |
| <p>Run the full benchmark locally. Results go to <code>results/</code>.</p> | |
| <pre><span class="cm"># install</span> | |
| <span class="kw">pip install</span> phi-exposure-guard | |
| <span class="cm"># run the benchmark</span> | |
| <span class="kw">python</span> -m amphi_rl_dpgraph.run_demo | |
| <span class="cm"># or run tests</span> | |
| <span class="kw">pytest</span> -vv</pre> | |
| <div class="btn-row" style="margin-top:20px"> | |
| <a class="btn btn-primary" href="https://colab.research.google.com/github/azithteja91/phi-exposure-guard/blob/main/notebooks/demo_colab.ipynb" target="_blank">▶ Open in Colab</a> | |
| <a class="btn btn-ghost" href="https://github.com/azithteja91/phi-exposure-guard" target="_blank">↗ View on GitHub</a> | |
| </div> | |
| </section> | |
| <section class="reveal"> | |
| <span class="sec-label">Citation & IP</span> | |
| <h2>Using this work</h2> | |
| <p>Cite via the <code>CITATION.cff</code> file in the GitHub repository, or use the BibTeX below.</p> | |
| <pre>@software{phi_exposure_guard, | |
| title = {Stateful Exposure-Aware De-Identification | |
| for Multimodal Streaming Data}, | |
| doi = {10.5281/zenodo.18865882}, | |
| url = {https://doi.org/10.5281/zenodo.18865882} | |
| }</pre> | |
| <div class="notice"> | |
| Associated with a U.S. provisional patent application filed 2025-07-05. Public release: 2026-03-02. | |
| All experiments run on fully synthetic data. Not a production compliance system. | |
| </div> | |
| </section> | |
| <footer> | |
| <span class="foot-note">MIT License · Synthetic data only · Research code</span> | |
| <div class="foot-links"> | |
| <a href="https://github.com/azithteja91/phi-exposure-guard" target="_blank">GitHub</a> | |
| <a href="https://huggingface.co/datasets/vkatg/streaming-phi-deidentification-benchmark" target="_blank">Dataset</a> | |
| <a href="https://doi.org/10.5281/zenodo.18865882" target="_blank">Zenodo</a> | |
| <a href="https://colab.research.google.com/github/azithteja91/phi-exposure-guard/blob/main/notebooks/demo_colab.ipynb" target="_blank">Colab</a> | |
| </div> | |
| </footer> | |
| </div> | |
| <script> | |
| function switchTab(btn, id) { | |
| document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); | |
| document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active')); | |
| btn.classList.add('active'); | |
| document.getElementById(id).classList.add('active'); | |
| } | |
| const obs = new IntersectionObserver(entries => { | |
| entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('visible'); }); | |
| }, { threshold: 0.07 }); | |
| document.querySelectorAll('.reveal').forEach(el => obs.observe(el)); | |
| </script> | |
| </body> | |
| </html> |