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%20random%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%20%20%20%20preprocess_input%2C%0A%20%20%20%20%20%20%20%20decode_predictions%2C%0A%20%20%20%20)%0A%20%20%20%20import%20cv2%0A%0A%20%20%20%20return%20ResNet50%2C%20cv2%2C%20decode_predictions%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%20The%20Grad-CAM%20output%20is%20an%20activation%20map%20which%20localises%20the%20detected%20objected%20to%20a%20region%20in%20the%20image.%20It%20is%20of%20width%20%24u%24%20and%20height%20%24v%24%2C%20for%20the%20class%20%24c%24.%0A%20%20%20%20%24%24%0A%20%20%20%20L%5E%7Bc%7D_%7B%5Ctextrm%7BGrad-CAM%7D%7D%20%5Cin%20%5Cmathbb%7BR%7D%5E%7Bu%20%5Ctimes%20v%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(load_img%2C%20np%2C%20plt)%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%20plt.imshow(image)%0A%20%20%20%20return%20(image%2C)%0A%0A%0A%40app.cell%0Adef%20_(ResNet50%2C%20tf)%3A%0A%20%20%20%20model%20%3D%20ResNet50()%0A%0A%20%20%20%20%23%20Get%20logits%20instead%20of%20softmax%0A%20%20%20%20logits_model%20%3D%20tf.keras.Model(%0A%20%20%20%20%20%20%20%20inputs%3Dmodel.inputs%2C%20outputs%3Dmodel.layers%5B-1%5D.output%0A%20%20%20%20)%0A%20%20%20%20return%20logits_model%2C%20model%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%20We%20get%20the%20output%20of%20the%20last%20convolution%20layer.%20We%20then%20create%20a%20model%20that%20goes%20up%20to%20only%20that%20layer.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(model%2C%20tf)%3A%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%20%20%20%20return%20last_conv_layer%2C%20last_conv_layer_model%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%20We%20create%20a%20model%20which%20then%20takes%20the%20output%20of%20the%20model%20above%2C%20and%20uses%20the%20remaining%20layers%20to%20get%20the%20final%20predictions.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(last_conv_layer%2C%20model%2C%20tf)%3A%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%20(classifier_model%2C)%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%20First%2C%20we%20get%20the%20output%20from%20the%20model%20up%20till%20the%20last%20convolution%20layer.%0A%20%20%20%20We%20ask%20%60tf%60%20to%20watch%20this%20tensor%20output%2C%20as%20we%20want%20to%20calculate%20the%20gradients%20of%20the%20predictions%20of%20our%20target%20class%20wrt%20to%20the%20output%20of%20this%20model%20(last%20convolution%20layer%20model).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%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%20inputs%20%3D%20image%5Bnp.newaxis%2C%20...%5D%0A%20%20%20%20%20%20%20%20last_conv_layer_output%20%3D%20last_conv_layer_model(inputs)%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%20return%20last_conv_layer_output%2C%20tape%2C%20top_class_channel%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%20The%20partial%20derivative%20%2F%20gradient%20of%20the%20model%20output%20(logits%20%2F%20prior%20to%20softmax)%2C%20%24y%5E%7Bc%7D%24%2C%20with%20respect%20to%20the%20feature%20map%20(filter)%20activations%20of%20a%20specified%20convolution%20layer%20(the%20last%20convolution%20layer%20in%20this%20case)%20is%3A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cfrac%7B%5Cpartial%20y%5E%7Bc%7D%7D%7B%5Cpartial%20A%5E%7Bk%7D_%7Bij%7D%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(last_conv_layer_output%2C%20tape%2C%20top_class_channel)%3A%0A%20%20%20%20grads%20%3D%20tape.gradient(top_class_channel%2C%20last_conv_layer_output)%0A%20%20%20%20return%20(grads%2C)%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%20The%20gradients%20have%20a%20shape%20of%20%60(u%2Cv%2CZ)%60%2C%20where%20%60(u%2Cv)%60%20comes%20from%20the%20shape%20of%20the%202D%20convolution%20filter%20(i.e.%20width%20and%20height)%2C%20and%20%60Z%60%20is%20the%20number%20of%20filters.%20The%20next%20step%20averages%20each%20of%20the%20filters%20to%20a%20single%20value%2C%20so%20that%20the%20final%20shape%20is%20%60Z%60%20or%20the%20number%20of%20filters.%20This%20is%20equivalent%20to%20the%20global%20average%20pooling%202D%20layer.%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Calpha_%7Bk%7D%5E%7Bc%7D%3D%5Cfrac%7B1%7D%7BZ%7D%5Csum_%7Bi%7D%5Csum_%7Bj%7D%5Cfrac%7B%5Cpartial%20y%5E%7Bc%7D%7D%7B%5Cpartial%20A%5E%7Bk%7D_%7Bij%7D%7D%0A%20%20%20%20%24%24%0A%0A%20%20%20%20Each%20one%20of%20these%20gradients%20represents%20the%20connection%20from%20one%20of%20the%20pixels%20in%20the%202D%20array%20to%20the%20neuron%20%2F%20output%20representing%20the%20target%20class%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(grads%2C%20tf)%3A%0A%20%20%20%20pooled_grads%20%3D%20tf.reduce_mean(grads%2C%20axis%3D(0%2C%201%2C%202))%0A%20%20%20%20return%20(pooled_grads%2C)%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%20This%20is%20what%20the%20next%20layer%20in%20the%20model%20does%20which%20is%20a%20global%20average%20pooling%202D%20layer%2C%20which%20%20averages%20and%20flattens%20the%20%24z%24%20number%20of%20filters%20of%20%24u%20%5Ctimes%20v%24%20shape%20to%20single%20numbers%20(exactly%20what%20we%20did%20in%20previous%20step).%20This%20is%20necessary%20to%20create%20a%20connection%20to%20the%20fully%20connected%20(Dense)%20layers%20for%20the%20final%20prediction%20outputs.%0A%0A%20%20%20%20The%20next%20step%20is%20to%20multiply%20the%20gradients%20(corresponding%20to%20the%20importance%20of%20the%20given%20feature%20map%20%2F%20filter)%20with%20the%20actual%20feature%20map%20(filter)%20it%20represents.%0A%0A%20%20%20%20%24%24%0A%20%20%20%20ReLU%5Cbigg(%5Csum_%7Bk%7D%20a%5E%7Bc%7D_%7Bk%7DA%5E%7Bk%7D%5Cbigg)%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(last_conv_layer_output%2C%20pooled_grads)%3A%0A%20%20%20%20last_conv_layer_output_%20%3D%20last_conv_layer_output.numpy()%5B0%5D%0A%20%20%20%20for%20i%20in%20range(pooled_grads.numpy().shape%5B-1%5D)%3A%0A%20%20%20%20%20%20%20%20last_conv_layer_output_%5B%3A%2C%20%3A%2C%20i%5D%20*%3D%20pooled_grads%5Bi%5D%0A%20%20%20%20return%20(last_conv_layer_output_%2C)%0A%0A%0A%40app.cell%0Adef%20_(cv2%2C%20last_conv_layer_output_%2C%20np)%3A%0A%20%20%20%20%23%20Average%20over%20all%20the%20filters%20to%20get%20a%20single%202D%20array%0A%20%20%20%20grad_cam%20%3D%20np.mean(last_conv_layer_output_%2C%20axis%3D-1)%0A%20%20%20%20%23%20Clip%20the%20values%20(equivalent%20to%20applying%20ReLU)%0A%20%20%20%20%23%20and%20then%20normalise%20the%20values%0A%20%20%20%20grad_cam%20%3D%20np.clip(grad_cam%2C%200%2C%20np.max(grad_cam))%20%2F%20np.max(grad_cam)%0A%20%20%20%20grad_cam%20%3D%20cv2.resize(grad_cam%2C%20(224%2C%20224))%0A%20%20%20%20return%20(grad_cam%2C)%0A%0A%0A%40app.cell%0Adef%20_(grad_cam%2C%20image%2C%20plt)%3A%0A%20%20%20%20plt.imshow(image)%0A%20%20%20%20plt.imshow(grad_cam%2C%20alpha%3D0.5)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(decode_predictions%2C%20image%2C%20logits_model%2C%20np%2C%20tf)%3A%0A%20%20%20%20decode_predictions(tf.nn.softmax(logits_model(image%5Bnp.newaxis%2C%20...%5D)).numpy())%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%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
ac82863584f3cc886237a0c27cdb02b5