随机响应(Randomized Response)机制
✅ 回答核心:
p = 1 1 + e ε 才是「翻转概率」 \boxed{p = \frac{1}{1 + e^{\varepsilon}}} \quad \text{才是「翻转概率」} p=1+eε1才是「翻转概率」
而:
q = e ε 1 + e ε 是「保留真实值」的概率 \boxed{q = \frac{e^{\varepsilon}}{1 + e^{\varepsilon}}} \quad \text{是「保留真实值」的概率} q=1+eεeε是「保留真实值」的概率
✅ 为什么 p 才是翻转概率?
随机响应机制:用于二元隐私(0 或 1)
给定真实值 x ∈ {0,1}
,你不直接返回 x
,而是使用以下方式:
- 以概率 q = e ε 1 + e ε q = \frac{e^\varepsilon}{1 + e^\varepsilon} q=1+eεeε 保留
x
- 以概率 p = 1 1 + e ε p = \frac{1}{1 + e^\varepsilon} p=1+eε1 返回
1 - x
(翻转)
公式来源(源自 Local Differential Privacy 的 Binary Randomized Response):
隐私预算 ε | 输出 1 的概率(如果 x=1) | 输出 1 的概率(如果 x=0) |
---|---|---|
ε = ∞ | 1(永远保留) | 0(永远不翻转) |
ε = 0 | 0.5(完全随机) | 0.5(完全随机) |
这满足 ε-LDP 要求的最小扰动机制。
✅ 举个例子:
假设真实值为 1,ε = 1:
- q = e 1 1 + e 1 ≈ 0.731 q = \frac{e^1}{1 + e^1} ≈ 0.731 q=1+e1e1≈0.731 → 有 73.1% 概率返回 1(保留真实值)
- p = 1 1 + e 1 ≈ 0.269 p = \frac{1}{1 + e^1} ≈ 0.269 p=1+e11≈0.269 → 有 26.9% 概率返回 0(翻转)
所以 p 是翻转概率,你撒谎的概率!
✅ 总结对比:
名称 | 公式 | 含义 |
---|---|---|
翻转概率 | 1 1 + e ε \boxed{\frac{1}{1 + e^\varepsilon}} 1+eε1 | "撒谎"的概率 |
保留真实值的概率 | e ε 1 + e ε \frac{e^\varepsilon}{1 + e^\varepsilon} 1+eεeε | 说实话的概率 |
总和 | 两者之和为 1 | 分别处理真实值/扰动 |
✅ 一句话总结:
p = 1 / (1 + exp(ε))
是你翻转真实值(加噪声)的概率 —— 所以它才叫 翻转概率。
🧠 代码1:
def matrix_randomized_response(x, eps):
x = x.to(torch.bool)
em = math.exp(eps)
flip_prob = 1.0 / (em + 1) # 翻转概率
p = torch.full_like(x, 1 - flip_prob, dtype=torch.float32) # 保留概率
keep_mask = torch.bernoulli(p).to(torch.bool)
x_noised = (~(x ^ keep_mask)).to(torch.float)
return x_noised
我们来逐行详细解读这段 matrix_randomized_response
函数,重点也会解释提到的核心扰动表达式:
✅ 目标:
这个函数实现 Binary Randomized Response (BRR):对布尔矩阵 x
进行局部差分隐私(LDP)扰动,即:
对于每个元素:
以概率 e ε 1 + e ε \frac{e^\varepsilon}{1 + e^\varepsilon} 1+eεeε 保留原值,
以概率 1 1 + e ε \frac{1}{1 + e^\varepsilon} 1+eε1 翻转(扰动)它
🔍 逐行解释:
①
x = x.to(torch.bool)
- 把输入
x
强制转为布尔型(0/1) - 假设原始
x
是邻接矩阵或 one-hot 标签
②
em = math.exp(eps)
flip_prob = 1.0 / (em + 1)
- 计算 e ε e^\varepsilon eε,这是 LDP 中用于设定隐私保护强度的值
- 计算翻转概率:
flip_prob = 1 1 + e ε \text{flip\_prob} = \frac{1}{1 + e^\varepsilon} flip_prob=1+eε1
③
p = torch.full_like(x, 1 - flip_prob, dtype=torch.float32)
- 构造与
x
同形状的张量,每个值为「保留原值的概率」:
1 − flip_prob = e ε 1 + e ε 1 - \text{flip\_prob} = \frac{e^\varepsilon}{1 + e^\varepsilon} 1−flip_prob=1+eεeε
④
keep_mask = torch.bernoulli(p).to(torch.bool)
对
p
进行伯努利采样,得到一个布尔掩码:True
表示保留原值False
表示需要翻转
⑤
✅ 🔍 解读核心扰动表达式:
x_noised = (~(x ^ keep_mask)).to(torch.float)
这行做的是:
✴️ 布尔异或(XOR):
x ^ keep_mask
:- 若
keep_mask=True
(保留):x ^ True = not x
❌ - 若
keep_mask=False
(翻转):x ^ False = x
❌
所以直接x ^ keep_mask
会反了!
- 若
✴️ 再加一个 ~
(取反)修正逻辑:
~(x ^ keep_mask)
恰好实现:
x |
keep_mask |
x ^ keep_mask |
~(x ^ keep_mask) |
结果解释 |
---|---|---|---|---|
0 | True | 1 | 0 | 保留 0 ✅ |
1 | True | 0 | 1 | 保留 1 ✅ |
0 | False | 0 | 1 | 翻转成 1 ✅ |
1 | False | 1 | 0 | 翻转成 0 ✅ |
✅ .to(torch.float)
:
- 最后把布尔张量转为浮点数张量(0.0 / 1.0),用于模型继续计算
✅ 最终效果:
这段代码实现的是对二值矩阵 x
中的每个值:
- 以概率 e ε 1 + e ε \frac{e^\varepsilon}{1 + e^\varepsilon} 1+eεeε 保留
- 以概率 1 1 + e ε \frac{1}{1 + e^\varepsilon} 1+eε1 翻转(扰动)
这是 Binary Randomized Response 的标准实现,适用于差分隐私保护场景。
🧠 代码2:
def rr_adj():
p = 1.0/(1.0+math.exp(self.eps_a))
res = ((adj + torch.bernoulli(torch.full((n, n), p)).to(device)) % 2).float()
res.fill_diagonal_(0)
return res
🔧 函数定义:
① 计算扰动概率:
p = 1.0 / (1.0 + math.exp(self.eps_a))
self.eps_a
是图结构扰动的隐私预算(epsilon)- 公式来自 Binary Randomized Response
- p = 1 1 + e ε p = \frac{1}{1 + e^{\varepsilon}} p=1+eε1 是“翻转边”的概率(即扰动概率)
② 生成随机掩码并执行扰动:
res = ((adj + torch.bernoulli(torch.full((n, n), p)).to(device)) % 2).float()
torch.full((n, n), p)
:创建一个全为p
的二维张量,大小为 n × n n \times n n×n,和邻接矩阵adj
一样大torch.bernoulli(...)
:对每个元素独立进行伯努利采样(值为 1 的概率为p
,表示翻转)adj + ...
:相当于原始邻接矩阵加上“翻转掩码”% 2
:实现XOR(异或),即:- 0 + 1 = 1 (加边)
- 1 + 1 = 0 (删边)
- 0 + 0 = 0 (保持原样)
.float()
:把最终的布尔(0/1)结果转成浮点张量
🔁 最终效果:以概率 p
对每个边进行是否存在的翻转
③ 去除对角线(防止自环):
res.fill_diagonal_(0)
- 强制将所有的对角线元素(即 self-loop)设为 0,防止扰动产生自环
✅ 总结一下逻辑:
这个函数 rr_adj()
实现的是:
对输入图的邻接矩阵
adj
:
- 对每一条边(包括 0)以概率 1 1 + e ε \frac{1}{1 + e^{\varepsilon}} 1+eε1 进行翻转(1→0,0→1)
- 对角线(self-loops)始终设为 0
📌 关键点理解:
步骤 | 解释 |
---|---|
torch.bernoulli(p) |
决定是否翻转 |
% 2 |
实现 XOR 翻转 |
fill_diagonal_(0) |
保证图中没有自环 |
eps_a 越大 |
翻转概率越小,扰动越少,隐私越弱 |
eps_a = 0 |
翻转概率 = 0.5,最随机,最大隐私保护 |
在这个上下文中:% 2
确实等价于 XOR(异或)操作,当我们处理的是布尔值或 0/1 值的张量时。
✅ 举个例子来说明:
设有两个值 a
和 b
,都在 {0, 1} 之间(也就是二值):
a |
b |
a + b |
(a + b) % 2 |
a ^ b (XOR) |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 | 1 |
1 | 0 | 1 | 1 | 1 |
1 | 1 | 2 | 0 | 0 |
✅ 在 PyTorch 中:
a ^ b
是布尔类型的异或(必须是 bool)(a + b) % 2
是数值类型的异或等价操作(适用于 int/float 形式)
✅ 总结:
表达式 | 类型要求 | 等价含义 |
---|---|---|
a ^ b |
布尔张量 | 异或(推荐用于 bool) |
(a + b) % 2 |
数值张量(0/1) | 异或(等价,更通用) |
✅ 一句话评价:
- ✅
matrix_randomized_response()
更通用、更清晰、语义更明确。 - ⚠️
rr_adj()
更简洁,但逻辑上容易误解,并且不适合复用到非邻接矩阵。
🔍 逐点比较:
点 | rr_adj() |
matrix_randomized_response() |
---|---|---|
✅ 功能完整 | 是(扰动 + 去自环) | 是(扰动,适合任意矩阵) |
📦 可复用性 | 差,只能用于邻接矩阵 | 强,可用于标签、特征、邻接等 |
💬 表达语义 | 模糊,% 2 实现 XOR 不直观 |
清晰,显示 ^ 表达异或逻辑 |
📐 翻转概率显式 | 是,直接使用 1 1 + e ε \frac{1}{1+e^\varepsilon} 1+eε1 | 是,明确区分“翻转/保留”概率 |
🚫 特殊处理对角线 | 有(显式 fill_diagonal_ ) |
无(需调用者自行处理) |
⚙️ 类型控制 | 隐式处理,全部 float | 明确布尔处理,更安全 |