Integrate the responsible-ai-toolbox into a PAI Data Science Workshop (DSW) instance to perform fairness analysis on AI models. This example evaluates income prediction bias across gender and race using the FairnessDashboard.
How it works
Fairness analysis identifies and mitigates bias in AI systems, ensuring equitable outcomes for all users. Core principles:
-
Avoid bias: Reduce bias in data and algorithms so that AI systems do not make unfair decisions based on gender, race, or age.
-
Representative data: Train on representative datasets so that the model serves all user groups, including minorities.
-
Transparency and interpretability: Apply explainable AI (XAI) techniques so users and decision-makers understand how the model reaches its outputs.
-
Continuous monitoring and evaluation: Monitor AI systems regularly to detect and correct biases that emerge over time.
-
Diversity and inclusion: Build diverse teams and consider multiple perspectives throughout AI system design and development.
-
Compliance and ethical principles: Follow applicable laws, regulations, and ethical standards to prevent harm or injustice.
The following example uses the responsible-ai-toolbox in PAI DSW to evaluate whether an income-prediction model treats gender and race groups fairly.
Prerequisites
-
A DSW instance with the following configurations. To create one, see Create a DSW instance.
-
Instance type: ecs.gn6v-c8g1.2xlarge (recommended)
-
Runtime image: Python 3.9 or later. This example uses tensorflow-pytorch-develop:2.14-pytorch2.1-gpu-py311-cu118-ubuntu22.04.
-
Model: The responsible-ai-toolbox supports Sklearn, PyTorch, and TensorFlow regression and binary classification models.
-
-
A training dataset. To use the sample dataset, see Step 3: Prepare the dataset.
-
An algorithm model. To use the sample model, see Step 5: Train the model.
Step 1: Open the DSW Gallery
-
Log on to the PAI console.
-
In the upper-left corner, select your region.
-
In the navigation pane on the left, choose QuickStart > Notebook Gallery. Search for Responsible AI-Fairness Analysis and click Open in DSW on the card.
-
Select a DSW instance and click Open Notebook. The system opens the Responsible AI-Fairness Analysis Notebook.
Step 2: Import dependencies
Install the raiwidgets package required by the responsible-ai-toolbox.
!pip install raiwidgets==0.34.1
Step 3: Prepare the dataset
Load the OpenML 1590 dataset for this example.
from raiutils.common.retries import retry_function
from sklearn.datasets import fetch_openml
class FetchOpenml:
def __init__(self):
pass
# Get the OpenML dataset with data_id = 1590.
def fetch(self):
return fetch_openml(data_id=1590, as_frame=True)
fetcher = FetchOpenml()
action_name = "Dataset download"
err_msg = "Failed to download openml dataset"
max_retries = 5
retry_delay = 60
data = retry_function(fetcher.fetch, action_name, err_msg,
max_retries=max_retries,
retry_delay=retry_delay)
To load your own CSV dataset instead:
import pandas as pd
# Load your own dataset in CSV format.
# Use pandas to read the CSV file.
data = pd.read_csv(filename)
Step 4: Preprocess the data
Get the feature and target variables
The target variable is the outcome the model predicts. Feature variables are all other variables per instance. In this example, the target is "class", and relevant features include "sex", "race", and "age":
-
class: Whether annual income is greater than 50K.
-
sex: Gender.
-
race: Race.
-
age: Age.
Read the data into X_raw and preview it:
# Get the feature variables, not including the target variable.
X_raw = data.data
# Show the first 5 rows of the feature variables, not including the target variable.
X_raw.head(5)
Set the target variable. "1" indicates income >50K, and "0" indicates income ≤50K.
from sklearn.preprocessing import LabelEncoder
# Convert the target variable to a binary classification target.
# data.target is the target variable "class".
y_true = (data.target == '>50K') * 1
y_true = LabelEncoder().fit_transform(y_true)
import matplotlib.pyplot as plt
import numpy as np
# View the distribution of the target variable.
counts = np.bincount(y_true)
classes = ['<=50K', '>50K']
plt.bar(classes, counts)
Get the sensitive features
Set gender (sex) and race as sensitive features. The responsible-ai-toolbox evaluates whether the model shows bias across these features. Set your own sensitive features as needed.
# Define "sex" and "race" as sensitive information.
# First, select the columns related to sensitive information from the dataset to form a new DataFrame `sensitive_features`.
sensitive_features = X_raw[['sex','race']]
sensitive_features.head(5)
Remove the sensitive features from X_raw.
# Delete the sensitive features from the feature variables.
X = X_raw.drop(labels=['sex', 'race'],axis = 1)
X.head(5)
Feature encoding and standardization
Standardize the data for the responsible-ai-toolbox.
import pandas as pd
from sklearn.preprocessing import StandardScaler
# One-hot encode.
X = pd.get_dummies(X)
# Standardize (scale) the features for dataset X.
sc = StandardScaler()
X_scaled = sc.fit_transform(X)
X_scaled = pd.DataFrame(X_scaled, columns=X.columns)
X_scaled.head(5)
Split the training and test data
Split 20% of the data into a test set. The rest is used for training.
from sklearn.model_selection import train_test_split
# Split the feature variables X and the target variable y into training and test sets according to the `test_size` ratio.
X_train, X_test, y_train, y_test = \
train_test_split(X_scaled, y_true, test_size=0.2, random_state=0, stratify=y_true)
# Use the same random seed to split the sensitive features into training and test sets to ensure consistency with the split above.
sensitive_features_train, sensitive_features_test = \
train_test_split(sensitive_features, test_size=0.2, random_state=0, stratify=y_true)
View the training and test set sizes.
print("Training dataset volume:", len(X_train))
print("Test dataset volume:", len(X_test))
Reset the indexes for both sets.
# Reset the DataFrame index to prevent index errors.
X_train = X_train.reset_index(drop=True)
sensitive_features_train = sensitive_features_train.reset_index(drop=True)
X_test = X_test.reset_index(drop=True)
sensitive_features_test = sensitive_features_test.reset_index(drop=True)
Step 5: Train the model
Train a logistic regression model using the training data. Tabs show Sklearn, PyTorch, and TensorFlow implementations.
Sklearn
from sklearn.linear_model import LogisticRegression
# Create a logistic regression model.
sk_model = LogisticRegression(solver='liblinear', fit_intercept=True)
# Train the model.
sk_model.fit(X_train, y_train)
PyTorch
import torch
import torch.nn as nn
import torch.optim as optim
# Define the logistic regression model.
class LogisticRegression(nn.Module):
def __init__(self, input_size):
super(LogisticRegression, self).__init__()
self.linear = nn.Linear(input_size, 1)
def forward(self, x):
outputs = torch.sigmoid(self.linear(x))
return outputs
# Instantiate the model.
input_size = X_train.shape[1]
pt_model = LogisticRegression(input_size)
# Loss function and optimizer.
criterion = nn.BCELoss()
optimizer = optim.SGD(pt_model.parameters(), lr=5e-5)
# Train the model.
num_epochs = 1
X_train_pt = X_train
y_train_pt = y_train
for epoch in range(num_epochs):
# Forward propagation.
# Convert DataFrame to Tensor.
if isinstance(X_train_pt, pd.DataFrame):
X_train_pt = torch.tensor(X_train_pt.values)
X_train_pt = X_train_pt.float()
outputs = pt_model(X_train_pt)
outputs = outputs.squeeze()
# Convert ndarray to Tensor.
if isinstance(y_train_pt, np.ndarray):
y_train_pt = torch.from_numpy(y_train_pt)
y_train_pt = y_train_pt.float()
loss = criterion(outputs, y_train_pt)
# Backward propagation and optimization.
optimizer.zero_grad()
loss.backward()
optimizer.step()
TensorFlow
import tensorflow as tf
from tensorflow.keras import layers
# Define the logistic regression model.
tf_model = tf.keras.Sequential([
layers.Dense(units=1, input_shape=(X_train.shape[-1],), activation='sigmoid')
])
# Compile the model. Use binary cross-entropy loss and the stochastic gradient descent optimizer.
tf_model.compile(optimizer='sgd', loss='binary_crossentropy', metrics=['accuracy'])
# Train the model.
tf_model.fit(X_train, y_train, epochs=1, batch_size=32, verbose=0)
Step 6: Evaluate the model
Import the FairnessDashboard to generate a fairness evaluation report from the test dataset.
The FairnessDashboard groups predicted and true results by sensitive variable, then compares prediction performance across groups.
For the "sex" feature, it calculates and compares metrics such as selection rate and precision for "Male" and "Female".
Evaluate the model using one of the following frameworks:
Sklearn
from raiwidgets import FairnessDashboard
import os
from urllib.parse import urlparse
# Generate predictions on the test dataset
y_pred_sk = sk_model.predict(X_test)
# Use the responsible-ai-toolbox to calculate fairness metrics for each sensitive group
metric_frame_sk = FairnessDashboard(sensitive_features=sensitive_features_test,
y_true=y_test,
y_pred=y_pred_sk, locale = 'en')
# Set the URL for redirection
metric_frame_sk.config['baseUrl'] = 'https://{}-proxy-{}.dsw-gateway-{}.data.aliyun.com'.format(
os.environ.get('JUPYTER_NAME').replace("dsw-",""),
urlparse(metric_frame_sk.config['baseUrl']).port,
os.environ.get('dsw_region') )
print(metric_frame_sk.config['baseUrl'])
PyTorch
from raiwidgets import FairnessDashboard
import torch
import os
from urllib.parse import urlparse
# Test the model and evaluate fairness
pt_model.eval() # Set the model to evaluation mode
X_test_pt = X_test
with torch.no_grad():
X_test_pt = torch.tensor(X_test_pt.values)
X_test_pt = X_test_pt.float()
y_pred_pt = pt_model(X_test_pt).numpy()
# Use responsible-ai-toolbox to calculate metrics for each sensitive group
metric_frame_pt = FairnessDashboard(sensitive_features=sensitive_features_test,
y_true=y_test,
y_pred=y_pred_pt.flatten().round(),locale='en')
# Set the URL redirect link
metric_frame_pt.config['baseUrl'] = 'https://{}-proxy-{}.dsw-gateway-{}.data.aliyun.com'.format(
os.environ.get('JUPYTER_NAME').replace("dsw-",""),
urlparse(metric_frame_pt.config['baseUrl']).port,
os.environ.get('dsw_region') )
print(metric_frame_pt.config['baseUrl'])
TensorFlow
from raiwidgets import FairnessDashboard
import os
from urllib.parse import urlparse
# Test the model and evaluate fairness.
y_pred_tf = tf_model.predict(X_test).flatten()
# Use the responsible-ai-toolbox to calculate data information for each sensitive group.
metric_frame_tf = FairnessDashboard(
sensitive_features=sensitive_features_test,
y_true=y_test,
y_pred=y_pred_tf.round(),locale='en')
# Set the URL redirect link.
metric_frame_tf.config['baseUrl'] = 'https://{}-proxy-{}.dsw-gateway-{}.data.aliyun.com'.format(
os.environ.get('JUPYTER_NAME').replace("dsw-",""),
urlparse(metric_frame_tf.config['baseUrl']).port,
os.environ.get('dsw_region') )
print(metric_frame_tf.config['baseUrl'])
Key parameters:
-
sensitive_features: The sensitive features.
-
y_true: The ground truth labels.
-
y_pred: The model's predicted results.
-
locale (optional): Display language of the panel. Supports Simplified Chinese ("zh-Hans") and Traditional Chinese ("zh-Hant"). Default: English ("en").
Step 7: View the evaluation report
After the evaluation completes, click the URL to view the report.

On the Fairness dashboard page, click Get started. Select a sensitive feature, performance metric, and fairness metric to compare results across groups.
When the sensitive feature is "sex"
-
Sensitive feature: sex
-
Performance metric: Accuracy
-
Fairness metric: Demographic parity difference

-
Accuracy: Ratio of correct predictions to total samples. The model's accuracy for males (81.6%) is lower than for females (93.1%), though both are close to the overall accuracy (85.4%).
-
Selection rate: Probability that the model predicts income >50K. Males have a higher selection rate (25.7%) than females (7.36%), which is well below the overall rate (19.6%).
-
Demographic parity difference: Difference in positive prediction probability between protected groups. A value closer to 0 indicates less bias. The overall difference is 18.3%.
When the sensitive feature is "race"
-
Sensitive feature: race
-
Performance metric: Accuracy
-
Fairness metric: Demographic parity difference

-
Accuracy: Ratio of correct predictions to total samples. "White" (84.9%) and "Asian-Pac-Islander" (79.1%) accuracies are lower than "Black" (90.9%), "Other" (90.8%), and "Amer-Indian-Eskimo" (91.9%), but all groups are close to the overall accuracy (85.4%).
-
Selection rate: Probability that the model predicts income >50K. "White" (21%) and "Asian-Pac-Islander" (23.9%) have higher rates than "Black" (8.34%), "Other" (10.3%), and "Amer-Indian-Eskimo" (8.14%), all well below the overall rate (19.6%).
-
Demographic parity difference: Difference in positive prediction probability between protected groups. A value closer to 0 indicates less bias. The overall difference is 15.8%.