File size: 4,549 Bytes
f26049e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Model

def make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_index=None):
    """
    Generate Grad-CAM heatmap showing what the model actually focuses on.
    
    Args:
        img_array: Preprocessed image array (1, 224, 224, 3)
        model: Trained model
        last_conv_layer_name: Name of the last convolutional layer
        pred_index: Index of the class to generate heatmap for (None = predicted class)
    
    Returns:
        heatmap: 2D array showing model attention
    """
    try:
        # Create a model that maps the input image to the activations of the last conv layer
        # as well as the output predictions
        grad_model = Model(
            inputs=[model.inputs],
            outputs=[model.get_layer(last_conv_layer_name).output, model.output]
        )
        
        # Compute the gradient of the top predicted class for our input image
        # with respect to the activations of the last conv layer
        with tf.GradientTape() as tape:
            last_conv_layer_output, preds = grad_model(img_array)
            if pred_index is None:
                pred_index = tf.argmax(preds[0])
            class_channel = preds[:, pred_index]
        
        # This is the gradient of the output neuron (top predicted or chosen)
        # with regard to the output feature map of the last conv layer
        grads = tape.gradient(class_channel, last_conv_layer_output)
        
        # This is a vector where each entry is the mean intensity of the gradient
        # over a specific feature map channel
        pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))
        
        # We multiply each channel in the feature map array
        # by "how important this channel is" with regard to the top predicted class
        # then sum all the channels to obtain the heatmap class activation
        last_conv_layer_output = last_conv_layer_output[0]
        heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis]
        heatmap = tf.squeeze(heatmap)
        
        # For visualization purpose, we will also normalize the heatmap between 0 & 1
        heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap)
        
        return heatmap.numpy()
        
    except Exception as e:
        print(f"Grad-CAM error: {e}")
        return None

def find_last_conv_layer(model):
    """
    Automatically find the last convolutional layer in the model.
    """
    conv_layers = []
    for layer in model.layers:
        if 'conv' in layer.name.lower():
            conv_layers.append(layer.name)
    
    if conv_layers:
        return conv_layers[-1]
    else:
        # Fallback: look for common layer names
        common_names = ['block5_conv3', 'conv5_block3_3_conv', 'top_conv', 'conv_7b']
        for name in common_names:
            try:
                model.get_layer(name)
                return name
            except:
                continue
        return None

def create_real_attention_heatmap(img, model, predictions):
    """
    Create a real attention heatmap using Grad-CAM.
    """
    try:
        # Preprocess image for Grad-CAM
        img_resized = img.resize((224, 224))
        img_array = np.array(img_resized, dtype=np.float32)
        
        # Handle grayscale
        if len(img_array.shape) == 2:
            img_array = np.stack([img_array] * 3, axis=-1)
        
        # Normalize and add batch dimension
        img_array = np.expand_dims(img_array, axis=0) / 255.0
        
        # Find the last convolutional layer
        last_conv_layer_name = find_last_conv_layer(model)
        
        if last_conv_layer_name is None:
            print("Could not find convolutional layer for Grad-CAM")
            return None
            
        print(f"Using layer: {last_conv_layer_name}")
        
        # Generate Grad-CAM heatmap
        heatmap = make_gradcam_heatmap(
            img_array, 
            model, 
            last_conv_layer_name,
            pred_index=np.argmax(predictions)
        )
        
        if heatmap is not None:
            # Resize heatmap to match input image size
            heatmap_resized = tf.image.resize(
                heatmap[..., tf.newaxis], 
                (224, 224)
            ).numpy()[:, :, 0]
            
            return heatmap_resized
        else:
            return None
            
    except Exception as e:
        print(f"Real attention heatmap error: {e}")
        return None