def basic_model_assert( self, model, inputs, expected, n_perturb_samples=10, max_batch_size=None, perturb_func=_local_perturb_func, local=True, ): ig = IntegratedGradients(model) if local: attrs = tuple(attr / input for input, attr in zip(inputs, ig.attribute(inputs))) else: attrs = ig.attribute(inputs) return self.infidelity_assert( model, attrs, inputs, expected, n_perturb_samples=n_perturb_samples, max_batch_size=max_batch_size, perturb_func=perturb_func, )
def integrated_gradients(self, dataset, file): self.model.load_state_dict(torch.load(file)) x, y, _ = self._str2dataset(dataset) baselines = np.zeros(x.shape) names = self.input_names for i in range(len(names)): if names[i] == "glucose": mean = np.mean(y) baselines[:, :, i] = mean * np.ones(x.shape[0:2]) # if names[i] == "mets": # baselines[:, :, i] = mean * np.ones(x.shape[0:2]) x = torch.Tensor(x).cuda() x.requires_grad_() baselines = torch.Tensor(baselines).cuda() self.model.train() ig = IntegratedGradients(self.model) attr = [] for i in range(len(x) // 10): attr_i = ig.attribute(x[i * 10:(i + 1) * 10], baselines=baselines[i * 10:(i + 1) * 10], n_steps=50) attr.append(attr_i.cpu().detach().numpy()) attr_i = ig.attribute(x[(i + 1) * 10:len(x)], baselines=baselines[(i + 1) * 10:len(x)], n_steps=50) attr.append(attr_i.cpu().detach().numpy()) attr = np.concatenate(attr, axis=0) return attr
def basic_model_assert( self, model: Module, inputs: TensorOrTupleOfTensorsGeneric, expected: Tensor, n_perturb_samples: int = 10, max_batch_size: int = None, perturb_func: Callable = _local_perturb_func, multiply_by_inputs: bool = False, normalize: bool = False, ) -> Tensor: ig = IntegratedGradients(model) if multiply_by_inputs: attrs = cast( TensorOrTupleOfTensorsGeneric, tuple(attr / input for input, attr in zip(inputs, ig.attribute(inputs))), ) else: attrs = ig.attribute(inputs) return self.infidelity_assert( model, attrs, inputs, expected, n_perturb_samples=n_perturb_samples, max_batch_size=max_batch_size, perturb_func=perturb_func, normalize=normalize, )
class IGExplainer: def __init__(self, model, activation=torch.nn.Softmax(-1)): self.device = 'cuda' #'cuda' if torch.cuda.is_available() else 'cpu' self.base_model = model.to(self.device) self.base_model.device = self.device self.explainer = IntegratedGradients(self.base_model) self.activation = activation def attribute(self, x, y, retrospective=False): #x, y = x.to(self.device), y.to(self.device) score = np.zeros(x.shape) self.base_model.zero_grad() if retrospective: score = self.attribute(x, target=y.long(), baselines=(x * 0)) score = score.detach().cpu().numpy() else: score = np.zeros(x.shape) for t in range(x.shape[-1]): x_in = x[:, :, :t + 1] pred = self.activation(self.base_model(x_in)) if type(self.activation).__name__ == type( torch.nn.Softmax(-1)).__name__: target = torch.argmax(pred, -1) imp = self.explainer.attribute(x_in, target=target, baselines=(x[:, :, :t + 1] * 0)) score[:, :, t] = imp.detach().cpu().numpy()[:, :, -1] else: #print(pred) n_labels = pred.shape[1] if n_labels > 1: imp = torch.zeros(list(x_in.shape) + [n_labels]) for l in range(n_labels): target = (pred[:, l] > 0.5).float() #[:,0] imp[:, :, :, l] = self.explainer.attribute( x_in, target=target.long(), baselines=(x_in * 0)) score[:, :, t] = (imp.detach().cpu().numpy()).max(3)[:, :, -1] else: #this is for spike with just one label. and we will explain one class target = (pred > 0.5).float()[:, 0] imp = self.explainer.attribute(x_in, target=target.long(), baselines=(x_in * 0)) score[:, :, t] = imp.detach().cpu().numpy()[:, :, -1] return score
def _calculate_attribution( self, net: Module, baselines: Optional[List[Tuple[Tensor, ...]]], data: Tuple[Tensor, ...], additional_forward_args: Optional[Tuple[Tensor, ...]], label: Optional[Union[Tensor]], ) -> Tensor: ig = IntegratedGradients(net) # TODO support multiple baselines baseline = baselines[0] if len(baselines) > 0 else None label = ( None if not self._use_label_for_attr or label is None or label.nelement() == 0 else label ) attr_ig = ig.attribute( data, baselines=baseline, additional_forward_args=additional_forward_args, target=label, n_steps=self._config.steps, ) return attr_ig
def explain(method, device, model, data): def model_forward(edge_mask, device, data, model): batch = torch.zeros(data.x.shape[0], dtype=int).to(device) out = model(data.x, data.edge_index, batch, edge_mask) return out data = data.to(device) target = data.y.to(device) input_mask = torch.ones(data.edge_index.shape[1]).requires_grad_(True).to(device) if method == 'ig': ig = IntegratedGradients(model_forward) mask = ig.attribute(input_mask, target=target, additional_forward_args=(data,), internal_batch_size=data.edge_index.shape[1]) elif method == 'saliency': saliency = Saliency(model_forward) mask = saliency.attribute(input_mask, target=target, additional_forward_args=(device, data, model)) else: raise Exception('Unknown explanation method') edge_mask = np.abs(mask.cpu().detach().numpy()) if edge_mask.max() > 0: # avoid division by zero edge_mask = edge_mask / edge_mask.max() edge_mask_dict = defaultdict(float) for val, u, v in list(zip(edge_mask, *data.edge_index)): u, v = u.item(), v.item() edge_mask_dict[(u, v)] += val return edge_mask_dict
class Explainer(): def __init__(self,model): self.model=model self.explain=IntegratedGradients(model) def get_attribution_map(self,img,target=None): if target is None: target=torch.argmax(self.model(img),1) baseline_dist = torch.randn_like(img) * 0.001 thrd=20 attributions=[] if img.size(0) > thrd: img=torch.split(img,thrd) target=torch.split(target,thrd) baseline_dist=torch.split(baseline_dist,thrd) else: img,target,baseline_dist=[img],[target],[baseline_dist] for i,t,b in zip(img,target,baseline_dist): temp = self.explain.attribute(i, b, target=t , return_convergence_delta=False) attributions.append(temp) attributions=torch.cat(tuple(attributions),0) return attributions
def get_attributions_to_interpret_all_positive_in_test(dataset_dir, pathway_dir, drug_ids_path, outdir, best_model_path, norm, do_layers, batchnorm, num_layers, hidden_gcn, hidden_fc, splits_dir, in_channels, n_classes, feature_names_path, model_type, k_hops, n_steps = 100): model, samples_of_interest, loader, feature_names, device = preparing_data_all_positive_in_test(dataset_dir, pathway_dir, drug_ids_path, outdir, best_model_path, norm, do_layers, batchnorm, num_layers, hidden_gcn, hidden_fc, splits_dir, in_channels, n_classes, feature_names_path, model_type, k_hops) #######Computing attributions # print('OUTDIR', outdir) scaler = MinMaxScaler() model.eval() ig = IntegratedGradients(model) attributions_list = [] for sample in samples_of_interest: sample = sample.to(device) baseline = torch.tensor(0 * sample.x, requires_grad=True) sample.x = torch.tensor(sample.x, requires_grad=True) attributions, delta = ig.attribute(sample.x.view(1, -1, 1), baselines=baseline.view(1, -1, 1), additional_forward_args=(torch.tensor([0], device=device)), target=1, return_convergence_delta=True, internal_batch_size=1, n_steps=n_steps) attributions_list.append(scaler.fit_transform(attributions.detach().cpu().numpy().squeeze().reshape(-1,1)).reshape(1,-1)) # print('IG Attributions:', attributions) # log_handle = open(outdir+'/log_get_attributions_to_interpret.txt', 'w') # scores = model(baseline.view(1, -1, 1), torch.tensor([0], device=device)).detach().cpu().numpy()[0] # log_handle.write('n_steps\t{}\n'.format(n_steps)) # log_handle.write('Baseline score\tDelta\t:{}\t{}\t{}\n'.format(scores[0], scores[1],delta.item())) # log_handle.close() # Creates output dataframe # attributions_list = scaler.fit_transform(np.array(attributions_list).squeeze().reshape(-1,1)).reshape(1,-1) attributions = pd.DataFrame(np.vstack(attributions_list)) attributions.columns = feature_names return attributions
def get_single_experiment_scores(path_to_net, X_input): X_input = torch.from_numpy(X_input) X_input = X_input.float() X_input = X_input.permute(0, 2, 1).contiguous() # --- Preparing the model --- use_gpu = torch.cuda.is_available() CNN = Multiple_Input_Model() if use_gpu: CNN = torch.nn.DataParallel(CNN, device_ids=[0]) X_input = X_input.to(DEVICE_ID) # --- Loading net and getting metrics --- CNN, start_epoch, valid_loss_min = load_net(path_to_net, CNN) CNN.eval() ig = IntegratedGradients(CNN) # ig = DeepLift(CNN) attributions, delta = ig.attribute(X_input, target=1, return_convergence_delta=True) attributions = attributions.cpu().numpy() return attributions
def predict_step(self, batch, batch_idx, dataloader_idx=0): """IG wrapped in predict_step for multi-GPU scaling via Trainer.predict()""" ig = IntegratedGradients(self.predict) attributions, delta = ig.attribute(tuple(batch), method='gausslegendre', return_convergence_delta=True) return attributions, delta
def explain_handle(self, model_wraper, text, target=1): # pylint: disable=too-many-locals,unused-argument,arguments-differ """Captum explanations handler. Args: data_preprocess (Torch Tensor): Preprocessed data to be used for captum raw_data (list): The unprocessed data to get target from the request Returns: dict : A dictionary response with the explanations response. """ model_wrapper = AGNewsmodelWrapper(self.model) tokenizer = BertTokenizer(self.vocab_file) model_wrapper.eval() model_wrapper.zero_grad() input_ids = torch.tensor( [tokenizer.encode(self.text, add_special_tokens=True)]) input_embedding_test = model_wrapper.model.bert_model.embeddings( input_ids) preds = model_wrapper(input_embedding_test) out = np.argmax(preds.cpu().detach(), axis=1) out = out.item() ig_1 = IntegratedGradients(model_wrapper) attributions, delta = ig_1.attribute( # pylint: disable=no-member input_embedding_test, n_steps=500, return_convergence_delta=True, target=1, ) tokens = tokenizer.convert_ids_to_tokens(input_ids[0].numpy().tolist()) feature_imp_dict = {} feature_imp_dict["words"] = tokens attributions_sum = self.summarize_attributions(attributions) feature_imp_dict["importances"] = attributions_sum.tolist() feature_imp_dict["delta"] = delta[0].tolist() return [feature_imp_dict]
def get_token_wise_attributions( fn, model, embedding_outputs, attention_masks, name, position, token_index, n_steps, internal_batch_size=4, method="riemann_right", ): int_grad = IntegratedGradients( fn, multiply_by_inputs=True, ) attributions, approximation_error = int_grad.attribute( embedding_outputs, target=token_index, n_steps=n_steps, method=method, additional_forward_args=(model, attention_masks, name, position), internal_batch_size=internal_batch_size, return_convergence_delta=True, ) return { "attributions": attributions, "delta": approximation_error, }
def test_basic_infidelity_multiple_with_normalize(self) -> None: input1 = torch.tensor([3.0] * 3) input2 = torch.tensor([1.0] * 3) inputs = (input1, input2) expected = torch.zeros(3) model = BasicModel2() ig = IntegratedGradients(model) attrs = ig.attribute(inputs) scaled_attrs = tuple(attr * 100 for attr in attrs) infid = self.infidelity_assert(model, attrs, inputs, expected, normalize=True) scaled_infid = self.infidelity_assert( model, scaled_attrs, inputs, expected, normalize=True, ) # scaling attr should not change normalized infidelity assertTensorAlmostEqual(self, infid, scaled_infid)
def PT_IntegratedGradient(model, x, y_onthot, baseline=None, n_steps=50, method='riemann_trapezoid', device='cuda:0', **kwargs): input = torch.tensor(x, requires_grad=True).to(device) model = model.to(device) model.eval() saliency = IntegratedGradients(model) target = torch.tensor(np.argmax(y_onthot, -1)).to(device) if baseline is not None: baseline = torch.tensor(baseline).to(device) # if method == Riemann.trapezoid: # return list(np.linspace(0, 1, n)) # elif method == Riemann.left: # return list(np.linspace(0, 1 - 1 / n, n)) # elif method == Riemann.middle: # return list(np.linspace(1 / (2 * n), 1 - 1 / (2 * n), n)) # elif method == Riemann.right: # return list(np.linspace(1 / n, 1, n)) # Ref: https://github.com/pytorch/captum/blob/master/captum/attr/_utils/approximation_methods.py attribution_map = saliency.attribute(input, target=target, baselines=baseline, n_steps=n_steps, method=method) return attribution_map.detach().cpu().numpy()
class IntegradedGradientsCallback(Callback): "Captum Callback for Resnet Interpretation" def __init__(self): pass def after_fit(self): self.integrated_gradients = IntegratedGradients(self.model) def visualize(self, inp_data, n_steps=200, cmap_name='custom blue', colors=None, N=256, methods=None, signs=None, outlier_perc=1): if methods is None: methods=['original_image','heat_map'] if signs is None: signs=["all", "positive"] dl = self.dls.test_dl(L(inp_data),with_labels=True, bs=1) self.enc_inp,self.enc_preds= dl.one_batch() dec_data=dl.decode((self.enc_inp,self.enc_preds)) self.dec_img,self.dec_pred=dec_data[0][0],dec_data[1][0] self.colors = [(0, '#ffffff'),(0.25, '#000000'),(1, '#000000')] if colors is None else colors self.attributions_ig = self.integrated_gradients.attribute(self.enc_inp.to(self.dl.device), target=self.enc_preds, n_steps=200) default_cmap = LinearSegmentedColormap.from_list(cmap_name, self.colors, N=N) _ = viz.visualize_image_attr_multiple(np.transpose(self.attributions_ig.squeeze().cpu().detach().numpy(), (1,2,0)), np.transpose(self.dec_img.numpy(), (1,2,0)), methods=methods, cmap=default_cmap, show_colorbar=True, signs=signs, outlier_perc=outlier_perc, titles=[f'Original Image - ({self.dec_pred})', 'IG'])
def feature_conductance(test_input_tensor): ig = IntegratedGradients(net) test_input_tensor.requires_grad_() attr, _ = ig.attribute(test_input_tensor, target=1, return_convergence_delta=True) attr = attr.detach().numpy() # To understand these attributions, we can first average them across all the inputs # and print and visualize the average attribution for each feature. feature_imp, feature_imp_dict = visualize_importances(feature_names, np.mean(attr, axis=0)) mlflow.log_metrics(feature_imp_dict) mlflow.log_text(str(feature_imp), "feature_imp_summary.txt") fig, (ax1, ax2) = plt.subplots(2, 1) fig.tight_layout(pad=3) ax1.hist(attr[:, 1], 100) ax1.set(title="Distribution of Sibsp Attribution Values") # we can bucket the examples by the value of the sibsp feature and # plot the average attribution for the feature. # In the plot below, the size of the dot is proportional to # the number of examples with that value. bin_means, bin_edges, _ = stats.binned_statistic( test_features[:, 1], attr[:, 1], statistic="mean", bins=6 ) bin_count, _, _ = stats.binned_statistic( test_features[:, 1], attr[:, 1], statistic="count", bins=6 ) bin_width = bin_edges[1] - bin_edges[0] bin_centers = bin_edges[1:] - bin_width / 2 ax2.scatter(bin_centers, bin_means, s=bin_count) ax2.set(xlabel="Average Sibsp Feature Value", ylabel="Average Attribution") mlflow.log_figure(fig, "Average_Sibsp_Feature_Value.png")
def integrated_gradients(self, xs, labels, baselines): attributor = IntegratedGradients(self) # BS, T, C if baselines is not None and baselines.ndim == 2: baselines = baselines.unsqueeze(0) attributions = attributor.attribute(xs, target=labels, baselines=baselines) attributions = attributions.mean(dim=-1) return attributions
def explain_ig(model, x, edge_index, target, include_edges=None): ig = IntegratedGradients(model_forward) input_mask = torch.ones(edge_index.shape[1]).requires_grad_(True).to(device) ig_mask = ig.attribute(input_mask, target=target, additional_forward_args=(model, x, edge_index), internal_batch_size=edge_index.shape[1]) edge_mask = ig_mask.cpu().detach().numpy() return edge_mask
def _compute_attributions( self, single_forward_output: Dict[str, numpy.ndarray], instance: Instance, n_steps: int = 50, ) -> List[List[Attribution]]: """Attributes the prediction to the input. The attributions are calculated by means of the [Integrated Gradients](https://arxiv.org/abs/1703.01365) method. Parameters ---------- single_forward_output Non-batched forward output containing numpy arrays instance The instance containing the input data n_steps The number of steps used when calculating the attribution of each token. Returns ------- attributions A list of list of attributions due to the the ListField level """ # captum needs `torch.Tensor`s and we need a batch dimension (-> unsqueeze) embeddings = torch.from_numpy( single_forward_output["embeddings"]).unsqueeze(0) mask = torch.from_numpy(single_forward_output["mask"]).unsqueeze(0) logits = torch.from_numpy(single_forward_output["logits"]).unsqueeze(0) ig = IntegratedGradients(self._encoder_and_head_forward) attributions, delta = ig.attribute( embeddings, n_steps=n_steps, target=torch.argmax(logits), additional_forward_args=mask, return_convergence_delta=True, ) attributions = attributions.sum(dim=3).squeeze(0) attributions = attributions / torch.norm(attributions) attributions = attributions.detach().numpy() document_tokens = [ cast(TextField, text_field).tokens for text_field in cast( ListField, instance.get(self.forward_arg_name)) ] return [[ Attribution( text=token.text, start=token.idx, end=self._get_token_end(token), field=self.forward_arg_name, attribution=attribution, ) for token, attribution in zip(sentence_tokens, sentence_attributions) ] for sentence_tokens, sentence_attributions in zip( document_tokens, attributions)]
def integrated_gradient_differential(net, input, target, adata, n_genes=None, target_class=1, clip="abs", save_name="feature_gradients", ig_pval=0.05, ig_fc=1, method="wilcoxon", batch_size=100): # Caculate integrated gradient ig = IntegratedGradients(net) df_results = {} attr, delta = ig.attribute(input, target=target_class, return_convergence_delta=True, internal_batch_size=batch_size) attr = attr.detach().cpu().numpy() if clip == 'positive': attr = np.clip(attr, a_min=0, a_max=None) elif clip == 'negative': attr = abs(np.clip(attr, a_min=None, a_max=0)) else: attr = abs(attr) igadata = sc.AnnData(attr) igadata.var.index = adata.var.index igadata.obs.index = adata.obs.index igadata.obs['sensitive'] = target igadata.obs['sensitive'] = igadata.obs['sensitive'].astype('category') sc.tl.rank_genes_groups(igadata, 'sensitive', method=method, n_genes=n_genes) for label in [0, 1]: try: df_degs = ut.get_de_dataframe(igadata, label) df_degs = df_degs.loc[(df_degs.pvals_adj < ig_pval) & (df_degs.logfoldchanges >= ig_fc)] df_degs.to_csv("saved/results/DIG_class_" + str(target_class) + "_" + str(label) + save_name + '.csv') df_results[label] = df_degs except: logging.warning("Only one class, no two calsses critical genes") return adata, igadata, list(df_results[0].names), list(df_results[1].names)
def explain_ig_node(model, x, edge_index, target, include_edges=None): ig = IntegratedGradients(model_forward_node) input_mask = x.clone().requires_grad_(True).to(device) ig_mask = ig.attribute(input_mask, target=target, additional_forward_args=(model, edge_index), internal_batch_size=input_mask.shape[0]) node_attr = ig_mask.cpu().detach().numpy().sum(axis=1) edge_mask = node_attr_to_edge(edge_index, node_attr) return edge_mask
def explain_prediction( self, prediction: Dict[str, numpy.array], instance: Instance, n_steps: int ) -> Dict[str, Any]: """Here, we must apply transformations for manage ListFields tensors shapes""" dataset = Batch([instance]) input_tokens_ids = dataset.as_tensor_dict() ig = IntegratedGradients(self._explain_embeddings) num_wrapping_dims = 1 document_tokens = [ [token.text for token in cast(TextField, text_field).tokens] for text_field in cast(ListField, instance.get(self.forward_arg_name)) ] document_tensors = input_tokens_ids.get(self.forward_arg_name) mask = get_text_field_mask( document_tensors, num_wrapping_dims=num_wrapping_dims ) text_embeddings = self.backbone.embedder.forward( document_tensors, num_wrapping_dims=num_wrapping_dims ) label_id = vocabulary.index_for_label( self.backbone.vocab, prediction.get(self.label_name) ) attributions, delta = ig.attribute( text_embeddings, target=label_id, additional_forward_args=mask, return_convergence_delta=True, n_steps=n_steps, ) attributions = attributions.sum(dim=3).squeeze(0) attributions = attributions / torch.norm(attributions) attributions = attributions.detach().numpy() return { **prediction, "explain": { self.forward_arg_name: [ [ {"token": token, "attribution": attribution} for token, attribution in zip( sentence_tokens, sentence_attribution ) ] for sentence_tokens, sentence_attribution in zip( document_tokens, attributions ) ] }, }
def visualizations(model, smile, feature_list, color_map=plt.cm.bwr): model.eval() adjacency, nodes, edges = smile_to_graph(smile) mols = Chem.MolFromSmiles(smile) ig = IntegratedGradients(model) adjacency, nodes, edges = molgraph_collate_fn( ((adjacency, nodes, edges), )) attr = ig.attribute(nodes, additional_forward_args=(edges, adjacency), target=0) attr1 = torch.squeeze(attr, dim=0) attr2 = attr1.sum(dim=1) vmax = max(attr2.abs().max(), 1e-16) vmin = -vmax node_colors = get_colors(attr1, color_map) node_colors = node_colors[:, :3] fig, ax = plt.subplots(figsize=(6, 1)) fig.subplots_adjust(bottom=0.5) norm = plt.Normalize(vmin, vmax) fig.colorbar(cm.ScalarMappable(norm=norm, cmap=color_map), cax=ax, orientation='horizontal', label='color_bar') b = BytesIO() b.write( moltopng(mols, node_colors=node_colors, edge_colors={}, molSize=(600, 600))) b.seek(0) display(Image.open(b)) b.close() symbols = { i: f'{mols.GetAtomWithIdx(i).GetSymbol()}{i}' for i in range(mols.GetNumAtoms()) } x_pos = (np.arange(len(feature_list))) y_pos = (np.arange(len(list(symbols.values())))) plt.matshow(attr1, cmap=color_map) plt.xticks(x_pos, feature_list, rotation='vertical') plt.yticks(y_pos, list(symbols.values())) plt.show() visualize_importances(list(symbols.values()), attr2)
def compute_integrated_gradients(self, img_path, target): # open image img, transformed_img, input = self.open_image(img_path) integrated_gradients = IntegratedGradients(self.model) attributions_ig = integrated_gradients.attribute(input, target=target, n_steps=200) attributions_ig = np.transpose( attributions_ig.squeeze().cpu().detach().numpy(), (1, 2, 0)) return attributions_ig
def test_classification_infidelity_tpl_target_w_baseline(self) -> None: model = BasicModel_MultiLayer() input = torch.arange(1.0, 13.0).view(4, 3) baseline = torch.ones(4, 3) additional_forward_args = (torch.arange(1, 13).view(4, 3).float(), True) targets: List = [(0, 1, 1), (0, 1, 1), (1, 1, 1), (0, 1, 1)] ig = IntegratedGradients(model) def perturbed_func2(inputs, baselines): return torch.ones(baselines.shape), baselines @infidelity_perturb_func_decorator(True) def perturbed_func3(inputs, baselines): return baselines attr, delta = ig.attribute( input, target=targets, additional_forward_args=additional_forward_args, baselines=baseline, return_convergence_delta=True, ) infid = self.infidelity_assert( model, attr, input, torch.tensor([0.10686, 0.0, 0.0, 0.0]), additional_args=additional_forward_args, baselines=baseline, target=targets, multi_input=False, n_perturb_samples=3, perturb_func=perturbed_func3, ) infid2 = self.infidelity_assert( model, attr, input, torch.tensor([0.10686, 0.0, 0.0, 0.0]), additional_args=additional_forward_args, baselines=baseline, target=targets, multi_input=False, n_perturb_samples=3, perturb_func=perturbed_func2, ) assertTensorAlmostEqual(self, infid, delta * delta) assertTensorAlmostEqual(self, infid, infid2)
def get_integrated_gradients_attribution_with_prediction( model: nn.Module, image: np.ndarray): torch.manual_seed(0) np.random.seed(0) transform_normalize = transforms.Compose([ transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), ]) input_tensor = transform_normalize(image) input_tensor = input_tensor.unsqueeze(0) print("Performing forward pass...") model = model.eval() output = model(input_tensor) output = F.softmax(output, dim=1) _, pred_label_idx = torch.topk(output, 1) pred_label_idx.squeeze_() prediction = pred_label_idx.item() print("Generating visualization...") tic = time.time() visualization = IntegratedGradients(model) attributions = visualization.attribute(input_tensor, target=pred_label_idx, n_steps=5) attributions = attributions.squeeze().cpu().detach().numpy() attributions = np.transpose(attributions, (1, 2, 0)) toc = time.time() print("Took %.02fs" % (toc - tic)) custom_cmap = LinearSegmentedColormap.from_list("custom blue", [(0, "#ffffff"), (0.25, "#000000"), (1, "#000000")], N=256) fig, ax = viz.visualize_image_attr( attributions, method="heat_map", sign="positive", cmap=custom_cmap, show_colorbar=True, ) ax.margins(0) fig.tight_layout(pad=0) return fig, prediction
def get_attribution(model, input, target, method, device, saliency_layer='features.norm5', iba_wrapper=None): input = input.to(device) input.requires_grad = True # get attribution if method == "grad_cam": saliency_map = grad_cam(model, input, target, saliency_layer=saliency_layer) elif method == "extremal_perturbation": saliency_map, _ = extremal_perturbation(model, input, target) elif method == 'ib': assert iba_wrapper, "Please give a iba wrapper as function parameter!" saliency_map = iba_wrapper.iba(model, input, target, device) elif method == 'reverse_ib': assert iba_wrapper, "Please give a iba wrapper as function parameter!" saliency_map = iba_wrapper.iba(model, input, target, device, reverse_lambda=True) elif method == "gradient": saliency_map = gradient(model, input, target) elif method == "excitation_backprop": saliency_map = excitation_backprop(model, input, target, saliency_layer=saliency_layer) elif method == "integrated_gradients": ig = IntegratedGradients(model) saliency_map, _ = ig.attribute(input, target=target, return_convergence_delta=True) saliency_map = saliency_map.squeeze().mean(0) # ib heatmap already a numpy array scaled to image size if method != 'ib' and method != 'reverse_ib': saliency_map = saliency_map.detach().cpu().numpy().squeeze() shape = (224, 224) saliency_map = resize(saliency_map, shape, order=1, preserve_range=True) return saliency_map
def interpret(self, data_loader, n_features, n_classes, save_path=None): batch = next(iter(data_loader)) e = batch.edge_index.to(self.device).long() def model_forward(input): out = self.net(input, e) return out self.net.eval() importances = np.zeros((n_features, n_classes)) for batch in data_loader: input = batch.x.to(self.device) target = batch.y.to(self.device) ig = IntegratedGradients(model_forward) attributions = ig.attribute(input, target=target) attributions = attributions.to('cpu').detach().numpy() importances[:, target.to('cpu').numpy()] += attributions return importances
class VisionHandler(BaseHandler, ABC): """ Base class for all vision handlers """ def initialize(self, context): super().initialize(context) self.ig = IntegratedGradients(self.model) self.initialized = True properties = context.system_properties if not properties.get("limit_max_image_pixels"): Image.MAX_IMAGE_PIXELS = None def preprocess(self, data): """The preprocess function of MNIST program converts the input data to a float tensor Args: data (List): Input data from the request is in the form of a Tensor Returns: list : The preprocess function returns the input image as a list of float tensors. """ images = [] for row in data: # Compat layer: normally the envelope should just return the data # directly, but older versions of Torchserve didn't have envelope. image = row.get("data") or row.get("body") if isinstance(image, str): # if the image is a string of bytesarray. image = base64.b64decode(image) # If the image is sent as bytesarray if isinstance(image, (bytearray, bytes)): image = Image.open(io.BytesIO(image)) image = self.image_processing(image) else: # if the image is a list image = torch.FloatTensor(image) images.append(image) return torch.stack(images).to(self.device) def get_insights(self, tensor_data, _, target=0): print("input shape", tensor_data.shape) return self.ig.attribute(tensor_data, target=target, n_steps=15).tolist()
def explain_handle(self, model_wraper, text, target=1): """Captum explanations handler Args: data_preprocess (Torch Tensor): Preprocessed data to be used for captum raw_data (list): The unprocessed data to get target from the request Returns: dict : A dictionary response with the explanations response. """ vis_data_records_base = [] model_wrapper = AGNewsmodelWrapper(self.model) tokenizer = BertTokenizer(self.VOCAB_FILE) model_wrapper.eval() model_wrapper.zero_grad() encoding = tokenizer.encode_plus(self.text, return_attention_mask=True, return_tensors="pt", add_special_tokens=False) input_ids = encoding["input_ids"] attention_mask = encoding["attention_mask"] input_ids = input_ids.to(self.device) attention_mask = attention_mask.to(self.device) input_embedding_test = model_wrapper.model.bert_model.embeddings( input_ids) preds = model_wrapper(input_embedding_test, attention_mask) out = np.argmax(preds.cpu().detach(), axis=1) out = out.item() ig_1 = IntegratedGradients(model_wrapper) attributions, delta = ig_1.attribute( # pylint: disable=no-member input_embedding_test, n_steps=500, return_convergence_delta=True, target=1, ) tokens = tokenizer.convert_ids_to_tokens( input_ids[0].cpu().numpy().tolist()) feature_imp_dict = {} feature_imp_dict["words"] = tokens attributions_sum = self.summarize_attributions(attributions) feature_imp_dict["importances"] = attributions_sum.tolist() feature_imp_dict["delta"] = delta[0].tolist() self.add_attributions_to_visualizer(attributions, tokens, self.score_func(preds), out, 2, 1, delta, vis_data_records_base) return [feature_imp_dict]