基于Paillier加密的SecureXGBoost精确率召回率计算问题
问题背景
- 研究方向为基于Paillier加密的隐私保护XGBoost(SecureXGBoost),已完成SecureSVM开发与明文域精确率、召回率指标计算,核心目标是实现隐私保护XGBoost训练算法。
- 基于隐私保护XGBoost推理项目完成了加密推理全流程跑通,卡点为无法在不解密预测结果的前提下计算加密域的精确率与召回率。
- 已跑通流程包括:数据集加载、训练测试集划分、明文XGBoost模型训练、模型解析、加密材料初始化、模型加密、查询样本加密、隐私推理,最终输出为Paillier加密的预测结果列表
values。
核心原理说明
Paillier属于半同态加密算法,仅支持两类密文运算:
- 密文间加法:
enc(a) + enc(b) = enc(a+b) - 密文与明文的标量乘法:
enc(a) * k = enc(a*k)(k为明文)
不支持密文间乘法、密文比较、密文除法运算,因此需要将精确率、召回率的计算逻辑拆解为Paillier支持的原子操作,对不支持的比较、除法步骤做适配。
精确率、召回率的基础计算公式为:
- 精确率
Precision = TP / (TP + FP) - 召回率
Recall = TP / (TP + FN)
其中:
- TP:真阳性,真实标签为正、预测结果为正的样本数
- FP:假阳性,真实标签为负、预测结果为正的样本数
- FN:假阴性,真实标签为正、预测结果为负的样本数
三个指标均为样本计数的加总与除法,加总操作可直接通过Paillier密文加法实现,仅需额外适配密文比较(判断预测结果是否为正类)、最终除法两个步骤。
实现步骤
1. 密文域阈值比较,得到加密的预测标签
二分类任务需要判断每个加密预测分数是否大于分类阈值(XGBoost原始输出对应0.5概率的阈值为0),输出加密的0/1指示值(预测为正类则为enc(1),否则为enc(0))。
采用半诚实模型下安全的掩码比较协议实现,全程不泄露原始预测分数明文,仅暴露差值符号,私钥持有方无法还原单个样本的预测结果:
import random from ppxgboost import PaillierAPI as paillier def enc_binary_predict(enc_score, threshold, public_key, private_key): """ 密文判断预测分数是否大于阈值,返回加密的0/1预测标签 """ # 生成大值随机掩码,掩盖原始差值 mask = random.randint(10**6, 10**9) # 计算enc(mask*(score - threshold)) enc_diff = paillier.add(enc_score, paillier.encrypt(public_key, -threshold)) enc_masked_diff = paillier.scalar_multiply(enc_diff, mask) # 解密仅能拿到掩码后的差值符号,无法还原原始分数 masked_diff = paillier.decrypt(private_key, enc_masked_diff) pred_label = 1 if masked_diff > 0 else 0 return paillier.encrypt(public_key, pred_label)
如果不想让私钥持有方接触任何中间差值,可复用项目已有的OPE保序加密组件:加密叶子节点权重时同步存储权重的OPE密文,预测阶段通过安全比较协议得到OPE加密的分数,直接通过OPE密文的顺序关系判断是否过阈值,全程不需要解密。
2. 密文域累加计算TP、FP、FN的加密值
测试集标签为评估方持有的明文(隐私保护评估场景下的常规设定,若标签也需加密,可提前用公钥加密标签,配合一次密文乘法协议实现指示值计算),遍历所有样本计算指示值并累加:
# 二分类阈值 BINARY_THRESHOLD = 0 # 初始化加密计数为0 enc_TP = paillier.encrypt(public_key, 0) enc_FP = paillier.encrypt(public_key, 0) enc_FN = paillier.encrypt(public_key, 0) for idx, enc_score in enumerate(values): real_label = y_test.iloc[idx] # 得到加密的预测标签 enc_pred = enc_binary_predict(enc_score, BINARY_THRESHOLD, public_key, private_key) # 计算单样本TP/FP/FN指示值(密文明文乘法,Paillier原生支持) enc_tp = paillier.scalar_multiply(enc_pred, real_label) enc_fp = paillier.scalar_multiply(enc_pred, 1 - real_label) # 计算enc(1-pred) enc_1_minus_pred = paillier.add( paillier.encrypt(public_key, 1), paillier.scalar_multiply(enc_pred, -1) ) enc_fn = paillier.scalar_multiply(enc_1_minus_pred, real_label) # 密文累加 enc_TP = paillier.add(enc_TP, enc_tp) enc_FP = paillier.add(enc_FP, enc_fp) enc_FN = paillier.add(enc_FN, enc_fn)
3. 计算最终精确率与召回率
Paillier不支持密文除法,因此无法直接在密文域得到最终的浮点指标值,这是半同态加密的固有特性,无绕开方案:
- 常规场景下,将
enc_TP/enc_FP/enc_FN发送给私钥持有方解密,得到三个明文计数值后计算指标即可。该过程私钥持有方仅能拿到总计数,无法获取单个样本的预测结果,满足隐私要求。 - 若需完全隐藏总计数,需替换为支持除法的全同态加密方案,或引入安全多方计算除法协议,性能开销会提升2~3个数量级,无工程落地必要。
最终指标计算代码:
# 私钥持有方解密计数值 TP = paillier.decrypt(private_key, enc_TP) FP = paillier.decrypt(private_key, enc_FP) FN = paillier.decrypt(private_key, enc_FN) # 计算指标 precision = TP / (TP + FP) if (TP + FP) > 0 else 0.0 recall = TP / (TP + FN) if (TP + FN) > 0 else 0.0 print(f"密文评估结果 | Precision: {precision:.4f}, Recall: {recall:.4f}")
注意事项
- 不要尝试直接在Paillier密文上实现比较、除法操作,这类操作超出了Paillier的同态能力边界,所有声称原生支持的方案均存在隐私泄露漏洞。
- 若你的场景要求私钥持有方无法获取任何中间信息,可采用分布式私钥拆分方案,将私钥分发给多个参与方,联合完成比较、解密步骤,全程无单方持有完整私钥。
内容的提问来源于stack exchange,提问作者Mohammad
相关产品推荐
相关产品推荐

