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