XAI applied to an autopilot decision tree
In this section, we will explain decision trees through scikit-learn's tree module, the decision tree classifier's parameters, and decision tree graphs. The goal is to provide the user with a step-by-step method to explain decision trees.
We will begin by parsing the structure of a decision tree.
Structure of a decision tree
The structure of a decision tree provides precious information for XAI. However, the default values of the decision tree classifier produce confusing outputs. We will first generate a decision tree structure with the default values. Then, we will use a what-if approach that will prepare us for the XAI tools in Chapter 5, Building an Explainable AI Solution from Scratch.
Let's start by implementing the default decision tree structure's output.
The default output of the default structure of a decision tree
The decision tree estimator contains a tree_ object that stores the attributes of the structure of a decision tree in arrays:
estimator.tree_
We can count the number of nodes:
n_nodes = estimator.tree_.node_count
We can obtain the ID of the left child of a node:
children_left = estimator.tree_.children_left
We can obtain the ID of the right child of a node:
children_right = estimator.tree_.children_right
We can also view the feature used to split the node into the left and right child nodes:
feature = estimator.tree_.feature
A threshold attribute will show the value at the node:
threshold = estimator.tree_.threshold
These arrays contain valuable XAI information.
The binary tree produces parallel arrays. The root node is node 0. The ith element contains information about the node.
The program now parses the tree structure using the attribute arrays:
# parsing the tree structure
node_depth = np.zeros(shape=n_nodes, dtype=np.int64)
is_leaves = np.zeros(shape=n_nodes, dtype=bool)
stack = [(0, -1)] # the seed is the root node id and its parent depth
while len(stack) > 0:
node_id, parent_depth = stack.pop()
node_depth[node_id] = parent_depth + 1
# Exploring the test mode
if (children_left[node_id] != children_right[node_id]):
stack.append((children_left[node_id], parent_depth + 1))
stack.append((children_right[node_id], parent_depth + 1))
else:
is_leaves[node_id] = True
Once the decision tree structure's attribute arrays have been parsed, the program prints the structure:
print("The binary tree structure has %s nodes and has "
"the following tree structure:" % n_nodes)
for i in range(n_nodes):
if is_leaves[i]:
print("%snode=%s leaf node." % (node_depth[i] * "\t", i))
else:
print("%snode=%s test node: go to node %s "
"if X[:, %s] <= %s else to node %s."
% (node_depth[i] * "\t", i,
children_left[i],
feature[i],
threshold[i],
children_right[i],
))
Our autopilot dataset will produce 255 nodes and list the tree structure, as shown in the following excerpt:
The binary tree structure has 255 nodes and has the following tree structure:
node=0 test node: go to node 1 if X[:, 3] <= 0.4599999934434891 else to node 92.
node=1 test node: go to node 2 if X[:, 0] <= 0.35999999940395355 else to node 45.
node=2 test node: go to node 3 if X[:, 2] <= 0.5600000023841858 else to node 30.
The customized output of a customized structure of a decision tree
The default output of a default decision tree structure challenges a user's ability to understand the algorithm. XAI is not just for AI developers, designers, and geeks. If a problem occurs and the autopilot kills somebody, an investigation will be conducted, and XAI will either lead to a plausible explanation or a lawsuit.
A user will first accept an explanation but rapidly start asking what-if questions. Let's answer two of the most common ones that come up during a project.
First question
Why are there so many nodes? What if we reduced the number of nodes?
Good questions! The user is willing to use the software and help control the results of the model before it goes into production. However, the user does not understand what they are looking at!
We will go back to the code and decide to customize the decision tree classifier:
estimator = DecisionTreeClassifier(max_depth=2, max_leaf_nodes=3,
min_samples_leaf=100)
We have modified three key parameters:
- max_depth=2: Limits the tree to a maximum of two branches. A decision tree is a tree. We just cut a lot of branches.
- max_leaf_nodes=3: Limits the leaves on a branch. We just cut a lot of leaves off. We are restricting the growth of the tree, just like we do with real trees.
- min_samples_leaf=100: Limits the node volume by only taking leaves that have 100 or more samples into account.
The result is a small and easy tree to understand and is self-explanatory:
The binary tree structure has 5 nodes and has the following tree structure:
node=0 test node: go to node 1 if X[:, 3] <= 0.4599999934434891 else to node 2.
node=1 leaf node.
node=2 test node: go to node 3 if X[:, 1] <= 0.6599999964237213 else to node 4.
node=3 leaf node.
node=4 leaf node.
To make things easier, in the following chapters, we will progressively provide XAI interfaces for a user. A user will be able to run some "what-if" scenarios on their own. The user will understand the scenarios through XAI but will customize the model in real time.
For this section and chapter, we did it manually. The user smiles. Our XAI scenario has worked! The user will now control the decision tree by progressively increasing the three key parameters.
However, a few seconds later, the user frowns again and comes up with the second common question.
Second question
We reduced the number of nodes, and I appreciate the explanation. However, why has the accuracy gone down?
Excellent question! What is the point of controlling a truncated decision tree? The estimator will produce inaccurate results. If you scroll up to the training cell, you will see that the accuracy has gone from 1 to 0.74:
Accuracy: 0.7483333333333333
Our customized scenario worked to explain AI. The user understands that, in some cases, it will be possible to:
- First, reduce the size of a decision tree to understand it
- Then increase its size to follow the decision-making process and control it
- Fine-tune the parameters to obtain efficient but explainable decision trees
However, once the user understands the structure, can we visualize the tree structure in another way?
Yes, we can use the graph of a decision tree, as described in the following section.
The output of a customized structure of a decision tree
In the previous section, we explored the structure of a decision tree and laid the ground for the XAI interfaces we will explore in the following chapters.
However, we discovered that if we simplify the decision tree classifier's job, we also reduce the accuracy of the model.
We have another tool that we can use to explain the structure of a decision tree iteratively. We can use the graph of the decision tree.
First, we go back, comment the customized estimator, and uncomment the default estimator:
# Create decision tree classifier object
# Default approach
estimator = DecisionTreeClassifier()
# Explainable AI approach
# estimator = DecisionTreeClassifier(max_depth=2, max_leaf_nodes=3,
# min_samples_leaf=100)
When we run the program again, the accuracy is back to 1:
prediction
[0 0 1 ... 1 1 0]
Accuracy: 1.0
Then, we run the default plot_tree function we implemented in the Displaying a decision tree section of this chapter:
from matplotlib.pyplot import figure
plt.figure(dpi=400, edgecolor="r", figsize=(10, 10))
F = ["f1", "f2", "f3", "f4"]
C = ["Right", "Left"]
plot_tree(estimator, filled=True, feature_names=F, rounded=True,
precision=2, fontsize=3, proportion=True, max_depth=None,
class_names=C)
plt.savefig('dt.jpg')
plt.show()
The output will puzzle the user because many of the nodes overlap at lower levels of the tree:
Figure 2.13: Nodes overlapping in a decision tree graph
Furthermore, the image of the graph is huge. Our goal, in this section, is to explain a decision tree with a graph. The next cells of the notebook will display a large graph structure.
What if we reduced the max_depth of the graph by reducing it to 2 and reduced the size of the figure as well? For example:
plot_tree(estimator, filled=True, feature_names=F, rounded=True,
precision=2, fontsize=3, proportion=True, max_depth=2,
class_names=C)
plt.savefig('dt.jpg')
plt.figure(dpi=400, edgecolor="r", figsize=(3, 3))
plt.show()
A user can now relate to this graph to understand how a decision tree works and help confirm its calculations:
Figure 2.14: Decision tree with reduced depth
The accuracy of the model is back to 1, but the graph displayed only shows a node depth of 2.
In this section, we explored some of the parameters of decision tree structures and decision tree graphs. For some projects, users might not be interested in this level of explanation. However, in many corporate projects, data scientist project managers want to understand an AI algorithm in depth before deploying it on site. Furthermore, a project manager often works on an AI solution with a user or group of end users who will ask questions that they will need to answer.
In Chapter 5, Building an Explainable AI Solution from Scratch, we will explore Google's What-If Tool. We will begin to run XAI interfaces for the project managers and users on their own and build customized what-if scenarios. We will go into further detail in the following chapters.
For the moment, let's first introduce XAI and ethics in an autopilot decision tree.