二元随机响应(Binary Randomized Response, RR)的翻转概率

发布于:2025-05-09 ⋅ 阅读:(19) ⋅ 点赞:(0)

随机响应(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+e1e10.731 → 有 73.1% 概率返回 1(保留真实值)
  • p = 1 1 + e 1 ≈ 0.269 p = \frac{1}{1 + e^1} ≈ 0.269 p=1+e110.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} 1flip_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 值的张量时。


✅ 举个例子来说明:

设有两个值 ab,都在 {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 明确布尔处理,更安全

网站公告

今日签到

点亮在社区的每一天
去签到