import%20marimo%0A%0A__generated_with%20%3D%20%220.20.1%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20mo%2C%20np%2C%20plt%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20tensorflow%20as%20tf%0A%20%20%20%20from%20tensorflow.keras.preprocessing.image%20import%20load_img%0A%20%20%20%20from%20tensorflow.keras.applications.resnet50%20import%20(%0A%20%20%20%20%20%20%20%20ResNet50%2C%0A%20%20%20%20)%0A%20%20%20%20import%20cv2%0A%0A%20%20%20%20return%20ResNet50%2C%20cv2%2C%20load_img%2C%20tf%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Guided%20Grad-CAM%0A%0A%20%20%20%20The%20Grad-CAM%20output%20can%20be%20improved%20further%20by%20combining%20with%20guided%20backpropagation%2C%20which%20zeroes%20elements%20in%20the%20gradients%20which%20act%20negatively%20towards%20the%20decision.%20Implementation%20from%20Raphael%20Meudec%20%2F%20%5BSicara%5D(https%3A%2F%2Fwww.sicara.ai%2Fblog%2F2019-08-28-interpretability-deep-learning-tensorflow)%2C%20%5BGitHub%20Gist%5D(https%3A%2F%2Fgist.github.com%2FRaphaelMeudec%2Fe9a805fa82880876f8d89766f0690b54).%0A%0A%20%20%20%20This%20output%2C%20however%2C%20is%20still%20a%20low%20resolution%20heatmap%2C%20and%20not%20quite%20as%20described%20in%20the%20original%20paper.%20The%20original%20paper%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ResNet50%2C%20load_img%2C%20np%2C%20tf)%3A%0A%20%20%20%20image%20%3D%20np.array(load_img(%22.%2Fdata%2Fcat.jpg%22%2C%20target_size%3D(224%2C%20224%2C%203)))%0A%20%20%20%20model%20%3D%20ResNet50()%0A%0A%20%20%20%20%23%20Get%20logits%20instead%20of%20softmax%0A%20%20%20%20last_conv_layer%20%3D%20model.get_layer(%22conv5_block3_out%22)%0A%20%20%20%20last_conv_layer_model%20%3D%20tf.keras.Model(model.inputs%2C%20last_conv_layer.output)%0A%0A%20%20%20%20classifier_input%20%3D%20tf.keras.Input(shape%3Dlast_conv_layer.output.shape%5B1%3A%5D)%0A%20%20%20%20x%20%3D%20classifier_input%0A%20%20%20%20for%20layer_name%20in%20%5B%22avg_pool%22%2C%20%22predictions%22%5D%3A%0A%20%20%20%20%20%20%20%20x%20%3D%20model.get_layer(layer_name)(x)%0A%20%20%20%20classifier_model%20%3D%20tf.keras.Model(classifier_input%2C%20x)%0A%20%20%20%20return%20classifier_model%2C%20image%2C%20last_conv_layer_model%2C%20model%0A%0A%0A%40app.cell%0Adef%20_(classifier_model%2C%20image%2C%20last_conv_layer_model%2C%20np%2C%20tf)%3A%0A%20%20%20%20with%20tf.GradientTape()%20as%20tape%3A%0A%20%20%20%20%20%20%20%20last_conv_layer_output%20%3D%20last_conv_layer_model(image%5Bnp.newaxis%2C%20...%5D)%0A%20%20%20%20%20%20%20%20tape.watch(last_conv_layer_output)%0A%20%20%20%20%20%20%20%20preds%20%3D%20classifier_model(last_conv_layer_output)%0A%20%20%20%20%20%20%20%20top_pred_index%20%3D%20tf.argmax(preds%5B0%5D)%0A%20%20%20%20%20%20%20%20top_class_channel%20%3D%20preds%5B%3A%2C%20top_pred_index%5D%0A%20%20%20%20grads%20%3D%20tape.gradient(top_class_channel%2C%20last_conv_layer_output)%5B0%5D%0A%20%20%20%20last_conv_layer_output%20%3D%20last_conv_layer_output%5B0%5D%0A%20%20%20%20return%20grads%2C%20last_conv_layer_output%0A%0A%0A%40app.cell%0Adef%20_(cv2%2C%20grads%2C%20last_conv_layer_output%2C%20np%2C%20tf)%3A%0A%20%20%20%20guided_grads%20%3D%20(%0A%20%20%20%20%20%20%20%20tf.cast(last_conv_layer_output%20%3E%200%2C%20%22float32%22)%0A%20%20%20%20%20%20%20%20*%20tf.cast(grads%20%3E%200%2C%20%22float32%22)%0A%20%20%20%20%20%20%20%20*%20grads%0A%20%20%20%20)%0A%0A%20%20%20%20pooled_guided_grads%20%3D%20tf.reduce_mean(guided_grads%2C%20axis%3D(0%2C%201))%0A%20%20%20%20guided_gradcam%20%3D%20np.ones(last_conv_layer_output.shape%5B%3A2%5D%2C%20dtype%3Dnp.float32)%0A%0A%20%20%20%20for%20i%2C%20w%20in%20enumerate(pooled_guided_grads)%3A%0A%20%20%20%20%20%20%20%20guided_gradcam%20%2B%3D%20w%20*%20last_conv_layer_output%5B%3A%2C%20%3A%2C%20i%5D%0A%0A%20%20%20%20guided_gradcam%20%3D%20cv2.resize(guided_gradcam.numpy()%2C%20(224%2C%20224))%0A%0A%20%20%20%20guided_gradcam%20%3D%20np.clip(guided_gradcam%2C%200%2C%20np.max(guided_gradcam))%0A%20%20%20%20guided_gradcam%20%3D%20(guided_gradcam%20-%20guided_gradcam.min())%20%2F%20(%0A%20%20%20%20%20%20%20%20guided_gradcam.max()%20-%20guided_gradcam.min()%0A%20%20%20%20)%0A%20%20%20%20return%20(guided_gradcam%2C)%0A%0A%0A%40app.cell%0Adef%20_(guided_gradcam%2C%20image%2C%20plt)%3A%0A%20%20%20%20plt.imshow(image)%0A%20%20%20%20plt.imshow(guided_gradcam%2C%20alpha%3D0.5)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Guided%20Grad-CAM%20(high%20resolution%20maps)%0A%20%20%20%20This%20approach%20reflects%20the%20paper's%20description%20better%20by%20first%20using%20the%20guided%20backpropagation%20approach%20to%20produce%20a%20high%20resolution%20map%20that%20is%20of%20the%20same%20resolution%20of%20the%20input%20image%2C%20which%20is%20then%20masked%20using%20the%20Grad-CAM%20heatmap%20to%20focus%20only%20on%20details%20that%20led%20to%20the%20prediction%20outcome.%20Based%20on%20the%20implementation%20on%20GitHub%20by%20%5Bjacobgil%5D(https%3A%2F%2Fgithub.com%2Fjacobgil%2Fkeras-grad-cam%2Fblob%2Fmaster%2Fgrad-cam.py).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(tf)%3A%0A%20%20%20%20%40tf.custom_gradient%0A%20%20%20%20def%20guided_relu(x)%3A%0A%20%20%20%20%20%20%20%20def%20grad(dy)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20tf.cast(dy%20%3E%200%2C%20%22float32%22)%20*%20tf.cast(x%20%3E%200%2C%20%22float32%22)%20*%20dy%0A%0A%20%20%20%20%20%20%20%20return%20tf.nn.relu(x)%2C%20grad%0A%0A%20%20%20%20return%20(guided_relu%2C)%0A%0A%0A%40app.cell%0Adef%20_(guided_relu%2C%20np%2C%20tf)%3A%0A%20%20%20%20class%20GuidedBackprop%3A%0A%20%20%20%20%20%20%20%20def%20__init__(self%2C%20model%2C%20layer_name%3A%20str)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.model%20%3D%20model%0A%20%20%20%20%20%20%20%20%20%20%20%20self.layer_name%20%3D%20layer_name%0A%20%20%20%20%20%20%20%20%20%20%20%20self.gb_model%20%3D%20self.build_guided_model()%0A%0A%20%20%20%20%20%20%20%20def%20build_guided_model(self)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20gb_model%20%3D%20tf.keras.Model(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.model.inputs%2C%20self.model.get_layer(self.layer_name).output%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20layers%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20layer%20for%20layer%20in%20gb_model.layers%5B1%3A%5D%20if%20hasattr(layer%2C%20%22activation%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20layer%20in%20layers%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20layer.activation%20%3D%3D%20tf.keras.activations.relu%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20layer.activation%20%3D%20guided_relu%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20gb_model%0A%0A%20%20%20%20%20%20%20%20def%20guided_backprop(self%2C%20image%3A%20np.ndarray)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20with%20tf.GradientTape()%20as%20tape%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inputs%20%3D%20tf.cast(image%2C%20tf.float32)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20tape.watch(inputs)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20outputs%20%3D%20self.gb_model(inputs)%0A%20%20%20%20%20%20%20%20%20%20%20%20grads%20%3D%20tape.gradient(outputs%2C%20inputs)%5B0%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20grads%0A%0A%20%20%20%20return%20(GuidedBackprop%2C)%0A%0A%0A%40app.cell%0Adef%20_(GuidedBackprop%2C%20model)%3A%0A%20%20%20%20gb%20%3D%20GuidedBackprop(model%2C%20%22conv5_block3_out%22)%0A%20%20%20%20return%20(gb%2C)%0A%0A%0A%40app.cell%0Adef%20_(gb%2C%20guided_gradcam%2C%20image%2C%20np%2C%20tf)%3A%0A%20%20%20%20saliency_map%20%3D%20gb.guided_backprop(image%5Bnp.newaxis%2C%20...%5D).numpy()%0A%20%20%20%20saliency_map%20%3D%20saliency_map%20*%20np.repeat(guided_gradcam%5B...%2C%20np.newaxis%5D%2C%203%2C%20axis%3D2)%0A%0A%20%20%20%20saliency_map%20-%3D%20saliency_map.mean()%0A%20%20%20%20saliency_map%20%2F%3D%20saliency_map.std()%20%2B%20tf.keras.backend.epsilon()%0A%20%20%20%20saliency_map%20*%3D%200.25%0A%20%20%20%20saliency_map%20%2B%3D%200.5%0A%20%20%20%20saliency_map%20%3D%20np.clip(saliency_map%2C%200%2C%201)%0A%20%20%20%20saliency_map%20*%3D%20(2**8)%20-%201%0A%20%20%20%20saliency_map%20%3D%20saliency_map.astype(np.uint8)%0A%20%20%20%20return%20(saliency_map%2C)%0A%0A%0A%40app.cell%0Adef%20_(plt%2C%20saliency_map)%3A%0A%20%20%20%20plt.imshow(saliency_map)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
2a6745de0124c272ae629bf21d3009aa