私有乘法权重算法:高效回答大量查询

原文课程: Lecture 8 — Private Multiplicative Weights (Gautam Kamath, CS 860, Fall 2020)

前面我们学习了拉普拉斯机制、高斯机制和指数机制。它们能回答单个或少量查询,但如果要回答海量查询呢?

比如一个数据分析师想问你一万个关于人口普查数据的问题——每个问题都精确到 1% 以内。用拉普拉斯机制,你需要至少 O(|Q|) 个样本——大约一万人才能回答一个问题(误差 1%),而回答一万个问题就需要一亿人

这合理吗?显然不合理——数据集中包含的信息是有限的,它不可能支持任意多查询的精确回答。但问题在于:能不能比拉普拉斯机制做得更好?

答案是。而且方法出乎意料地优雅——它来自一个叫"乘法权重"的古老算法。


1. 从一条数据到"合成数据"

线性查询的定义

先来定义我们要回答的问题。**线性查询(Linear Query)是一类非常基础的问题:给定一个查询函数 q: X → [0,1],它在某个数据集 D 上的答案是所有样本取值的平均值**:

q(D) = (1/n) · Σ_{x∈D} q(x)

举个具体例子:

查询q(x) 的含义在数据集上的平均值
"有多少人是女性?"q(x)=1 如果是女性,否则 0女性比例
"平均年龄是多少?"将年龄归一化到 [0,1]归一化平均年龄
"有多少人年收入超过 5 万?"q(x)=1 如果收入>5万高收入比例
"有多少人是大学以上学历?"q(x)=1 如果学历≥本科高学历比例

线性查询看起来简单,但几乎所有基础统计都可以表示为线性查询的组合。

拉普拉斯机制需要多少数据?

如果要回答 |Q| 个查询,每个误差 ≤ α,拉普拉斯机制需要多少个样本?根据组合定理:

n = O(|Q| / (αε))

这是线性增长的——查询越多,所需数据越多。如果 |Q| = 10,000,α = 0.01,ε = 1,你需要 n = 1,000,000 条数据。

graph LR
    A["|Q| 个查询"] --> B{"所需样本量"}
    B --> C["拉普拉斯: O(|Q|/αε) 📈"]
    B --> D["乘法权重: O(√|Q|/αε) 📉"]

乘法权重算法能把这个需求降到 O(√|Q|)——从线性变成平方根,质的飞跃!


2. 核心思想:你需要一个"草稿"

直觉

想象你在考试前复习。你手上没有标准答案(原始数据集),但你有一个草稿本(一个对数据的初步估计)。一开始,你的草稿很粗糙——你可能随便猜了猜数据的分布。

然后你开始做题(回答查询)。每次做完一道题,你发现自己的答案和标准答案有差距,你就回到草稿本上修正一下。

经过多次迭代,你的草稿本越来越接近真实数据。

graph TB
    A["初始化: 均匀分布 D₁"] --> B["找出差异最大的查询 q"]
    B --> C["用指数机制选查询"]
    C --> D["比较 D₁ 与真实数据在 q 上的答案"]
    D --> E["用乘法权重更新 → D₂"]
    E --> B
    E --> F["重复 T 轮后输出最终分布"]

    style A fill:#e3f2fd,stroke:#1565c0
    style E fill:#e8f5e9,stroke:#2e7d32
    style F fill:#fff3e0,stroke:#e65100

算法总览

Private Multiplicative Weights (PMW) 算法包含以下组件:

组件作用类比
真实数据集 D我们想保护的原始数据标准答案
合成分布对真实数据的逐步修正的估计草稿本
查询集合 Q所有待回答的问题考题
指数机制选择"当前误差最大"的查询标记最需要纠正的题
乘法权重更新根据误差调整合成分布修正草稿

3. 算法拆解:一步一步来

第一步:初始化

假设数据集来自某个空间 X(例如,X 是"性别、年龄、收入"等所有可能属性的组合)。一开始,我们对真实数据一无所知,所以合成分布 D₁ 设为均匀分布

D₁(x) = 1/|X|  对所有 x ∈ X

如果 X 有 100 种可能的属性组合,那每种组合的初始概率都是 1%。

第二步:找到"最不准确"的查询

现在,对每个查询 q ∈ Q,我们计算它的误差

Error(q) = |q(合成分布) - q(真实数据)|

误差衡量了当前合成分布在这个查询上的表现有多糟糕。

graph TD
    A["查询 q₁: 女性比例真实: 52% 合成: 50%误差: 2%"] 
    B["查询 q₂: 平均年龄真实: 0.42 合成: 0.50误差: 8%"]
    C["查询 q₃: 高收入比例真实: 30% 合成: 28%误差: 2%"]
    D["查询 q₄: 高学历比例真实: 35% 合成: 50%误差: 15% ← 最大!"]

    D --> E["指数机制选中 q₄ 的概率最高"]

但我们不能直接选误差最大的查询——那会暴露真实数据信息。我们用指数机制来选:

Pr[选择 q] ∝ exp(ε' · Error(q) / 2)

其中 ε' 是每轮分配的隐私预算。这样选中最差查询的概率最高,但也不排除其他查询——这保护了隐私。

第三步:用乘法权重更新分布

找到误差最大的查询 q 后,我们就知道了真实数据在这个查询上的答案 v = q(真实数据),以及当前合成分布的答案 u = q(合成分布)。

如果 u < v(低估了),意味着真实数据中满足 q 条件的人比我们猜的要多。我们就要提高那些满足 q 条件的个体的权重。

具体更新规则是:

对每个 x ∈ X:
  如果 q(x) 接近 1(满足条件):
    D_new(x) = D_old(x) · exp(η · (v - u))
  如果 q(x) 接近 0(不满足条件):
    D_new(x) = D_old(x) · exp(-η · (v - u))

其中 η 是学习率(类似于机器学习中的步长)。然后归一化,使所有概率之和为 1。

graph LR
    subgraph "更新前"
    A1["满足条件的个体权重: 较低"]
    A2["不满足条件的个体权重: 较高"]
    end
    subgraph "乘法权重更新"
    B["乘以 exp(η·(v-u)) ↑"]
    C["乘以 exp(-η·(v-u)) ↓"]
    end
    subgraph "更新后"
    D1["满足条件的个体权重: 提高 ✅"]
    D2["不满足条件的个体权重: 降低"]
    end
    A1 --> B --> D1
    A2 --> C --> D2

直观理解:这就像你发现自己的草稿低估了女性比例,于是你调高了"女性"这一格的权重,同时调低了"男性"的权重。多次迭代后,分布会越来越准确。

第四步:重复 T 轮

重复第 2-3 步 T 次。每轮分配 ε' = ε/T 的隐私预算(用高级组合定理可以做得更好)。

最后把最后一轮的合成分布作为输出——所有查询在这个分布上的答案就是最终答案。


4. 为什么它能节省样本量?

信息论视角

拉普拉斯机制浪费了大量信息——它对每个查询独立地添加噪声,完全没有利用查询之间的相关性。

而 PMW 算法通过维护一个合成分布,把所有查询的答案"拧成一股绳"——合成分布只需描述数据本身(|X| 维),而不是 |Q| 个独立答案。

graph TD
    subgraph "拉普拉斯机制"
    L1["查询 q₁: 真实答案 + 噪声₁"]
    L2["查询 q₂: 真实答案 + 噪声₂"]
    L3["查询 q₃: 真实答案 + 噪声₃"]
    L4["......"]
    end
    subgraph "PMW 算法"
    P1["合成分布 D(一个统一的模型)"]
    P2["在 D 上同时回答所有查询"]
    P3["答案自动满足一致性"]
    end

    L1 --> L4
    P1 --> P2 --> P3

拉普拉斯:每个答案独立加噪 → O(|Q|) 条数据/查询 PMW:用合成分布统一建模 → O(√|Q|) 条数据/查询

理论保证

PMW 的核心定理是:

对于 |Q| 个线性查询,如果样本量满足:

n ≥ O( √(|Q| · log|X|) / (αε) )

则 PMW 算法能以高概率回答所有查询,误差 ≤ α。

注意是 √|Q| 而不是 |Q|——当查询数量很大时,这是巨大的改进!

查询数拉普拉斯所需 nPMW 所需 n节省
10010,0001,00010x
1,000100,0003,16232x
10,0001,000,00010,000100x
1,000,000100,000,000100,0001,000x

查询越多,优势越明显!


5. 一个具体例子

假设 X = {0,1}³(3 个二进制属性,共 8 种组合)。我们有 n=100 条数据,想回答 |Q|=100 个线性查询。

初始状态

合成分布 D₁ = [1/8, 1/8, ..., 1/8](均匀分布)。

真实数据分布 D_real = [0.10, 0.05, 0.15, 0.10, 0.20, 0.08, 0.12, 0.20]。

第 1 轮

指数机制选中查询 q = "第一个属性 = 1"。

  • 真实答案: 0.10 + 0.05 + 0.15 + 0.10 = 0.40
  • 合成答案: 0.50(均匀分布下,一半的个体第一个属性=1)
  • 误差: 0.10

乘法权重更新:提高前 4 种组合的权重,降低后 4 种组合的权重。

第 2 轮

指数机制选中查询 q = "第二个属性 = 0 且第三个属性 = 1"。

  • 真实答案: 0.15 + 0.12 = 0.27
  • 合成答案: 约 0.25(经过了第一轮修正)
  • 误差: 0.02

乘法权重更新:进一步微调。

第 20 轮

经过 20 轮迭代,合成分布已经非常接近真实分布:

组合真实第 1 轮后第 5 轮后第 20 轮后
0000.100.080.090.10
0010.050.060.050.05
0100.150.170.160.15
0110.100.110.110.10
1000.200.170.190.20
1010.080.090.080.08
1100.120.140.130.12
1110.200.180.190.20

所有 100 个查询的误差都 ≤ 0.01。

Python 代码实现

下面我们用 Python 实现一个简化的 PMW 算法,复现上面这个具体例子。代码会模拟 100 条数据记录(8 种属性组合)、随机生成 100 个线性查询,然后运行 20 轮 PMW 迭代。

import numpy as np

# ========== 设置参数 ==========
# 数据域 X = {0,1}³,共 8 种可能的属性组合
domain_size = 8
num_records = 100
num_queries = 100
num_rounds = 20
epsilon = 1.0          # 总隐私预算
epsilon_per_round = epsilon / num_rounds  # 每轮隐私预算
learning_rate = 0.5    # 乘法权重更新步长 η

# ========== 1. 模拟真实数据集 ==========
# 真实分布:8 种组合的概率
true_distribution = np.array([0.10, 0.05, 0.15, 0.10, 0.20, 0.08, 0.12, 0.20])
# 生成 100 条数据记录
np.random.seed(42)
dataset = np.random.choice(domain_size, size=num_records, p=true_distribution)

# 真实数据直方图(归一化)
true_histogram = np.bincount(dataset, minlength=domain_size) / num_records
print("真实数据直方图:", np.round(true_histogram, 3))

# ========== 2. 生成随机查询集合 ==========
# 每个查询是一个长度为 domain_size 的向量,元素在 [0,1] 之间
# q(D) = (1/n) * Σ q(x_i)
queries = np.random.rand(num_queries, domain_size)

# 计算所有查询在真实数据上的答案
true_answers = queries @ true_histogram

# ========== 3. PMW 核心循环 ==========
# 初始化:均匀分布
synth_dist = np.ones(domain_size) / domain_size

print("\n开始 PMW 迭代...\n")

for t in range(1, num_rounds + 1):
    # 计算所有查询在当前合成分布上的回答
    synth_answers = queries @ synth_dist

    # 计算每个查询的误差
    errors = np.abs(synth_answers - true_answers)

    # 指数机制:选择误差最大的查询(添加隐私保护噪声)
    # 每轮隐私预算为 epsilon_per_round
    scores = errors * epsilon_per_round / 2
    scores = scores - np.max(scores)       # 防止数值溢出
    probs = np.exp(scores)
    probs = probs / np.sum(probs)

    # 按概率选择查询
    chosen_idx = np.random.choice(num_queries, p=probs)
    chosen_query = queries[chosen_idx]

    # 获取真实答案和合成答案
    v = true_answers[chosen_idx]   # 真实答案
    u = synth_answers[chosen_idx]  # 合成答案

    # 乘法权重更新
    # 对每个 x ∈ X,如果 q(x) 大(满足条件)且 v > u(低估),提高权重
    # 如果 q(x) 大(满足条件)且 v < u(高估),降低权重
    update_factor = np.exp(learning_rate * (v - u) * (2 * chosen_query - 1))
    synth_dist = synth_dist * update_factor
    synth_dist = synth_dist / np.sum(synth_dist)  # 归一化

    if t % 5 == 0 or t == 1:
        max_error = np.max(errors)
        avg_error = np.mean(errors)
        print(f"第 {t:2d} 轮 | 最大误差: {max_error:.4f} | 平均误差: {avg_error:.4f}")

# ========== 4. 最终结果对比 ==========
print("\n" + "=" * 50)
print("PMW 估计 vs 真实分布")
print("=" * 50)

final_errors = np.abs((queries @ synth_dist) - true_answers)
print(f"\n所有 {num_queries} 个查询的最大误差: {np.max(final_errors):.4f}")
print(f"所有 {num_queries} 个查询的平均误差: {np.mean(final_errors):.4f}")

print(f"\n{'组合':>6} | {'真实':>8} | {'PMW估计':>8} | {'误差':>8}")
print("-" * 35)
for i in range(domain_size):
    err = abs(true_histogram[i] - synth_dist[i])
    print(f"  {i:03b}  | {true_histogram[i]:.4f}   | {synth_dist[i]:.4f}   | {err:.4f}")

代码说明:这个实现使用指数机制代替直接选择最大误差查询(差分隐私的关键),然后通过乘法权重调整合成分布。每轮迭代中,合成分布都会向真实数据靠近一步。你可以调整 num_roundslearning_rateepsilon 来观察不同参数对收敛速度和精度的影响。


6. 与在线学习的巧妙联系

PMW 算法的设计思路和**在线学习(Online Learning)**有着惊人的相似:

graph TB
    subgraph "在线学习"
    L1["初始化模型参数"] 
    L2["接收一个样本/问题"]
    L3["做出预测"]
    L4["看到真实标签"]
    L5["计算损失并更新模型"]
    L1 --> L2 --> L3 --> L4 --> L5 --> L2
    end

    subgraph "PMW 算法"
    P1["初始化合成分布"]
    P2["用指数机制选一个查询"]
    P3["用合成分布回答该查询"]
    P4["看到真实答案"]
    P5["计算误差并用乘法权重更新"]
    P1 --> P2 --> P3 --> P4 --> P5 --> P2
    end
在线学习概念PMW 中的对应
模型参数合成分布 D
训练样本查询 q
预测值q(合成分布)
真实标签q(真实数据)
损失函数
梯度下降乘法权重更新

PMW 本质上是在"在线地"学习真实数据的分布——每一轮选取一个"最有信息量"的查询,用它的答案来修正当前分布。这正是为什么它比一次性加噪的拉普拉斯机制高效得多:它迭代地从查询中提取信息,而不是一次性处理所有查询。


7. PMW 的局限与变体

不是万能的

PMW 虽然强大,但也有其局限:

局限原因应对
仅适用于线性查询更新规则依赖线性性质对非线性查询需特殊处理
空间复杂度高需要维护X
**需要知道X**
轮数 T 需预先设定隐私预算需按轮分配可用自适应停止改进

重要变体

  • Multiplicative Weights with Exponential Mechanism (MWEM):Hardt、Ligett 和 McSherry 在 2012 年提出的变体,是 PMW 家族中最著名的版本。它显式地使用指数机制选择查询,每轮仅消耗少量的隐私预算。
  • Iterative Database Construction:Roth 和 Roughgarden 的版本,更强调博弈论的解释。
  • 在线 PMW:对动态数据流的版本,不需要预先知道所有查询。
graph TD
    A["Private Multiplicative Weights 家族"]
    A --> B["原始 PMWHardt & Rothberg (2009)"]
    A --> C["MWEMHardt, Ligett & McSherry (2012)"]
    A --> D["Iterative DatabaseRoth & Roughgarden"]
    A --> E["在线 PMW动态查询流"]

    C --> F["最实用的版本指数机制 + 乘法权重"]

8. 直观理解:为什么它能成立?

"压缩"的力量

想象你有两种方式描述一个数据集:

方式一(拉普拉斯):针对每个问题,单独记录答案,每个答案都加噪声。如果问题数量 |Q| = 10,000,你需要记录 10,000 个数字。

方式二(PMW):先描述数据的分布(比如 100 个可能的属性组合,每种的概率),然后用这个分布自动回答所有问题。你只需要维护 100 个数字。

PMW 本质上在进行信息压缩:用 O(|X|) 的参数来描述数据,而不是用 O(|Q|) 的参数来描述所有可能的查询。由于 |X| 通常远小于 |Q|(特别是当数据维度不高时),这就产生了巨大的效率提升。

graph LR
    A["原始数据集 Dn 条记录"] --> B["合成分布|X| 个参数"]
    B --> C["无限查询在合成分布上计算"]
    B --> D["但只选 T 个最有价值的查询来更新"]
    D -.->|"修正"| B

"主动学习"的视角

PMW 不是被动地回答所有查询,而是主动选择那些最有信息量的查询。这就像学生复习时不是看所有课本内容,而是只刷"不会的题"——显然高效得多。

每次迭代,PMW 做两件事:

  1. 诊断:找出当前分布与真实数据差异最大(即最不了解)的查询
  2. 修正:根据这个查询的答案,调整分布

经过 T 轮,那些"最需要修正"的查询都被处理了,分布也就接近真实数据了。


小结

要点说明
核心思想用合成分布逐步逼近真实数据
关键机制指数机制选择最差查询 + 乘法权重更新分布
样本复杂度O(√\
适用范围线性查询集合,有限数据域
与在线学习的关系本质上是迭代学习过程的差分隐私版本

PMW 算法展示了差分隐私中的一个重要设计范式:与其直接保护每个查询,不如构建一个受保护的数据模型,然后在这个模型上自由回答问题。这种"先建模、再回答"的思路,在后续的很多差分隐私算法中都有体现,包括我们后面会讲的稀疏向量技术。

下一讲,我们将学习稀疏向量技术(Sparse Vector Technique)——另一种高效处理大量查询的方法,它通过只回答"足够大"的查询来节省隐私预算。


下一篇: 稀疏向量技术:只回答"大"查询

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值