
本文由 DataRobot 的数据科学家 Sarah Khatry 和 Haniyeh Mahmoudian 撰写。
近年来,关于机器学习模型偏差的问题备受关注。模型出错的报道频上头条,人道主义律师、政治家和记者都参与了关于我们希望在模型中体现哪些伦理与价值观的讨论。
虽然人类偏见复杂且并非总能轻易界定,但机器学习中的偏差归根结底是数学问题。您可以对模型实施多种测试,以识别其预测中的不同类型偏差。选择哪种测试,主要取决于您关注什么以及模型使用的具体情境。
最广泛适用的测试之一是统计公平性(statistical parity),本教程将带您动手实践。需要注意的是,偏差总是相对于数据中由受保护属性(例如种族、性别、年龄、性取向、国籍等)区分的人群来评估的。
在统计公平性中,您的目标是衡量不同群体获得有利结果的概率是否相等。一个经典示例是招聘模型,您希望确保男性和女性申请者被录用的概率相同。在存在偏差的模型中,您会发现某一群体被“优待”,拥有更高的录用概率,而另一群体则处于不利地位。
为了展示其在实践中的运作方式,我们将先构造带有预设偏差的合成数据,然后通过分析确认数据反映了我们预期的情形,最后应用统计公平性测试。
生成合成数据
在本教程中,我们将使用 Python 的 pandas 包,但流程中的每一步也都可以在 R 中复现。
import pandas as pd
要生成包含一个受保护属性和模型预测的合成数据,我们首先需要指定几个输入:记录总数、受保护属性本身(此处为两个通用取值 A 和 B),以及与有利结果相关联的模型预测(本示例为取值 1)。
num_row = 1000 # number of rows in the data
prot_att = 'prot_att' # column name for protected attribute
model_pred_bin = 'prediction' # column name for predictions
pos_label = 1 # prediction value associated to positive/favorable outcome
prot_group = ['A','B'] # two groups in our protected attribute
与现实相同,群体 A 和 B 在我们的数据中可能并非均匀分布。下面的代码中,我们设定数据中 60% 的人群来自被优待的 B 组,他们获得有利结果的概率为 30%。处于不利地位的 A 组占剩余 40%,获得有利结果的概率仅为 15%。
priv_g = 'B' # privileged group
priv_g_rate = 60 # 60% of the population is from group B
priv_p_rate = 30 # 30% of the predictions for group B was for favorable outcome 1
unpriv_p_rate = 15 # 15% of the predictions for group A was for favorable outcome 1
biased_list_priv = [prot_group[0]] * (100 - priv_g_rate) + [prot_group[1]] * priv_g_rate
biased_list_pos_priv = [0] * (100 - priv_p_rate) + [1] * priv_p_rate
biased_list_pos_unpriv = [0] * (100 - unpriv_p_rate) + [1] * unpriv_p_rate
对于数据中的每条记录,我们根据之前设定的权重,随机分配一个受保护群体和一个预测值,然后将这些记录组成列表并创建为数据框。
list_df = [] # empty list to store the synthetic records
for i in range(0, num_row):
# generating random value representing protected groups with bias towards B
prot_rand = random.choices(biased_list_priv)[0]
if prot_rand == priv_g:
prot_g = priv_g
# generating random binary value representing prediction with bias towards 0
pred = random.choices(biased_list_pos_priv)[0]
# adding the new record to the list
list_df.append([prot_g,pred])
else:
prot_g = prot_group[0]
pred = random.choices(biased_list_pos_unpriv)[0]
list_df.append([prot_g,pred])
# create a dataframe from the list
df = pd.DataFrame(list_df,columns=['prot_att','prediction'])
解读数据
现在我们有了合成数据,来分析一下我们构建的内容。对于每个群体(A 与 B),他们获得有利或不利结果的类别概率分别是多少?
df_group = (df.groupby([prot_att])[model_pred_bin].value_counts() / df.groupby([prot_att])[
model_pred_bin].count()).reset_index(name='probability')
print(df_group)
prot_att prediction probability
0 A 0 0.849490
1 A 1 0.150510
2 B 0 0.713816
3 B 1 0.286184
查看该表,我们不难发现 B 组获得有利结果的可能性几乎是 A 组的两倍,概率为 28.6%。我们的合成数据被设计为 30% 的概率,因此已非常接近。随后我们将这些概率保存到一个字典中。
由于是随机生成,您的代码可能得到略有不同的结果。
prot_att_dic = {}
for att in prot_group:
temp = df_group[(df_group[prot_att] == att) & (df_group[model_pred_bin] == pos_label)]
prob = float(temp['probability'])
prot_att_dic[att] = prob
统计公平性测试
对于每个群体,统计公平性会输出其获得有利结果的概率与被优待群体获得有利结果概率的比值。我们遍历包含各受保护群体及其有利结果概率的字典,计算这些比值。
prot_test_res = {}
for k in prot_att_dic.keys():
res = float(prot_att_dic[k] / prot_att_dic[priv_g])
prot_test_res[k] = res
for key in prot_test_res.keys():
value = prot_test_res[key]
print(key, ' : ', value)
A : 0.5259207131128314
B : 1.0
对于被优待群体 B,统计公平性得分为 1,这与预期一致。对于另一群体 A,其得分为 0.526,表明他们获得有利结果的可能性大约为 B 组的一半。
统计偏差测试为您提供了一个简明的评估方式,用以判断数据中选定群体的预测结果可能有多大差异。衡量偏差的目标有二:一方面,该测试能产生一个透明的度量,便于清晰、具体地沟通;另一方面,理想情况下,识别偏差是开始缓解模型中偏差的第一步。这是机器学习领域的热门研究方向,已有许多技术被开发出来,以适配不同类型的偏差和建模方法。
通过恰当组合测试与缓解技术,您可以迭代改进模型,降低偏差并保持准确率。您可以设计机器学习系统,不仅从历史结果中学习,还能在未来的决策中体现您的价值观。
了解如何借助 DataRobot 构建值得信赖的 AI。