差分隐私代码实现系列(三)

  • 写在前面的话
  • 回顾
  • kkk-匿名(kkk-Anonymity)
  • 检查kkk-匿名(Checking for kkk-Anonymity)
  • 生成满足kkk-匿名的数据(Generalizing Data to Satisfy kkk-Anonymity)
  • 数据越多泛化越好?(Does More Data Improve Generalization?)
  • 删除异常值(Removing Outliers)
  • 总结

写在前面的话

书上学来终觉浅,绝知此事要躬行

回顾

数据:

1、显式标识符(ID,能够唯一地确定一条用户记录)。
2、准标识符(QI,能够以较高的概率结合一定的外部信息确定一条用户记录):单列并不能定位个人,但是多列信息可用来潜在的识别某个人。
3、敏感属性(需要保护的信息)。
4、非敏感属性(一般可以直接发布的信息)。

隐私:用户敏感数据与个体身份之间的对应关系。

个人标识泄露。当数据使用人员通过任何方式确认数据表中某条数据属于某个人时,称为个人标识泄露。个人标识泄露最为严重,因为一旦发生个人标识泄露,数据使用人员就可以得到具体个人的敏感信息。

属性泄露,当数据使用人员根据其访问的数据表了解到某个人新的属性信息时,称为属性泄露。个人标识泄露肯定会导致属性泄露,但属性泄露也有可能单独发生。

成员关系泄露。当数据使用人员可以确认某个人的数据存在于数据表中时,称为成员关系泄露。成员关系泄露相对风险较小,个人标识泄露与属性泄露肯定意味着成员关系泄露,但成员关系泄露也有可能单独发生。

方法:删除标识符的方式发布数据。

缺点:攻击者可以通过链接攻击和差分攻击获取个体的隐私数据。

链接攻击:攻击者通过对发布的数据和其他渠道获取的外部数据进行链接操作,以推理出隐私数据,从而造成隐私泄露,相当于一种个人信息维度的扩充。最简单的例子就是数据库里两张表通过主键关联,得到更多的信息。

差分攻击:攻击者通过对发布的数据进行查询操作,通过查询结果做差从而推理出隐私数据,造成隐私泄露。最简单的例子就是先查n个人的信息,再查n-1个人的信息,再做差。

kkk-匿名(kkk-Anonymity)

长篇大论之前先抛一个最直白的理解,kkk-Anonymity就是处理数据记录让大家看起来一样。再进一步解释就是,通过发布精度较低的数据,使得每条记录至少与数据表中其他k-1 条记录具有完全相同的准标识符属性值,从而减少链接攻击所导致的隐私泄露

长篇大论来了~

为了攻击者可以通过链接攻击和差分攻击获取个体的隐私数据,通常需要对准标识列进行脱敏处理,如数据泛化等。

数据泛化是将准标识列的数据替换为语义一致但更通用的数据,经过泛化后,有多条纪录的准标识列属性值相同,所有准标识列属性值相同的行的集合被称为相等集。kkk-Anonymity要求对于任意一行纪录,其所属的相等集内纪录数量不小于k,即至少有k-1条纪录半标识列属性值与该条纪录相同。

kkk-Anonymity[2]是一个正式的隐私定义。kkk-Anonymity的定义旨在正式化我们的对隐私保护的看法,即一条辅助信息不应该缩小个人"太多"的可能记录集。换句话说,kkk-Anonymity旨在确保每个人都可以"融入人群"

非正式地说,如果数据集中的每个个体都是大小至少为kkk的组的成员,则说数据集对于特定kkk是"kkk-Anonymity",使得组的每个成员都与组的所有其他成员共享相同的准标识符(所有数据集列的选定子集)。因此,每个组中的个人"融入"他们的组 - 可以将个人缩小到特定组中的成员身份,但不能确定哪个组成员是目标。

定义:

Definition
Formally, we say that a dataset DDD satisfies kkk-Anonymity for a value of kkk if:

  • For each row r1∈Dr_1 \in Dr1​∈D, there exist at least k−1k-1k−1 other rows r2…rk∈Dr_2 \dots r_k \in Dr2​…rk​∈D such that Πqi(D)r1=Πqi(D)r2,…,Πqi(D)r1=Πqi(D)rk\Pi_{qi(D)} r_1 = \Pi_{qi(D)} r_2, \dots, \Pi_{qi(D)} r_1 = \Pi_{qi(D)} r_kΠqi(D)​r1​=Πqi(D)​r2​,…,Πqi(D)​r1​=Πqi(D)​rk​

where qi(D)qi(D)qi(D) is the quasi-identifiers of DDD, and Πqi(D)r\Pi_{qi(D)} rΠqi(D)​r represents the columns of rrr containing quasi-identifiers (i.e. the projection of the quasi-identifiers).

形式上,我们说数据集DDD满足kkk值kkk-Anonymity的话,则:

对于每行r1∈Dr_1 \in Dr1​∈D,至少存在k−1k-1k−1其他行r2…rk∈Dr_2 \dots r_k \in Dr2​…rk​∈D,使得Πqi(D)r1=Πqi(D)r2,…,Πqi(D)r1=Πqi(D)rk\Pi_{qi(D)} r_1 = \Pi_{qi(D)} r_2, \dots, \Pi_{qi(D)} r_1 = \Pi_{qi(D)} r_kΠqi(D)​r1​=Πqi(D)​r2​,…,Πqi(D)​r1​=Πqi(D)​rk​

其中qi(D)qi(D)qi(D)是DDD的准标识符,Πqi(D)r\Pi_{qi(D)} rΠqi(D)​r表示包含准标识符的rrr 列(即准标识符的投影)。

k-匿名通过概括(对数据进行更加概括、抽象的描述)和隐匿(不发布某些数据项)技术。

检查kkk-匿名(Checking for kkk-Anonymity)

我们将从一个小数据集开始,以便通过查看数据可以立即看到它是否满足kkk-Anonymity。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid')
raw_data = {'first_name': ['Jason', 'Molly', 'Tina', 'Jake', 'Amy'], 'last_name': ['Miller', 'Jacobson', 'Ali', 'Milner', 'Cooze'], 'age': [42, 52, 36, 24, 73], 'preTestScore': [4, 24, 31, 2, 3],'postTestScore': [25, 94, 57, 62, 70]}
#df = pd.DataFrame(raw_data, columns = ['first_name', 'last_name', 'age', 'preTestScore', 'postTestScore'])
df = pd.DataFrame(raw_data, columns = ['age', 'preTestScore', 'postTestScore'])

此数据集包含年龄加上两个测试分数,显然它不满足当k>1k>1k>1时kkk-Anonymity的定义,但是满足k=1k=1k=1时kkk-Anonymity的定义。具体来说就是每行都可以形成自己的大小为 1 的组,每行都不同。

用看的太麻烦,写个函数吧~

def isKAnonymized(df, k):for index, row in df.iterrows():query = ' & '.join([f'{col} == {row[col]}' for col in df.columns])rows = df.query(query)if rows < k:return Falsereturn True

循环访问行,对于每一行,我们查询数据记录以查看有多少行与准标识符的值匹配。

如果任何组中的行数小于kkk,则数据帧不满足该值kkk-Anonymity,我们返回 False。

请注意,在这个简单的定义中,我们认为所有列都包含准标识符;要将检查限制为所有列的子集,我们需要将表达式替换为其他内容。

看看效果~

当k=1的时候是满足条件的:

isKAnonymized(df, 1)

当k=2的时候是不满足条件的:

isKAnonymized(df, 2)

生成满足kkk-匿名的数据(Generalizing Data to Satisfy kkk-Anonymity)

修改数据集以使其满足所需 kkk-匿名性的过程通常通过泛化数据来完成

修改值以使其不那么具体,因此更有可能与数据集中其他个体的值匹配。

例如,精确到年份的年龄可以通过舍入到最接近的 10 年来泛化,或者邮政编码可能将其最右边的数字替换为零。

对于数值,这很容易实现。我们将使用数据帧的方法,并传入一个名为字典的字典,该字典指定每列要用零替换的数字数。

这使我们能够灵活地试验不同列的不同泛化级别。

还是老样子,写个函数吧~
df.apply(lambda表达式)

def generalize(df, depths):return df.apply(lambda x: x.apply(lambda y: int(int(y/(10**depths[x.name]))*(10**depths[x.name]))))

这个函数里面的y表示表中实际数据,比如42。depths[x.name]表示当前属性下设定的映射值,下面在depths中将各个属性设为1,也就是对42/10再*10得到40。整个函数就是对所有数字向下取到10的倍数。

现在,我们可以概括我们的示例数据帧。

首先,我们将尝试将每列概括为一个"级别" , 即舍入到最接近的10。

depths = {'age': 1,'preTestScore': 1,'postTestScore': 1
}
df2 = generalize(df, depths)
df2


请注意,即使在泛化之后,我们的示例数据仍然不满足当k>1k>1k>1时kkk-Anonymity的定义。


我们可以尝试泛化更多,但随后我们最终会删除所有数据!各个属性的深度又1变成2,等于是42/100直接为0了。

depths = {'age': 2,'preTestScore': 2,'postTestScore': 2
}
generalize(df, depths)


这样的例子也说明了kkk组有意义的数据帧对于kkk-Anonymity,需要从数据中删除大量信息。

数据越多泛化越好?(Does More Data Improve Generalization?)

我们的示例数据集太小,无法正常工作。由于数据集中只有 5 个个体,因此很难构建由 2 个或更多共享相同属性的个体组成的组。这个问题的解决方案是更多的数据:在具有更多个体的数据集中,通常需要较少的泛化来满足kkk组有意义的数据帧对于kkk-Anonymity。

让我们尝试一下我们检查的用于去识别化的相同人口普查数据。此数据集包含超过 32,000 行,因此实现kkk-Anonymity应该更容易。

我们将每个人的邮政编码,年龄和教育成就视为准标识符。我们将只投影这些列,并尝试实现k=2k=2k=2的kkk-Anonymity。因为数据已为k=1k=1k=1的kkk-Anonymity。

请注意,我们只从数据集中获取前 100 行进行此检查 。

老样子,读数据~

adult_data = pd.read_csv("adult_with_pii.csv")
adult_data.head()


当前情况k=2k=2k=2的kkk-Anonymity不满足,k=1k=1k=1的kkk-Anonymity已经满足。

df = adult_data[['Age', 'Education-Num']]
df.columns = ['age', 'edu']
isKAnonymized(df.head(100), 1)
isKAnonymized(df.head(100), 2)


还是老方法,调用generalize对数据进行处理。

# outliers are a real problem!
depths = {'age': 1,'edu': 1
}
df2 = generalize(df.head(1000), depths)
isKAnonymized(df2, 2)


结果仍然不满足k=2k=2k=2的kkk-Anonymity!

事实上,我们可以对所有 32,000 行执行此泛化,但仍然无法满足k=2k=2k=2的kkk-Anonymity。

因此添加更多数据并不一定像我们预期的那样有帮助。

原因是数据集包含异常值,即与其他人群截然不同的个体。这些人不容易融入任何群体,即使在泛化之后也是如此。

即使只考虑年龄,我们也可以看到添加更多数据不太可能有所帮助,因为非常低和非常高的年龄在数据集中的代表性很差。

df2['age'].hist();


在这种情况下,实现kkk-Anonymity的最佳泛化是非常具有挑战性的。

对于年龄在20-40岁之间的代表性良好的个体来说,将每一行泛化得更多是过分的,并且会损害效用。

然而,对于年龄范围上端和下端的个体,显然需要更多的泛化。

这是在实践中经常发生的那种挑战,很难自动解决。

事实上,kkk-Anonymity的最佳泛化已被证明是NP-hard。

异常值使得实现kkk-Anonymity非常具有挑战性,即使对于大型数据集也是如此。kkk的最优泛化kkk-Anonymity是NP-hard。

删除异常值(Removing Outliers)

那我们就束手无策了吗?

这个问题的一个解决方案是简单地将数据集中每个人的年龄裁剪为位于特定范围内,从而完全消除异常值。

这也会损害效用,因为它用假的年龄取代了真实的年龄,但它可能比进一步泛化每一行要好。

我们可以使用Numpy的方法来执行此裁剪。我们将年龄裁剪为60岁或以下,而将教育水平单独保留(通过将其裁剪为非常大的值)。
Python pandas.DataFrame.clip函数方法的使用

# clipping away outliers
depths = {'age': 1,'edu': 1
}
dfp = df.clip(upper=np.array([60, 10000000000000]), axis='columns')
df2 = generalize(dfp.head(500), depths)
isKAnonymized(df2, 7)


此时处理过的数据集就满足k=7k=7k=7的kkk-Anonymity。我们的泛化水平是适当的,但是异常值仍旧阻止我们实现kkk-Anonymity。

总结

1、kkk-Anonymity是数据的一个属性,它确保每个个体都与至少一组kkk个体"融合"。

2、kkk-Anonymity甚至在计算上也很昂贵:朴素算法是O(n2)O(n^2)O(n2),更快的算法占用相当大的空间。

3、kkk-Anonymity可以通过泛化数据集来修改数据集来实现,这样特定值变得更加常见,组更容易形成。

4、最优泛化极其困难,异常值可能使其更具挑战性。自动解决这个问题是NP困难的。

【k-匿名(k-Anonymity)代码实现】差分隐私代码实现系列(三)相关推荐

  1. 【链接攻击,差分攻击,去标识化代码实现】差分隐私代码实现系列(二)

    差分隐私代码实现系列(二) 写在前面的话 去识别化(De-identification) 数据处理 去识别化操作 链接攻击 Karrie特别吗? 我们可以重新识别多少人? 聚合(Aggregation ...

  2. 【本地差分隐私与随机响应代码实现】差分隐私代码实现系列(十三)

    差分隐私代码实现系列(十三) 写在前面的话 回顾 本地差分隐私 随机响应 一元编码 总结 写在前面的话 书上学来终觉浅,绝知此事要躬行. 回顾 1.梯度下降是一种通过根据损失的梯度更新模型来使损失变小 ...

  3. 【机器学习与差分隐私代码实现】差分隐私代码实现系列(十二)

    差分隐私代码实现系列(十二) 写在前面的话 回顾 机器学习与差分隐私 使用 Scikit-Learn 进行逻辑回归 什么是模型? 使用梯度下降训练模型 梯度下降的单一步骤 梯度下降算法 梯度下降与差分 ...

  4. 【指数机制代码实现】差分隐私代码实现系列(十)

    差分隐私代码实现系列(十) 写在前面的话 回顾 指数机制 发明指数机制的动机 指数机制的描述 日期的例子 指数机制的特点 有限集合的指数机制 报告最大噪声算法 指数机制作为差分隐私的基本机制 总结 写 ...

  5. 【Rényi差分隐私和零集中差分隐私(差分隐私变体)代码实现】差分隐私代码实现系列(九)

    差分隐私代码实现系列(九) 写在前面的话 回顾 差分隐私的变体 发明变体的动机 最大散度和Rényi散度 Rényi差分隐私 零集中差分隐私 差分隐私变体的组合情况 总结 写在前面的话 书上学来终觉浅 ...

  6. 【局部敏感度的问题代码实现】差分隐私代码实现系列(八)

    差分隐私代码实现系列(八) 写在前面的话 回顾 局部敏感度(Local Sensitivity) 均值的局部灵敏度(Local Sensitivity of the Mean) 通过局部灵敏度实现差分 ...

  7. 【差分隐私组合定理,直方图,列联表代码实现】差分隐私代码实现系列(五)

    差分隐私代码实现系列(五) 写在前面的话 回顾 差分隐私的属性(Properties of Differential Privacy) 顺序组成(Sequential composition) 平行组 ...

  8. 【我们为什么用高斯机制?】差分隐私代码实现系列(七)

    差分隐私代码实现系列(七) 写在前面的话 回顾 松弛差分隐私(Approximate Differential Privacy) 高斯机制(The Gaussian Mechanism) 矢量值函数及 ...

  9. 【敏感度,查询,裁剪代码实现】差分隐私代码实现系列(六)

    差分隐私代码实现系列(六) 写在前面的话 回顾 敏感性(Sensitivity) 距离(Distance) 计算灵敏度(Calculating Sensitivity) 计算查询数(Counting ...

最新文章

  1. 重定向程序无法决定链接类型 解决方案
  2. CxImage图像处理类库说明3(转载)
  3. Python open()函数用法详解
  4. 逻辑短路 java_逻辑操作符中的短路现象
  5. Linux三大共享文件的方法
  6. arm tbh_TBH的完整形式是什么?
  7. SpringBoot系列五:SpringBoot错误处理(数据验证、处理错误页、全局异常)
  8. django mysql windows_Django+MySQL配置:Windows+Centos
  9. C# 视频播放控件 wmp、vlc、aplayer
  10. 联通实时计算平台演进与实践
  11. 学生选课系统代码-2view视图层代码【MVC--v】代码
  12. 开源数据库系统之SQLite3.2.0、FireBird2.0 Alpha-1等
  13. AWG#线规及其载流能力和电阻值
  14. 二进制转换八进制图解_二进制如何转换成八进制?
  15. 计算机网络第三弹——数据链路层
  16. 【Love2D】第0章-从零开始学习Love2D
  17. HTML表格之合并单元格
  18. 如何在Excel中调节折线图和柱形图(组合图)的高低(位置),让图中的折线和柱形不会出现重叠
  19. 多智时代,人工智能发展历史的时间表
  20. wireshark执行XDG问题

热门文章

  1. JAVA正则表达式,matcher.find()和 matcher.matches()的区别
  2. 种草营销怎么玩?如何借小红书KOL、KOC笔记种草提升转化效果
  3. 推箱子游戏java毕业答辩ppt_基于Java推箱子游戏的设计与实现
  4. 红米5plus刷android one,安卓刷机必备!TWRP恢复工具添加支持红米5/雷蛇手机
  5. Python ffmpeg视频压缩
  6. Java数据类型学习
  7. 数学建模——规划问题
  8. 关于登录账号时提示系统不存在此账户,但其它电脑能够登录成功的解决方案
  9. 【Python】windows下Eclipse中安装集成webpy框架
  10. php十进制转ascii字符,(5条消息)php ASCII字符和十六进制数之间的相互转化