File size: 4,211 Bytes
3366f95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import json
import os
import numpy as np
# removed tensorflow import
import matplotlib
matplotlib.use('Agg')
from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score
import matplotlib.pyplot as plt


def save_history(history, filepath):
    if history is None:
        return
    data = {key: value for key, value in history.history.items()}
    np.savez_compressed(filepath, **data)


def plot_training_history(history, output_dir):
    if history is None:
        return
    os.makedirs(output_dir, exist_ok=True)
    plt.figure(figsize=(10, 4))
    plt.plot(history.history['loss'], label='train_loss')
    plt.plot(history.history['val_loss'], label='val_loss')
    plt.plot(history.history.get('accuracy', []), label='train_acc')
    plt.plot(history.history.get('val_accuracy', []), label='val_acc')
    plt.legend()
    plt.xlabel('Epoch')
    plt.ylabel('Value')
    plt.title('Training History')
    plt.tight_layout()
    plt.savefig(os.path.join(output_dir, 'training_history.png'))
    plt.close()


def save_metrics_json(metrics, filepath):
    with open(filepath, 'w', encoding='utf-8') as f:
        json.dump(metrics, f, indent=2)


def load_metrics_json(filepath):
    with open(filepath, 'r', encoding='utf-8') as f:
        return json.load(f)


def load_history_npz(filepath):
    data = np.load(filepath, allow_pickle=True)
    return {key: data[key].tolist() for key in data.files}


def compute_metrics(model, dataset):
    y_true = []
    y_pred = []
    y_score = []
    for images, labels in dataset:
        logits = model.predict(images, verbose=0)
        probs = logits.flatten()
        predictions = (probs >= 0.5).astype(int)
        y_true.extend(labels.numpy().tolist())
        y_pred.extend(predictions.tolist())
        y_score.extend(probs.tolist())

    report = classification_report(y_true, y_pred, output_dict=True, zero_division=0)
    cm = confusion_matrix(y_true, y_pred)
    roc_auc = roc_auc_score(y_true, y_score)
    metrics = {
        'classification_report': report,
        'confusion_matrix': cm.tolist(),
        'roc_auc': float(roc_auc),
    }
    return metrics


def _find_layer(model, layer_name):
    try:
        return model.get_layer(layer_name)
    except ValueError:
        for layer in model.layers:
            if hasattr(layer, 'layers'):
                try:
                    return layer.get_layer(layer_name)
                except ValueError:
                    continue
        raise


def make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_index=None):
    conv_layer = _find_layer(model, last_conv_layer_name)
    grad_model = tf.keras.models.Model(
        [model.inputs],
        [conv_layer.output, model.output],
    )
    with tf.GradientTape() as tape:
        conv_outputs, predictions = grad_model(img_array)
        if pred_index is None:
            pred_index = 0
        loss = predictions[:, pred_index]
    grads = tape.gradient(loss, conv_outputs)
    pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))
    conv_outputs = conv_outputs[0]
    heatmap = conv_outputs @ pooled_grads[..., tf.newaxis]
    heatmap = tf.squeeze(heatmap)
    heatmap = tf.maximum(heatmap, 0) / (tf.math.reduce_max(heatmap) + 1e-8)
    return heatmap.numpy()


def overlay_heatmap(image, heatmap, alpha=0.4, colormap='viridis'):
    import matplotlib.cm as cm
    image = np.array(image, dtype=np.uint8)
    if heatmap.ndim == 2 and heatmap.shape[:2] != image.shape[:2]:
        heatmap = tf.image.resize(heatmap[..., np.newaxis], image.shape[:2], method='bilinear').numpy().squeeze()
    heatmap = np.uint8(255 * heatmap)
    colormap = cm.get_cmap(colormap)
    colored = colormap(heatmap)
    colored = tf.keras.preprocessing.image.array_to_img(colored)
    colored = np.array(colored)
    if colored.shape[:2] != image.shape[:2]:
        colored = tf.image.resize(colored, image.shape[:2], method='bilinear').numpy().astype(np.uint8)
    overlay = colored[:, :, :3] * alpha + image * (1 - alpha)
    overlay = np.clip(overlay, 0, 255).astype(np.uint8)
    return overlay