差分隐私学习第二章(自用)
学习目标
学习完这章内容你将学会
- 下列定义:
- 去识别化(de-identification)
- 行人重识别(re-identification)
- 个人可识别信息(Identifying information / personally identifying information(PII))
- 链接攻击(linkage attack)
- 数据聚合(Aggregation and aggregate statistics)
- 差分攻击
- 实现一个链接攻击
- 实现一次差分攻击
- 解释去识别化技术的局限性
- 解释数据聚合技术的局限性
初始化
先下载一个基于人口普查数据的csv文件。将csv文件和用于阅读csv文件的python代码文件放在同一目录下,方便后续读取文件。
csv文件地址
本人vscode阅读提取出来的数据有格式问题,所以将结果转化为excel表格形式,故与源文档有所不同
#如果没有pandas或其他扩展库可在cmd中使用pip install xxx指令下载
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#读取csv文件并规定编码
adult = pd.read_csv("adult_with_pii.csv", encoding='utf-8')
#将提取的文件转化为excel格式,文件名为output,表名为testing,索引为否
adult.to_excel("output.xlsx", sheet_name="Testing", index=False,header=True)
#输出csv文件的前五项
print(adult.head())
去识别化
去识别化是从数据集中删除标识信息的过程,去识别化有时也和匿名化和假名化被当做同义词。
识别信息这个概念,它没有正式定义。它通常被理解为在日常生活中用于唯一识别我们的信息 - 姓名,地址,电话号码,电子邮件地址等。正如我们稍后将看到的,不可能将识别信息的概念形式化,因为所有信息都是识别的。个人身份信息(PII)通常与识别信息同义使用。
如何进行去识别化呢?只要把带有识别信息的列删除即可。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#复制一个不包含姓名和ssn列的adult副本命名为adult_data
adult_data = adult.copy().drop(columns=['Name', 'SSN'])
adult_data.to_excel("output2.xlsx", sheet_name="Testing", index=False,header=True)
#复制一个仅包含姓名、ssn、dob和zip列的adult副本命名为adult_pli
adult_pii = adult[['Name', 'SSN', 'DOB', 'Zip']]
adult_pii.to_excel("output3.xlsx", sheet_name="Testing", index=False,header=True)
#print(adult_data.head())
output2
output3省略
链接攻击
想象一下一个场景,如何从去识别化的数据中确定一个人的收入。名字已被删除,但碰巧知道一些关于这个人的辅助信息。比如有个叫Karrie Trusslove,我们知道Karrie的出生日期和邮政编码(zip)。也就是结合上面内容的adult_pii数据集。
为了实现简单的链接攻击,我们专注于尝试攻击的数据集和所知道的辅助数据之间的重叠列。
两个数据集都有birth和zip,我们在要攻击的数据集中寻找birth和zip与Karrie相同的行。在数据库中,这叫做两个表的链接,我们使用pandas扩展库中的merge
函数。如果结果只有一行,那么这就是我们要找的Karrie的信息。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
adult = pd.read_csv('adult_with_pii.csv', encoding='utf-8')
adult_pii = adult[['Name', 'SSN', 'DOB', 'Zip']]
karries_row = adult_pii[adult_pii['Name'] == 'Karrie Trusslove']
#这里直接使用on参数也可以
#要合并的列名在两个数据集不同时,on参数就没有效果了,需要使用left_on和right_on参数
karries_row2=pd.merge(karries_row, adult_data, left_on=['DOB', 'Zip'], right_on=['DOB', 'Zip'])
karries_row2.to_excel("output2.xlsx", sheet_name="Testing", index=False)
只有一行是匹配的。我们使用辅助数据在去识别化的数据集中重新识别个人,我们能够推断出 Karrie 的收入低于 50 美元。
对Karrie进行行人重识别(re-identification)有多难
上面的情况是虚构的,但链接攻击在实践中执行起来非常容易。有多容易?在许多情况下,只有一个数据点就足以精确定位一行!
根据zip
#与上面代码改动较少故只放一行代码
pd.merge(karries_row, adult_data, left_on=['Zip'], right_on=['Zip'])
根据生日
#与上面代码改动较少故只放一行代码
pd.merge(karries_row, adult_data, left_on=['DOB'], right_on=['DOB'])
此时我们不知道哪一行是真正的Karrie,但是我们可以知道
1、凯莉的收入有2/3的可能性低于5万美元。
2、我们可以查看行之间的差异,以确定哪些额外的辅助信息可以帮助我们区分它们(例如性别,职业,婚姻状况。
Karrie特别吗?
再次识别数据集中的其他人有多难?对Karrie 重新识别是特别容易还是特别难?
衡量此类攻击有效性的一个好方法是查看某些数据的“选择性”程度
它们在缩小可能属于目标个人的潜在属性功能有多好。例如同一个出生日期是否出现多次?
我们希望知道有多少出生日期可能对进行攻击有用,我们可以通过查看数据集中“独特”出生日期的普遍程度来进行分析。
下面的直方图显示,绝大多数出生日期在数据集中出现1、2或3次,没有任何出生日期出现超过8次。这意味着出生日期是有选择性的,它在缩小个人可能的记录范围方面是有效的。
那么我们是如何实现的
根据出生日期绘制直方图
adult_pii['DOB'].value_counts() .hist()
plt.xlabel('Number of Dates of Birth')
plt.ylabel('Number of Occurrences');
#python版本较高的话,plt不显示图表,需要加上下面代码
plt.show()
根据zip绘制直方图
adult_pii['Zip'].value_counts().hist()
plt.xlabel('Number of ZIP Codes')
plt.ylabel('Number of Occurrences');
plt.show()
我们可以对多少人进行重识别
在这个数据集中,我们可以唯一地重新识别多少人?
我们可以使用辅助信息寻找答案。
首先我们观察只有出生日期的情况
我们想知道数据集中的每个数据记录返回了多少个可能的标识。
attack = pd.merge(adult_pii, adult_data, left_on=['DOB'], right_on=['DOB'])
attack['Name'].value_counts().hist();
结果表明,我们可以唯一地识别近24000条数据记录(总数据量为32,000条数据记录),另外10,000条数据记录被缩小到两个或三个可能的身份。
因此,仅使用出生日期就不可能重新识别大多数个体。如果我们收集更多信息,以进一步缩小范围,该怎么办?如果我们同时使用出生日期和ZIP,我们可以做得更好。
事实上,我们能够唯一地重新识别整个数据集。
即使用dob和zip两个条件
当我们使用这两条信息时,我们基本上可以重新识别每个人。这是一个令人惊讶的结果,因为我们通常假设许多人共享相同的生日,并且许多人居住在相同的邮政编码中。事实证明,这些因素的结合是极具选择性的。根据调查显示,87%的美国人可以通过出生日期,性别和邮政编码的组合来唯一地重新识别。
让我们检查一下,我们实际上已经重新识别了每个人,通过打印出每个身份的可能数据记录的数量,这里就看看前5条就可以了:
print(attack['Name'].value_counts().head())
在数据集中,只有两个人共享邮政编码和出生日期的组合
聚合
防止泄露私人信息的另一种方法是仅发布汇总数据。
print(adult['Age'].mean())
``
### 小团体问题
在许多情况下,汇总统计数据被分解为更小的组。例如,我们可能想知道具有特定教育水平的人的平均年龄。
```python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
adult = pd.read_csv('adult_with_pii.csv', encoding='utf-8')
print(adult[['Education', 'Age']].groupby('Education', as_index=False).mean().head(3))
聚合应该对保护隐私有所贡献,因为很难识别特定个人对聚合统计信息的贡献。
但是,如果我们在一个只有一个人的群体中聚合呢?
在这种情况下,汇总统计数据准确地揭示了一个人的年龄,并且根本不提供隐私保护!
在我们的数据集中,大多数人都有一个唯一的邮政编码 - 所以如果我们按邮政编码计算平均年龄,那么大多数"平均值"实际上揭示了一个人的确切年龄。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
adult = pd.read_csv('adult_with_pii.csv', encoding='utf-8')
adult[['Zip', 'Age']].groupby('Zip', as_index=False).mean().head()
例如美国人口普查局在区块级别发布汇总统计数据。
一些人口普查区块的人口众多,但有些区块的人口为零!
上述情况就是小团体阻止聚集隐藏有关个人的信息,事实证明这是相当普遍的。
一个群体"足够大",总统计数据可以提供帮助吗?很难说,因为这取决于数据和攻击,因此很难建立对汇总统计数据真正保护隐私的信心。
但是,即使是非常大的组也无法使聚合完全可靠地抵御攻击,比如差分攻击。
差分攻击
当对相同数据发布多个聚合统计信息时,聚合对隐私保护的程度会变得低。
例如,考虑对数据集中的大型组执行以下两个求和查询:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
adult = pd.read_csv('adult_with_pii.csv', encoding='utf-8')
print('所有人age的总和:')
print(adult['Age'].sum())
print('\n')
print('除了karrie的所有人age总和')
print(adult[adult['Name'] != 'Karrie Trusslove']['Age'].sum())
下面对两组数据作差运算即可得出karrie的age
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
adult = pd.read_csv('adult_with_pii.csv', encoding='utf-8')
print('数据作差得karrie的age:')
print(adult['Age'].sum() - adult[adult['Name'] != 'Karrie Trusslove']['Age'].sum())
即使聚合统计信息位于非常大的组上,这种攻击也可以继续进行。
所以我们会反复遇到并思这些问题
1、发布有用的数据使得确保隐私变得非常困难。
2、我们无法区分恶意和非恶意查询。
总结
- 链接攻击涉及将辅助数据与去标识化数据相结合,以重新识别个人。
- 在最简单的情况下,可以通过连接包含这些数据集的两个表来执行链接攻击。
- 简单的链接攻击非常有效:
- 只需一个数据点就足以将范围缩小到几条记录
- 缩小的记录集有助于建议可能有用的其他辅助数据
- 两个数据点通常足以重新识别特定数据集中的很大一部分人口
发布有用的数据使得确保隐私变得非常困难。
2、我们无法区分恶意和非恶意查询。
总结
- 链接攻击涉及将辅助数据与去标识化数据相结合,以重新识别个人。
- 在最简单的情况下,可以通过连接包含这些数据集的两个表来执行链接攻击。
- 简单的链接攻击非常有效:
- 只需一个数据点就足以将范围缩小到几条记录
- 缩小的记录集有助于建议可能有用的其他辅助数据
- 两个数据点通常足以重新识别特定数据集中的很大一部分人口
- 三个数据点(性别、邮政编码、出生日期)唯一地识别了 87% 的美国人