visualization
May 13, 2025
0.0.1 Faithfulness
[1]: import pickle
import numpy as np
from [Link] import softmax
import copy
import os
import glob
from [Link] import tqdm
root = "."
datasets = sorted([x for x in [Link](root + "/checkpoints/SBM") if [Link].
↪isdir([Link](root + "/checkpoints/SBM", x))])
datasets
faithfulness_all = {}
def compute_faithfulness(dataset):
pkl_file = [Link](f'{root}/checkpoints/SBM/{dataset}/*/test_results.pkl')
d = [Link](open(pkl_file[0], 'rb'))
p = d['predicate']
orignial_prob = softmax(d['pred'], axis=1)
predicted_label = [Link](orignial_prob, axis=1)
acc = (predicted_label == d['target']).mean()
print(dataset, acc)
faithfulness = []
weight = []
for i in range([Link][1]):
pi = [Link](p)
pi[:, i] = 0
new_logits = pi @ d['w'].T
new_prob = softmax(new_logits, axis=1)
prob_diff = orignial_prob - new_prob
1
faith_list = []
weight_list = []
for sample_id in range(prob_diff.shape[0]):
faith_list.append(prob_diff[sample_id, predicted_label[sample_id]])
weight_list.append(d['w'][predicted_label[sample_id], i])
[Link]([Link](faith_list))
[Link]([Link](weight_list))
faithfulness = [Link](faithfulness, axis=0)
weight = [Link](weight, axis=0)
corr = []
for i in range([Link][1]):
[Link]([Link](weight[:, i], faithfulness[:, i])[0, 1])
return corr
dataset_map = {
"ArticularyWordRecognition": "AWR",
"AtrialFibrillation": "AF",
"BasicMotions": "BM",
"CharacterTrajectories": "CT",
"Cricket": "CK",
"DuckDuckGeese": "DDG",
"ERing": "ER",
"EigenWorms": "EW",
"Epilepsy": "EP",
"EthanolConcentration": "EC",
"FaceDetection": "FD",
"FingerMovements": "FM",
"HandMovementDirection": "HM",
"Handwriting": "HW",
"Heartbeat": "HB",
"InsectWingbeat": "IW",
"JapaneseVowels": "JV",
"LSST": "LSST",
"Libras": "LB",
"MotorImagery": "MI",
"NATOPS": "NT",
"PEMS-SF": "PM",
"PenDigits": "PD",
"PhonemeSpectra": "PH",
"RacketSports": "RS",
"SelfRegulationSCP1": "SCP1",
"SelfRegulationSCP2": "SCP2",
2
"SpokenArabicDigits": "SAD",
"StandWalkJump": "SWJ",
"UWaveGestureLibrary": "UGL"
}
for dataset in tqdm(datasets):
try:
corr = compute_faithfulness(dataset)
faithfulness_all[dataset_map[dataset]] = corr
except:
print(f'{dataset} failed')
0%| | 0/30 [00:00<?, ?it/s]
ArticularyWordRecognition 0.9933333333333333
AtrialFibrillation 0.5333333333333333
BasicMotions 1.0
CharacterTrajectories 0.9818941504178273
Cricket 0.9861111111111112
DuckDuckGeese 0.38
ERing 0.9629629629629629
EigenWorms 0.5877862595419847
Epilepsy 0.9927536231884058
EthanolConcentration 0.3155893536121673
FaceDetection 0.6555051078320091
FingerMovements 0.57
HandMovementDirection 0.5135135135135135
Handwriting 0.33176470588235296
Heartbeat 0.7365853658536585
InsectWingbeat 0.50152
JapaneseVowels 0.9594594594594594
LSST 0.6362530413625304
Libras 0.8888888888888888
MotorImagery 0.64
NATOPS 0.8833333333333333
PEMS-SF 0.8497109826589595
/home/weny2/anaconda3/envs/ts/lib/python3.11/site-
packages/numpy/lib/function_base.py:2897: RuntimeWarning: invalid value
encountered in divide
c /= stddev[:, None]
/home/weny2/anaconda3/envs/ts/lib/python3.11/site-
packages/numpy/lib/function_base.py:2898: RuntimeWarning: invalid value
encountered in divide
c /= stddev[None, :]
PenDigits 0.9705546026300743
PhonemeSpectra 0.2573814494482553
RacketSports 0.8947368421052632
3
SelfRegulationSCP1 0.856655290102389
SelfRegulationSCP2 0.5333333333333333
SpokenArabicDigits 0.9931787175989086
StandWalkJump 0.5333333333333333
UWaveGestureLibrary 0.9125
/tmp/ipykernel_23742/[Link]: UserWarning: set_ticklabels() should
only be used with a fixed number of ticks, i.e. after set_ticks() or using a
FixedLocator.
ax.set_xticklabels(faithfulness_all.keys(), fontsize=16)
[19]: import seaborn as sns
import [Link] as plt
k1 = list(faithfulness_all.keys())[:15]
k2 = list(faithfulness_all.keys())[15:]
d1 = {k:faithfulness_all[k] for k in k1}
d2 = {k:faithfulness_all[k] for k in k2}
with sns.axes_style('whitegrid'):
4
fig, ax = [Link](2, figsize=(12, 10))
[Link](data=d1, ax=ax[0])
ax[0].set_xticklabels([Link](), fontsize=16)
ax[0].set_xlabel('Dataset', fontsize=16)
ax[0].set_ylabel('Faithfulness Estimate', fontsize=16)
ax[0].set_ylim(0, 1)
[Link](data=d2, ax=ax[1])
ax[1].set_xticklabels([Link](), fontsize=16)
ax[1].set_xlabel('Dataset', fontsize=16)
ax[1].set_ylabel('Faithfulness Estimate', fontsize=16)
ax[1].set_ylim(0, 1)
plt.tight_layout()
[Link]('../figures/[Link]')
/tmp/ipykernel_23742/[Link]: UserWarning: set_ticklabels() should only
be used with a fixed number of ticks, i.e. after set_ticks() or using a
FixedLocator.
ax[0].set_xticklabels([Link](), fontsize=16)
/tmp/ipykernel_23742/[Link]: UserWarning: set_ticklabels() should only
be used with a fixed number of ticks, i.e. after set_ticks() or using a
FixedLocator.
ax[1].set_xticklabels([Link](), fontsize=16)
5
0.0.2 Global Explanations
[4]: import pickle
import numpy as np
import [Link] as plt
import [Link] as gridspec
def smooth_array(data, window_size=1):
if window_size % 2 == 0:
raise ValueError("Window size must be an odd number")
pad_size = window_size // 2
padded_data = [Link](data, pad_size, mode='edge')
smoothed = [Link]([Link])
for i in range(len(data)):
6
smoothed[i] = [Link](padded_data[i:i+window_size])
return smoothed
datasets = ['BasicMotions', 'Epilepsy', 'SelfRegulationSCP1',␣
↪'SelfRegulationSCP2', 'UWaveGestureLibrary']
def plot_explanations(dataset, top_k=5, max_sample=100):
d = [Link](open(f'../checkpoints/SBM/{dataset}/
↪dnn-FCN_seed-0_k-10_div-0.1_reg-0.1_eps-1.
↪0_beta-constant_dfunc-euclidean_cls-linear/test_results.pkl', 'rb'))
fontsize = 16
legend_fontsize = 14
smooth_window_size = 1 # Optionally smooth the shapelets for visualization␣
↪only.
for k, v in [Link]():
try:
print(k, [Link])
except:
print(k)
num_class = d['w'].shape[0]
num_sample = d['x'].shape[0]
num_channel = d['x'].shape[-1]
length = d['x'].shape[1]
figures = []
for label in range(num_class):
fig = [Link](figsize=(21, num_channel*1.5))
data_axs = []
for c in range(num_channel, 0, -1):
ax = fig.add_subplot(num_channel, 3, 1 + 3*(c-1))
if c != num_channel:
ax.set_xticklabels([])
ax.set_ylabel(f"$x^{{{c}}}$", fontsize=fontsize)
data_axs.append(ax)
data_axs.reverse()
data_axs[0].set_title(f"{dataset}: Category {label+1}",␣
↪fontsize=fontsize)
pos_axs = []
for i in range(top_k, 0, -1):
ax = fig.add_subplot(top_k, 3, 2 + 3*(i-1))
7
if i != top_k:
ax.set_xticklabels([])
pos_axs.append(ax)
pos_axs.reverse()
pos_axs[0].set_title(f"Top-{top_k} Positive Relevant Shapelets",␣
↪fontsize=fontsize)
neg_axs = []
for i in range(top_k, 0, -1):
ax = fig.add_subplot(top_k, 3, 3 + 3*(i-1))
if i != top_k:
ax.set_xticklabels([])
neg_axs.append(ax)
neg_axs.reverse()
neg_axs[0].set_title(f"Top-{top_k} Negative Relevant Shapelets",␣
↪fontsize=fontsize)
# plt.tight_layout()
[Link]((fig, data_axs, pos_axs, neg_axs))
for sample_id in range(num_sample):
if sample_id >= max_sample:
break
label = d['target'][sample_id]
fig, data_axs, pos_axs, neg_axs = figures[int(label)]
for c in range(num_channel):
t = [Link](0, 1, d['x'].shape[1])
data_axs[c].plot(t, d['x'][sample_id, :, c].flatten(), color="tab:
↪gray", alpha=0.1, linewidth=2)
alpha_factor = 0.15
for label in range(num_class):
fig, data_axs, pos_axs, neg_axs = figures[label]
class_w = d['w'][label, :]
top_k_idx = [Link](-class_w)[:top_k]
neg_k_idx = [Link](class_w)[:top_k]
for i, s_id in enumerate(top_k_idx):
shapelet, s_channel = d['shapelets'][s_id]
t = [Link](0, [Link][0]/length, [Link][0])
pos_axs[i].plot(t, smooth_array(shapelet,␣
↪window_size=smooth_window_size), color='tab:blue', alpha=1 - alpha_factor*i,␣
↪linewidth=3,
8
label=f"$s_{{{s_id}}}: w={{{class_w[s_id]:.2f}}}$␣
↪on $x^{{{s_channel+1}}}$")
pos_axs[i].set_xlim(0, 1)
pos_axs[i].legend(loc='upper right', fontsize=legend_fontsize)
for i, s_id in enumerate(neg_k_idx):
shapelet, s_channel = d['shapelets'][s_id]
t = [Link](0, [Link][0]/length, [Link][0])
neg_axs[i].plot(t, smooth_array(shapelet,␣
↪window_size=smooth_window_size), color='tab:red', alpha=1 - alpha_factor*i,␣
↪linewidth=3,
label=f"$s_{{{s_id}}}: w={{{class_w[s_id]:.2f}}}$␣
↪on $x^{{{s_channel+1}}}$")
neg_axs[i].set_xlim(0, 1)
neg_axs[i].legend(loc='upper right', fontsize=legend_fontsize)
return figures
dataset = 'BasicMotions'
figs = plot_explanations(dataset, max_sample=300)
# save_path = f'../figures/global/{dataset}'
# import os
# if not [Link](save_path):
# [Link](save_path)
# for i, fig in enumerate(figs):
# fig[0].savefig(f'{save_path}/{dataset}_channel{i}.svg',␣
↪bbox_inches='tight', pad_inches=0.2)
# [Link](fig[0])
# [Link](fig[0])
x (40, 100, 6)
pred (40, 4)
target (40,)
predicate (40, 360)
w (4, 360)
shapelets
eta
sbm_pred
9
10
0.0.3 Local Explanations
[80]: import [Link] as mcolors
colors = list(mcolors.TABLEAU_COLORS)
def shapelet_position(shapelet, x):
start_t = [Link]([[Link](x[t:t+[Link][0]] - shapelet, 2).
↪mean() for t in range([Link][0] - [Link][0] + 1)])
return start_t
def cosine_similarity(a, b):
return [Link](a, b) / ([Link](a) * [Link](b))
def shapelet_position_cosine(shapelet, x):
start_t = [Link]([cosine_similarity(x[t:t+[Link][0]], shapelet)␣
↪for t in range([Link][0] - [Link][0] + 1)])
return start_t
def pearson_correlation(a, b):
return [Link](a, b)[0, 1]
def shapelet_position_pearson(shapelet, x):
start_t = [Link]([pearson_correlation(x[t:t+[Link][0]],␣
↪shapelet) for t in range([Link][0] - [Link][0] + 1)])
return start_t
def normalize_x(x):
11
return (x - [Link](axis=0, keepdims=True)) / ([Link](axis=0, keepdims=True)␣
↪+ 1e-8)
def plot_local_explanations(dataset, top_k=5, max_sample=100,␣
↪max_sample_per_class=5, dist_func='euclidean'):
d = [Link](open(f'../checkpoints/SBM_distance_func/{dataset}/
↪dnn-FCN_seed-0_k-10_div-0.1_reg-0.1_eps-1.
↪0_beta-constant_dfunc-{dist_func}_cls-linear/test_results.pkl', 'rb'))
fontsize = 16
legend_fontsize = 14
smooth_window_size = 9
for k, v in [Link]():
try:
print(k, [Link])
except:
print(k)
num_class = d['w'].shape[0]
num_sample = d['x'].shape[0]
num_channel = d['x'].shape[-1]
length = d['x'].shape[1]
sample_label_count = [Link](num_class)
figs = []
for sample_id in range(num_sample):
if sample_id >= max_sample:
break
label = int(d['target'][sample_id])
pred_label = int([Link](d['pred'][sample_id, :]))
if pred_label != label:
continue
sample_label_count[label] += 1
if sample_label_count[label] > max_sample_per_class:
continue
predictes = d['predicate'][sample_id, :]
weights = d['w'][int(label), :]
importance = predictes * weights
top_k_idx = [Link](-importance)[:top_k]
neg_k_idx = [Link](importance)[:top_k]
fig, axs = [Link](nrows=num_channel, ncols=1, figsize=(4,␣
↪num_channel*1), sharex=True)
12
[Link](fig)
axs[0].set_title(f"{dataset}: Category {int(label)+1}",␣
↪fontsize=fontsize)
x = normalize_x(d['x'][sample_id, :, :])
for c in range(num_channel):
t = [Link](0, 1, length)
axs[c].plot(t, x[:, c].flatten(), color="tab:gray", alpha=0.7,␣
↪linewidth=1)
axs[c].set_ylabel(f"$x^{{{c+1}}}$", fontsize=fontsize)
for i, s_id in enumerate(top_k_idx):
shapelet, s_channel = d['shapelets'][s_id]
if dist_func == 'euclidean':
start_t = shapelet_position(shapelet, x[:, s_channel])
elif dist_func == 'cosine':
start_t = shapelet_position_cosine(shapelet, x[:, s_channel])
elif dist_func == 'pearson':
start_t = shapelet_position_pearson(shapelet, x[:, s_channel])
t = [Link](start_t/length, (start_t+[Link][0])/length,␣
↪[Link][0])
axs[s_channel].plot(t, smooth_array(shapelet,␣
↪window_size=smooth_window_size), linewidth=3,
color=colors[i], alpha=1 - 0.15*i)
# axs[s_channel].set_xlim(0, 1)
# axs[s_channel].legend(loc='upper right', fontsize=legend_fontsize)
return figs
dataset = 'UWaveGestureLibrary'
dist_func = 'euclidean'
import os
if not [Link](f'../figures/local-{dist_func}/{dataset}'):
[Link](f'../figures/local-{dist_func}/{dataset}')
from [Link] import Bbox
bbox = Bbox.from_bounds(-0.3, -0.2, 4.4, 3.5)
figs = plot_local_explanations(dataset, max_sample=100, max_sample_per_class=4,␣
↪dist_func=dist_func)
for i, fig in enumerate(figs):
13
[Link](f'../figures/local-{dist_func}/{dataset}/{dataset}_sample{i}.
↪svg', bbox_inches=bbox)
# [Link](fig)
[Link](fig)
x (320, 315, 3)
pred (320, 8)
target (320,)
predicate (320, 180)
w (8, 180)
shapelets
eta
sbm_pred
/tmp/ipykernel_537234/[Link]: RuntimeWarning: More than 20 figures
have been opened. Figures created through the pyplot interface
(`[Link]`) are retained until explicitly closed and may
consume too much memory. (To control this warning, see the rcParam
`figure.max_open_warning`). Consider using `[Link]()`.
fig, axs = [Link](nrows=num_channel, ncols=1, figsize=(4,
num_channel*1), sharex=True)
14