Guest User

Untitled

a guest
Dec 2nd, 2024
117
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 22.31 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # coding: utf-8
  3.  
  4. # In[1]:
  5.  
  6.  
  7. # This notebook is adapted from " https://github.com/Trusted-AI/AIF360 "
  8.  
  9. import sys
  10. import warnings
  11.  
  12. # Redirections of warnings to /dev/null (Linux/macOS)
  13. warnings.filterwarnings("ignore")
  14. sys.stderr = open('/dev/null', 'w')
  15.  
  16. # Some library imports
  17. from matplotlib import pyplot as plt
  18. import seaborn as sns
  19.  
  20. import sys
  21. sys.path.append("../")
  22.  
  23. import numpy as np
  24. import random as rd
  25. import pandas as pd
  26. import scipy.stats as stats
  27.  
  28. from sklearn.model_selection import train_test_split
  29. from sklearn.linear_model import LogisticRegression
  30. from sklearn.preprocessing import MinMaxScaler
  31. from sklearn.metrics import f1_score, accuracy_score
  32.  
  33. from aif360.algorithms.postprocessing.reject_option_classification import RejectOptionClassification
  34. from aif360.sklearn.datasets import fetch_german
  35. from aif360.datasets import AdultDataset, BinaryLabelDataset
  36.  
  37. from aif360.metrics import BinaryLabelDatasetMetric
  38.  
  39. from matplotlib import rc
  40. from matplotlib.patches import Patch
  41.  
  42. # Fix randomness and some parameters
  43. seed = 8858 #rd.randint(1000, 10000)
  44.  
  45. np.random.seed(seed)
  46.  
  47. rd.seed(seed)
  48.  
  49. plotting = True
  50.  
  51. sns.set_style(style="ticks")
  52.  
  53. params = {
  54. 'axes.spines.right' : False,
  55. 'axes.spines.top' : False,
  56. 'text.usetex' : True,
  57. 'pdf.fonttype' : 42,
  58. 'legend.fontsize': 'large',
  59. 'figure.figsize': (10, 6),
  60. 'axes.labelsize': 'large',
  61. 'axes.titlesize':'large',
  62. 'xtick.labelsize':'large',
  63. 'ytick.labelsize':'large',
  64. 'font.size': 24}
  65. plt.rcParams.update(params)
  66. plt.rc('lines', linewidth=2.0)
  67.  
  68. scaler = MinMaxScaler(copy=False)
  69.  
  70. repetitions = 50
  71. budget = 1500
  72.  
  73.  
  74. # # General setting
  75. # Load the Adult dataset, generate the name, the proxy and learn the black-box model.
  76.  
  77. # In[2]:
  78.  
  79.  
  80. # Importing the dataset "AdultDataset" from: https://archive.ics.uci.edu/ml/datasets/adult.
  81. # As in the example, we keep the following features: 'age', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week'
  82. # The protected variable is: 'sex'
  83. # The variable to predict is: 'income-per-year'
  84.  
  85. ad = AdultDataset(protected_attribute_names=['sex'],
  86. privileged_classes=[['Male']], categorical_features=[],
  87. features_to_keep=['age', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week'])
  88. label_name = ad.label_names[0]
  89.  
  90. # See appendix B, table II in Barry III and Harper (2000)
  91. female_names = ['Ashley', 'Jessica', 'Amanda', 'Brittany', 'Samantha', 'Sarah', 'Lauren', 'Nicole', 'Megan', 'Stephanie', 'Emily', 'Jennifer', 'Elizabeth', 'Kayla', 'Rachel', 'Amber', 'Rebecca', 'Danielle', 'Chelsea', 'Alyssa', 'Melissa', 'Heather', 'Kelly', 'Christina', 'Michelle']
  92. male_names = ['Michael', 'Matthew', 'Christopher', 'Joshua', 'Andrew', 'Joseph', 'John', 'Daniel', 'David', 'Robert', 'James', 'Justin', 'Nicholas', 'Anthony', 'William', 'Kyle', 'Zachary', 'Kevin', 'Tyler', 'Thomas', 'Eric', 'Brian', 'Brandon', 'Jonathan', 'Timothy']
  93.  
  94. def propose_name(s):
  95. if s == 1:
  96. return rd.choice(male_names)
  97. else:
  98. return rd.choice(female_names)
  99.  
  100. def proxy_sex_3letters(name):
  101. '''
  102. Predict the sex (1: Male, 0: Male) depending on first names.
  103. p(g^i(n) = Female)=
  104. 1 if the name ends by a, e or i
  105. 0.5 if the name ends by h or y
  106. 0 otherwise.
  107. '''
  108. l = name[-1]
  109. if l in ['a', 'e', 'i']:
  110. return 0
  111. elif l in ['y', 'h']:
  112. return rd.choices([0, 1], [0.5, 0.5], k=1)[0]
  113. else:
  114. return 1
  115.  
  116. # Increase of the dataset with the following two features: 'name' and 'proxy' where 'proxy' is the reconstructed "sex" using the intermediate proxy.
  117. df = ad.convert_to_dataframe()[0]
  118. df['name'] = df['sex'].map(propose_name)
  119.  
  120. del df['sex']
  121.  
  122. # some characteristics of the dataset, used for "poor proxy"
  123. # Age: 25-86 ans (Int)
  124. # education-num: 1-16 (Int)
  125. # capital-gain: 0-99999 (Int)
  126. # capital-loss: 0-4356 (Int)
  127. # hours-per-week: 1-99 (Int)
  128. # len(audit_df)
  129.  
  130. # Side of the platform: split the dataset, learn the linear regression etc
  131. train_and_test_df, audit_df = train_test_split(df, test_size=0.05, random_state=42)
  132. nbr_audit = len(audit_df)
  133.  
  134. protected = 'proxy'
  135. p = [{protected: 1}]
  136. u = [{protected: 0}]
  137.  
  138.  
  139. ori_D = train_and_test_df.copy()
  140. ori_D['proxy'] = ori_D['name'].map(proxy_sex_3letters)
  141.  
  142. hat_D = audit_df.copy()
  143. hat_D = hat_D[~hat_D['name'].str.endswith(('y', 'h'))]
  144. hat_D['proxy'] = hat_D['name'].map(proxy_sex_3letters)
  145.  
  146. del ori_D['name']
  147. del hat_D['name']
  148.  
  149. ### The complete dataset
  150. # Using https://towardsdatascience.com/mitigating-bias-in-ai-with-aif360-b4305d1f88a9, loading the data
  151. P_d = BinaryLabelDataset(
  152. favorable_label=1,
  153. unfavorable_label=0,
  154. df=ori_D,
  155. label_names = ['income-per-year'],
  156. protected_attribute_names=[protected])
  157.  
  158. # Split the data
  159. P_test, P_train = P_d.split([16281])
  160. P_train.features = scaler.fit_transform(P_train.features)
  161. P_test.features = scaler.fit_transform(P_test.features)
  162.  
  163. print("The dataset is split as follow:")
  164. print("Train set:", len(P_train.features), "Test set:", len(P_test.features), "Audit set:", nbr_audit)
  165.  
  166. index_P = P_train.feature_names.index(protected)
  167.  
  168. # Train the model to predict 'income-per-year'
  169. lmod = LogisticRegression(class_weight='balanced', solver='liblinear')
  170. lmod.fit(P_train.features, P_train.labels.ravel())
  171.  
  172.  
  173. # Test it
  174. P_test_repd_pred = P_test.copy()
  175. P_test_repd_pred.labels = lmod.predict(P_test_repd_pred.features).reshape(-1, 1)
  176.  
  177. p_A = accuracy_score(P_test.labels, P_test_repd_pred.labels)
  178. print("Accuracy of the model", p_A)
  179.  
  180. P_di = BinaryLabelDatasetMetric(P_test_repd_pred, privileged_groups=p, unprivileged_groups=u).disparate_impact()
  181.  
  182. ### On the hat_D with perfect proxy
  183. hat_P_d = BinaryLabelDataset(
  184. favorable_label=1,
  185. unfavorable_label=0,
  186. df=hat_D,
  187. label_names = ['income-per-year'],
  188. protected_attribute_names=[protected])
  189.  
  190. hat_P_d.features = scaler.fit_transform(hat_P_d.features)
  191.  
  192. hat_P_d.labels = lmod.predict(hat_P_d.features).reshape(-1, 1)
  193.  
  194. hat_P_di = BinaryLabelDatasetMetric(hat_P_d, privileged_groups=p, unprivileged_groups=u).disparate_impact()
  195.  
  196. print("Disparate Impact before the production", P_di, hat_P_di)
  197. new_budget = len(hat_D)
  198.  
  199.  
  200. # # Fairwashing method
  201.  
  202. # In[3]:
  203.  
  204.  
  205. def ROC_mitigation(lmod, test_set, theta, index_protected):
  206. '''
  207. Implementation of the Reject Option based Classification method (Kamiran et al., (2012)).
  208. '''
  209. test_p = lmod.predict_proba(test_set)
  210. y_pred = lmod.predict(test_set)
  211. max_values = np.maximum(test_p[:, 0], 1 - test_p[:, 0])
  212. critical_region = np.where(max_values <= theta)[0]
  213. for i in critical_region:
  214. if not test_set[i][index_protected]:
  215. y_pred[i] = 1
  216. else:
  217. y_pred[i] = 0
  218. return y_pred, 100*len(critical_region)/len(test_set)
  219.  
  220. def ROC_mitigation_by_prop_lies(lmod, test_set, prop, index_protected):
  221. test_p = lmod.predict_proba(test_set)
  222. y_pred = lmod.predict(test_set)
  223. max_values = np.maximum(test_p[:, 0], 1 - test_p[:, 0])
  224. critical_region = sorted(range(len(max_values)), key=lambda k: max_values[k])[:int(prop*len(test_set))]
  225. for i in critical_region:
  226. if not test_set[i][index_protected]:
  227. y_pred[i] = 1
  228. else:
  229. y_pred[i] = 0
  230. return y_pred.reshape(-1, 1), 100*len(critical_region)/len(test_set)
  231.  
  232. def ROC_until_fair(lmod, audit_set, index_protected, first_theta = 0, theta_pas = 0.01):
  233. theta = first_theta
  234. current_answers = audit_set.copy()
  235. current_answers.labels = lmod.predict(current_answers.features).reshape(-1, 1)
  236. cm = BinaryLabelDatasetMetric(current_answers, privileged_groups=p, unprivileged_groups=u).disparate_impact()
  237. fair = (cm >= 0.8)
  238. if fair:
  239. return current_answers, theta, True
  240. while ((theta <= 1) and (not fair)):
  241. theta += theta_pas
  242. current_answers.labels, fairwash_rate = ROC_mitigation_by_prop_lies(lmod, current_answers.features, theta, index_protected)
  243. cm = BinaryLabelDatasetMetric(current_answers, privileged_groups=p, unprivileged_groups=u).disparate_impact()
  244. fair = (cm >= 0.8)
  245. return current_answers, theta, False
  246.  
  247.  
  248.  
  249. # # Definition of the three proxies
  250.  
  251. # In[4]:
  252.  
  253.  
  254. def poor_df(budget = budget):
  255. '''
  256. Implementation of \psi^{\emptyset}, the poor proxy
  257. '''
  258. age = np.random.randint(25, 87, size=budget)
  259. education_num = np.random.randint(1, 17, size=budget)
  260. capital_gain = np.random.randint(0, 100000, size=budget)
  261. capital_loss = np.random.randint(0, 4357, size=budget)
  262. hours_per_week = np.random.randint(1, 100, size=budget)
  263. proxy = np.random.randint(0, 2, size = budget)
  264. income_per_year = np.zeros(budget)
  265.  
  266. data = {
  267. 'age': age,
  268. 'education-num': education_num,
  269. 'capital-gain': capital_gain,
  270. 'capital-loss': capital_loss,
  271. 'hours-per-week': hours_per_week,
  272. 'proxy' : proxy,
  273. 'income-per-year':income_per_year
  274. }
  275. R_df = pd.DataFrame(data)
  276. R_d = BinaryLabelDataset(
  277. favorable_label=1,
  278. unfavorable_label=0,
  279. df=R_df,
  280. label_names = ['income-per-year'],
  281. protected_attribute_names=[protected])
  282. R_d.features = scaler.fit_transform(R_d.features)
  283. return R_d
  284.  
  285. def intermediate_df(budget = budget):
  286. '''
  287. Implementation of \psi^i, the intermediate proxy
  288. '''
  289. R_df = audit_df.copy()
  290. R_df['proxy'] = R_df['name'].map(proxy_sex_3letters)
  291. del R_df['name']
  292. # assert budget <= len(audit_df)
  293. R_df = R_df.sample(budget, random_state = seed)
  294.  
  295. R_d = BinaryLabelDataset(
  296. favorable_label=1,
  297. unfavorable_label=0,
  298. df=R_df,
  299. label_names = ['income-per-year'],
  300. protected_attribute_names=[protected])
  301. R_d.features = scaler.fit_transform(R_d.features)
  302.  
  303. return R_d
  304.  
  305. def perfect_df(budget = budget):
  306. '''
  307. Implementation of \psi^p, the perfect proxy.
  308. It corresponds to the intermediate proxy on names not ending by 'y' or 'h'.
  309. '''
  310. def select_determinist_names(name):
  311. l = name[-1]
  312. return l not in ['y', 'h']
  313.  
  314. R_df = audit_df.copy()
  315. R_df = R_df[~R_df['name'].str.endswith(('y', 'h'))]
  316.  
  317. R_df['proxy'] = R_df['name'].map(proxy_sex_3letters)
  318. del R_df['name']
  319. R_df = R_df.sample(budget)
  320. R_d = BinaryLabelDataset(
  321. favorable_label=1,
  322. unfavorable_label=0,
  323. df=R_df,
  324. label_names = ['income-per-year'],
  325. protected_attribute_names=[protected])
  326. R_d.features = scaler.fit_transform(R_d.features)
  327. return R_d
  328.  
  329.  
  330. # In[5]:
  331.  
  332.  
  333. thresholds= np.arange(0, 0.30, 0.001)
  334. res = []
  335. for _ in range(repetitions):
  336. for sample_func in [poor_df, intermediate_df, perfect_df]:
  337. R_d = sample_func(budget)
  338. R_d.labels = lmod.predict(R_d.features).reshape(-1, 1)
  339. cm = BinaryLabelDatasetMetric(R_d, privileged_groups=p, unprivileged_groups=u).disparate_impact()
  340. index_protected = R_d.feature_names.index(protected)
  341. fairwash_rate = 0
  342. res.append([sample_func.__name__, None, cm, fairwash_rate])
  343. for th in thresholds:
  344. R_d.labels, fairwash_rate = ROC_mitigation_by_prop_lies(lmod, R_d.features, th, index_protected)
  345. cm = BinaryLabelDatasetMetric(R_d, privileged_groups=p, unprivileged_groups=u).disparate_impact()
  346. res.append([sample_func.__name__, th, cm, np.round(th,3)*100])
  347. df = pd.DataFrame(res, columns = ['Proxy', 'th', 'Disparate Impact', 'Fairwashing rate'])
  348.  
  349. poor_di = df[df['Proxy'] == 'poor_df']
  350. intermediate_di = df[df['Proxy'] == 'intermediate_df']
  351. perfect_di = df[df['Proxy'] == 'perfect_df']
  352.  
  353. res_by_proxies ={
  354. "poor": poor_di,
  355. "intermediate": intermediate_di,
  356. "perfect": perfect_di
  357. }
  358.  
  359. intermediate_di
  360.  
  361.  
  362. # # Section 4.1, Table 1
  363.  
  364. # In[6]:
  365.  
  366.  
  367. print("\n on D:")
  368. print("The original disparate impact is {}".format(np.round(P_di,2)))
  369.  
  370. intermediate_DI = np.mean(intermediate_di[intermediate_di['th'] == 0]['Disparate Impact'])
  371. print("The measured disparate impact is {} (with the intermediate proxy)".format(np.round(intermediate_DI,2)))
  372.  
  373. poor_DI = np.mean(poor_di[poor_di['th'] == 0]['Disparate Impact'])
  374. print("The measured disparate impact is {} (with the poor proxy)".format(np.round(poor_DI,2)))
  375.  
  376. print("\n on hat_D:")
  377. print("The original disparate impact is {}".format(np.round(hat_P_di,2)))
  378.  
  379. perfect_DI = np.mean(perfect_di[perfect_di['th'] == 0]['Disparate Impact'])
  380. print("The measured disparate impact is {} (with the perfect proxy)".format(np.round(perfect_DI,2)), flush = True)
  381.  
  382.  
  383.  
  384. # # Section 4.4, Figure 4
  385.  
  386. # In[7]:
  387.  
  388.  
  389. for name, res_di in res_by_proxies.items():
  390.  
  391. grouped = res_di.groupby('Fairwashing rate')['Disparate Impact'].agg(['mean', 'std']).reset_index()
  392. gamma_min = grouped[grouped['mean'] >= 0.8].iloc[0]['Fairwashing rate']
  393.  
  394. print(f"Gamma min for {name} is {gamma_min}")
  395.  
  396. left, bottom, width, height = (0, 0.8, max(grouped['Fairwashing rate']), 0.2)
  397. rect = plt.Rectangle((left, bottom), width, height,
  398. facecolor='black', alpha=0.1)
  399. plt.gca().add_patch(rect)
  400.  
  401. real_value = res_di[res_di['th'] == 0]['Disparate Impact'].mean()
  402. real_std = res_di[res_di['th'] == 0]['Disparate Impact'].std()
  403.  
  404. plt.axhline(real_value, label = r'$\hat{\mu}_\mathbf{B}$', color=sns.color_palette("magma", 6)[3]) #color=sns.color_palette("hls", 3)[0])
  405.  
  406. plt.axvline(gamma_min,
  407. color=sns.color_palette("magma", 6)[2],
  408. linestyle='dashed')
  409.  
  410.  
  411. plt.fill_between(
  412. grouped['Fairwashing rate'],
  413. real_value - real_std,
  414. real_value + real_std,
  415. color=sns.color_palette("magma", 6)[3],
  416. alpha=0.2
  417. )
  418.  
  419. plt.plot(grouped['Fairwashing rate'], grouped['mean'], label=r'$\hat{\mu}_\mathbf{A}$', color=sns.color_palette("magma", 6)[0]) #'#9c179e')
  420.  
  421. plt.fill_between(
  422. grouped['Fairwashing rate'],
  423. grouped['mean'] - grouped['std'],
  424. grouped['mean'] + grouped['std'],
  425. color=sns.color_palette("magma", 6)[0],
  426. alpha=0.2
  427. )
  428.  
  429. plt.ylim(bottom = 0.4, top = 1)
  430. plt.xlim(left = -0.3, right = 27)
  431. plt.ylabel("Disparate Impact: "+r'$\mu$')
  432. plt.xlabel("Percentage of manipulated API's answers: "+r'$\gamma$')
  433. legend = plt.legend(loc='upper right')
  434. handles, labels = plt.gca().get_legend_handles_labels()
  435. handles.append(Patch(facecolor='black', edgecolor='black', alpha = 0.2))
  436. labels.append(r'$Z = 1$')
  437. handles.append(Patch(facecolor='white', edgecolor='black'))
  438. labels.append(r'$Z = 0$')
  439.  
  440. legend._legend_box = None
  441. legend._init_legend_box(handles, labels)
  442. legend._set_loc(legend._loc)
  443. legend.set_title(legend.get_title().get_text())
  444. plt.savefig("fig_lies_"+name+".pdf", bbox_inches='tight')
  445. plt.show()
  446.  
  447.  
  448. # In[ ]:
  449.  
  450.  
  451. import numpy as np
  452. from aif360.metrics import BinaryLabelDatasetMetric
  453.  
  454. def calculate_disparate_impact_confidence_interval(hat_P_test, privileged_groups, unprivileged_groups):
  455. metric = BinaryLabelDatasetMetric(hat_P_test,
  456. privileged_groups=privileged_groups,
  457. unprivileged_groups=unprivileged_groups)
  458.  
  459. p_privileged = metric.base_rate(privileged=True)
  460. p_unprivileged = metric.base_rate(privileged=False)
  461.  
  462. disparate_impact = metric.disparate_impact()
  463.  
  464. def get_group_mask(dataset, group_conditions):
  465. mask = np.ones(len(dataset.labels), dtype=bool)
  466. for attr, value in group_conditions.items():
  467. attr_column = dataset.features[:, dataset.feature_names.index(attr)]
  468. mask &= (attr_column == value)
  469. return mask
  470.  
  471. privileged_mask = get_group_mask(hat_P_test, privileged_groups[0])
  472. unprivileged_mask = get_group_mask(hat_P_test, unprivileged_groups[0])
  473.  
  474. n_privileged = np.sum(privileged_mask)
  475. n_unprivileged = np.sum(unprivileged_mask)
  476.  
  477. std_privileged = np.sqrt(p_privileged * (1 - p_privileged) / n_privileged)
  478. std_unprivileged = np.sqrt(p_unprivileged * (1 - p_unprivileged) / n_unprivileged)
  479.  
  480. std_deviation = disparate_impact * np.sqrt((std_privileged / p_privileged)**2 +
  481. (std_unprivileged / p_unprivileged)**2)
  482.  
  483. n_total = len(hat_P_test.labels)
  484.  
  485. standard_error = std_deviation / np.sqrt(n_total)
  486.  
  487. z_score = 1.96
  488. margin_of_error = z_score * standard_error
  489.  
  490. return disparate_impact, margin_of_error
  491.  
  492.  
  493. # In[9]:
  494.  
  495.  
  496. def double_poor_df(budget = budget):
  497. R_d1 = poor_df(budget)
  498. R_d2 = poor_df(budget)
  499. return R_d1, R_d2
  500.  
  501. def double_intermediate_df(budget = budget):
  502. '''
  503. Implementation of \psi^i, the intermediate proxy
  504. '''
  505. R_df1 = audit_df.sample(budget)
  506. R_df2 = R_df1.copy()
  507. R_df1['proxy'] = R_df1['name'].map(proxy_sex_3letters)
  508. del R_df1['name']
  509.  
  510. R_df2['proxy'] = R_df2['name'].map(proxy_sex_3letters)
  511. del R_df2['name']
  512.  
  513. R_d1 = BinaryLabelDataset(
  514. favorable_label=1,
  515. unfavorable_label=0,
  516. df=R_df1,
  517. label_names = ['income-per-year'],
  518. protected_attribute_names=[protected])
  519. R_d1.features = scaler.fit_transform(R_d1.features)
  520.  
  521. R_d2 = BinaryLabelDataset(
  522. favorable_label=1,
  523. unfavorable_label=0,
  524. df=R_df2,
  525. label_names = ['income-per-year'],
  526. protected_attribute_names=[protected])
  527. R_d2.features = scaler.fit_transform(R_d2.features)
  528. return R_d1, R_d2
  529.  
  530. def accuracy_intermediaire(budget):
  531. total_acc = 0
  532. for _ in range(budget):
  533. R_d1, R_d2 = double_intermediate_df(1)
  534. R_d1.labels = lmod.predict(R_d1.features).reshape(-1, 1)
  535. R_d2.labels = lmod.predict(R_d2.features).reshape(-1, 1)
  536.  
  537. total_acc += len(R_d1.convert_to_dataframe()[0].compare(R_d2.convert_to_dataframe()[0]))/budget
  538. return 1-total_acc
  539.  
  540. def double_perfect_df(budget = budget):
  541. R_d = perfect_df(budget)
  542. return R_d, R_d
  543.  
  544.  
  545. acc_by_proxies = {
  546. "poor": 1/104537,
  547. 'intermediate': 0.91, # accuracy_intermediaire(10000)
  548. "perfect": 1
  549. }
  550.  
  551.  
  552. # In[ ]:
  553.  
  554.  
  555. t_A = 1500
  556. repetitions = 50
  557. beta_range = np.arange(0.01, 1.01, 0.01)
  558. def exploitation(t, acc, res_du, beta, alpha = 0.95, ratio = 1):
  559. tfair = int(t*beta + (1-beta)*t/(1+ratio))
  560. A_queries, _ = res_du(tfair)
  561.  
  562. ## Answers on A
  563. A_queries.labels = lmod.predict(A_queries.features).reshape(-1, 1)
  564. A_audit, _, _ = ROC_until_fair(lmod, A_queries, index_protected)
  565. disparate_impact, margin_of_error = calculate_disparate_impact_confidence_interval(A_audit, p, u)
  566.  
  567. ## Answers on B + proxy
  568. tfraud = max(int((1-beta)*t/(1+ratio)), 1)
  569. A_queries, B_queries = res_du(tfraud)
  570. B_queries.labels = lmod.predict(B_queries.features).reshape(-1, 1)
  571. A_queries.labels = lmod.predict(A_queries.features).reshape(-1, 1)
  572. A_audit, gamma, fair = ROC_until_fair(lmod, A_queries, index_protected)
  573.  
  574. acc_gamma = gamma * acc + (1 - gamma)*(1 - acc)
  575. n_max = stats.binom.ppf(0.95, tfraud, 1-acc)
  576. flag_proba = 1 - stats.binom.cdf(n_max, tfraud, acc_gamma)
  577. return margin_of_error, flag_proba, fair
  578.  
  579. name = "intermediate"
  580. res_du = double_intermediate_df
  581. acc = acc_by_proxies[name]
  582. res = []
  583. tot_lies = 0
  584. for _ in range(repetitions):
  585. for beta in beta_range:
  586. error, detected, fair = exploitation(t_A, acc, res_du, beta)
  587. res.append([beta, error, detected])
  588.  
  589.  
  590. # In[11]:
  591.  
  592.  
  593. def find_last_exceeding_threshold(data, threshold=0.995):
  594. for i in range(len(data)-1, -1, -1):
  595. if data[i][0] >= threshold:
  596. return i, data[i]
  597. return None
  598.  
  599. def aux_pareto(Xs, Ys, Betas, maxX=True, maxY=True):
  600. '''Pareto frontier selection process'''
  601. sorted_list = sorted([[Xs[i], Ys[i], Betas[i]] for i in range(len(Xs))], reverse=maxY)
  602.  
  603. pareto_front = [sorted_list[0]]
  604. for pair in sorted_list[1:]:
  605. if maxY:
  606. if pair[1] >= pareto_front[-1][1]:
  607. pareto_front.append(pair)
  608. else:
  609. if pair[1] <= pareto_front[-1][1]:
  610. pareto_front.append(pair)
  611. return pareto_front
  612.  
  613. def aux_pareto_and_last_fig(res, scatter_all = True):
  614. res_df = pd.DataFrame(res, columns=['beta', 'margin of error', 'proba detectability'])
  615. grouped = res_df.groupby('beta')['proba detectability', 'margin of error'].agg(['mean']).reset_index()
  616.  
  617. if scatter_all:
  618. plt.scatter(1-grouped['margin of error'],grouped['proba detectability'], c=sns.color_palette("magma", 6)[4], alpha=.5)
  619.  
  620.  
  621. Xs = (1-grouped['margin of error']['mean']).to_list()
  622. Ys = grouped['proba detectability']['mean'].to_list()
  623. Betas = grouped['beta'].to_list()
  624.  
  625. pareto_front = aux_pareto(Xs, Ys, Betas)
  626. i, [x, y, b]=find_last_exceeding_threshold(pareto_front)
  627. return i, x, y, b, pareto_front
  628.  
  629. i, x, y, b, pareto_front = aux_pareto_and_last_fig(res)
  630. pf_X = [pair[0] for pair in pareto_front]
  631. pf_Y = [pair[1] for pair in pareto_front]
  632. plt.plot(pf_X, pf_Y,
  633. c = sns.color_palette("magma", 6)[1])
  634. print(i, [x, y, b])
  635. plt.scatter(x,y, c=sns.color_palette("magma", 6)[1], alpha=1)
  636.  
  637. plt.text(0.995, 0.4+0.15, r'$\beta$ = '+str(np.round(b, 2)), ha='center', va='center', color=sns.color_palette("magma", 6)[1])
  638. plt.text(0.995, 0.4+0.1, r'$\epsilon$ = '+str(np.round(1-x, 3)), ha='center', va='center', color=sns.color_palette("magma", 6)[1])
  639. plt.text(0.995, 0.4+0.05, r'$p(\bar{\phi})$ = '+str(np.round(y, 2)), ha='center', va='center', color=sns.color_palette("magma", 6)[1])
  640. plt.annotate('', xy=(x, y), xytext=(0.995, 0.6),
  641. arrowprops=dict(arrowstyle='->', color=sns.color_palette("magma", 6)[1],# lw=2
  642. ))
  643.  
  644. plt.xlabel("Accuracy : "+r'$1-\epsilon$')
  645. plt.ylabel("Manipulation's probability: "+r'$p_{manip}$')
  646. plt.savefig("pareto43.pdf", bbox_inches='tight')
  647.  
  648.  
Advertisement
Add Comment
Please, Sign In to add comment