Skip to content

Commit 18463d8

Browse files
committed
variable names more general; remove use_cuda
1 parent 7c6e16d commit 18463d8

File tree

15 files changed

+158
-108
lines changed

15 files changed

+158
-108
lines changed

interpretdl/evaluate_interpreter/abc_evaluator.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,20 @@ class InterpreterEvaluator(ABC):
1414
All evaluators aim to evaluate the trustworthiness of the interpretation algorithms. Besides theoretical
1515
verification of the algorithm, here the evaluators validate the trustworthiness by looking through the obtained
1616
explanations from the interpretation algorithms. Different evaluators are provided.
17-
18-
.. warning:: ``use_cuda`` would be deprecated soon. Use ``device`` directly.
1917
"""
2018

21-
def __init__(self, paddle_model: callable or None, device: str = 'gpu:0', use_cuda: bool = None, **kwargs):
19+
def __init__(self, model: callable or None, device: str = 'gpu:0', **kwargs):
2220
"""
2321
2422
Args:
25-
paddle_model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions. This
23+
model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions. This
2624
is not always required if the model is not involved.
27-
device (str): The device used for running ``paddle_model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"``
25+
device (str): The device used for running ``model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"``
2826
etc. Again, this is not always required if the model is not involved.
2927
"""
3028

31-
if use_cuda in [True, False]:
32-
warnings.warn('``use_cuda`` would be deprecated soon. '
33-
'Use ``device`` directly.', stacklevel=2)
34-
device = 'gpu' if use_cuda and device[:3] == 'gpu' else 'cpu'
35-
3629
self.device = device
37-
self.paddle_model = paddle_model
30+
self.model = model
3831
self.predict_fn = None
3932

4033
def _build_predict_fn(self, rebuild: bool = False):
@@ -52,7 +45,7 @@ def _build_predict_fn(self, rebuild: bool = False):
5245
paddle.set_device(self.device)
5346

5447
# to get gradients, the ``train`` mode must be set.
55-
self.paddle_model.eval()
48+
self.model.eval()
5649

5750
def predict_fn(inputs):
5851
"""predict_fn for input gradients based interpreters,
@@ -69,7 +62,7 @@ def predict_fn(inputs):
6962
with paddle.no_grad():
7063
inputs = tuple(paddle.to_tensor(inp) for inp in inputs) if isinstance(inputs, tuple) \
7164
else (paddle.to_tensor(inputs), )
72-
logits = self.paddle_model(*inputs) # get logits, [bs, num_c]
65+
logits = self.model(*inputs) # get logits, [bs, num_c]
7366
probas = paddle.nn.functional.softmax(logits, axis=1) # get probabilities.
7467
return probas.numpy()
7568

interpretdl/evaluate_interpreter/deletion_insertion.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,25 @@ class DeletionInsertion(InterpreterEvaluator):
2626
"""
2727

2828
def __init__(self,
29-
paddle_model: callable,
29+
model: callable,
3030
device: str,
31-
use_cuda: bool = None,
3231
compute_deletion: bool = True,
3332
compute_insertion: bool = True,
3433
**kwargs):
3534
"""
3635
3736
Args:
38-
paddle_model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions. This
37+
model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions. This
3938
is not always required if the model is not involved.
40-
device (str): The device used for running ``paddle_model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"``
39+
device (str): The device used for running ``model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"``
4140
etc. Again, this is not always required if the model is not involved.
4241
compute_deletion (bool, optional): Whether compute deletion score. Defaults to ``True``.
4342
compute_insertion (bool, optional): Whether compute insertion score. Defaults to ``True``.
4443
4544
Raises:
4645
ValueError: At least one of ``compute_deletion`` and ``compute_insertion`` must be True.
4746
"""
48-
super().__init__(paddle_model, device, use_cuda, **kwargs)
47+
super().__init__(model, device, **kwargs)
4948

5049
if (not compute_deletion) or (not compute_insertion):
5150
raise ValueError('At least one of ``compute_deletion`` and ``compute_insertion`` must be True.')

interpretdl/evaluate_interpreter/infidelity.py

Lines changed: 86 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,22 @@ class Infidelity(InterpreterEvaluator):
3333
pixels; while the difference (the latter term) should also be large because the model depends on important pixels
3434
to make decisions. Like this, large values would be offset by large values if the explanation is faithful to the
3535
model. Otherwise, for uniform explanations (all being constant), the former term would be a constant value and the
36-
infidelity would become large
36+
infidelity would become large.
3737
3838
More details about the measure can be found in the original paper: https://arxiv.org/abs/1901.09392.
3939
"""
4040
def __init__(self,
41-
paddle_model: callable,
41+
model: callable,
4242
device: str = 'gpu:0',
4343
**kwargs):
4444
"""
4545
4646
Args:
47-
paddle_model (callable): _description_
47+
model (callable): _description_
4848
device (_type_, optional): _description_. Defaults to 'gpu:0'.
4949
"""
5050

51-
super().__init__(paddle_model, device, None, **kwargs)
51+
super().__init__(model, device, **kwargs)
5252
self.results = {}
5353

5454
def _build_predict_fn(self, rebuild: bool = False):
@@ -74,7 +74,7 @@ def _build_predict_fn(self, rebuild: bool = False):
7474
paddle.set_device(self.device)
7575

7676
# to get gradients, the ``train`` mode must be set.
77-
self.paddle_model.eval()
77+
self.model.eval()
7878

7979
def predict_fn(data):
8080
"""predict_fn for input gradients based interpreters,
@@ -91,7 +91,7 @@ def predict_fn(data):
9191
with paddle.no_grad():
9292
# Follow the `official implementation <https://github.com/chihkuanyeh/saliency_evaluation>`_
9393
# to use logits as output.
94-
logits = self.paddle_model(paddle.to_tensor(data)) # get logits, [bs, num_c]
94+
logits = self.model(paddle.to_tensor(data)) # get logits, [bs, num_c]
9595
# probas = paddle.nn.functional.softmax(logits, axis=1) # get probabilities.
9696
return logits.numpy()
9797

@@ -234,34 +234,92 @@ def evaluate(self,
234234

235235

236236
class InfidelityNLP(InterpreterEvaluator):
237-
def __init__(self, paddle_model: callable or None, device: str = 'gpu:0', **kwargs):
238-
super().__init__(paddle_model, device, **kwargs)
237+
def __init__(self, model: callable or None, device: str = 'gpu:0', **kwargs):
238+
super().__init__(model, device, **kwargs)
239239
self.results = {}
240240

241-
def _generate_samples(self, input_ids, masked_id=0):
241+
def _generate_samples(self, input_ids, masked_id: int, is_random_samples: bool):
242242
num_tokens = len(input_ids)
243243

244-
# like 1d-conv, stride=1, kernel-size={1,2,3,4,5}
245-
generated_samples = []
246-
input_ids_array = np.array([input_ids])
247-
for ks in range(1, 6):
248-
if ks > num_tokens - 2:
249-
break
250-
for i in range(1, num_tokens-ks):
251-
tmp = np.copy(input_ids_array)
252-
tmp[0, i:i+ks] = masked_id
253-
generated_samples.append(tmp)
254-
255-
perturbed_samples = np.concatenate(generated_samples, axis=0)
256-
Is = perturbed_samples != input_ids_array
257-
258-
return perturbed_samples, Is
259-
260-
def evaluate(self, raw_text: str, explanation: list or np.ndarray, tokenizer: callable, recompute: bool = False):
244+
if is_random_samples:
245+
# This is more suitable for long documents.
246+
# we concat three kinds of perturbations:
247+
# randomly perturbing 1%, 2%, 3%, 4% or 5% tokens respectively
248+
# with 40 times
249+
num_repeats = 40
250+
results = []
251+
ids_array = np.array([input_ids]*num_repeats)
252+
for p in range(1, 6):
253+
_k = int(num_tokens * p / 100)
254+
255+
# not choose from {0, -1}, i.e., [CLS] and [SEP]
256+
# https://stackoverflow.com/a/53893160/4834515
257+
pert_k = np.random.rand(num_repeats, num_tokens-2).argpartition(_k, axis=1)[:,:_k] + 1
258+
259+
pert_array = np.copy(ids_array)
260+
# vectorized slicing.
261+
# https://stackoverflow.com/a/74024396/4834515
262+
row_indexes = np.arange(num_repeats)[:, None]
263+
pert_array[row_indexes, pert_k] = masked_id
264+
265+
results.append(pert_array)
266+
267+
perturbed_samples = np.concatenate(results) # [200, num_tokens]
268+
Is = perturbed_samples != np.array([input_ids]) # [200, num_tokens]
269+
270+
return perturbed_samples, Is
271+
else:
272+
# This is more suitable for short documents.
273+
# like 1d-conv, stride=1, kernel-size={1,2,3,4,5}
274+
generated_samples = []
275+
input_ids_array = np.array([input_ids])
276+
for ks in range(1, 6):
277+
if ks > num_tokens - 2:
278+
break
279+
for i in range(1, num_tokens-ks):
280+
tmp = np.copy(input_ids_array)
281+
tmp[0, i:i+ks] = masked_id
282+
generated_samples.append(tmp)
283+
284+
perturbed_samples = np.concatenate(generated_samples, axis=0)
285+
Is = perturbed_samples != input_ids_array
286+
287+
return perturbed_samples, Is
288+
289+
# def _generate_samples(self, input_ids, masked_id=0):
290+
# num_tokens = len(input_ids)
291+
292+
# # we concat three kinds of perturbations:
293+
# # randomly perturbing 1, 2 or 3 tokens respectively
294+
# # with 33 times
295+
# num_repeats = 33
296+
297+
# ids_array = np.array([input_ids]*num_repeats)
298+
299+
# # not choose from {0, -1}, [CLS] and [SEP]
300+
# # https://stackoverflow.com/a/53893160/4834515
301+
# pert_1 = np.random.rand(num_repeats, num_tokens-2).argpartition(1, axis=1)[:,:1] + 1
302+
# pert_2 = np.random.rand(num_repeats, num_tokens-2).argpartition(2, axis=1)[:,:2] + 1
303+
# pert_3 = np.random.rand(num_repeats, num_tokens-2).argpartition(3, axis=1)[:,:3] + 1
304+
305+
# pert_1_array = np.copy(ids_array)
306+
# pert_2_array = np.copy(ids_array)
307+
# pert_3_array = np.copy(ids_array)
308+
309+
# # https://stackoverflow.com/a/74024396/4834515
310+
# row_indexes = np.arange(num_repeats)[:, None]
311+
# pert_1_array[row_indexes, pert_1] = masked_id
312+
# pert_2_array[row_indexes, pert_2] = masked_id
313+
# pert_3_array[row_indexes, pert_3] = masked_id
314+
315+
# perturbed_samples = np.concatenate([pert_1_array, pert_2_array, pert_3_array])
316+
# return perturbed_samples, perturbed_samples != ids_array
317+
318+
def evaluate(self, raw_text: str, explanation: list or np.ndarray, tokenizer: callable, max_seq_len=128, is_random_samples=False, recompute: bool = False):
261319
self._build_predict_fn()
262320

263321
# tokenizer text to ids
264-
encoded_inputs = tokenizer(raw_text, max_seq_len=128)
322+
encoded_inputs = tokenizer(raw_text, max_seq_len=max_seq_len)
265323
# order is important. *_batched_and_to_tuple will be the input for the model.
266324
_batched_and_to_tuple = tuple([np.array([v]) for v in encoded_inputs.values()])
267325

@@ -276,7 +334,7 @@ def evaluate(self, raw_text: str, explanation: list or np.ndarray, tokenizer: ca
276334
# generate perturbation samples.
277335
if 'proba_diff' not in self.results or recompute:
278336
## x and I related.
279-
generated_samples, Is = self._generate_samples(encoded_inputs['input_ids'], tokenizer.pad_token_id)
337+
generated_samples, Is = self._generate_samples(encoded_inputs['input_ids'], tokenizer.pad_token_id, is_random_samples)
280338
self.results['generated_samples'] = generated_samples
281339
self.results['Is'] = Is
282340
proba_pert = self.predict_fn(generated_samples)[:, label]

interpretdl/evaluate_interpreter/perturbation.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,25 @@ class Perturbation(InterpreterEvaluator):
3030
"""
3131

3232
def __init__(self,
33-
paddle_model: callable,
33+
model: callable,
3434
device: str = 'gpu:0',
3535
compute_MoRF: bool = True,
3636
compute_LeRF: bool = True,
3737
**kwargs):
3838
"""_summary_
3939
4040
Args:
41-
paddle_model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions. This
41+
model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions. This
4242
is not always required if the model is not involved.
43-
device (str): The device used for running ``paddle_model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"``
43+
device (str): The device used for running ``model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"``
4444
etc. Again, this is not always required if the model is not involved.
4545
compute_MoRF (bool, optional): Whether comptue MoRF score. Defaults to True.
4646
compute_LeRF (bool, optional): Whether comptue LeRF score. Defaults to True.
4747
4848
Raises:
4949
ValueError: 'At least one of ``compute_MoRF`` and ``compute_LeRF`` must be True.'
5050
"""
51-
super().__init__(paddle_model, device, None, **kwargs)
51+
super().__init__(model, device, **kwargs)
5252

5353
if (not compute_MoRF) and (not compute_LeRF):
5454
raise ValueError('At least one of ``compute_MoRF`` and ``compute_LeRF`` must be True.')
@@ -281,23 +281,23 @@ class PerturbationNLP(InterpreterEvaluator):
281281
"""
282282

283283
def __init__(self,
284-
paddle_model: callable,
284+
model: callable,
285285
device: str = 'gpu:0',
286286
**kwargs):
287287
"""_summary_
288288
289289
Args:
290-
paddle_model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions. This
290+
model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions. This
291291
is not always required if the model is not involved.
292-
device (str): The device used for running ``paddle_model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"``
292+
device (str): The device used for running ``model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"``
293293
etc. Again, this is not always required if the model is not involved.
294294
compute_MoRF (bool, optional): Whether comptue MoRF score. Defaults to True.
295295
compute_LeRF (bool, optional): Whether comptue LeRF score. Defaults to True.
296296
297297
Raises:
298298
ValueError: 'At least one of ``compute_MoRF`` and ``compute_LeRF`` must be True.'
299299
"""
300-
super().__init__(paddle_model, device, None, **kwargs)
300+
super().__init__(model, device, None, **kwargs)
301301
self._build_predict_fn()
302302

303303
def evaluate(self,

interpretdl/interpreter/_normlime_base.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ class NormLIMECVInterpreter(LIMECVInterpreter):
2020
2121
"""
2222

23-
def __init__(self, paddle_model: callable, device: str = 'gpu:0', use_cuda=None):
23+
def __init__(self, model: callable, device: str = 'gpu:0'):
2424
"""
2525
2626
Args:
27-
paddle_model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions.
28-
device (str): The device used for running ``paddle_model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"`` etc.
27+
model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions.
28+
device (str): The device used for running ``model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"`` etc.
2929
"""
3030

31-
LIMECVInterpreter.__init__(self, paddle_model, use_cuda=use_cuda, device=device)
31+
LIMECVInterpreter.__init__(self, model, device)
3232
self.lime_interpret = super().interpret
3333

3434
def _get_lime_weights(self, data, num_samples, batch_size, save=False):
@@ -188,14 +188,14 @@ class NormLIMENLPInterpreter(LIMENLPInterpreter):
188188
189189
"""
190190

191-
def __init__(self, paddle_model: callable, device: str = 'gpu:0', use_cuda=None):
191+
def __init__(self, model: callable, device: str = 'gpu:0'):
192192
"""
193193
194194
Args:
195-
paddle_model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions.
196-
device (str): The device used for running ``paddle_model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"`` etc.
195+
model (callable): A model with :py:func:`forward` and possibly :py:func:`backward` functions.
196+
device (str): The device used for running ``model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"`` etc.
197197
"""
198-
LIMENLPInterpreter.__init__(self, paddle_model, device, use_cuda)
198+
LIMENLPInterpreter.__init__(self, model, device)
199199
self.lime_interpret = super().interpret
200200

201201
def _get_lime_weights(self, data, preprocess_fn, num_samples, batch_size, unk_id, pad_id, lod_levels, save=False):

interpretdl/interpreter/abc_interpreter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ def __init__(self, model: callable, device: str = 'gpu:0') -> None:
541541
device (str): The device used for running ``model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"``
542542
etc.
543543
"""
544-
Interpreter.__init__(self, model, device, None)
544+
Interpreter.__init__(self, model, device)
545545

546546
def _build_predict_fn(self, rebuild=False, layer_name='word_embeddings', gradient_of='probability'):
547547

interpretdl/interpreter/consensus.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import numpy as np
2-
from .abc_interpreter import Interpreter
2+
3+
try:
4+
from .abc_interpreter_m import Interpreter
5+
except:
6+
from .abc_interpreter import Interpreter
37

48

59
class ConsensusInterpreter(object):
@@ -16,21 +20,20 @@ class ConsensusInterpreter(object):
1620
`PPClas <https://github.com/PaddlePaddle/PaddleClas/blob/release/2.3/ppcls/arch/backbone/__init__.py>`_.
1721
"""
1822

19-
def __init__(self, InterpreterClass, list_of_models: list, device: str = 'gpu:0', use_cuda=None, **kwargs):
23+
def __init__(self, InterpreterClass, list_of_models: list, device: str = 'gpu:0', **kwargs):
2024
"""
2125
2226
Args:
2327
InterpreterClass ([type]): The given Interpreter defined in InterpretDL.
2428
list_of_models (list): a list of trained models.
25-
device (str): The device used for running ``paddle_model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"``
29+
device (str): The device used for running ``model``, options: ``"cpu"``, ``"gpu:0"``, ``"gpu:1"``
2630
etc.
2731
"""
2832
assert issubclass(InterpreterClass, Interpreter)
2933

3034
self.InterpreterClass = InterpreterClass
3135
self.list_of_models = list_of_models
3236
self.device = device
33-
self.use_cuda = use_cuda
3437
self.other_args = kwargs
3538

3639
def interpret(self, inputs: str or list(str) or np.ndarray, **kwargs) -> np.ndarray:
@@ -86,7 +89,7 @@ def interpret(self, inputs: str or list(str) or np.ndarray, **kwargs) -> np.ndar
8689

8790
exps = []
8891
for model in self.list_of_models:
89-
interpreter = self.InterpreterClass(model, self.device, self.use_cuda, **self.other_args)
92+
interpreter = self.InterpreterClass(model, self.device, **self.other_args)
9093
raw_explanation = interpreter.interpret(inputs, visual=False, save_path=None, **kwargs)
9194
exps.append(raw_explanation)
9295

0 commit comments

Comments
 (0)