38.12 隐私计算
一句话
隐私计算不是“一种加密算法”,而是在不看见明文的前提下完成计算的四条路线——联邦学习、安全多方计算、可信执行环境、差分隐私;选型只看三件事:数据能不能出域、要做哪种计算、能接受多少精度与性能损失。
四个误区,先拆掉
| 误区 | 现实 |
|---|---|
| 隐私计算 = 加密 | 加密只解决“存储与传输”,解决不了“计算时必须有明文”。四条路线各自用不同办法绕开这一点,信任假设完全不同 |
| 用了联邦学习,数据就没出域 | 梯度与模型参数本身就携带信息。没有安全聚合或差分隐私的联邦学习,可以从不更新的参数反推出参与方的样本特征 |
| 差分隐私 = 加噪声 | 加噪声只是手段,真正的约束是隐私预算:总 ε 花完了,再问一次就不私密了(1000 次 ε=0.1 等价于一次 ε=100) |
| TEE 万能 | 它把信任从“云厂商运维”转移到“CPU 厂商 + 侧信道防护”。可信内存有限、性能有折损,且漏洞(侧信道)一旦出现影响面是整个平台 |
四条路线:先看“信任谁”,再看性能
| 路线 | 数据出域? | 信任假设 | 擅长 | 开销 | 精度损失 | 不适用 |
|---|---|---|---|---|---|---|
| 联邦学习 | 不出域(只传参数/梯度) | 参与方诚实但好奇 → 需配合安全聚合或 DP | 联合训练模型(风控、推荐、医疗影像) | 通信量 = 模型大小 × 轮数 × 客户端数 | 低于集中式训练(非独立同分布时明显) | 需要精确统计、样本极少 |
| 安全多方计算 (MPC) | 不出域(密文分片) | 参与方不完全互信,无单一可信方 | 求交(PSI)、求和、比较、联合统计 | 通信量比明文高 1–3 个数量级(经验量级) | 无(精确结果,除非用定点近似) | 大规模深度学习、低延迟场景 |
| 可信执行环境 (TEE) | 可以出域(进 enclave) | 信任 CPU 厂商 + 硬件无侧信道漏洞 | 通用计算(几乎任意代码) | 比明文慢(内存加密 + 远程证明),可信内存受平台规格限制 | 无 | 需要超大内存、强侧信道威胁模型 |
| 差分隐私 (DP) | 可以出域,但只出“加噪结果” | 只信任数学,不信任任何参与方 | 统计发布、看板、A/B 指标 | 可忽略(只是加噪) | 有,且随 ε 变小而变大 | 需要逐行精确结果、小样本 |
🔑 选型顺序:先问“能不能不出域”(联邦/MPC),再问“能不能接受明文进硬件”(TEE),最后才问“能不能接受精度损失”(DP)。反过来先上 DP,通常是因为前三条都做不到——这时要确认“精度损失是否让业务结论失效”,而不是直接接受。
三个必须能算的数字
① 联邦学习的通信量——它常常是项目死掉的原因,不是算法:
通信量 = 模型大小 × 2(上行+下行)× 轮数 × 客户端数
100 MB 模型、1000 轮、100 个客户端:100 MB × 2 × 1000 × 100 = 20 TB。把它压到 10 MB 模型,通信量降到 2 TB;把轮数压到 100,降到 200 GB。模型大小与轮数是唯一能动的两个旋钮,加带宽不如减模型。
② MPC 的通信放大——明文算 1 GB 数据,MPC 可能要传几十到几百 GB。经验上,参与方越多、轮数越多,放大越厉害;所以能用两方 PSI 解决的,不要上三方通用 MPC。
③ DP 的预算——这是唯一有严格数学定义的。基本组合定理:ε_total = Σ ε_i。1000 次 ε=0.1 的查询,等于一次 ε=100 的查询。下面用代码把这件事量出来。
可运行的隐私预算实验
拉普拉斯机制:对计数查询,敏感度 Δ=1,噪声尺度 b = Δ/ε;95% 误差上界约 ±b·ln(20) ≈ ±3b。
package main
import (
"fmt"
"math"
"math/rand"
)
// 拉普拉斯机制:对计数查询,敏感度 Δ=1,噪声尺度 b=Δ/ε。
// ε 越小越私密,噪声越大;但隐私预算是"会花完的"——每次查询都在扣余额。
func laplace(r *rand.Rand, b float64) float64 {
u := r.Float64() - 0.5
return -b * math.Copysign(math.Log(1-2*math.Abs(u)), u)
}
func main() {
const total = 10000.0
const positive = 3000.0
fmt.Println("【一】一次计数查询:ε 与噪声幅度的关系(Δ=1,b=1/ε)")
fmt.Printf("%-6s %10s %14s %14s\n", "ε", "噪声尺度b", "95%误差上界", "一次发布结果")
r := rand.New(rand.NewSource(42))
for _, eps := range []float64{0.1, 1, 10} {
b := 1 / eps
bound := b * math.Log(20) // 拉普拉斯 95% 区间:±b·ln(20)
fmt.Printf("%-6.2f %10.1f %14.1f %14.0f\n", eps, b, bound, positive+laplace(r, b))
}
fmt.Println("\n【二】隐私预算会被花完:基本组合定理 ε_total = Σε_i")
fmt.Printf("%-10s %10s %14s %14s\n", "单次ε", "查询次数", "累计ε", "噪声尺度")
for _, c := range []struct {
eps float64
k int
}{{0.1, 10}, {0.1, 100}, {0.1, 1000}, {0.01, 100}} {
fmt.Printf("%-10.2f %10d %14.1f %14.1f\n", c.eps, c.k, c.eps*float64(c.k), 1/(c.eps*float64(c.k)))
}
fmt.Println("读法:1000 次 ε=0.1 的查询,等价于一次 ε=100 的查询——此时噪声几乎为零,隐私等于没有。")
fmt.Println("\n【三】成员推断实验:数据集只差一条记录,能不能问出来")
fmt.Println("A 有 3000 个阳性,B 有 2999 个(目标记录在 A 不在 B)。")
fmt.Println("攻击者:把 k 次回答取平均,超过 2999.5 就判定\"目标在里面\"。")
fmt.Printf("%-8s %6s %10s %14s\n", "单次ε", "次数k", "累计ε", "判定成功率")
for _, c := range []struct {
eps float64
k int
}{{0.01, 100}, {0.05, 100}, {0.1, 100}, {0.1, 1000}} {
r := rand.New(rand.NewSource(7))
trials, ok := 400, 0
for i := 0; i < trials; i++ {
var sumA, sumB float64
for j := 0; j < c.k; j++ {
sumA += positive + laplace(r, 1/c.eps)
sumB += (positive - 1) + laplace(r, 1/c.eps)
}
if sumA/float64(c.k)-sumB/float64(c.k) > 0 {
ok++
}
}
fmt.Printf("%-8.2f %6d %10.2f %13.1f%%\n", c.eps, c.k, c.eps*float64(c.k), 100*float64(ok)/float64(trials))
}
fmt.Println("\n【四】噪声怎么随次数收敛(平均 k 次回答后的噪声标准差)")
fmt.Printf("%-6s %10s %10s %10s\n", "ε单次", "k=10", "k=100", "k=1000")
for _, eps := range []float64{0.1, 0.01} {
b := 1 / eps
fmt.Printf("%-6.2f", eps)
for _, k := range []float64{10, 100, 1000} {
fmt.Printf(" %10.2f", math.Sqrt(2)*b/math.Sqrt(k))
}
fmt.Println()
}
}
本机运行结果(GO111MODULE=off go run main.go,Go 1.27.1 darwin/arm64):
【一】一次计数查询:ε 与噪声幅度的关系(Δ=1,b=1/ε)
ε 噪声尺度b 95%误差上界 一次发布结果
0.10 10.0 30.0 3003
1.00 1.0 3.0 3002
10.00 0.1 0.3 3000
【二】隐私预算会被花完:基本组合定理 ε_total = Σε_i
单次ε 查询次数 累计ε 噪声尺度
0.10 10 1.0 1.0
0.10 100 10.0 0.1
0.10 1000 100.0 0.0
0.01 100 1.0 1.0
读法:1000 次 ε=0.1 的查询,等价于一次 ε=100 的查询——此时噪声几乎为零,隐私等于没有。
【三】成员推断实验:数据集只差一条记录,能不能问出来
A 有 3000 个阳性,B 有 2999 个(目标记录在 A 不在 B)。
攻击者:把 k 次回答取平均,超过 2999.5 就判定"目标在里面"。
单次ε 次数k 累计ε 判定成功率
0.01 100 1.00 52.2%
0.05 100 5.00 59.8%
0.10 100 10.00 70.2%
0.10 1000 100.00 95.2%
【四】噪声怎么随次数收敛(平均 k 次回答后的噪声标准差)
ε单次 k=10 k=100 k=1000
0.10 4.47 1.41 0.45
0.01 44.72 14.14 4.47
逐条读这份输出:
- ε=0.1 时一次发布的结果是 3003,真值是 3000,误差 3——单次看起来很“安全”(95% 上界 30)。但安全是按“总预算”算的,不是按单次。
- 1000 次 ε=0.1 = 一次 ε=100,噪声尺度从 10 掉到 0.01。此时发布结果几乎等于真值,差分隐私形同虚设。这就是“同一个看板允许无限次查询”最危险的地方:每次看起来都合规,累计起来已经泄露。
- 成员推断成功率从 52.2% 升到 95.2%:累计 ε=1(约等于随机猜)时只比瞎猜高 2.2 个百分点;累计 ε=10 时 70.2%;ε=100 时 95.2%——攻击者只需要重复问。图中 52.2% 就是“隐私还在”的样子:它离 50% 只差 2.2 个点,统计上几乎无法区分。
- 平均 k 次后噪声标准差按
1/√k收敛(ε=0.1 时 4.47 → 1.41 → 0.45)。攻击者不需要更聪明的算法,只需要更多次查询;防线只能建在“总预算”上:同一数据集、同一用户群体的查询要记账,超过预算就拒绝或加噪。
⚠️ 这三个数字说明一件事:差分隐私的安全性来自“预算管理”,不来自“加噪”。工程上必须有一个隐私预算账本(谁、对哪个数据集、花了多少 ε),否则加噪只是让第一次查询看起来无害。
决策表:你的情况选哪条
| 你的情况 | 选 | 关键动作 | 主要坑 |
|---|---|---|---|
| 两家机构联合建模,样本不能出域,模型可以共享 | 联邦学习 | 安全聚合或 DP-SGD;先算通信量 | 非独立同分布导致精度骤降;梯度泄露 |
| 只想求交集/求和/统计,结果要精确 | MPC(PSI / 秘密共享) | 限定两方、限定量级,先做小规模 POC | 通信量放大 1–3 个数量级,跨公网基本不可行 |
| 计算逻辑复杂、想复用现有代码,信任云厂商但不想让它看数据 | TEE | 远程证明 + 代码度量白名单 | 可信内存限制、侧信道、性能折损 |
| 要对外发布统计结果/看板,可接受误差 | 差分隐私 | 建预算账本,按查询记账 | 预算叠加、多次查询、差分攻击 |
| 只是内部数据要给分析师用 | 脱敏 / 假名化 / 最小权限(38.5 数据分级与脱敏) | 先做分级与访问控制 | 脱敏不可逆性误判、准标识符重识别 |
什么时候别用隐私计算
- 数据量极小(< 1000 条):DP 的噪声会淹没信号,MPC 的通信开销占比离谱。此时更该做的是“不出域、不共享”。
- 需要逐行精确结果(对账、结算):DP 不适用;MPC 只在参与方少、数据量小时可行。
- 合规只要求“去标识化 + 访问控制”:先做分级、脱敏、最小权限,成本低一个数量级;隐私计算是“必须联合计算”时才付的溢价。
- 为了投标而做 POC:没有真实数据方与真实业务问题的隐私计算项目,99% 停在演示阶段——它解决的是“多方数据不能出域”的问题,不是“想用 AI”的问题。
反模式
| 反模式 | 后果 |
|---|---|
| 只看单次 ε,不做预算账本 | 累计 ε 无限增长,成员推断成功率可达 95% 以上(见输出) |
| 联邦学习不做安全聚合 | 梯度反演可还原参与方样本特征,等于数据出了域 |
| 用 TEE 代替数据分级 | 硬件可信不等于“该给的数据给了、不该给的也给了” |
| MPC 全量替换现有计算 | 通信量放大 1–3 个数量级,延迟与成本双双失控 |
| 把“脱敏”当隐私计算 | 准标识符组合可重识别,且不可逆性无法证明 |
| 项目只验收“跑通” | 没有精度损失、性能、预算消耗三个数字,无法判断能不能上线 |
关联
- 前置:38.5 数据分级与脱敏(先判断哪些字段必须保护,再决定要不要上隐私计算)
- 并列:38.4 加密(传输与静态加密是更便宜的第一层,先做它再谈隐私计算)
- 下游:46.5 数据治理与血缘(隐私计算要落在血缘上,才追得清“哪份数据被谁算过”)、46.6 数据生命周期(最短留存是成本最低的隐私保护)
自检
Q1 某看板对同一用户群体提供 100 次 ε=0.1 的统计查询,产品经理说“每次都加了噪声,肯定安全”。错在哪?用数字说明。
▸ 答案:错在把单次预算当总预算。基本组合定理给出 ε_total = 100 × 0.1 = 10;而成员推断实验显示:累计 ε=10 时,判定成功率是 70.2%,累计 ε=100 时是 95.2%——每多一次查询,攻击者的判断就准一点。正确做法是建预算账本:同一数据集、同一群体的查询总量上限(例如 ε_total ≤ 1 或 ≤ 2),超了就拒绝或降精度。
Q2 联邦学习的通信量怎么估?100 MB 模型、1000 轮、100 个客户端,量级是多少?如果通信成为瓶颈,先动哪个参数?
▸ 答案:100 MB × 2(上下行)× 1000 轮 × 100 客户端 = 20 TB。先动模型大小(100 MB → 10 MB 就降到 2 TB,降 90%),其次动轮数(1000 → 100 降到 2 TB);动客户端数通常不可行(那是业务规模)。经验判据:若单轮通信时间 > 单轮计算时间,先做模型压缩(剪枝/量化/蒸馏),而不是加带宽。
Q3 客户要求“联合计算,但结果必须精确到个位数”,且两家数据不能出域。你会推荐什么?说出它的两个硬约束。
▸ 答案:推荐 MPC(两方 PSI 或秘密共享求和),因为 DP 会引入误差,TEE 需要明文进硬件且依赖厂商。两个硬约束:① 通信量放大 1–3 个数量级——明文 1 GB 的数据,跨公网做 MPC 基本不可行,通常要求同城专线或同机房;② 参与方与轮数受限——两方、简单算子(交、和、比较)可行;三方以上或深度计算会让轮数与通信爆炸。先做“小数据量 + 精确算子”的 POC,再评估是否值得扩面。
Q4 为什么说“TEE 把信任从云厂商转移到 CPU 厂商”?在企业选型时,这个转移意味着哪些新的尽职调查?
▸ 答案:因为 TEE 的安全性由硬件与固件保证:数据在 enclave 内明文,云厂商运维看不到,但 CPU 厂商(及其微码)理论上能看到,侧信道漏洞也由它修补。转移带来的尽调:① 远程证明链是否可验证(谁来签、度量值怎么白名单化);② 漏洞响应 SLA(历史上多起侧信道漏洞需要微码/内核配合修复);③ 可信内存规格是否够装下你的工作集(不够就要分片,性能与复杂度上升);④ 合规认可度(部分行业不接受“数据明文进第三方硬件”的模型,需法务确认)。