Source code for peal.teachers.segmentation_mask_teacher
"""
A teacher that judges counterfactuals with ground-truth segmentation masks.
Teachers are PEAL's sources of feedback on counterfactuals. This one uses the
dataset's hint masks (a binary mask of the region that legitimately carries the
class, e.g. the object rather than the background) to decide whether the
attribution map of a counterfactual concentrates on the right region. It is
the automatic stand-in for a human annotator in CFKD experiments on datasets
with segmentation hints.
"""
from peal.teachers.interfaces import TeacherInterface
[docs]
class SegmentationMaskTeacher(TeacherInterface):
"""
Teacher that scores attribution maps against segmentation hints.
For every counterfactual the (mean-centred) hint mask is multiplied with
the attribution heatmap; if the mean of that product exceeds
``attribution_threshold`` the change lies inside the annotated region and
the feedback is ``"true"``, otherwise ``"false"``.
Parameters
----------
attribution_threshold : float
Decision threshold on the mean of ``heatmap * (hint - hint.mean())``.
dataset : peal dataset
Dataset providing ``generate_contrastive_collage`` for tracking.
counterfactual_type : {"1sided", "2sided"}
With ``"1sided"`` a counterfactual whose original prediction was
wrong gets the feedback ``"student originally wrong!"``.
tracking_level : int
Verbosity level; ``>= 5`` writes collage images to ``base_dir``.
"""
[docs]
def __init__(
self,
attribution_threshold,
dataset,
counterfactual_type="1sided",
tracking_level=0,
):
self.attribution_threshold = attribution_threshold
self.dataset = dataset
self.counterfactual_type = counterfactual_type
self.tracking_level = tracking_level
[docs]
def get_feedback(
self,
x_attribution_list,
hint_list,
base_dir,
x_counterfactual_list,
y_source_list,
y_target_list,
x_list,
y_list,
y_target_end_confidence_list,
student=None,
**kwargs,
):
"""
Produce one feedback string per counterfactual.
Parameters
----------
x_attribution_list : list of torch.Tensor
Attribution heatmap of each counterfactual (same spatial shape as
the hint mask).
hint_list : list of torch.Tensor
Binary segmentation mask of the legitimate region per sample.
base_dir : str
Directory the collage is written to when ``tracking_level >= 5``.
x_counterfactual_list : list of torch.Tensor
Counterfactual images.
y_source_list : list
Class the student predicted for each original sample.
y_target_list : list
Target class of each counterfactual.
x_list : list of torch.Tensor
Original images.
y_list : list
Ground-truth labels of the originals.
y_target_end_confidence_list : list of float
Student confidence for the target class on the counterfactual.
student : torch.nn.Module
The classifier being explained; only used to determine the device.
**kwargs
Forwarded to ``dataset.generate_contrastive_collage``.
Returns
-------
list of str
One of ``"true"``, ``"false"``, ``"student originally wrong!"``
or ``"student not swapped!"`` per counterfactual.
"""
feedback = []
device = "cuda" if next(student.parameters()).is_cuda else "cpu"
for idx, heatmap in enumerate(x_attribution_list):
#
if (
self.counterfactual_type == "1sided"
and y_list[idx] != y_source_list[idx]
):
feedback.append("student originally wrong!")
elif y_target_end_confidence_list[idx] < 0.5:
feedback.append("student not swapped!")
else:
hints = hint_list[idx].float()
hints = hints - hints.mean()
joint_map = heatmap * hints
true_counterfactual_score = joint_map.mean()
if true_counterfactual_score > self.attribution_threshold:
feedback.append("true")
else:
feedback.append("false")
if self.tracking_level >= 5:
self.dataset.generate_contrastive_collage(
y_counterfactual_teacher_list=y_list,
y_target_end_confidence_list=y_target_end_confidence_list,
y_original_teacher_list=list(
map(
lambda x: x[0] if x[1] == "true" else abs(1 - x[0]),
zip(y_list, feedback),
)
),
feedback_list=feedback,
x_counterfactual_list=x_counterfactual_list,
y_source_list=y_source_list,
y_target_list=y_target_list,
x_list=x_list,
y_list=y_list,
base_path=base_dir,
**kwargs,
)
return feedback