当前位置: 首页 > article >正文

【隐私计算篇】利用多方安全计算MPC实现VGG16人脸识别隐私推理

1. 背景介绍

        本文主要介绍一种利用多方安全计算MPC技术,实现VGG16的人脸识别模型,侧重于模型推理阶段,目前已经公开专利,因此以下内容的分享都是基于公开材料。该分享涉及到最小化多方安全计算(MPC)以及明密文混合计算的思想,仅供参考。

        人脸识别是一种基于生物特征识别技术的身份验证和识别方法,在多种场景中广泛应用,比如安防、银行、终端设备等,更贴近生活的还有支付宝的扫脸支付。人脸识别在各领域的广泛应用带来了便利,但也引发了隐私和数据安全方面的讨论以及担忧,传统的人脸识别系统在数据采集、存储和处理过程中很可能会存在隐私泄露的风险。

        比如传统人脸识别系统通常需要将用户的面部数据上传到中央服务器进行处理,如果这些数据被黑客入侵、内部人员滥用或系统漏洞利用,可能会导致用户敏感信息泄露。此外,某些公司或机构可能会将收集到的人脸数据用于未经用户同意的商业或其他用途,侵犯个人隐私。

        鉴于上述原因,很有必要采用隐私保护技术。隐私计算技术(如联邦学习、差分隐私、多方安全计算等)允许在不直接共享原始数据的前提下进行人脸识别模型的训练和推理,从而防止数据滥用和泄露。实现隐私计算的人脸识别是为了在保证人脸识别技术有效性的同时,最大程度地保护用户的个人隐私。     

2. 算法介绍

        本文主要介绍利用安全多方计算(MPC)实现VGG16人脸识别模型的推理预测。在介绍具体算法之前,需要对MPC有一定的了解,有助于理解后续的深度学习算法隐私计算化改造。安全多方计算(MPC)是一种密码学技术,它允许多个参与方在不泄露各自私有数据的前提下,协同计算一个共同的函数结果。关于MPC的介绍,这里不做详细展开,有兴趣的话可以看下冯登国院士关于MPC的基础知识分享,包括 基于秘密分享方法的MPC以及基于混淆电路方法的MPC,另外关于混淆电路的知识也可以参考我们之前的文章《混淆电路深入浅出》,涉及的密码原语不经意传输可以参考我们的系列文章《OT&OT扩展(不经意传输扩展)深入浅出》、《不经意传输协议(OT/OTE)的进一步补充》。

2.1 具体算法介绍

        整体算法介绍会涉及采集端分片处理、分布式存储、分布式推理、模型本身的MPC化等工作。

2.1.1 人脸分布式存储

        在人脸图像采集、识别环节都采用分片形式的分布式存储。采用MPC加性碎片形式,将原始人脸图像数据分成多个小碎片,每个碎片单独存储在不同的存储节点上,不同的存储点可以分布在物理独立的地区,即使某个库被攻破,也无法窃取到具体的人脸明文数据。

2.1.2 VGG模型介绍

        在进行VGG模型的改造之前,也调研了相应的其他模型,比如AlexNet模型,总体来看,结构类似,但vgg的层数更深、参数更多。

        AlexNet: 共有 8 层,其中 5 层是卷积层,3 层是全连接层。

 VGG16: 共有 16 层,其中包括 13 层卷积层和 3 层全连接层【1,2】。

        

        VGG16由13个卷积层、5个最大池化层和3个全连接层组成。因此,具有可调参数的层数为16(13个卷积层和3个全连接层)。这也是该模型被命名为VGG16的原因。第一组卷积层中的滤波器数量为64,之后每个卷积模块中的滤波器数量逐步加倍,直到达到512个。该模型最后由两个全连接隐藏层和一个输出层构成。两个全连接隐藏层的神经元数量相同,均为4096。

        因此从模型的结构、复杂度、参数量来看,vgg会比alexnet更强大,因此我们选择vgg16作为mpc化改造的基座模型。

2.1.3 VGG模型MPC化

        对于VGG模型的mpc化方案,同样也适合AlexNet网络结构,可以迁移使用。该面向的多方计算场景为两方,当然也可以平滑扩展到N方,但考虑到推理的耗时问题,两方、三方会比较合适。因为随着参与方的增多,进行矩阵乘法的时候,彼此之间的通信量会显著增加。另外,场景面向的是1对N的人脸识别任务。

方案描述

        对人脸特征提取算法MPC化,将人脸特征提取算法进行拆分,拆分成若干层,每层都是基本的CNN中的处理层,包括卷积层、激活层、池化层、全连接层等。对每个层次进行MPC化改造,在保证
安全性的情况下进行特定的优化。首先对单层MPC改造的优化,包括卷积层的MPC化、激活层的MPC化、池化层的MPC化、全连接层的MPC化,其次对多个层次的组合优化。

2.1.3.1 对单层MPC改造的优化

        将人脸特征提取算法进行拆分,拆分成若干层,每层都是基本的CNN中的处理层,包括卷积层、激活层、池化层、全连接层等,对每个层次进行MPC化改造,在保证安全性的情况下进行特定的优
化。

        (1)卷积层的MPC化

        方式一: 执行纯碎片态的卷积操作,其中生物信息输入是以碎片态,而卷积核采用明文态。

    步骤:
        各个参与方用本方碎片化的输入与明文态的卷积核进行向量内积计算,得到碎片态的卷积输入结果。卷积层的MPC化改造不涉及到交互,仅在各个参与方内部进行本地化碎片态的计算。

        此外,在某些场景下,也可以选择完全碎片态的卷积核执行分布式MPC计算。更加安全。

        方式二: 随机部分恢复后卷积,我们也称之为随机掩码态。

        这种模式下,各参与方在本地会进行随机恢复某些信息,但不足以用于判别,并且每一次使用都会用户重分片处理,重新随机。各个参与方随机恢复出明文,非明文位置用0填充,在本地进行
明文卷积运算,得到碎片态输出。这里有个注意点,需要设计一种新的随机化策略,避免恢复对于卷积贡献度较大的随机块。此外,应用该方法,对于输入的矩阵块大小有一定的要求,因为size过小容易造成信息的暴露,所以对于矩阵块的大小以及随机化策略需要重点考虑。

        (2)激活层的MPC化   

     参与方之间需要根据某种机制形成互补的索引集合,标注各个参与方在此步中需要恢复处明文的索引,根据索引各方相互交互,恢复部分明文。各方根据自己恢复出的明文,进行本地明文下的激活函数计算。各方对持有的激活函数计算出来的明文结果进行重分片。由于后续需要对激活层的记过进行进一步的卷积、池化等层操作,参与方在本层恢复出来的明文,在后续的层操作中需要与其他
自己不掌握数据的索引位置进行相应计算,所以随机部分明文化激活操作,不会泄露隐私数据。

        这里给出一种数值处理示例:

        (3)池化层的MPC化 

        参与方之间形成互补的池子索引集合,双方恢复各自掌握的池子里的明文信息。对自己恢复的池子里的数据进行本地明文池化。参与方对自己掌握的池化结果进行重分片。

       (4)全连接层的MPC化

        各个参与方根据公开的权重参数,进行向量内积计算,获得全连接的输出结果,这里采用的是纯MPC乘法计算方式。

2.1.3.2 对多层的组合优化       

        由于大多数特征提取算法中的单个层次之间的组合模式较为通用。例如"卷积->池化->激活"的操作,可以作如下的进一步优化。对于较为常用的层次组合,可定制化开发优化的算子。


2.1.3.3 MPC化推理模块组成

        以下展示整体模型的MPC化推理示例:


2.2 MPC人脸识别系统的整体架构

        模型训练参数的生成,可以采用完全MPC的方式直接生成碎片态参数,也可以采用明文训练后对模型参数进行碎片化处理。具体的方式根据实际业务需求进行选择。

       (1)一种模型训练参数的碎片化处理方式

        (2)人脸识别推理的处理流程

        (3)代码示例

        使用mpc化的模型,其使用方式和普通的明文模型其实很类似,只不过内部的底层算子是MPC的基本算子,其中的Conv2D、MaxPooling2D、Flatten、activation都是MPC化的算子。

        代码使用示例:

def VGG16(x):
    # Block 1
    kernel, bias = get_kernel_bias_by_name('conv1_1')
    x = Conv2D(64, (3, 3), activation='relu', padding='same', name='conv1_1')(x, kernel, bias)
    kernel, bias = get_kernel_bias_by_name('conv1_2')
    x = Conv2D(64, (3, 3), activation='relu', padding='same', name='conv1_2')(x, kernel, bias)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='pool1')(x)

    # Block 2
    kernel, bias = get_kernel_bias_by_name('conv2_1')
    x = Conv2D(128, (3, 3), activation='relu', padding='same', name='conv2_1')(x, kernel, bias)
    kernel, bias = get_kernel_bias_by_name('conv2_2')
    x = Conv2D(128, (3, 3), activation='relu', padding='same', name='conv2_2')(x, kernel, bias)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='pool2')(x)

    # Block 3
    kernel, bias = get_kernel_bias_by_name('conv3_1')
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='conv3_1')(x, kernel, bias)
    kernel, bias = get_kernel_bias_by_name('conv3_2')
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='conv3_2')(x, kernel, bias)
    kernel, bias = get_kernel_bias_by_name('conv3_3')
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='conv3_3')(x, kernel, bias)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='pool3')(x)

    # Block 4
    kernel, bias = get_kernel_bias_by_name('conv4_1')
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv4_1')(x, kernel, bias)
    kernel, bias = get_kernel_bias_by_name('conv4_2')
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv4_2')(x, kernel, bias)
    kernel, bias = get_kernel_bias_by_name('conv4_3')
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv4_3')(x, kernel, bias)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='pool4')(x)

    # Block 5
    kernel, bias = get_kernel_bias_by_name('conv5_1')
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv5_1')(x, kernel, bias)
    kernel, bias = get_kernel_bias_by_name('conv5_2')
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv5_2')(x, kernel, bias)
    kernel, bias = get_kernel_bias_by_name('conv5_3')
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv5_3')(x, kernel, bias)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='pool5')(x)

    # Block 6
    kernel, bias = get_kernel_bias_by_name('fc6')
    x = Conv2D(4096, (7, 7), activation='relu', name='fc6')(x, kernel, bias)
    # x = Dropout(0.5, name='fc6/dropout')(x)
    kernel, bias = get_kernel_bias_by_name('fc7')
    x = Conv2D(4096, (1, 1), activation='relu', name='fc7')(x, kernel, bias)
    # x = Dropout(0.5, name='fc7/dropout')(x)
    kernel, bias = get_kernel_bias_by_name('fc8')
    x_fc = Conv2D(1024, (1, 1), name='fc8')(x, kernel, bias)
    x_flatten = Flatten(name='flatten')(x_fc)
    return x_flatten


def deepface(random_idx, output_path, img):
    # y: [5947, 1024]  图像特征碎片化数据库
    y = mpc.read(ff_db_5947, 5947, 1024, ss_img_feature_db)
    # x: [1, 224, 224, 3] 图像数据碎片
    x = mpc.read_img(random_idx, 1, [224, 224, 3], ss_img)
    # 模型推理得到的输出
    x = VGG16(x)  # -> [1, 1024]

3. 参考材料

【1】An overview of VGG16 and NiN models

【2】Difference between AlexNet, VGGNet, ResNet, and Inception

【3】GitHub - rcmalli/keras-vggface: VGGFace implementation with Keras Framework

        


http://www.kler.cn/a/321034.html

相关文章:

  • 初识ElasticSearch
  • 利用OpenAI进行测试需求分析——从电商网站需求到测试用例的生成
  • 如何处理 iOS 客户端内 Webview H5 中后台播放的音视频问题
  • 设计模式(四)装饰器模式与命令模式
  • Python小试牛刀:第一次爬虫,获取国家编码名称
  • opencv kdtree pcl kdtree 效率对比
  • C++学习笔记(34)
  • 【MySQL】字符集与Collation
  • MySQL 预处理语句:强大的数据库工具
  • en造数据结构与算法C# 用Unity实现简单的群组行为算法 之 分散
  • 运算符两边的数据类型
  • [数据库] Redis学习笔记(一):介绍、安装、基本数据结构、常见命令
  • 在Windows系统上安装的 zstd C++ 库
  • ADB 安装教程:如何在 Windows、macOS 和 Linux 上安装 Android Debug Bridge
  • Spring 事务与 MySQL 事务:深度解析与实战指南
  • 使用docker创建zabbix服务器
  • 2024华为杯E题成品文章已出!
  • 使用Crawler实例进行网页内容抓取
  • 制造企业为何需要PLM系统?PLM系统解决方案对制造业重要性分析
  • Python Web 分布式系统性能监控与链路追踪技术解析
  • vue实现鼠标滚轮控制页面横向滑动
  • 你知道吗?制造手机芯片的关键竟然是一台“打印机”?
  • Redis配置文件详解(上)
  • 【报告阅读】chatgpt-o1 技术报告阅读 | 新的迭代开始了~
  • 大数据新视界 --大数据大厂之数据清洗工具 OpenRefine 实战:清理与转换数据
  • Java 入门指南:获取对象的内存地址