工程的博客

使用SHAP和机器学习检测数据偏差

机器学习和SHAP可以告诉我们开发者工资和性别工资差距之间的关系

2019年6月17日 数据科学与机器学习

分享这篇文章
尝试使用SHAP笔记本检测数据偏差复制下面概述的步骤和观看我们的点播网络研讨会了解更BOB低频彩多。

StackOverflow年度开发者调查今年早些时候结束,他们大方地公布了2019年(匿名)的分析结果。它们为世界各地的软件开发人员提供了丰富的体验——他们最喜欢的编辑器是什么?有多少年的工作经验?制表符或空格?最关键的是,薪水。软件工程师的薪水是不错的,有时还会让人热泪盈眶,成为新闻。

科技行业也痛苦地意识到,它并不总是能实现其所谓的精英理想。薪酬并不是一个纯粹的能力函数,一个又一个故事告诉我们,名牌学校、年龄、种族和性别等因素都会对薪酬等结果产生影响。

机器学习能做的不仅仅是预测吗?它能否解释工资,从而突出这些因素可能造成不受欢迎的工资差异的情况?本例将概述如何使用SHAP (SHapley加法解释)为了发现其预测可能令人担忧的个别实例,然后深入挖掘数据导致这些预测的具体原因。

模型偏倚还是数据偏倚?

虽然这个主题通常被描述为检测“模型偏差”,但模型只是它所训练的数据的镜像。如果模型是“有偏见的”,那么它是从数据的历史事实中学习到的。模型本身不是问题;它们是一个分析数据以寻找偏见证据的机会。

解释模型并不新鲜,大多数库都可以评估模型输入的相对重要性。这些是对投入效果的总体看法。然而,一些机器学习模型的输出具有高度的个人效应:你的贷款被批准了吗?你会得到经济援助吗?你是一个可疑的旅行者吗?

事实上,StackOverflow提供了一个方便的计算器根据其调查估计自己的期望工资。我们只能推测这些预测的总体准确性,但开发者特别关心的是他或她自己的前景。

正确的问题可能不是,数据是否暗示了整体的偏见?而是,数据是否显示了偏见的个别实例?

评估StackOverflow调查数据

值得庆幸的是,2019年的数据是干净的,没有数据问题。它包含了大约8.8万名开发人员对85个问题的回答。

本例只关注全职开发人员。数据集包含大量相关信息,如工作年限、教育程度、角色和人口统计信息。值得注意的是,该数据集不包含奖金和股权信息,只包含工资信息。

此外,它还收集了对区块链、fizz buzz以及调查本身的态度等广泛问题的回答。这些因素在这里被排除在外,因为它们不太可能反映经验和技能应该确定补偿。同样,为了简单起见,它也只关注美国的开发者。

在建模之前,数据需要进行更多的转换。有几个问题允许多种回答,比如“作为开发者,你对工作效率最大的挑战是什么?”这些单一的问题会产生多个是/否的回答,需要分解成多个是/否的特征。

一些选择题,比如“你所在的公司或组织大约有多少员工?”做出这样的回应“2 - 9员工”.这些是有效的连续值,将它们映射回推断的连续值(如“2”)可能是有用的,这样模型就可以考虑它们的顺序和相对大小。不幸的是,这种翻译是手动的,需要一些判断。

Apache火花可以完成此操作的代码在附带的笔记本,为感兴趣的人。

使用Apache Spark选择模型

有了机器学习更友好的数据形式,下一步是拟合一个回归模型,根据这些特征预测工资。经过Spark过滤和转换后的数据集本身只有4MB,包含来自大约12,600名开发人员的206个特性,可以轻松地放入内存中手表上的数据帧,更不用说服务器了。

xgboost(一个流行的梯度增强树包)可以在几分钟内在一台机器上为这些数据拟合一个模型,而不需要Spark。xgboost提供许多影响模型质量的可调“超参数”:最大深度、学习率、正则化等等。与其猜测,简单的标准实践是尝试这些值的大量设置,并选择产生最准确模型的组合。

幸运的是,这正是Spark重新发挥作用的地方。它可以并行地构建数百个这样的模型,并收集每个模型的结果。因为数据集很小,所以很容易将其广播给工作者,创建这些超参数的一堆组合进行尝试,并使用Spark应用相同的简单非分布式xgboost可以使用每个组合在本地为数据构建模型的代码。

...deftrain_model参数个数):(max_depth, learning_rate, reg_alpha, reg_lambda, gamma, min_child_weight) = paramsxgb_regressor = xgbregresor (objective=“注册:squarederror”、max_depth = max_depth \Learning_rate = Learning_rate, reg_alpha=reg_alpha, reg_lambda=reg_lambda, gamma=gamma,\min_child_weight = min_child_weight n_estimators =3000、base_score = base_score \importance_type =“total_gain”random_state =0xgb_model = xgb_regresor .fit(b_X_train. fit)值、b_y_train.value \eval_set = [(b_X_test。价值,b_y_test.value)] \eval_metric =“rmse”early_stopping_rounds =30.n_estimators =len(xgb_model.evals_result () (“validation_0”] [“rmse”])y_pred = xgb_model.predict(b_X_test.value)Mae = mean_absolute_error(y_pred, b_y_test.value)Rmse = sqrt(mean_squared_error(y_pred, b_y_test.value))返回(params + (n_estimators,), (mae, rmse), xgb_model)
              ...Max_depth = np.unique(np.geomspace(3.7num =5, dtype = np.int32)) .tolist ()Learning_rate = np.unique(np.around(np.geomspace(0.010.1num =5),小数=3.) .tolist ()Reg_alpha = [0+ np.unique(np.around(np.geomspace(150num =5),小数=3.) .tolist ()Reg_lambda = [0+ np.unique(np.around(np.geomspace(150num =5),小数=3.) .tolist ()Gamma = np.unique(np.around(np.geomspace(520.num =5),小数=3.) .tolist ()Min_child_weight = np.unique(np.geomspace(530.num =5, dtype = np.int32)) .tolist ()并行性=128Param_grid = [(choice(max_depth), choice(learning_rate), choice(reg_alpha),\选择(reg_lambda),选择(gamma),选择(min_child_weight))_范围(并行)Params_evals_models = sc.parallelize(param_grid, parallelism)。地图(train_model) .collect ()

这将创造出许多模型。为了跟踪和评估结果,mlflow可以记录每一个指标和超参数,并在笔记本的实验中查看它们。在这里,将多次运行中的一个超参数与得到的精度(平均绝对误差)进行比较:

模型误差与超参数<br />的MLflow散点图

在保留的验证数据集上显示最小误差的单个模型是值得关注的。在平均工资约为11.9万美元的情况下,该研究得出的平均绝对误差约为2.8万美元。并不可怕,尽管我们应该意识到模型只能解释大多数关于工资的变化。

解释xgboost模型

虽然这个模型可以用来预测未来的工资,但问题是这个模型对数据说了什么。在准确预测薪水时,哪些特征最重要?的xgboost模型本身计算特征重要性的概念:

进口mlflow.sklearnbest_run_id =“…”Model = mlflow.sklearn.load_model(“运行:/+ best_run_id +“/ xgboost”排序((邮政编码(模型。feature_importances_, X.columns)), reverse=真正的):6

对机器学习预测nxgboost模型中各因素的重要性进行排序。< br / >

诸如专业编码年限、组织规模和使用Windows等因素是最“重要的”。这很有趣,但很难解释。这些价值反映的是相对的重要性,而不是绝对的重要性。也就是说,这种影响不是用美元来衡量的。这里的重要性(总收益)的定义也具体到如何构建决策树,并且很难映射到直观的解释。这些重要的特征甚至不一定与薪水呈正相关。

更重要的是,这是一种“全局”的观点,即总体上功能有多重要。性别和种族等因素直到后来才出现在这个列表上。这并不意味着这些因素不重要。首先,特征可以相互关联或相互作用。性别等因素可能与树选择的其他特征相关,这在一定程度上掩盖了它们的影响。

更有趣的问题不是这些因素在整体上是否重要——它们的平均影响可能相对较小——而是它们在某些个别情况下是否有显著影响。在这些例子中,模型告诉我们一些关于个人经验的重要信息,而对这些人来说,经验才是最重要的。

为开发人员级解释应用包SHAP

幸运的是,在过去五年左右的时间里,在个体预测水平上出现了一套理论上更合理的模型解释技术。他们都是"上海apley一个dditive交货p,并且方便地在Python包中实现世鹏科技电子

对于任何模型,这个库都会从模型中计算“SHAP值”。这些值很容易解释,因为每个值都是一个特征对预测的影响。这里的SHAP值为1000表示“解释工资+ 1000美元的预测工资”。计算SHAP值的方法也试图隔离相关性和交互。

进口世鹏科技电子explainer = shape . treeexplainer(模型)Shap_values =解释器。shap_values(X, y=y.values)

SHAP值也是为每个输入计算的,而不是整个模型,因此这些解释可单独用于每个输入。对于每个预测,它还可以从每个特征的主要影响中单独估计特征相互作用的影响。

可解释的AI:揭示特征的整体效果

开发人员级别的解释可以通过简单地平均它们的绝对值,聚合成对整个数据集中的特性对工资影响的解释。SHAP对总体最重要特性的评估是类似的:

根据绝对SHAP值评估特征重要性。< br / >

SHAP值也说明了类似的情况。首先,SHAP能够用美元来量化对工资的影响,这大大改善了对结果的解释。上面是一个图绝对每个功能对开发者预期工资的影响。多年的专业编码经验仍然占主导地位,平均约1.5万美元的薪酬效应。

用SHAP值检验性别的影响

我们专门研究了性别、种族和其他因素的影响,这些因素本身可能根本不能预测工资。这个例子将研究性别的影响,尽管这并不意味着这是唯一或最重要的偏见类型。

性别并不是二元的,调查中分别出现了“男性”、“女性”、“非二元、性别酷儿或性别不一致”以及“跨性别”的回答。(请注意,虽然调查也单独记录了关于性的回答,但这里不考虑这些。)SHAP计算了这些因素对预期工资的影响。对于男性开发者(只认为自己是男性)来说,性别的影响不仅仅是男性的影响,还包括不认为自己是女性、变性人等等。

SHAP值让我们可以读出这些影响的总和,为开发人员识别为四个类别中的每一个:

举例SHAP值解释性别对预测工资的影响<br />

男性开发者的收入范围从230美元到890美元不等,平均收入约为225美元,而女性开发者的收入范围则更大,从4260美元到690美元不等,平均收入为1320美元。跨性别和非二元开发者的结果是相似的,尽管没有那么消极。

在评估这意味着什么时,重要的是要回顾这里的数据和模型的局限性:

  • 相关性不是因果关系;“解释”预测的薪资只是暗示,但不能证明某个特征直接导致薪资升高或降低
  • 这个模型并不完全准确
  • 这只是1年的数据,且仅来自美国开发者
  • 这只反映了基本工资,不包括奖金或股票,后者的变化可能更大

使用SHAP可视化与性别交互的功能

SHAP库提供了有趣的可视化,利用其隔离特性交互效果的能力。例如,上面的数值表明,男性开发人员的薪水预计会比其他人略高,但还有其他原因吗?像这样的依赖关系图可以有所帮助:

使用SHAP可视化与性别交互的特征<br />

点是开发者。左边是那些不认为自己是男性的开发者,右边是那些认为自己是男性的开发者,主要是那些认为自己只是男性的开发者。(为了清晰起见,这些点是水平随机分布的。)y轴是SHAP值,即男性或非男性对每个开发人员预期薪酬的解释。如上所述,那些不认为自己是男性的人总体上表现出负的SHAP值,而且变化很大,而其他人则始终表现出小的正SHAP值。

方差背后是什么?给定的值,SHAP可以选择第二个影响最大的特征,在这里,识别为男性或非男性。它在“你的工作有多大的结构和计划?”的问题上选择了“我做最重要或最紧急的事情”的答案。在男性开发者中,以这种方式回答问题的人(红点)的SHAP值略高。在其他类型中,效果比较复杂,但似乎具有较低的SHAP值。

解释留给读者,但也许是这样的:在这种意义上感到赋权的男性开发人员也享受着略高的薪水,而其他开发人员则享受着低薪工作?

探索具有巨大性别影响的实例

调查那些薪水受到最大负面影响的开发者怎么样?就像我们可以从整体上看到与性别相关的功能所产生的影响一样,我们也可以搜索那些拥有与性别相关功能的开发者最大对预期工资的影响。这个人是女性,效果是负面的。根据该模型,由于性别原因,她的年收入预计会减少约4260美元:

使用Python SHAP来可视化一个开发人员<br />的预测工资的解释

她的预期工资略高于15.7万美元,在这种情况下是准确的,因为她实际报告的工资是15万美元。

影响预期工资的三个最积极和最消极的特征是:

  • 大学学历(仅限)(+ 18,200美元)
  • 有10年专业经验(+ 9,400美元)
  • 东亚人(+$9,100)
  • ...
  • 每周工作40小时(- 4000美元)
  • 非男性(- 4,250美元)
  • 在100-499人的中型企业工作(- 9,700美元)

考虑到非男性身份对预期薪酬的影响程度,我们可能会在此停止,并在线下调查该案例的细节,以更好地了解该开发人员周围的环境,以及她的经验或薪酬,或两者都需要改变。

使用SHAP值解释交互

有更多的细节可在- 4260美元。SHAP可以将这些特性的效果分解为交互。认定为女性对预测的总影响可以分解为认定为女性的影响而且作为一名工程经理,而且使用Windows等。

性别因素本身对预测工资的影响加起来只有大约- 630美元。相反,SHAP将性别的大部分影响分配给与其他特征的交互作用:

Gender_interactions = interaction_values[gender_feature_locs]。总和(轴=0Max_c = np.argmax(gender_interactions)Min_c = np.argmin(性别交互)打印(X.columns [max_c])打印(gender_interactions [max_c])打印(X.columns [min_c])打印(gender_interactions [min_c])
              DatabaseWorkedWith_PostgreSQL110.64005Ethnicity_East_Asian-1372.6714

认定自己是女性并在PostgreSQL工作对薪资预测有轻微的积极影响,而认定自己是东亚人则对薪资预测有更消极的影响。在这个上下文中,在这种粒度级别上解释这些值是困难的,但是,这种额外级别的解释是可用的。

使用Apache Spark应用SHAP

给定模型,每一行的SHAP值都是独立计算的,所以这也可以与Spark并行完成。下面的示例并行计算SHAP值,并类似地定位与性别相关的SHAP值过大的开发人员:

X_dfpruned_parsed_df.drop(“ConvertedComp”).repartition (16X_columnsX_df.columnsdef add_shap ():rows_pdpd。DataFrame (、列X_columns)shap_valuesexplainer.shap_values (rows_pd。((“被告”),轴下降1))返回([int(rows_pd["被"][我]))+浮动(f)fshap_values[我]]))范围(len (shap_values)))shap_dfX_df.rdd.mapPartitions (add_shap) .toDF (X_columns)effects_dfshap_df。\withColumn(“gender_shap坳(“Gender_Woman”)+坳(“Gender_Man”)+坳(“Gender_Non_binary__genderqueer__or_gender_non_conforming”)+坳\(“反式”))选择(“被”、“gender_shap”)top_effects_dfeffects_df.filter (腹肌(坳(“gender_shap”))> =2500) .orderBy(“gender_shap”)

与性别相关的SHAP值为负的开发人员的示例表,与Spark并行计算。

聚类SHAP值

当有大量的预测需要用SHAP进行评估时,应用Spark是有利的。有了这样的输出,还可以使用Spark将结果与以下内容聚类:角平分线k - means

汇编程序VectorAssembler (inputCols[ccto_review_df。列如果c! =“被申请人”),\outputCol“特性”)assembled_dfassembler.transform (shap_df) .cache ()clusterer运算BisectingKMeans () .setFeaturesCol(“特性”).setK (50) .setMaxIter (50) .setSeed (0cluster_modelclusterer.fit (assembled_df)transformed_dfcluster_model.transform (assembled_df)。选择(“被”,“预测”)

与性别相关的总体SHAP影响最为负面的群集可能需要进一步研究。集群中这些应答者的SHAP值是什么?集群的成员相对于整个开发人员群体是什么样子的?

min_shap_cluster_dftransformed_df。f我lter("prediction = 5").\加入(effects_df,“被申请人”)。\加入(X_df,“被申请人”)。\选择(gender_cols) .groupBy (gender_cols)。() .orderBy (gender_cols)all_shap_dfX_df.select (gender_cols) .groupBy (gender_cols)。() .orderBy (gender_cols)expected_ratiotransformed_df。f我lter("prediction = 5").()/X_df。()显示器(min_shap_cluster_df.join (all_shap_dfgender_cols)。\withColumn(“比率”(min_shap_cluster_df(“计数”)/all_shap_df["数"])/expected_ratio)。\orderBy(“比例”))

示例表显示了在与性别相关的SHAP值最负的集群中性别身份的相对流行率。

例如,在这一群体中,女性开发人员的比例几乎是整体开发人员比例的2.8倍。考虑到前面的分析,这并不令人惊讶。可以进一步调查这一群体,以评估导致整体预期工资较低的其他特定因素。

结论

使用SHAP的这种类型的分析可以用于任何模型,也可以在一定规模上运行。作为一种分析工具,它把模型变成了数据侦探,让个体实例浮出水面,这些实例的预测表明它们值得更多的研究。SHAP的输出很容易解释,并产生直观的图形,业务用户可以逐个评估这些图形。

当然,这种分析并不局限于检查性别、年龄或种族偏见的问题。更简单地说,它可以应用于客户流失模型。在这里,问题不仅仅是“客户会流失吗?”而是“客户为什么流失?”由于价格原因而取消服务的客户可能会得到折扣,而由于使用量有限而取消服务的客户可能需要追加销售。

最后,这种分析可以作为验证过程的一部分运行,为机器学习模型带来更大的透明度。模型验证通常关注模型的整体准确性。它还应该关注模型的“推理”,或者哪些特征对预测贡献最大。使用SHAP,它还可以帮助检测个体数量过多预测的解释与整体特征的重要性不一致。

免费试用Databricks

相关的帖子

看到所有数据科学与机器学习的帖子