--- title: Model explainability keywords: fastai sidebar: home_sidebar summary: "Functionality to help with both global and local explainability." description: "Functionality to help with both global and local explainability." nb_path: "nbs/100c_models.explainability.ipynb" ---
{% raw %}
{% endraw %} {% raw %}
{% endraw %} {% raw %}
from tsai.data.all import *
from tsai.models.XCM import *

dsid = 'NATOPS'
X, y, splits = get_UCR_data(dsid, split_data=False)
tfms = [None, Categorize()]
dls = get_ts_dls(X, y, splits=splits, tfms=tfms)
model =  XCM(dls.vars, dls.c, dls.len)
learn = Learner(dls, model, metrics=accuracy)
xb, yb = dls.one_batch()
x = xb[0]
model.eval().to(xb.device)(xb).shape
torch.Size([64, 6])
{% endraw %} {% raw %}

get_acts_and_grads[source]

get_acts_and_grads(model, modules, x, y=None, detach=True, cpu=False)

Returns activations and gradients for given modules in a model and a single input or a batch. Gradients require y value(s). If they rae not provided, it will use the predicttions.

{% endraw %} {% raw %}

get_attribution_map[source]

get_attribution_map(model, modules, x, y=None, detach=True, cpu=False, apply_relu=True)

{% endraw %} {% raw %}
{% endraw %} {% raw %}
acts, grads = get_acts_and_grads(model, model.conv2dblock, x)
acts.shape, grads.shape
/Users/nacho/opt/anaconda3/envs/py36/lib/python3.6/site-packages/torch/nn/modules/module.py:974: UserWarning: Using a non-full backward hook when the forward contains multiple autograd Nodes is deprecated and will be removed in future versions. This hook will be missing some grad_input. Please use register_full_backward_hook to get the documented behavior.
  warnings.warn("Using a non-full backward hook when the forward contains multiple autograd Nodes "
(torch.Size([1, 128, 24, 51]), torch.Size([1, 128, 24, 51]))
{% endraw %} {% raw %}
acts, grads = get_acts_and_grads(model, model.conv2dblock, xb)
acts.shape, grads.shape
(torch.Size([64, 128, 24, 51]), torch.Size([64, 128, 24, 51]))
{% endraw %} {% raw %}
acts, grads = get_acts_and_grads(model, model.conv2dblock, xb, yb)
acts.shape, grads.shape
(torch.Size([64, 128, 24, 51]), torch.Size([64, 128, 24, 51]))
{% endraw %} {% raw %}
acts, grads = get_acts_and_grads(model, model.conv1dblock, xb)
acts.shape, grads.shape
(torch.Size([64, 128, 51]), torch.Size([64, 128, 51]))
{% endraw %} {% raw %}
acts, grads = get_acts_and_grads(model, [model.conv2dblock, model.conv1dblock], xb, yb)
[act.shape for act in acts], [grad.shape for grad in grads]
([torch.Size([64, 128, 24, 51]), torch.Size([64, 128, 51])],
 [torch.Size([64, 128, 24, 51]), torch.Size([64, 128, 51])])
{% endraw %} {% raw %}
att_maps = get_attribution_map(model, model.conv2dblock, xb, yb)
att_maps.shape
torch.Size([64, 24, 51])
{% endraw %} {% raw %}
att_maps = get_attribution_map(model, model.conv1dblock, xb)
att_maps.shape
torch.Size([64, 24, 51])
{% endraw %} {% raw %}
att_maps = get_attribution_map(model, [model.conv2dblock, model.conv1dblock], xb)
[am.shape for am in att_maps]
[torch.Size([64, 24, 51]), torch.Size([64, 24, 51])]
{% endraw %} {% raw %}
acts, grads = get_acts_and_grads(model, [model.conv2dblock, model.conv1dblock], xb[0], yb[0], detach=True, cpu=False)
print(len(acts), len(grads), acts[0].shape, grads[0].shape)
acts, grads = get_acts_and_grads(model, model.conv2dblock, xb[0], y=None, detach=True, cpu=False)
print(acts.shape, grads.shape)
2 2 torch.Size([1, 128, 24, 51]) torch.Size([1, 128, 24, 51])
torch.Size([1, 128, 24, 51]) torch.Size([1, 128, 24, 51])
{% endraw %} {% raw %}
att_maps = get_attribution_map(model, model.conv2dblock, x)
att_maps.shape
torch.Size([24, 51])
{% endraw %} {% raw %}
att_maps = get_attribution_map(model, model.conv1dblock, x)
att_maps.shape
torch.Size([24, 51])
{% endraw %} {% raw %}
att_maps = get_attribution_map(model, [model.conv2dblock, model.conv1dblock], x)
[am.shape for am in att_maps]
[torch.Size([24, 51]), torch.Size([24, 51])]
{% endraw %} {% raw %}
att_maps = get_attribution_map(model, [model.conv2dblock, model.conv1dblock], x)
att_maps[0] = (att_maps[0] - att_maps[0].min()) / (att_maps[0].max() - att_maps[0].min())
att_maps[1] = (att_maps[1] - att_maps[1].min()) / (att_maps[1].max() - att_maps[1].min())

fig = plt.figure(figsize=(10, 10))
ax = plt.axes()
plt.title('Observed variables')
im = ax.imshow(att_maps[0].cpu().numpy(), cmap='inferno')
cax = fig.add_axes([ax.get_position().x1+0.01,ax.get_position().y0,0.02,ax.get_position().height])
plt.colorbar(im, cax=cax)
plt.show()

fig = plt.figure(figsize=(10, 10))
ax = plt.axes()
plt.title('Time')
im = ax.imshow(att_maps[1].cpu().numpy(), cmap='inferno')
cax = fig.add_axes([ax.get_position().x1+0.01,ax.get_position().y0,0.02,ax.get_position().height])
plt.colorbar(im, cax=cax)
plt.show()
{% endraw %} {% raw %}
# Color parts of a line based on its properties, e.g., slope.
# '''

# import numpy as np
# import matplotlib.pyplot as plt
# from matplotlib.collections import LineCollection
# from matplotlib.colors import ListedColormap, BoundaryNorm

# x = np.linspace(0, 3 * np.pi, 500)
# y = np.sin(x)
# z = np.cos(0.5 * (x[:-1] + x[1:]))  # first derivative

# # Create a colormap for red, green and blue and a norm to color
# # f' < -0.5 red, f' > 0.5 blue, and the rest green
# cmap = ListedColormap(['r', 'g', 'b'])
# norm = BoundaryNorm([-1, -0.5, 0.5, 1], cmap.N)

# # Create a set of line segments so that we can color them individually
# # This creates the points as a N x 1 x 2 array so that we can stack points
# # together easily to get the segments. The segments array for line collection
# # needs to be numlines x points per line x 2 (x and y)
# points = np.array([x, y]).T.reshape(-1, 1, 2)
# segments = np.concatenate([points[:-1], points[1:]], axis=1)

# # Create the line collection object, setting the colormapping parameters.
# # Have to set the actual values used for colormapping separately.
# lc = LineCollection(segments, cmap=cmap, norm=norm)
# lc.set_array(z)
# lc.set_linewidth(3)

# fig1 = plt.figure()
# plt.gca().add_collection(lc)
# plt.xlim(x.min(), x.max())
# plt.ylim(-1.1, 1.1)

# # Now do a second plot coloring the curve using a continuous colormap
# t = np.linspace(0, 10, 200)
# x = np.cos(np.pi * t)
# y = np.sin(t)
# points = np.array([x, y]).T.reshape(-1, 1, 2)
# segments = np.concatenate([points[:-1], points[1:]], axis=1)

# lc = LineCollection(segments, cmap=plt.get_cmap('copper'), norm=plt.Normalize(0, 10))
# lc.set_array(t)
# lc.set_linewidth(3)

# fig2 = plt.figure()
# plt.gca().add_collection(lc)
# plt.xlim(-1, 1)
# plt.ylim(-1, 1)
# plt.show()
{% endraw %}