Open vidushi27 opened 1 year ago
Hello @vidushi27,
There is no research paper associated to this metric as it has been created ad hoc. However, I can give you some additional explanation of how it works. We also wrote a notebook tutorial here.
The idea behind the compacity metric is to determine how well a small set of features can approximate the model (if you rely on too many features for the explanation, it will be hard to get a sense of how the model actually works, so we usually prefer to deal with fewer). Thus, two experiences are proposed in the compacity metric to help quantify how model predictions can be explained by smaller sets of features.
Input: You select an explainability method of your choice and you calculate the contributions of each feature on all data points. You end up having a matrix of contributions of the same size as the original data.
Some metrics are calculated (you can find the related Python code here) and the following graphs are built:
Left graph: For a given model approximation level, the graph is used to determine the minimum number of required features to reach that approximation (i.e. approximation level is fixed, number of features varies)
The approximation (expressed as a distance) is defined below :
distance = \frac{|output_{allFeatures} - output_{currentFeatures}|}{|output_{allFeatures}|}
distance = |output_{allFeatures} - output_{currentFeatures}|
Implementation is detailed below :
def get_min_nb_features(selection, contributions, mode, distance):
assert 0 <= distance <= 1
if mode == "classification" and len(contributions) == 2:
contributions = contributions[1]
contributions = contributions.loc[selection].values
features_needed = []
# For each instance, add features one by one (ordered by SHAP) until we get close enough
for i in range(contributions.shape[0]):
ids = np.flip(np.argsort(np.abs(contributions[i, :])))
output_value = np.sum(contributions[i, :])
score = 0
for j, idx in enumerate(ids):
# j : number of features needed
# idx : positions of the j top shap values
score += contributions[i, idx]
# CLOSE_ENOUGH
if mode == "regression":
if abs(score - output_value) < distance * abs(output_value):
break
elif mode == "classification":
if abs(score - output_value) < distance:
break
features_needed.append(j + 1)
return features_needed
Right graph: It is the opposite. This time we keep the number of selected features fixed, and we want to determine how close we get to the model. The iteration process is very similar :
Implementation is detailed below:
def get_distance(selection, contributions, mode, nb_features):
if mode == "classification" and len(contributions) == 2:
contributions = contributions[1]
assert nb_features <= contributions.shape[1]
contributions = contributions.loc[selection].values
top_features = np.array([sorted(row, key=abs, reverse=True) for row in contributions])[:, :nb_features]
output_top_features = np.sum(top_features[:, :], axis=1)
output_all_features = np.sum(contributions[:, :], axis=1)
if mode == "regression":
distance = abs(output_top_features - output_all_features) / abs(output_all_features)
elif mode == "classification":
distance = abs(output_top_features - output_all_features)
return
Hope this helps
Dear contributors,
Please direct me toward the research paper/reference for the metrics "Compacity" used here. I would like to understand it's background in detail. I've gone through the medium article mentioned.
Thank you so much.
Best regards, Vidushi