Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Final convolutional layer tensor for activation map with Grad-CAM #64

Closed
hnguyentt opened this issue May 14, 2020 · 20 comments
Closed

Final convolutional layer tensor for activation map with Grad-CAM #64

hnguyentt opened this issue May 14, 2020 · 20 comments

Comments

@hnguyentt
Copy link

hnguyentt commented May 14, 2020

Hello,

Thank you for sharing your work!
I am trying to make the activation map to see the important features from your two trained models (COVIDNet-CXR Small and COVIDNet-CXR Large).
To do that I would like to know the name of the final convolutional layer tensor. I have checked your document train_eval_inference.md but have found that tensor name.

I listed all tensor names in your models by this code:

tensors = [t.name for op in tf.get_default_graph().get_operations() for t in op.values()]
for t in tensors:
    print(t)

And this is a subset of the tensor names:

...
conv5_block3_preact_relu/Relu:0
conv5_block3_1_conv/convolution:0
conv5_block3_1_bn/FusedBatchNorm_1:0
conv5_block3_1_bn/FusedBatchNorm_1:1
conv5_block3_1_bn/FusedBatchNorm_1:2
conv5_block3_1_bn/FusedBatchNorm_1:3
conv5_block3_1_bn/FusedBatchNorm_1:4
conv5_block3_1_relu/Relu:0
conv5_block3_2_pad/Pad/paddings:0
conv5_block3_2_pad/Pad:0
conv5_block3_2_conv/convolution:0
conv5_block3_2_bn/FusedBatchNorm_1:0
conv5_block3_2_bn/FusedBatchNorm_1:1
conv5_block3_2_bn/FusedBatchNorm_1:2
conv5_block3_2_bn/FusedBatchNorm_1:3
conv5_block3_2_bn/FusedBatchNorm_1:4
conv5_block3_2_relu/Relu:0
conv5_block3_3_conv/convolution:0
conv5_block3_3_conv/BiasAdd:0
conv5_block3_out/add:0
post_bn/FusedBatchNorm_1:0
post_bn/FusedBatchNorm_1:1
post_bn/FusedBatchNorm_1:2
post_bn/FusedBatchNorm_1:3
post_bn/FusedBatchNorm_1:4
post_relu/Relu:0
flatten_1/Shape:0
flatten_1/strided_slice/stack:0
flatten_1/strided_slice/stack_1:0
flatten_1/strided_slice/stack_2:0
flatten_1/strided_slice:0
flatten_1/Const:0
flatten_1/Prod:0
flatten_1/stack/0:0
flatten_1/stack:0
flatten_1/Reshape:0
dense_1/MatMul:0
dense_1/BiasAdd:0
dense_1/Relu:0
dense_2/MatMul:0
dense_2/BiasAdd:0
dense_2/Relu:0
dense_3/MatMul:0
dense_3/BiasAdd:0
dense_3/Softmax:0
loss/mul/x:0
loss/dense_3_loss/Sum/reduction_indices:0
loss/dense_3_loss/Sum:0
...

Based on that, I guess the final convolutional layer tensor is conv5_block3_out/add:0 and make the activation map based on that.

To confirm what I have done, my question: Is the final convolutional layer tensor is actually conv5_block3_out/add:0?

Thank you for your time.

@lindawangg
Copy link
Owner

Should be either conv5_block3_3_conv/BiasAdd:0 or conv5_block3_out/add:0. Can print out the shapes to verify which one.

@hnguyentt
Copy link
Author

Both of them have shape (<num of samples>, 7, 7, 2048). They match the shape with the architecture in your document: https://github.com/lindawangg/COVID-Net/blob/master/assets/COVIDNet_CXR.pdf

@borjaMinano
Copy link

borjaMinano commented May 22, 2020

@nguyenhoa93 , did you finally managed to run Grad-Cam?
I have tried to implement it, but it seems GradientTape does not register operations runs with Session.run

with tf.GradientTape() as tape:
	tape.watch(image_tensor)
	pred = sess.run(pred_tensor, feed_dict={image_tensor: np.expand_dims(x, axis=0)})
	convOutputs = sess.run(conv_op, feed_dict={image_tensor: np.expand_dims(x, axis=0)})
	loss = pred[:, np.argmax(pred[0])]

If I print the watched variables, there is no variables watched, so grads = tape.gradient(loss, convOutputs) returns none. However loss and convOutputs have data.
What confuses me is that those variables are numpy arrays instead of tensors.

I am pretty new to tensorflow and a bit lost yet. I'll appreciate any help.

@hnguyentt
Copy link
Author

hnguyentt commented May 23, 2020

@borjaMinano tf.GradientTape() is applicable for Tensorflow 2. The model was trained with TensorFlow v 1.x`. Did you retrain the model with TF 2.?

Edit: After reading @haydengunraj 's comment, I read again the document of tf.GradientTap() and realized that it's available in Eager Execution - TF 1.15 as well. Like @haydengunraj said, we need to sess.run() to get the values.

First, define last_conv_tensor and grads_tensor:
lass_conv_tensor = graph.get_tensor_by_name(<last_conv_layer_tensor_name>)

with tf.GradientTape() as tape:
	tape.watch(image_tensor)
	one_hot = tf.sparse_to_dense(classIdx, [len(self.classes)], 1.0)
	signal = tf.multiply(graph.get_tensor_by_name(<output_tensor_name>),one_hot)
	loss_tensor = tf.reduce_mean(signal)
	grads_tensor = tape.gradient(loss_tensor, last_conv_tensor)

last_conv, grads = sess.run([last_conv_tensor, grad_tensors], feed_dict={image_tensor: np.expand_dims(x, axis=0)})

I did the activation map already but want to make sure that I select the last convolutional layer correctly. You can see my code here: https://gist.github.com/nguyenhoa93/d49564875234f6722ff89e65db34a00b
To run the code you should create a json file with the keys:

{
  "weightspath": ,
  "metaname": ,
  "ckptname": ,
  "input_tensor": "input_1:0",
  "output_tensor": "dense_3/Softmax:0",
  "final_conv_tensor": "conv5_block3_out/add:0",
  "input_size":
  "top_percent": 
}

I took conv5_block3_out/add:0 as final_conv_tensor.

This is the GradCAM result of COVID-19 when the model predict the image as COVID-19.

img

@haydengunraj
Copy link
Collaborator

haydengunraj commented May 23, 2020

@borjaMinano I don't have much experience with Grad-CAM, but the main issue in the code snippet you posted above is more of a general Tensorflow thing. When you use session.run(), you are running the operation in the graph, and Tensorflow returns the result as a numpy array. That numpy array is completely separate from the Tensorflow graph, so calling tape.gradient(loss, convOutputs) does nothing. You need to pass Tensorflow Tensors to tape.gradient(), which should return a gradient Tensor that you can then compute using session.run().

TL;DR, you need to do something like:

with tf.GradientTape() as tape:
    tape.watch(image_tensor)
    loss_tensor = ...define your loss tensor here
    grad_tensor = tape.gradient(loss_tensor, conv_op)

grads = sess.run(grad_tensor, feed_dict={image_tensor: np.expand_dims(x, axis=0)})

@borjaMinano
Copy link

Thank you very much for your help.
I adapted @nguyenhoa93 code and get it finally working.

@hnguyentt
Copy link
Author

My pleasure, @borjaMinano
Happy to hear that it worked for you.

@ankitdata
Copy link

hi @nguyenhoa93 i was referring your code

on line no. 144 what is 'im' ?
im_name = im.split("/")[-1])

I got below error :
im_name = (im.split("/")[-1])
NameError: name 'im' is not defined

thanks

@hnguyentt
Copy link
Author

hnguyentt commented May 27, 2020

@ankitdata it should be args.impath.split(“/“)[-1]
I editted here: https://gist.github.com/nguyenhoa93/d49564875234f6722ff89e65db34a00b#file-covid-net-gradcam-py-L144-L145

@ankitdata
Copy link

ankitdata commented May 28, 2020

Thanks @nguyenhoa93, it worked for me.

Any idea what will be the 'final convolutional layer tensor' for Model: COVIDNet-CXR3-A
with following inputs

{
"input_tensor": "input_1:0",
  "output_tensor": "**norm_dense_1/Softmax:0**",
  "final_conv_tensor": " ? ",
}  

I checked tensors with below code

tensors = [t.name for op in tf.get_default_graph().get_operations() for t in op.values()]
for t in tensors:
    print(t)

which tensor will work from the list ?
@lindawangg @nguyenhoa93

Thanks

@hnguyentt
Copy link
Author

@ankitdata
I am not sure because I didn't build the network. For COVIDNet-CXR3-A, I guess: conv_7b/convolution:0

@ankitdata
Copy link

@nguyenhoa93

Do you have any plan to build network for COVIDNet-CXR3-A ?
If you build, please share the heatmap here.

thanks .

@hnguyentt
Copy link
Author

@nguyenhoa93

Do you have any plan to build network for COVIDNet-CXR3-A ?
If you build, please share the heatmap here.

thanks .

You can use the same code that I shared and change the name of final conv to conv_7b/convolution:0 . Input size (480, 480, 3).

The code will work for all TensorFlow models (TF 1.15).

@ankitdata
Copy link

ankitdata commented Jun 4, 2020

Hi @nguyenhoa93

I tried with 3 models (small ,Large & COVIDNet-CXR3-A) heatmaps i shared below
in ' COVIDNet-CXR3-A' I used conv_7b/convolution:0 and Input size (480, 480, 3) but heatmap in not correct and prediction is Pneumonia.
for model COVIDNet-CXR3-A sensitivity is
Normal: 0.000, Pneumonia: 0.530, COVID-19: 0.470

for small model and large model sensitivity is higher for COVID-19 so prediction is COVID-19

what is your suggestion on this ?
let me know if you get more accuracy in heatmap for COVIDNet-CXR3-A.

Heatmaps
predictions for :
COVIDNet-CXR Small - COVID-19
COVIDNet-CXR Large - COVID-19
COVIDNet-CXR3-A - Pneumonia

heatmaps

input-image - assets/ex-covid.jpeg
thanks

@borjaMinano
Copy link

@ankitdata , I tried with the three new models (and also with Large model) and I see the same detection problem with model-A, but not with models B and C, even though the heatmaps are different from Large model. I think the Large model's heatmap is the most accurate, watching at the raw image.

modB-COVID-19_ex-covid
Model B

modC-COVID-19_ex-covid
Model C

@Electro1111
Copy link

@nguyenhoa93 Hello! I was wondering if you had done this with model: COVIDNet-CXR4-A

The naming convention for the convolutional layers seems quite different and I was wondering what to use for the final conv layer name. any help would be so much appreciated! Thank you.

@Electro1111
Copy link

Electro1111 commented Dec 31, 2020

using @nguyenhoa93 gradcam code these are the results I get using these input specifications:

using model COVIDNet-CXR4-A from:
https://github.com/lindawangg/COVID-Net/blob/master/docs/models.md

{
"weightspath": "path/to/models/COVIDNet-CXR4-A",
"metaname":"model.meta" ,
"ckptname":"model-18540" ,
"input_tensor": "input_1:0",
"output_tensor": "norm_dense_1/Softmax:0",
"final_conv_tensor": "conv2d_203/convolution:0",
"input_size": 480,
"top_percent": .08,
}

using this model

running on image: /data/test/0088be53-27f2-4c30-882b-a73a3a5c8c71.png

I get these results

Pneumonia
pneumonia_0088be53-27f2-4c30-882b-a73a3a5c8c71

Normal
normal_0088be53-27f2-4c30-882b-a73a3a5c8c71

COVID-19
COVID-19_0088be53-27f2-4c30-882b-a73a3a5c8c71

It could be that my final convolutional layer name is incorrect, can anyone verify? @lindawangg @haydengunraj

EDIT:

ok so I also tried it with:

{
"weightspath": "path/to/models/COVIDNet-CXR4-A",
"metaname":"model.meta" ,
"ckptname":"model-18540" ,
"input_tensor": "input_1:0",
"output_tensor": "norm_dense_1/Softmax:0",
"final_conv_tensor": "conv_7b/convolution:0",
"input_size": (480,480,3),
"top_percent": .08,
}

and actually got some result that looks like an actual heatmap, but I just wanted to confirm that "conv_7b/convolution:0" is the final conv layer and the correct layer for computing gradcams in model COVIDNet-CXR4-A? @lindawangg @haydengunraj @nguyenhoa93

@Electro1111
Copy link

@ankitdata , I tried with the three new models (and also with Large model) and I see the same detection problem with model-A, but not with models B and C, even though the heatmaps are different from Large model. I think the Large model's heatmap is the most accurate, watching at the raw image.

modB-COVID-19_ex-covid
Model B

modC-COVID-19_ex-covid
Model C

@borjaMinano what tensornames did you use for final_conv_tensor for the models CXR3 B and C because they seem to be named different from CXR3 A

@Electro1111
Copy link

Electro1111 commented Jan 3, 2021 via email

@ankitdata
Copy link

Hi Ankit, Is there any way for you to confirm the layers I posted about and whether they are the final conv layers? I also asked a question in another issue about the training tensors for the small and large models. It would be extremely helpful. Best, Robbie Sadre

On Sun, Jan 3, 2021 at 10:50 AM Ankit Chilkalwar @.***> wrote: Thanks for sharing this information. — You are receiving this because you commented. Reply to this email directly, view it on GitHub <#64 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/AP7YI3TDWTJFZ57C7WJH7WDSYC37LANCNFSM4NBAZGCQ .

For Large Model:
input tensorname = 'input_1:0', type=str
output tensorname = 'dense_3/Softmax:0'

final conv layer for heatmaps
conv5_block3_out/add:0

if you want list all tensors use below code.
tensors = [t.name for op in tf.get_default_graph().get_operations() for t in op.values()]
for t in tensors:
print(t)

I am not sure on sharing information about small model.

thanks.!

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants