lindawangg / COVID-Net

COVID-Net Open Source Initiative
Other
1.15k stars 480 forks source link

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

Closed hnguyentt closed 4 years ago

hnguyentt commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

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 commented 4 years ago

@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 commented 4 years ago

@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 commented 4 years ago

@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 commented 4 years ago

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

hnguyentt commented 4 years ago

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

ankitdata commented 4 years ago

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 commented 4 years ago

@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 commented 4 years ago

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 commented 4 years ago

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

ankitdata commented 4 years ago

@nguyenhoa93

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

thanks .

hnguyentt commented 4 years ago

@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 commented 4 years ago

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 commented 4 years ago

@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 commented 3 years ago

@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 commented 3 years ago

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 commented 3 years ago

@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 commented 3 years ago

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 notifications@github.com wrote:

Thanks for sharing this information.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/lindawangg/COVID-Net/issues/64#issuecomment-753660384, or unsubscribe https://github.com/notifications/unsubscribe-auth/AP7YI3TDWTJFZ57C7WJH7WDSYC37LANCNFSM4NBAZGCQ .

ankitdata commented 3 years ago

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.!