Skip to content

Commit de9ffd8

Browse files
committed
Add new files and update existing files
1 parent 10f7b26 commit de9ffd8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+723
-0
lines changed

antora-playbook.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
site:
2+
title: TrustyAI
3+
start_page: trustyai-site::main.adoc
4+
content:
5+
sources:
6+
- url: https://gitlab.com/antora/demo/demo-component-a.git
7+
branches: HEAD
8+
- url: .
9+
start_path: docs
10+
ui:
11+
bundle:
12+
url: ./ui-bundle
13+
output:
14+
clean: true

docs/antora.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: trustyai-site
2+
title: TrustyAI
3+
version: 1.5.6
4+
start_page: main.adoc
5+
asciidoc:
6+
attributes:
7+
source-language: asciidoc@
8+
table-caption: false
9+
nav:
10+
- modules/ROOT/nav.adoc
Loading

docs/modules/ROOT/nav.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
* xref:main.adoc[]
2+
* Tutorials
3+
** xref:bias-monitoring.adoc[]
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
= Bias Monitoring via TrustyAI in ODH
2+
3+
Ensuring that your models are fair and unbiased is a crucial part of establishing trust in your models amongst
4+
your users. While fairness can be explored during model training, it is only during deployment
5+
that your models have exposure to the outside world. It does not matter if your models are unbiased on the training data,
6+
if they are dangerously biased over real-world data, and therefore it is absolutely crucial to monitor your models for
7+
fairness during real-world deployments:
8+
9+
This demo will explore how to use TrustyAI to monitor models for bias, and how not all model biases are visible at training time.
10+
11+
== Context
12+
13+
We will take on the persona of a dev-ops engineer for a credit lender.
14+
Our data scientists have created two candidate neural networks to predict if a borrower will default on the loan they
15+
hold with us. Both models use the following information about the applicant to make their prediction:
16+
17+
* Number of Children
18+
* Total Income:
19+
* Number of Total Family Members
20+
* Is Male-Identifying?
21+
* Owns Car?
22+
* Owns Realty?
23+
* Is Partnered?
24+
* Is Employed?
25+
* Lives with Parents?
26+
* Age (in days)
27+
* Length of Employment (in days)
28+
29+
What we want to verify is that neither of our models are not biased over the gender field of `Is Male-Identifying?`.
30+
To do this, we will monitor our models with link:Statistical-Parity-Difference.md[Statistical Parity Difference] (SPD)
31+
metric, which will tell us how the difference between how often male-identifying and non-male-identifying applicants are
32+
given favorable predictions (_i.e._, they are predicted to pay back their loans).
33+
Ideally, the SPD value would be 0, indicating that both groups have equal likelihood of getting a good outcome.
34+
However, an SPD value between -0.1 and 0.1 is also indicative of reasonable fairness,
35+
indicating that the two groups' rates of getting good outcomes only varies by +/-10%.
36+
37+
== Setup
38+
39+
Follow the instructions within the link:Install-on-Open-Data-Hub.md[installation section].
40+
Afterwards, you should have an ODH installation, a link:TrustyAI-operator.md[TrustyAI operator], and a `model-namespace` project containing
41+
an instance of the link:TrustyAI-service.md[TrustyAI service].
42+
43+
== Deploy Models
44+
45+
. Navigate to the `model-namespace` created in the setup section: `oc project model-namespace`
46+
. Deploy the model's storage container: `oc apply -f resources/model_storage_container`
47+
. Deploy the OVMS 1.x serving runtime: `oc apply -f resources/ovms-1.x.yaml`
48+
. Deploy the first model: `oc apply -f resources/model_alpha.yaml`
49+
. Deploy the second model: `oc apply -f resources/model_beta.yaml`
50+
. From the OpenShift Console, navigate to the `model-namespace` project and look at the Workloads -> Pods screen.
51+
.. You should see four pods
52+
+
53+
image::model_namespace_pods.png[]
54+
+
55+
.. Once the TrustyAI Service registers the deployed models, you will see the `modelmesh-serving-ovms-1.x-xxxxx` pods get re-deployed.
56+
.. Verify that the models are registered with TrustyAI by selecting one of the `modelmesh-serving-ovms-1.x-xxxxx` pods.
57+
.. In the Environment tab, if the field `MM_PAYLOAD_PROCESSORS` is set, then your models are successfully registered with TrustyAI: image::model_environment.png[]
58+
59+
== Send Training Data to Models
60+
61+
Here, we'll pass all the training data through the models, such as to be able to compute baseline fairness values:
62+
63+
[source,shell]
64+
----
65+
for batch in 0 250 500 750 1000 1250 1500 1750 2000 2250; do
66+
scripts/send_data_batch data/training/$batch.json
67+
done
68+
----
69+
70+
This will take a few minutes. The script will print out verification messages indicating whether TrustyAI is receiving the data, but we can also verify in the Cluster Metrics:
71+
72+
. Navigate to Observe -> Metrics in the OpenShift console.
73+
. Set the time window to 5 minutes (top left) and the refresh interval to 15 seconds (top right)
74+
. In the "Expression" field, enter `trustyai_model_observations_total` and hit "Run Queries".
75+
You should see both models listed, each reporting around 2250 observed inferences: image::observed_inferences.png[].
76+
This means that TrustyAI has catalogued 2250 inputs and outputs for each of the two models, more than enough to begin analysis.
77+
78+
== Examining TrustyAI's Model Metadata
79+
80+
We can also verify that TrustyAI sees the models via the `/info` endpoint:
81+
82+
. Find the route to the TrustyAI Service: `TRUSTY_ROUTE=https://$(oc get route/trustyai-service --template={{.spec.host}})`
83+
. Query the `/info` endpoint: `curl $TRUSTY_ROUTE/info | jq ".[0].data"`. This will output a json file (link:resources/info_response.json[sample provided here]) containing the following information for each model:
84+
.. The names, data types, and positions of fields in the input and output
85+
.. The observed values that these fields take
86+
.. The total number of input-output pairs observed
87+
88+
== Label Data Fields
89+
90+
As you can see, our models have not provided particularly useful field names for our inputs and outputs (all some form of `customer_data+input-x`). We can apply a set of _name mappings_ to these to apply meaningful names to the fields. This is done via POST'ing the `/info/names` endpoint:
91+
92+
----
93+
./scripts/apply_name_mapping.sh
94+
----
95+
96+
Explore the link:scripts/apply_name_mapping.sh[apply_name_mapping.sh] script to understand how the payload is structured.
97+
98+
== Check Model Fairness
99+
100+
To compute the model's cumulative fairness up to this point, we can check the `/metrics/group/fairness/spd` endpoint:
101+
102+
[source,shell]
103+
----
104+
echo "=== MODEL ALPHA ==="
105+
curl -sk -X POST --location $TRUSTY_ROUTE/metrics/group/fairness/spd/ \
106+
--header 'Content-Type: application/json' \
107+
--data "{
108+
\"modelId\": \"demo-loan-nn-onnx-alpha\",
109+
\"protectedAttribute\": \"Is Male-Identifying?\",
110+
\"privilegedAttribute\": 1.0,
111+
\"unprivilegedAttribute\": 0.0,
112+
\"outcomeName\": \"Will Default?\",
113+
\"favorableOutcome\": 0,
114+
\"batchSize\": 5000
115+
}"
116+
117+
echo "\n=== MODEL BETA ==="
118+
curl -sk -X POST --location $TRUSTY_ROUTE/metrics/group/fairness/spd \
119+
--header 'Content-Type: application/json' \
120+
--data "{
121+
\"modelId\": \"demo-loan-nn-onnx-beta\",
122+
\"protectedAttribute\": \"Is Male-Identifying?\",
123+
\"privilegedAttribute\": 1.0,
124+
\"unprivilegedAttribute\": 0.0,
125+
\"outcomeName\": \"Will Default?\",
126+
\"favorableOutcome\": 0,
127+
\"batchSize\": 5000
128+
}"
129+
----
130+
The payload is structured as follows:
131+
132+
* `modelId`: The name of the model to query
133+
* `protectedAttribute`: The name of the feature that distinguishes the groups that we are checking for fairness over.
134+
* `privilegedAttribute`: The value of the `protectedAttribute` that describes the suspected favored (positively biased) class.
135+
* `unprivilegedAttribute`: The value of the `protectedAttribute` that describes the suspected unfavored (negatively biased) class.
136+
* `outcomeName`: The name of the output that provides the output we are examining for fairness.
137+
* `favorableOutcome`: The value of the `outcomeName` output that describes the favorable or desired model prediction.
138+
* `batchSize`: The number of previous inferences to include in the calculation.
139+
140+
These requests will return the following messages:
141+
142+
=== Model Alpha
143+
144+
[source,json]
145+
----
146+
{
147+
"timestamp":"2023-10-24T12:06:04.586+00:00",
148+
"type":"metric",
149+
"value":-0.0029676404469311524,
150+
"namedValues":null,
151+
"specificDefinition":"The SPD of -0.002968 indicates that the likelihood of Group:Is Male-Identifying?=1.0 receiving Outcome:Will Default?=0 was -0.296764 percentage points lower than that of Group:Is Male-Identifying?=0.0.",
152+
"name":"SPD",
153+
"id":"d2707d5b-cae9-41aa-bcd3-d950176cbbaf",
154+
"thresholds":{"lowerBound":-0.1,"upperBound":0.1,"outsideBounds":false}
155+
}
156+
----
157+
158+
=== Model Beta
159+
160+
[source,json]
161+
----
162+
{
163+
"timestamp":"2023-10-24T12:06:04.930+00:00",
164+
"type":"metric",
165+
"value":0.027796371582978097,
166+
"namedValues":null,
167+
"specificDefinition":"The SPD of 0.027796 indicates that the likelihood of Group:Is Male-Identifying?=1.0 receiving Outcome:Will Default?=0 was 2.779637 percentage points higher than that of Group:Is Male-Identifying?=0.0.",
168+
"name":"SPD",
169+
"id":"21252b73-651b-4b09-b3af-ddc0be0352d8",
170+
"thresholds":{"lowerBound":-0.1,"upperBound":0.1,"outsideBounds":false}
171+
}
172+
----
173+
The `specificDefinition` field is quite useful in understanding the real-world interpretation of these metric values. From these, we see that both model Alpha and Beta are quite fair over the `Is Male-Identifying?` field, with the two groups' rates of positive outcomes only differing by -0.3% and 2.8% respectively.
174+
175+
== Schedule a Fairness Metric Request
176+
177+
However, while it's great that our models are fair over the training data, we need to monitor that they remain fair over real-world inference data as well. To do this, we can _schedule_ some metric requests,
178+
such as to compute at recurring intervals throughout deployment. To do this, we simply pass the same payloads to the `/metrics/group/fairness/spd/request` endpoint:
179+
180+
[source,shell]
181+
----
182+
echo "=== MODEL ALPHA ==="
183+
curl -sk -X POST --location $TRUSTY_ROUTE/metrics/group/fairness/spd/request \
184+
--header 'Content-Type: application/json' \
185+
--data "{
186+
\"modelId\": \"demo-loan-nn-onnx-alpha\",
187+
\"protectedAttribute\": \"Is Male-Identifying?\",
188+
\"privilegedAttribute\": 1.0,
189+
\"unprivilegedAttribute\": 0.0,
190+
\"outcomeName\": \"Will Default?\",
191+
\"favorableOutcome\": 0,
192+
\"batchSize\": 5000
193+
}"
194+
195+
echo "\n=== MODEL BETA ==="
196+
curl -sk -X POST --location $TRUSTY_ROUTE/metrics/group/fairness/spd/request \
197+
--header 'Content-Type: application/json' \
198+
--data "{
199+
\"modelId\": \"demo-loan-nn-onnx-beta\",
200+
\"protectedAttribute\": \"Is Male-Identifying?\",
201+
\"privilegedAttribute\": 1.0,
202+
\"unprivilegedAttribute\": 0.0,
203+
\"outcomeName\": \"Will Default?\",
204+
\"favorableOutcome\": 0,
205+
\"batchSize\": 5000
206+
}"
207+
----
208+
These commands will return the created request's IDs, which can later be used to delete these scheduled requests if desired.
209+
210+
== Schedule an Identity Metric Request
211+
212+
Furthermore, let's monitor the average values of various data fields over time, to see the average ratio of loan-payback to loan-default predictions, as well as the average ratio of male-identifying to non-male-identifying applicants. We can do this by creating an _Identity Metric Request_ via POST'ing the `/metrics/identity/request` endpoint:
213+
214+
[source,shell]
215+
----
216+
for model in "demo-loan-nn-onnx-alpha" "demo-loan-nn-onnx-beta"; do
217+
for field in "Is Male-Identifying?" "Will Default?"; do
218+
curl -sk -X POST --location $TRUSTY_ROUTE/metrics/identity/request \
219+
--header 'Content-Type: application/json' \
220+
--data "{
221+
\"columnName\": \"$field\",
222+
\"batchSize\": 250,
223+
\"modelId\": \"$model\"
224+
}"
225+
done
226+
done
227+
----
228+
The payload is structured as follows:
229+
230+
* `columnName`: The name of the field to compute the averaging over
231+
* `batchSize`: The number of previous inferences to include in the average-value calculation
232+
* `modelId`: The name of the model to query
233+
234+
== Check the Metrics
235+
236+
. Navigate to Observe -> Metrics in the OpenShift console. If you're already on that page, you may need to refresh before the new metrics appear in the suggested expressions.
237+
. Set the time window to 5 minutes (top left) and the refresh interval to 15 seconds (top right)
238+
. In the "Expression" field, enter `trustyai_spd` or `trustyai_identity`
239+
. Explore the Metric Chart:
240+
* image::initial_spd.png[Initial SPD]
241+
* image::initial_identities.png[Initial Identities]
242+
243+
== Simulate Some Real World Data
244+
245+
Now that we've got our metric monitoring set up, let's send some "real world" data through our models to see if they remain fair:
246+
247+
[source,shell]
248+
----
249+
for batch in "01" "02" "03" "04" "05" "06" "07" "08"; do
250+
scripts/send_data_batch data/batch_$batch.json
251+
sleep 5
252+
done
253+
----
254+
Once the data is being sent, return to Observe -> Metrics page and watch the link:Statistical-Parity-Difference.md[SPD] and Identity metric values change.
255+
256+
== Results
257+
258+
Let's first look at our two models' fairness:
259+
260+
* image::final_spd.png[Final SPD Values]
261+
262+
Immediately, we notice that the two models have drastically different fairnesses over the real world data. Model Alpha (blue) remained within the "acceptably fair" range between -0.1 and 0.1, ending at around 0.09. However, Model Beta (yellow) plummeted out of the fair range, ending at -0.274, meaning that non-male-identifying applicants were _*27 percent*_ less likely to get a favorable outcome from the model than male-identifying applicants; clearly an unacceptable bias.
263+
264+
We can investigate this further by examining our identity metrics, first looking at the inbound ratio of male-identifying to non-male-identufying applicants:
265+
266+
* image::final_male_ident.png[Final Male-Identifying Values]
267+
268+
We can immediately see that in our training data, the ratio between male/non-male was around 0.8, but in the real-world data, it quickly dropped to _*0*_, meaning every single applicant was non-male. This is a strong indicator that our
269+
training data did not match our real-world data, which is very likely to indicate poor or biased model performance.
270+
271+
Meanwhile, looking at the will-default to will-not-default ratio:
272+
273+
* image::final_default.png[Final Default Prediction Values]
274+
275+
We can see that despite seeing only non-male applicants, Model Alpha (green) still provided varying outcomes to the various applicants, predicting "will-default" around 25% of the time. Model Beta (purple) predicted "will-default" 100% of the time: every single applicant was predicted to default on their loan. Again, this is a clear indicator that our model is performing poorly on the real-world data and/or has encoded a systematic bias from its training; it is predicting that every single non-male applicant will default.
276+
277+
These examples show exactly why monitoring bias in production is so important: models that are equally fair at training time may perform _drastically_ differently over real-world data, with hidden biases only manifesting over real-world data. This means these biases are exposed to the public, being imposed upon whoever is subject to your models decisions, and therefore using TrustyAI to provide early warning of these biases can protect you from the damages that problematic models in production can do.

docs/modules/ROOT/pages/main.adoc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
= Overview
2+
3+
== What is TrustyAI?
4+
5+
TrustyAI is, at its core, a Java library and service for Explainable AI (XAI).
6+
TrustyAI offers fairness metrics, explainable AI algorithms, and link:Features.md[various other XAI tools] at a library-level as well as a containerized service and Kubernetes deployment.
7+
TrustyAI consists of several link:Components.md[components], including:
8+
9+
* link:TrustyAI-core.md[TrustyAI core], the core TrustyAI Java module, containing fairness metrics, AI explainers, and other XAI utilities.
10+
* link:TrustyAI-service.md[TrustyAI service], TrustyAI-as-a-service, a REST service for fairness metrics and explainability algorithms including ModelMesh integration.
11+
* link:TrustyAI-operator.md[TrustyAI operator], a Kubernetes operator for TrustyAI service.
12+
* link:TrustyAI-Python.md[Python TrustyAI], a Python library allowing the usage of TrustyAI's toolkit from Jupyter notebooks
13+
14+
A more detailed explanation of all the components is available in the link:Components.md[Components page].
15+
16+
== Glossary
17+
18+
[horizontal]
19+
XAI:: Explainable AI
20+
Fairness:: This is the definition of the second term.

ui-bundle/css/site.css

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
10.1 KB
Binary file not shown.
Binary file not shown.
10.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)