From d7b0ac788f2d5362cee597f93a2b1515102f7cfe Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 22:33:52 -0400 Subject: [PATCH 01/24] r --- pl_bolts/callbacks/vision/__init__.py | 0 pl_bolts/callbacks/vision/confused_logit.py | 110 ++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 pl_bolts/callbacks/vision/__init__.py create mode 100644 pl_bolts/callbacks/vision/confused_logit.py diff --git a/pl_bolts/callbacks/vision/__init__.py b/pl_bolts/callbacks/vision/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py new file mode 100644 index 0000000000..4a1cdf5f92 --- /dev/null +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -0,0 +1,110 @@ +import torch +from torch import nn +from matplotlib import pyplot as plt +from pytorch_lightning import Callback + + +class ConfusionCallback(Callback): + + def __init__(self, top_k, projection_factor=3): + """ + Takes the logit predictions of a model and when the probabilities of two classes are very close, the model + doesn't have high certainty that it should pick one vs the other class. + + This callback shows how the input would have to change to swing the model from one label prediction + to the other. + + .. note:: whenever called, this model will look for self.last_batch and self.last_logits in the LightningModule + + .. note:: this callback supports tensorboard only right now + + Args: + top_k: How many "offending" images we should plot + projection_factor: How much to multiply the input image to make it look more like this logit label + """ + self.top_k = top_k + self.projection_factor = projection_factor + + def on_batch_end(self, trainer, pl_module): + + # show images only every 20 batches + if (trainer.batch_idx + 1) % 20 != 0: + return + + # pick the last batch and logits + # TODO: use context instead + x, y = pl_module.last_batch + l = pl_module.last_logits + + # only check when it has opinions (ie: the logit > 5) + if l.max() > 5.0: + # pick the top two confused probs + (values, idxs) = torch.topk(l, k=2, dim=1) + + # care about only the ones that are at most eps close to each other + eps = 0.1 + mask = (values[:, 0] - values[:, 1]).abs() < eps + + if mask.sum() > 0: + # pull out the ones we care about + confusing_x = x[mask, ...] + confusing_y = y[mask] + confusing_l = l[mask] + + mask_idxs = idxs[mask] + + self._plot(confusing_x, confusing_y, trainer, pl_module, mask_idxs) + + def _plot(self, confusing_x, confusing_y, trainer, model, mask_idxs): + batch_size = confusing_x.size(0) + + confusing_x = confusing_x[:self.top_k] + confusing_y = confusing_y[:self.top_k] + + model.eval() + x_param_a = nn.Parameter(confusing_x) + x_param_b = nn.Parameter(confusing_x) + + for logit_i, x_param in enumerate((x_param_a, x_param_b)): + l = model(x_param.view(batch_size, -1)) + l[:, mask_idxs[:, logit_i]].sum().backward() + + # reshape grads + grad_a = x_param_a.grad.view(batch_size, 28, 28) + grad_b = x_param_b.grad.view(batch_size, 28, 28) + + for img_i in range(len(confusing_x)): + x = confusing_x[img_i].squeeze(0) + y = confusing_y[img_i] + ga = grad_a[img_i] + gb = grad_b[img_i] + + mask_idx = mask_idxs[img_i] + + fig = plt.figure(figsize=(15, 10)) + plt.subplot(231) + plt.imshow(x) + plt.colorbar() + plt.title(f'True: {y}', fontsize=20) + + plt.subplot(232) + plt.imshow(ga) + plt.colorbar() + plt.title(f'd{mask_idx[0]}-logit/dx', fontsize=20) + + plt.subplot(233) + plt.imshow(gb) + plt.colorbar() + plt.title(f'd{mask_idx[1]}-logit/dx', fontsize=20) + + plt.subplot(235) + plt.imshow(ga * 2 + x) + plt.colorbar() + plt.title(f'd{mask_idx[0]}-logit/dx', fontsize=20) + + plt.subplot(236) + plt.imshow(gb * 2 + x) + plt.colorbar() + plt.title(f'd{mask_idx[1]}-logit/dx', fontsize=20) + + trainer.logger.experiment.add_figure('confusing_imgs', fig, global_step=trainer.global_step) From 96209705bae9f8eaf7b3ab0ee1079858a7d0ad6d Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 22:34:54 -0400 Subject: [PATCH 02/24] r --- pl_bolts/callbacks/vision/__init__.py | 1 + pl_bolts/callbacks/vision/confused_logit.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pl_bolts/callbacks/vision/__init__.py b/pl_bolts/callbacks/vision/__init__.py index e69de29bb2..f7b0500bca 100644 --- a/pl_bolts/callbacks/vision/__init__.py +++ b/pl_bolts/callbacks/vision/__init__.py @@ -0,0 +1 @@ +from pl_bolts.callbacks.vision.confused_logit import ConfusedLogitCallback diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 4a1cdf5f92..2ddd35991c 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -4,7 +4,7 @@ from pytorch_lightning import Callback -class ConfusionCallback(Callback): +class ConfusedLogitCallback(Callback): def __init__(self, top_k, projection_factor=3): """ @@ -22,6 +22,7 @@ def __init__(self, top_k, projection_factor=3): top_k: How many "offending" images we should plot projection_factor: How much to multiply the input image to make it look more like this logit label """ + super().__init__() self.top_k = top_k self.projection_factor = projection_factor From 72bc915a79a15e66bc4aab02f6300e69d6333210 Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 22:36:57 -0400 Subject: [PATCH 03/24] r --- docs/source/index.rst | 1 + docs/source/vision_callbacks.rst | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 docs/source/vision_callbacks.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index 83995b6289..7e359313e9 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -21,6 +21,7 @@ PyTorch-Lightning-Bolts documentation callbacks info_callbacks variational_callbacks + vision_callbacks .. toctree:: :maxdepth: 2 diff --git a/docs/source/vision_callbacks.rst b/docs/source/vision_callbacks.rst new file mode 100644 index 0000000000..fe51d31d7f --- /dev/null +++ b/docs/source/vision_callbacks.rst @@ -0,0 +1,15 @@ +.. role:: hidden + :class: hidden-section + +Vision Callbacks +===================== +Useful callbacks for vision models + +--------------- + +Confused Logit Callback +----------------------- +Shows how the input would have to change to move the prediction from one logit to the other + +.. autoclass:: pl_bolts.callbacks.vision.confused_logit.ConfusedLogitCallback + :noindex: From 10b23aa9080f2ee696b8211fc0afbccc80a47bff Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 22:40:32 -0400 Subject: [PATCH 04/24] r --- docs/source/vision_callbacks.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/source/vision_callbacks.rst b/docs/source/vision_callbacks.rst index fe51d31d7f..2b5ef9dd48 100644 --- a/docs/source/vision_callbacks.rst +++ b/docs/source/vision_callbacks.rst @@ -11,5 +11,12 @@ Confused Logit Callback ----------------------- Shows how the input would have to change to move the prediction from one logit to the other + +Example outputs: + + .. image:: _images/vision/confused_logit.png + :width: 400 + :alt: Example of prediction confused between 5 and 8 + .. autoclass:: pl_bolts.callbacks.vision.confused_logit.ConfusedLogitCallback :noindex: From 10a5ccac12d9d11552a02e903efcb0e909b33e45 Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 22:40:36 -0400 Subject: [PATCH 05/24] r --- docs/source/_images/vision/confused_logit.png | Bin 0 -> 56767 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/source/_images/vision/confused_logit.png diff --git a/docs/source/_images/vision/confused_logit.png b/docs/source/_images/vision/confused_logit.png new file mode 100644 index 0000000000000000000000000000000000000000..1192e1198209dad050ad4b29e5a6d53864291fcf GIT binary patch literal 56767 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 Date: Tue, 14 Jul 2020 22:47:34 -0400 Subject: [PATCH 06/24] r --- pl_bolts/callbacks/vision/confused_logit.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 2ddd35991c..4c85b98fca 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -1,6 +1,5 @@ import torch from torch import nn -from matplotlib import pyplot as plt from pytorch_lightning import Callback @@ -14,6 +13,12 @@ def __init__(self, top_k, projection_factor=3): This callback shows how the input would have to change to swing the model from one label prediction to the other. + Example: + + from pl_bolts.callbacks.vision import ConfusedLogitCallback + + trainer = Trainer(callbacks=[ConfusedLogitCallback()]) + .. note:: whenever called, this model will look for self.last_batch and self.last_logits in the LightningModule .. note:: this callback supports tensorboard only right now @@ -57,6 +62,8 @@ def on_batch_end(self, trainer, pl_module): self._plot(confusing_x, confusing_y, trainer, pl_module, mask_idxs) def _plot(self, confusing_x, confusing_y, trainer, model, mask_idxs): + from matplotlib import pyplot as plt + batch_size = confusing_x.size(0) confusing_x = confusing_x[:self.top_k] From db284f25484dc5f7cbf3c469758ee2ccd47d7692 Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 22:48:20 -0400 Subject: [PATCH 07/24] r --- pl_bolts/callbacks/vision/confused_logit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 4c85b98fca..d83441e751 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -13,12 +13,12 @@ def __init__(self, top_k, projection_factor=3): This callback shows how the input would have to change to swing the model from one label prediction to the other. - Example: + Example:: from pl_bolts.callbacks.vision import ConfusedLogitCallback - trainer = Trainer(callbacks=[ConfusedLogitCallback()]) + .. note:: whenever called, this model will look for self.last_batch and self.last_logits in the LightningModule .. note:: this callback supports tensorboard only right now From 684dc994187915de2913a3d33bd2efe499f6adb3 Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 22:51:42 -0400 Subject: [PATCH 08/24] r --- pl_bolts/callbacks/vision/confused_logit.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index d83441e751..4a2dc5ae2b 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -13,6 +13,9 @@ def __init__(self, top_k, projection_factor=3): This callback shows how the input would have to change to swing the model from one label prediction to the other. + For each confused logit $l_i$ the confused images are generated by $\frac{\partial l_i}{\partial x}$, + where $i \in \{0, 1\}$ means the top two "confused" logits. + Example:: from pl_bolts.callbacks.vision import ConfusedLogitCallback From 0a4a8ca03a963c1b7870a6de4b1238a0c96dea97 Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 22:59:09 -0400 Subject: [PATCH 09/24] r --- pl_bolts/callbacks/vision/confused_logit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 4a2dc5ae2b..7070d95cff 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -13,8 +13,8 @@ def __init__(self, top_k, projection_factor=3): This callback shows how the input would have to change to swing the model from one label prediction to the other. - For each confused logit $l_i$ the confused images are generated by $\frac{\partial l_i}{\partial x}$, - where $i \in \{0, 1\}$ means the top two "confused" logits. + For each confused logit $l_i$ the confused images are generated by $\partial l_i/ \partial x$, + where $i \in \{0, 1\}$ means the top two confused logits. Example:: From 3b614b19f642ce4a73a22906e5ef36777f1e2e0a Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 23:02:02 -0400 Subject: [PATCH 10/24] r --- pl_bolts/callbacks/vision/confused_logit.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 7070d95cff..1a63a00891 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -13,6 +13,9 @@ def __init__(self, top_k, projection_factor=3): This callback shows how the input would have to change to swing the model from one label prediction to the other. + In this case, the network predicts a 5... but gives almost equal probability to an 8. + The images show what about the original 5 would have to change to make it more like a 5 or more like an 8. + For each confused logit $l_i$ the confused images are generated by $\partial l_i/ \partial x$, where $i \in \{0, 1\}$ means the top two confused logits. From a34f0da1e1697e8d79b2033757d8097fc89c581e Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 23:03:27 -0400 Subject: [PATCH 11/24] r --- pl_bolts/callbacks/vision/confused_logit.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 1a63a00891..26f1efb9de 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -32,6 +32,11 @@ def __init__(self, top_k, projection_factor=3): Args: top_k: How many "offending" images we should plot projection_factor: How much to multiply the input image to make it look more like this logit label + + Authored by: + + - Alfredo Canziani + """ super().__init__() self.top_k = top_k From 3eb893730b8b85f5ce184f0e75f5187c197786cf Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 23:04:06 -0400 Subject: [PATCH 12/24] r --- docs/source/vision_callbacks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/vision_callbacks.rst b/docs/source/vision_callbacks.rst index 2b5ef9dd48..d4cfb81c28 100644 --- a/docs/source/vision_callbacks.rst +++ b/docs/source/vision_callbacks.rst @@ -2,7 +2,7 @@ :class: hidden-section Vision Callbacks -===================== +================ Useful callbacks for vision models --------------- From 1ac8d7f1a1eb1251ad28378d2ed0c13618ee8417 Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 23:07:20 -0400 Subject: [PATCH 13/24] r --- docs/source/vision_callbacks.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/vision_callbacks.rst b/docs/source/vision_callbacks.rst index d4cfb81c28..f6c46b892e 100644 --- a/docs/source/vision_callbacks.rst +++ b/docs/source/vision_callbacks.rst @@ -7,8 +7,8 @@ Useful callbacks for vision models --------------- -Confused Logit Callback ------------------------ +Confused Logit +-------------- Shows how the input would have to change to move the prediction from one logit to the other From 2edb29e707e1cb1b96a4ba415d1d9fa0ca9f245e Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 23:08:22 -0400 Subject: [PATCH 14/24] r --- docs/source/variational_callbacks.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/variational_callbacks.rst b/docs/source/variational_callbacks.rst index 4804ecec8d..6a1d22bdd5 100644 --- a/docs/source/variational_callbacks.rst +++ b/docs/source/variational_callbacks.rst @@ -11,5 +11,11 @@ Latent Dim Interpolator ----------------------- Interpolates latent dims. +Example outputs: + + .. image:: _images/gans/basic_gan_interpolate.jpg + :width: 400 + :alt: Example of prediction confused between 5 and 8 + .. autoclass:: pl_bolts.callbacks.variational.LatentDimInterpolator :noindex: From d99fd6f15d8e00fde3517deacd4726cc7468f607 Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 23:08:52 -0400 Subject: [PATCH 15/24] r --- docs/source/variational_callbacks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/variational_callbacks.rst b/docs/source/variational_callbacks.rst index 6a1d22bdd5..3a3f6921fb 100644 --- a/docs/source/variational_callbacks.rst +++ b/docs/source/variational_callbacks.rst @@ -15,7 +15,7 @@ Example outputs: .. image:: _images/gans/basic_gan_interpolate.jpg :width: 400 - :alt: Example of prediction confused between 5 and 8 + :alt: Example latent space interpolation .. autoclass:: pl_bolts.callbacks.variational.LatentDimInterpolator :noindex: From 3c2277b5374d32795f161a3257925156d3b0521d Mon Sep 17 00:00:00 2001 From: William Falcon Date: Tue, 14 Jul 2020 23:09:04 -0400 Subject: [PATCH 16/24] r --- docs/source/variational_callbacks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/variational_callbacks.rst b/docs/source/variational_callbacks.rst index 3a3f6921fb..3f33e1f20d 100644 --- a/docs/source/variational_callbacks.rst +++ b/docs/source/variational_callbacks.rst @@ -11,7 +11,7 @@ Latent Dim Interpolator ----------------------- Interpolates latent dims. -Example outputs: +Example output: .. image:: _images/gans/basic_gan_interpolate.jpg :width: 400 From e1e137620f0ef23858004b20b668c73949cba0b7 Mon Sep 17 00:00:00 2001 From: William Falcon Date: Wed, 15 Jul 2020 06:41:28 -0400 Subject: [PATCH 17/24] r --- pl_bolts/callbacks/vision/confused_logit.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 26f1efb9de..655dcfb3fe 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -5,7 +5,7 @@ class ConfusedLogitCallback(Callback): - def __init__(self, top_k, projection_factor=3): + def __init__(self, top_k, projection_factor=3, min_logit_value=5.0, logging_batch_interval=20, max_logit_difference=0.1): """ Takes the logit predictions of a model and when the probabilities of two classes are very close, the model doesn't have high certainty that it should pick one vs the other class. @@ -32,6 +32,9 @@ def __init__(self, top_k, projection_factor=3): Args: top_k: How many "offending" images we should plot projection_factor: How much to multiply the input image to make it look more like this logit label + min_logit_value: Only consider logit values above this threshold + logging_batch_interval: how frequently to inspect/potentially plot something + max_logit_difference: when the top 2 logits are within this threshold we consider them confused Authored by: @@ -41,25 +44,27 @@ def __init__(self, top_k, projection_factor=3): super().__init__() self.top_k = top_k self.projection_factor = projection_factor + self.max_logit_difference = max_logit_difference + self.logging_batch_interval = logging_batch_interval + self.min_logit_value = min_logit_value def on_batch_end(self, trainer, pl_module): # show images only every 20 batches - if (trainer.batch_idx + 1) % 20 != 0: + if (trainer.batch_idx + 1) % self.logging_batch_interval != 0: return # pick the last batch and logits - # TODO: use context instead x, y = pl_module.last_batch l = pl_module.last_logits # only check when it has opinions (ie: the logit > 5) - if l.max() > 5.0: + if l.max() > self.min_logit_value: # pick the top two confused probs (values, idxs) = torch.topk(l, k=2, dim=1) # care about only the ones that are at most eps close to each other - eps = 0.1 + eps = self.max_logit_difference mask = (values[:, 0] - values[:, 1]).abs() < eps if mask.sum() > 0: @@ -75,7 +80,7 @@ def on_batch_end(self, trainer, pl_module): def _plot(self, confusing_x, confusing_y, trainer, model, mask_idxs): from matplotlib import pyplot as plt - batch_size = confusing_x.size(0) + batch_size, c, w, h = confusing_x.size() confusing_x = confusing_x[:self.top_k] confusing_y = confusing_y[:self.top_k] @@ -89,8 +94,8 @@ def _plot(self, confusing_x, confusing_y, trainer, model, mask_idxs): l[:, mask_idxs[:, logit_i]].sum().backward() # reshape grads - grad_a = x_param_a.grad.view(batch_size, 28, 28) - grad_b = x_param_b.grad.view(batch_size, 28, 28) + grad_a = x_param_a.grad.view(batch_size, w, h) + grad_b = x_param_b.grad.view(batch_size, w, h) for img_i in range(len(confusing_x)): x = confusing_x[img_i].squeeze(0) From b7f00df9536f9d64602177b3037b0e2f40cd89c6 Mon Sep 17 00:00:00 2001 From: William Falcon Date: Wed, 15 Jul 2020 06:42:35 -0400 Subject: [PATCH 18/24] r --- pl_bolts/callbacks/vision/confused_logit.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 655dcfb3fe..622c2ee150 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -5,7 +5,14 @@ class ConfusedLogitCallback(Callback): - def __init__(self, top_k, projection_factor=3, min_logit_value=5.0, logging_batch_interval=20, max_logit_difference=0.1): + def __init__( + self, + top_k, + projection_factor=3, + min_logit_value=5.0, + logging_batch_interval=20, + max_logit_difference=0.1 + ): """ Takes the logit predictions of a model and when the probabilities of two classes are very close, the model doesn't have high certainty that it should pick one vs the other class. From d6194d59c3a5d1f84cc2ebe049d0710cd551ef3d Mon Sep 17 00:00:00 2001 From: William Falcon Date: Wed, 15 Jul 2020 06:59:36 -0400 Subject: [PATCH 19/24] r --- pl_bolts/callbacks/vision/confused_logit.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 622c2ee150..1b7c18dbe8 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -63,12 +63,12 @@ def on_batch_end(self, trainer, pl_module): # pick the last batch and logits x, y = pl_module.last_batch - l = pl_module.last_logits + logits = pl_module.last_logits # only check when it has opinions (ie: the logit > 5) - if l.max() > self.min_logit_value: + if logits.max() > self.min_logit_value: # pick the top two confused probs - (values, idxs) = torch.topk(l, k=2, dim=1) + (values, idxs) = torch.topk(logits, k=2, dim=1) # care about only the ones that are at most eps close to each other eps = self.max_logit_difference @@ -78,7 +78,6 @@ def on_batch_end(self, trainer, pl_module): # pull out the ones we care about confusing_x = x[mask, ...] confusing_y = y[mask] - confusing_l = l[mask] mask_idxs = idxs[mask] From 4684a8253bf54cf65274a7460009b9fd532a738f Mon Sep 17 00:00:00 2001 From: William Falcon Date: Wed, 15 Jul 2020 07:00:22 -0400 Subject: [PATCH 20/24] r --- pl_bolts/callbacks/vision/confused_logit.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 1b7c18dbe8..4ea9534e34 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -96,8 +96,8 @@ def _plot(self, confusing_x, confusing_y, trainer, model, mask_idxs): x_param_b = nn.Parameter(confusing_x) for logit_i, x_param in enumerate((x_param_a, x_param_b)): - l = model(x_param.view(batch_size, -1)) - l[:, mask_idxs[:, logit_i]].sum().backward() + logits = model(x_param.view(batch_size, -1)) + logits[:, mask_idxs[:, logit_i]].sum().backward() # reshape grads grad_a = x_param_a.grad.view(batch_size, w, h) From 598783433a8dced8c76a39f07e049fa3a248d696 Mon Sep 17 00:00:00 2001 From: Jirka Date: Wed, 15 Jul 2020 13:27:58 +0200 Subject: [PATCH 21/24] flake8 --- setup.cfg | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index d920943978..62dd227648 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,6 +16,7 @@ exclude_lines = add_model_specific_args [coverage:run] +# TODO, remove this ignores in future omit = pl_bolts/datamodules/stl10_datamodule.py pl_bolts/datamodules/ssl_imagenet_datamodule.py @@ -36,11 +37,13 @@ doctests = True verbose = 2 # https://pep8.readthedocs.io/en/latest/intro.html#error-codes format = pylint +# see: https://www.flake8rules.com/ ignore = - E731 - W504 - F401 - F841 + E731 # Do not assign a lambda expression, use a def + W504 # Line break occurred after a binary operator + F401 # Module imported but unused + F841 # Local variable name is assigned to but never used + W605 # Invalid escape sequence 'x' # setup.cfg or tox.ini [check-manifest] From 5404a759f0a00f501910303ec746909109623ed6 Mon Sep 17 00:00:00 2001 From: Jirka Date: Wed, 15 Jul 2020 14:18:39 +0200 Subject: [PATCH 22/24] plotting --- pl_bolts/callbacks/vision/confused_logit.py | 37 +++++++-------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 4ea9534e34..d3e06157ca 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -111,30 +111,17 @@ def _plot(self, confusing_x, confusing_y, trainer, model, mask_idxs): mask_idx = mask_idxs[img_i] - fig = plt.figure(figsize=(15, 10)) - plt.subplot(231) - plt.imshow(x) - plt.colorbar() - plt.title(f'True: {y}', fontsize=20) - - plt.subplot(232) - plt.imshow(ga) - plt.colorbar() - plt.title(f'd{mask_idx[0]}-logit/dx', fontsize=20) - - plt.subplot(233) - plt.imshow(gb) - plt.colorbar() - plt.title(f'd{mask_idx[1]}-logit/dx', fontsize=20) - - plt.subplot(235) - plt.imshow(ga * 2 + x) - plt.colorbar() - plt.title(f'd{mask_idx[0]}-logit/dx', fontsize=20) - - plt.subplot(236) - plt.imshow(gb * 2 + x) - plt.colorbar() - plt.title(f'd{mask_idx[1]}-logit/dx', fontsize=20) + fig, axarr = plt.subplots(nrows=2, ncols=3, figsize=(15, 10)) + self.__draw_sample(fig, axarr, 0, 0, x, f'True: {y}') + self.__draw_sample(fig, axarr, 0, 1, ga, f'd{mask_idx[0]}-logit/dx') + self.__draw_sample(fig, axarr, 0, 2, gb, f'd{mask_idx[1]}-logit/dx') + self.__draw_sample(fig, axarr, 1, 1, ga * 2 + x, f'd{mask_idx[0]}-logit/dx') + self.__draw_sample(fig, axarr, 1, 2, gb * 2 + x, f'd{mask_idx[1]}-logit/dx') trainer.logger.experiment.add_figure('confusing_imgs', fig, global_step=trainer.global_step) + + @staticmethod + def __draw_sample(fig, axarr, row_idx, col_idx, img, title): + im = axarr[row_idx, col_idx].imshow(img) + fig.colorbar(im, ax=axarr[row_idx, col_idx]) + axarr[row_idx, col_idx].set_title(title, fontsize=20) \ No newline at end of file From 3159d8b98ebe84db68e63003229fb275fde70bbe Mon Sep 17 00:00:00 2001 From: William Falcon Date: Fri, 31 Jul 2020 08:41:34 -0400 Subject: [PATCH 23/24] added more dm tests --- pl_bolts/callbacks/vision/confused_logit.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index d3e06157ca..55221ca792 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -23,8 +23,8 @@ def __init__( In this case, the network predicts a 5... but gives almost equal probability to an 8. The images show what about the original 5 would have to change to make it more like a 5 or more like an 8. - For each confused logit $l_i$ the confused images are generated by $\partial l_i/ \partial x$, - where $i \in \{0, 1\}$ means the top two confused logits. + For each confused logit the confused images are generated by taking the gradient from a logit wrt an input + for the top two closest logits. Example:: @@ -124,4 +124,4 @@ def _plot(self, confusing_x, confusing_y, trainer, model, mask_idxs): def __draw_sample(fig, axarr, row_idx, col_idx, img, title): im = axarr[row_idx, col_idx].imshow(img) fig.colorbar(im, ax=axarr[row_idx, col_idx]) - axarr[row_idx, col_idx].set_title(title, fontsize=20) \ No newline at end of file + axarr[row_idx, col_idx].set_title(title, fontsize=20) From e2dd574c1017cee7b9b68b5f37a67330a056699f Mon Sep 17 00:00:00 2001 From: William Falcon Date: Fri, 31 Jul 2020 08:42:18 -0400 Subject: [PATCH 24/24] added more dm tests --- pl_bolts/callbacks/vision/confused_logit.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pl_bolts/callbacks/vision/confused_logit.py b/pl_bolts/callbacks/vision/confused_logit.py index 55221ca792..2af3ceb652 100644 --- a/pl_bolts/callbacks/vision/confused_logit.py +++ b/pl_bolts/callbacks/vision/confused_logit.py @@ -3,7 +3,7 @@ from pytorch_lightning import Callback -class ConfusedLogitCallback(Callback): +class ConfusedLogitCallback(Callback): # pragma: no-cover def __init__( self,