上个月我把公司几个开源大模型接入内部知识库同事们用得很开心但法务和合规部门几乎同时找上门用户提交的合同、病历、财务流水就这么明文扔给云端模型出了问题算谁的这个问题其实不是个例。大模型能力越强输入就越敏感隐私优先不再是一句口号而是每个落地项目都得正面回应的问题。我花了大概三周把“同态加密”这个听起来很学术的东西和大模型推理链路实际结合跑了一遍。这篇文章不打算重复教科书理论只记录一条已经验证过的可行路径边界在哪里、哪些能做、哪些目前做不了、参数怎么选、坑在哪里。适合正在做大模型应用、又对数据隐私有要求的开发者或架构师参考。1. 隐私优先的大模型到底在保护什么1.1 三种典型的隐私泄露风险做大模型应用的人第一反应通常都是“用户输入的提示词里不能有敏感信息”。这个直觉对但不完整。一条完整的推理链路里隐私泄露至少有三个方向后面做方案时容易漏。第一个方向是推理输入的泄露。用户把一段病历摘要、一份没有脱敏的合同发给云端模型模型服务方理论上能看到全部内容。很多内部系统的做法是让用户自己“注意别发敏感内容”这本质上把责任转嫁给了用户不可控。第二个方向是模型权重的泄露。商业公司花钱微调出来的模型部署在公有云上之后攻击者可以通过大量构造输入、观察输出用蒸馏的方式把模型能力“偷”出来。模型本身是公司资产这个泄露方向经常被忽略。第三个方向是推理结果与中间状态的泄露。日志系统、监控系统、缓存层里可能残留用户输入和模型输出甚至包括embedding向量。这些中间态一旦被拖库敏感信息照样外流。同态加密Homomorphic Encryption简称HE解决的是前两个方向的强相关问题把输入和模型权重都变成密文让计算在密文上进行服务方只能看到“加密状态下的计算过程”看不到原始输入和原始权重。听起来很理想但工程上能否落地是另一回事。1.2 同态加密与其他隐私计算方案的取舍我在选技术路线的时候把市面上能用的隐私计算方案都过了一遍主要对比了安全多方计算MPC、可信执行环境TEE、联邦学习和同态加密。方案核心思路通信开销计算开销难点安全多方计算多方各自持有分片联合计算高多轮交互中等网络延迟敏感可信执行环境硬件隔离密钥不出CPU低低依赖硬件需信任厂商联邦学习模型参数共享数据不出域中低只能用于训练推理保护弱同态加密密文直接参与计算低一次性交互高计算量成倍增长性能、噪声、实现复杂度大模型推理场景有一个特点用户发一次请求服务端算完直接返回结果交互轮次最好不超过一次。同态加密天然适合这种“一次交互”的模式因为客户端把输入加密后发给服务端服务端在密文上算完返回客户端用私钥解密即可中间不需要像MPC那样多轮协商。TEE在性能上确实最优但需要你信任硬件厂商和云服务商的供应链有些合规场景并不接受。所以我的结论是如果目标是“推理阶段保护输入和权重”同态加密是方向上最合适的候选难点在性能和工程实现。下面这些内容全部围绕“怎么把HE用到大模型推理上”展开。2. 同态加密的大白话原理与库选型2.1 “能算数的保险箱”加密状态下的运算同态加密用一句话概括就是在不可见内容的前提下对密文做运算结果解密出来等于对明文做同样运算的结果。用数学表示就是E(a) E(b) E(a b)或者E(a) * E(b) E(a * b)。你可以理解成一个保险箱箱子是透明的但内容是被锁住看不到的你可以在不打开箱子的情况下往里面塞一个数或对箱子里的数做加法乘法最后打开箱子拿到的结果和你直接对原始数字做运算拿到的结果一致。这个性质对大模型推理的意义很直接用户把问题加密后发给模型服务方服务方在密文上做矩阵乘法、做激活函数计算模型方全程看不到真实问题内容最后用户解密拿到的还是正常推理结果。同理如果模型权重也加密部署服务方自己都不知道自己跑的是什么样的网络结构。大模型领域常用的同态加密方案叫CKKS它的特点是支持浮点数的近似计算这和机器学习推理的数值表达习惯一致。CKKS在做加密之前会把浮点数编码成多项式同时乘上一个缩放因子来保留精度计算结束后再把结果缩放回去。2.2 四种主流HE方案怎么选同态加密按能力强弱分成几个层级选型时容易混淆先看对比方案类型支持运算适合场景Paillier部分同态PHE仅加法聚合统计、投票、梯度聚合RSA/ElGamal变体部分同态仅乘法简单聚合场景有限BFV/BGV全同态FHE加法乘法整数精确计算、隐私查询CKKS全同态FHE加法乘法浮点近似计算、机器学习推理TFHE全同态FHE布尔电路运算任意函数逐bit运算Paillier在很多隐私计算项目里流行因为它效率高、容易理解但它只能做加法无法完成矩阵乘法大模型推理基本用不上。BFV能做精确整数运算适合计数和聚合但浮点权重需要缩放成整数精度损失控制很麻烦。CKKS是现阶段做机器学习推理的最优选择代价是结果是近似的需要在使用时容忍一定的误差。所以下面实践部分我全部使用CKKS这是目前大模型落地相对现实的入口。2.3 动手前必须搞懂的三个参数scale、模数链、明文槽第一次看HE代码的人很容易被一堆参数劝退其实核心只有三个。第一个是缩放因子scale。CKKS把浮点数编码成整数时需要放大scale就是放大倍数通常取2的幂次比如2的40次方。scale越大浮点精度越高但也会更快耗尽后面的噪声预算所以不是越大越好。第二个是模数链。CKKS的密文噪声会随着乘法次数增长为了控制噪声每做一次乘法之后可以做一次“重缩放”把密文缩小回原来的规模。模数链就是预留的一串模数每重缩放一次消耗掉一级。模数链越长能支持的乘法深度越大但密文体积也越大。第三个是明文槽。CKKS支持把多个数打包进同一个密文的多个槽里用一个密文模拟出一整条向量运算这就是SIMD单指令多数据效果。做大模型推理时一个矩阵乘法的十几个中间神经元可以塞进同一个密文并行计算这是目前压缩性能差距的最重要手段后面的实践会用到。我把这三个概念记成一句话scale决定单次计算的精度模数链决定能算多深明文槽决定一次能并行算多少。所有参数配置都是在三者之间找平衡。3. 为什么“整个大模型加密”在工程上并不现实3.1 算一笔账7B模型密文化的资源占用网上有人畅想“把Llama直接部署成同态加密版本”这个说法听着性感但你先算一笔账7B参数模型如果用fp16存储光权重就是14GB。CKKS密文膨胀系数通常在20到50倍保守按30倍算14GB权重加密完就是420GB这还只是权重没算中间激活值。420GB什么概念当下主流数据中心的GPU显存是80GB到192GB。就算你有四块A100拼接权重放进去之后中间计算也完全放不下。更麻烦的是密文计算时中间结果同样膨胀每一层激活值都按几十倍体积增长很快爆内存。我的实测经验是哪怕把小块权重加密后做一次普通矩阵乘法内存开销也是明文的几十倍起跳。所以结论很直接现阶段把完整大模型整包加密工程上不可行。这不是同态加密算法不行而是密文膨胀率和计算速度还没有跟上来。明白了这件事落地策略就会从“全加密”转向“分层加密”具体做法在3.3节讲。3.2 非线性激活函数在密文上的代价Transformer里到处都是ReLU、GELU、Softmax这些非线性函数这是同态加密落地大模型的第二道坎。CKKS本身支持加法和乘法但ReLU这种分段函数没法直接在密文上原样执行只能找多项式近似比如用x^3或更高阶多项式去拟合ReLU。近似就会引入误差层数一多误差累积最终结果可能完全偏离正确分类。Softmax更麻烦里面有个指数运算在密文上做指数要展开成很长的多项式乘法深度瞬间抬高。前面说过模数链是有限资源一次Softmax可能吃掉大半条预算。实际项目中我见过有人用平方函数近似ReLU虽然损失一点准确率但乘法深度少了两层速度和精度权衡下来反而更实用。所以结论是大模型的非线性结构决定了它不能直接塞进HE电路。要么换一个更简单的模型结构来承载部分敏感计算要么把非线性部分留给明文端做只把线性部分放到密文上。3.3 务实的落地形态分层加密与混合推理既然整体加密不可行那实际能落地的形态是“分层加密、混合推理”。先把模型按敏感程度和计算类型拆开只有最需要保护的部分进入密文域其余部分保持明文计算。我试过比较可行的三种切法。第一种只加密输入嵌入层和输出层。用户输入先被加密在密文上做embedding得到的结果解密后进入明文Transformer反向再加密输出层。这样用户的关键输入内容不会在明文链路里出现但模型主体计算不受影响性能损失相对可控。第二种把模型拆成端侧小模型加云端大模型的串行结构。端侧跑一个小模型负责特征提取和初筛只把脱敏后的中间特征发给云端大模型。这个方案里敏感原始数据不出本地云端只接触到低敏感度的高维特征。第三种把模型权重按模块加密配合安全聚合用在联邦微调场景。推理时用明文大模型训练时用HE保护梯度聚合路径是通的但距离成熟还有距离。我见过很多团队在做第一种因为它能用比较小的代价覆盖掉最痛的“用户提示词泄露”问题后面的实践也围绕这种形态展开。4. 用TenSEAL跑通一个最小密文推理实践4.1 环境准备与上下文参数实践环节我用了TenSEAL这个库。它是Microsoft SEAL的Python封装API简单适合快速验证推理链路底层仍然是C实现的CKKS方案。安装命令很简单pip install tenseal0.3.14建议用Python 3.9或3.10太新的Python版本有时会遇到预编译包缺失。另外需要numpy、scikit-learn、torch这些常规依赖训练部分我直接用PyTorch。创建CKKS上下文时需要指定三个关键参数多项式模度、模数链、全局缩放因子。我实测下来微型模型用下面的配置足够跑两层矩阵乘法import tenseal as ts context ts.context( ts.SCHEME_TYPE.CKKS, poly_modulus_degree8192, coeff_mod_bit_sizes[60, 40, 40, 40, 60] ) context.generate_galois_keys() context.global_scale 2**40这里的乘法深度预算大概是3层左右poly_modulus_degree越大能容纳的数据量越大但计算也越慢。第一次入门建议就用8192别一上来追求大规模先把链路走通。4.2 明文训练一个小型分类模型为了演示HE推理我用鸢尾花数据集训练一个两层MLP输入4维特征中间层8个神经元输出3个类别。这个模型足够小权重可以直接转成明文向量用于加密推理。from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler import torch import torch.nn as nn data load_iris() X, y data.data, data.target X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) scaler StandardScaler().fit(X_train) X_train scaler.transform(X_train) X_test scaler.transform(X_test) model nn.Sequential( nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 3) ) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr0.01) model.train() for epoch in range(200): inputs torch.tensor(X_train, dtypetorch.float32) labels torch.tensor(y_train, dtypetorch.long) optimizer.zero_grad() outputs model(inputs) loss criterion(outputs, labels) loss.backward() optimizer.step()训练完之后要把模型设为eval模式并取出权重这是后面加密推理的输入。不要带着梯度和BN层所有参数必须是纯线性层的权重和偏置否则后续转换会非常麻烦。4.3 把权重和输入搬进密文推理前需要把明文输入加密同时把训练好的权重转成明文向量列表。这一步是整个实践的核心代码逻辑很直观但你会在打包细节上踩不少坑。import numpy as np model.eval() W1 model[0].weight.detach().numpy() # shape (8, 4) b1 model[0].bias.detach().numpy() # shape (8,) W2 model[2].weight.detach().numpy() # shape (3, 8) b2 model[2].bias.detach().numpy() # shape (3,) def encrypted_forward(context, x, W1, b1, W2, b2): enc_x ts.ckks_vector(context, x) # 第一层线性变换 enc_h enc_x.dot(W1.transpose()) b1 # 用平方函数近似ReLU enc_h enc_h * enc_h # 第二层线性变换 enc_logits enc_h.dot(W2.transpose()) b2 return enc_logits x_test scaler.transform([[5.1, 3.5, 1.4, 0.2]]).flatten() enc_logits encrypted_forward(context, x_test, W1, b1, W2, b2) decrypted_logits enc_logits.decrypt()解密后的结果应该接近明文模型对同一个输入的输出。第一次跑通时我吃了一惊真的能在“看不到输入内容”的情况下算出几乎一样的分类置信度。注意dot方法执行的是内积操作W1.transpose()负责把权重矩阵转成按行计算的形式这一步需要保证维度对齐很多报错都出在这。4.4 结果验证与误差意识我实测下来明文推理一次大约是微秒级而同样的计算在密文上跑单条样例大概要1到3秒。减速比在1000倍以上这就是同态加密的现实代价。解密结果和明文结果对比时误差通常在1e-3量级分类结果是稳定的。CKKS是近似方案允许日常服务里出现这种误差但不能接收误差无限增长。如果发现误差到了0.1甚至更大基本可以判定是参数配置有问题重点检查scale和模数链是否匹配。这个最小实践虽然只覆盖了两层线性网络但增删网络层、修改损失函数、更换数据集都通用。核心要记住链路是五步创建上下文、加密输入、密文矩阵乘法、多项式近似激活、解密校验。后续任何更复杂的模型都可以按这个模式扩展。5. 实测避坑我踩过的四个典型坑5.1 解密结果变成噪声噪声预算计算失误第一次跑通之前我先试了一个三层MLP结果解密出来的数字千奇百怪甚至有负数几十亿。查了一圈发现不是代码bug而是模型乘法深度超过了上下文支持的深度。CKKS每次乘法都会消耗噪声预算enc_h * enc_h这一步已经把预算用掉一大半后续再加一层矩阵乘法就直接崩了。解决办法有两个方向要么减少乘法层数要么增加模数链的长度。我的做法是在程序里打印剩余噪声预算print(remaining noise budget:, context.remaining_noise_budget())这个数字低于20的时候基本可以断定结果会出错。开始设计模型前先用手画一遍计算图数清楚有几个乘法层再决定coeff_mod_bit_sizes的长度。我就是凭这个习惯避开了后面大半的废操作。5.2 内存被密文体积撑爆打包与分批看代码示例时一个普通向量加密后占用几十KB你不会觉得大。但一旦输入从4维变成128维中间层的神经元数变成256同时处理100条请求内存直接爆掉。我第一次尝试同时加密32个特征向量时操作系统开始疯狂swap程序卡死。解决套路是打包。CKKS的明文槽支持SIMD把多个样本的同一维度塞进同一个密文的不同槽里让一次密文计算同时处理多条数据。实际写起来要处理槽对齐、mask、旋转代码复杂度上一个台阶。我的建议是入门阶段不碰复杂打包先用单条输入跑通流程等真正要上线时再研究批量打包否则调试成本会高到怀疑人生。另一个曲线救国方案是把权重矩阵按对角线打包成若干个向量把矩阵乘法拆成多次向量内积。这个方案代码量更少但乘法次数会增加不少需要权衡。5.3 精度莫名其妙丢失scale选型与重缩放类似模型结构换一组参数之后解密结果的精度突然从1e-3掉到1e-1原因通常是scale设得太小。CKKS里浮点数编码本身就带近似误差scale太小编码时小数部分就被截断掉了解密出来的结果跟明文对不上。我后来形成一套固定配置模型深度三到五层时scale用2的40次方模数链每级大小按[60, 40, 40, 40, 60]这种结构排。小模型用这个配置基本不会翻车。更稳的做法是在正式加密推理前先用“明文加噪声仿真”跑一遍业务逻辑。就是直接把每一次运算结果人为加上高斯噪声模拟HE的数值误差曲线。这个方法成本很低能提前判断整个链路能否容忍近似误差比反复跑真加密快得多。精度问题还有一个隐藏点激活函数近似。用平方函数替代ReLU分类边界会变形可能丢掉一两个百分点的准确率。如果你不能接受可以升级到三阶或五阶多项式近似代价是乘法深度加大速度更慢。5.4 “加密了模型”不等于“一切安全”这是认知上的坑也是我特别想提醒的。很多同学以为把权重和输入都加密了整个系统就固若金汤实际远不是这样。即使模型权重是密文攻击者依然能通过输入输出对做差分攻击判断模型边界和提取部分信息。日志系统如果记录了中间密文和解密结果也可能被用来做侧信道攻击。我的建议是把同态加密当成隐私防御体系里的一块砖而不是全部。日志脱敏、访问控制、审计、模型水印、输入输出频率限制这些传统手段一个都不能少。我见过不少团队做完HE加密之后沾沾自喜结果一个未加密的API日志把用户输入全暴露了那加密就没有意义。6. 现阶段最值得尝试的技术路线与优化方向6.1 三种可以工程落地的组合方案如果你看完上面的内容准备开始做隐私优先的大模型应用我给三个可以落地参考的组合方案。方案A私密输入加明文大模型。用户输入加密后进入云端云端在密文上完成embedding和第一层线性变换中间结果交互解密后再走明文Transformer输出层再做加密保护。适合“不想让模型服务方看到用户具体查询内容”的场景比如医疗咨询、法律文书分析。这个方案改动相对小只需在前处理和后处理加两层HE计算。方案B私密模型加公钥推理。把经过微调的模型权重加密部署到第三方云用户持有公钥对输入加密发起推理云服务商无法获取模型原始权重。适合模型版权保护、AI模型交易场景。缺点是目前只能支持较小模型或模型的分片部分整体交付还需要时间。方案C联邦微调加同态加密梯度聚合。多个数据持有方各自用本地数据微调模型只把加密后的梯度上传到中心服务器中心服务器在密文上聚合实现“数据不出域、模型共同优化”。这条路更适合金融、医疗联盟场景推理部分仍然走明文训练阶段隐私问题被HE覆盖。6.2 性能优化的几个核心方向现在HE和大模型的性能差距确实大但这不是死路。我看好几个方向已经有实际进展。第一个是SIMD打包的工程化。把多个样本、多个矩阵元素塞进同一批密文槽是性价比最高的优化方式能把推理吞吐量提升一到两个数量级。开源社区已经有基于TenSEAL的批量推理封装轮子值得关注。第二个是GPU加速。CKKS的底层运算是多项式乘法和数论变换这些操作在GPU上的并行化效果很好。OpenFHE和部分商业库已经支持GPU后端实测矩阵乘法的密文计算速度能提升几十倍。第三个是专用硬件。FPGA和ASIC实现同态加密已经有了原型原型芯片把大数乘法、模运算这些重计算放进硬件功耗和延迟都更可控。虽然离大规模商用还有距离但方向已经非常明确。第四个是模型轻量化与HE协同。把大模型做剪枝、量化和蒸馏得到一个小而精的敏感计算模块再把这部分放到HE域里加密执行。思路和端云协同一致大模型负责通用能力小模型负责敏感数据的低风险处理。6.3 我的第一版实践建议如果你准备在自己的项目里试水我的建议很直接不要一上来就追求把完整大模型加密先把“最小泄露面”梳理出来把最痛的那一个环节用HE保护起来再逐步扩大加密范围。我踩过的弯路里最典型的就是想一步到位做全链路加密花了两周时间调参数最后发现模型权重太大、激活函数太复杂根本推不动。调整策略之后先用一个端侧小模型处理原始敏感输入再让云端大模型处理脱敏特征项目很快就有了可演示的版本。另一个经验是先跑“模拟密文”再跑真密文。你在明文上模拟加噪声和延迟先把业务逻辑、API接口、异常处理全部写对最后换真的HE后端这样调试成本会低非常多。我第一次跑通整个链路时最大的收获不是学会了TenSEAL的API而是理解了“隐私优先”在大模型落地中不是一个开关而是一条路径。先把最容易出事的用户输入保护起来再逐步把权重、中间梯度纳入保护范围这条路走得通而且越走越顺。如果你也在做类似的事情建议从这篇里的最小实践开始跑一遍然后回到自己的业务场景里找出那个最需要加密的环节先把它保护起来。