Dictionary of Applied Machine Learning · transparency
Numerical companion to the entry transparency: it recomputes what the entry states and prints one line per check
One block per paragraph of the entry (marked [P...]): the entry is a regulation term, so its legal paragraphs (EU AI Act Arts. 13/26/50/86, documentation duties) are expository; the demo illustrates the entry's technical paragraph on ML methods that inherently offer transparency. Self-contained (numpy/matplotlib only), fixed seed.
Run it with python3 transparency.py, from any directory — it writes its output files into the current directory. Requires NumPy and Matplotlib only, and uses fixed seeds, so the printed numbers reproduce exactly. Download transparency.py
One cell per block of the script: the code, and what that code printed when it last ran here
"""
transparency.py — numerical companion to the glossary entry
'transparency'.
One block per paragraph of the entry (marked [P...]): the entry is a
regulation term, so its legal paragraphs (EU AI Act Arts. 13/26/50/86,
documentation duties) are expository; the demo illustrates the entry's
technical paragraph on ML methods that inherently offer transparency.
Self-contained (numpy/matplotlib only), fixed seed.
Blocks
------
[P-methods] "Some ML methods inherently offer transparency": (a)
logistic regression quantifies the confidence of a
classification via |h(x)| — predictions with large |h(x)|
are empirically far more reliable than low-|h(x)| ones,
so disclosing this value (as the entry's medical example
requires) is informative; (b) a depth-2 decision tree is
printable as human-readable if-then rules that exactly
reproduce its predictions.
Outputs
-------
transparency.png : preview figure (checking only).
Data generated by pythondemos/transparency.py.
"""
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
rng = np.random.default_rng(42)
report = []
def check(name, ok):
report.append((name, bool(ok)))
print(f" [{'ok' if ok else 'FAIL'}] {name}")
"Some ML methods inherently offer transparency": (a) logistic regression quantifies the confidence of a classification via |h(x)| — predictions with large |h(x)| are empirically far more reliable than low-|h(x)| ones, so disclosing this value (as the entry's medical example requires) is informative; (b) a depth-2 decision tree is printable as human-readable if-then rules that exactly reproduce its predictions.
print("[P-methods] (a) logistic regression: confidence via |h(x)|")
m = 4000
X = rng.normal(size=(m, 2))
w_true = np.array([2.0, -1.5])
p = 1 / (1 + np.exp(-(X @ w_true)))
y = (rng.uniform(size=m) < p).astype(float)
w = np.zeros(2)
for _ in range(400): # logistic regression GD
s = 1 / (1 + np.exp(-(X @ w)))
w -= 0.5 * X.T @ (s - y) / m # gradient of log loss
h = X @ w # h(x) = w^T x
pred = (h > 0).astype(float)
lo = np.abs(h) < np.quantile(np.abs(h), 0.3) # low-confidence tercile
hi = np.abs(h) > np.quantile(np.abs(h), 0.7) # high-confidence tercile
acc_lo, acc_hi = np.mean(pred[lo] == y[lo]), np.mean(pred[hi] == y[hi])
print(f" accuracy at low / high |h(x)|: {acc_lo:.2f} / {acc_hi:.2f}")
check("|h(x)| quantifies reliability: high-|h| predictions are far "
"more accurate", acc_hi > acc_lo + 0.15)
check("disclosing |h(x)| separates confident from uncertain "
"predictions (medical-example requirement)",
acc_hi > 0.9)
print("[P-methods] (b) decision tree: human-readable rules")
x1_split, x2_split = 0.0, 0.5
def tree_predict(X):
out = np.empty(len(X))
for i, (a, b) in enumerate(X):
if a <= x1_split:
out[i] = 0.0 if b <= x2_split else 1.0
else:
out[i] = 1.0 if b <= x2_split else 0.0
return out
rules = [
f"IF x1 <= {x1_split} AND x2 <= {x2_split} THEN predict 0",
f"IF x1 <= {x1_split} AND x2 > {x2_split} THEN predict 1",
f"IF x1 > {x1_split} AND x2 <= {x2_split} THEN predict 1",
f"IF x1 > {x1_split} AND x2 > {x2_split} THEN predict 0",
]
for r in rules:
print(" " + r)
def rules_predict(X):
out = np.empty(len(X))
for i, (a, b) in enumerate(X):
if a <= x1_split and b <= x2_split: out[i] = 0.0
elif a <= x1_split: out[i] = 1.0
elif b <= x2_split: out[i] = 1.0
else: out[i] = 0.0
return out
Xt = rng.normal(size=(500, 2))
check("the printed if-then rules exactly reproduce the tree's "
"predictions on every input",
np.array_equal(tree_predict(Xt), rules_predict(Xt)))
check("the rule list is small enough to read (4 rules, depth 2)",
len(rules) == 4)
# ------------------------------------------------------------ preview
fig, ax = plt.subplots(figsize=(5.0, 3.2))
bins = np.quantile(np.abs(h), np.linspace(0, 1, 9))
accs = [np.mean(pred[(np.abs(h) >= a) & (np.abs(h) < b)]
== y[(np.abs(h) >= a) & (np.abs(h) < b)])
for a, b in zip(bins[:-1], bins[1:])]
ax.plot(0.5 * (bins[:-1] + bins[1:]), accs, "o-")
ax.set_xlabel("|h(x)|"); ax.set_ylabel("empirical accuracy")
ax.set_title("[P-methods] confidence |h(x)| tracks reliability")
fig.tight_layout()
fig.savefig("transparency.png", dpi=110)
print(f"\n{sum(ok for _, ok in report)}/{len(report)} checks passed")
assert all(ok for _, ok in report)
[P-methods] (a) logistic regression: confidence via |h(x)|
accuracy at low / high |h(x)|: 0.60 / 0.97
[ok] |h(x)| quantifies reliability: high-|h| predictions are far more accurate
[ok] disclosing |h(x)| separates confident from uncertain predictions (medical-example requirement)
[P-methods] (b) decision tree: human-readable rules
IF x1 <= 0.0 AND x2 <= 0.5 THEN predict 0
IF x1 <= 0.0 AND x2 > 0.5 THEN predict 1
IF x1 > 0.0 AND x2 <= 0.5 THEN predict 1
IF x1 > 0.0 AND x2 > 0.5 THEN predict 0
[ok] the printed if-then rules exactly reproduce the tree's predictions on every input
[ok] the rule list is small enough to read (4 rules, depth 2)
4/4 checks passed

P-methods writes when the script runs