BrainModel / ClassiferTest.py
leminhhung0101's picture
Create ClassiferTest.py
08f875f verified
Raw
History Blame Contribute Delete
9.83 kB
"""
Script doc lap de test model phan loai u nao da train (.keras).
Khong can toan bo pipeline training - chi can model + 1 file de test.
Ho tro 2 loai input:
1. File .h5 (dung dinh dang giong data training, co dataset 'image')
2. File anh thong thuong (.png, .jpg, .jpeg) - se duoc chuyen ve grayscale
Cach dung:
python test_inference.py --model /path/to/model.keras --input /path/to/slice.h5
python test_inference.py --model /path/to/model.keras --input /path/to/image.png
"""
import argparse
import numpy as np
import cv2
import h5py
import tensorflow as tf
from tensorflow.keras.layers import Layer, Dense, Dropout
from tensorflow.keras.applications.efficientnet_v2 import preprocess_input as efficientnetv2_preprocess
IMG_SIZE = (299, 299)
NUM_SLICES = 3
CLASS_NAMES = ["non-tumor", "tumor"]
# ============================================================
# CAC CUSTOM LAYER / LOSS - BAT BUOC PHAI CO DE load_model() HOAT DONG
# (copy y nguyen tu script training goc)
# ============================================================
class AttentionVisualizer(Layer):
def __init__(self, **kwargs):
super(AttentionVisualizer, self).__init__(**kwargs)
def call(self, attention_weights):
spatial_attention = tf.reduce_mean(attention_weights, axis=-1, keepdims=True)
spatial_attention = (spatial_attention - tf.reduce_min(spatial_attention, axis=(1, 2), keepdims=True)) / \
(tf.reduce_max(spatial_attention, axis=(1, 2), keepdims=True) -
tf.reduce_min(spatial_attention, axis=(1, 2), keepdims=True) + 1e-8)
return spatial_attention
class MCDropout(Layer):
def __init__(self, rate, **kwargs):
super(MCDropout, self).__init__(**kwargs)
self.rate = rate
self.dropout = Dropout(rate)
def call(self, inputs, training=None):
return self.dropout(inputs, training=True)
class EvidentialLoss(tf.keras.losses.Loss):
def __init__(self, class_weights=None, name="evidential_loss",
reduction=tf.keras.losses.Reduction.SUM_OVER_BATCH_SIZE):
super(EvidentialLoss, self).__init__(name=name, reduction=reduction)
self.class_weights = class_weights if class_weights is not None else {}
def call(self, y_true, y_pred):
ce_loss = tf.keras.losses.categorical_crossentropy(y_true, y_pred)
if self.class_weights:
class_indices = tf.argmax(y_true, axis=1)
weights = tf.gather(tf.constant(list(self.class_weights.values()), dtype=tf.float32), class_indices)
ce_loss = ce_loss * weights
reg_loss = tf.reduce_mean(tf.square(y_pred - y_true))
return tf.reduce_mean(ce_loss + 0.01 * reg_loss)
def get_config(self):
config = super(EvidentialLoss, self).get_config()
config.update({'class_weights': self.class_weights})
return config
class EvidentialLayer(Layer):
def __init__(self, num_classes, **kwargs):
super(EvidentialLayer, self).__init__(**kwargs)
self.num_classes = num_classes
def build(self, input_shape):
self.dense = Dense(self.num_classes, dtype=tf.float32)
super(EvidentialLayer, self).build(input_shape)
def call(self, inputs):
evidence = tf.nn.softplus(self.dense(inputs))
evidence = tf.clip_by_value(evidence, 0, 1e6)
alpha = evidence + 1
S = tf.reduce_sum(alpha, axis=1, keepdims=True) + 1e-10
prob = alpha / S
epistemic_uncertainty = self.num_classes / S
aleatoric_uncertainty = tf.reduce_sum(prob * (1 - prob) / (S + 1), axis=1, keepdims=True)
return prob, epistemic_uncertainty, aleatoric_uncertainty
def get_config(self):
config = super(EvidentialLayer, self).get_config()
config.update({'num_classes': self.num_classes})
return config
class ExplainableSelfAttention(Layer):
def __init__(self, units, return_attention=False, **kwargs):
super(ExplainableSelfAttention, self).__init__(**kwargs)
self.units = units
self.return_attention = return_attention
def build(self, input_shape):
self.W_q = Dense(self.units, use_bias=False)
self.W_k = Dense(self.units, use_bias=False)
self.W_v = Dense(self.units, use_bias=False)
self.dense = Dense(input_shape[-1])
self.attention_visualizer = AttentionVisualizer()
super(ExplainableSelfAttention, self).build(input_shape)
def call(self, inputs, return_attention=None):
return_attention = return_attention if return_attention is not None else self.return_attention
batch_size = tf.shape(inputs)[0]
height, width, channels = inputs.shape[1:]
x = tf.reshape(inputs, [batch_size, height * width, channels])
q = self.W_q(x)
k = self.W_k(x)
v = self.W_v(x)
scale = tf.cast(tf.sqrt(tf.cast(self.units, inputs.dtype)), inputs.dtype)
scores = tf.matmul(q, k, transpose_b=True) / scale
attention_weights = tf.nn.softmax(scores, axis=-1)
attended = tf.matmul(attention_weights, v)
output = self.dense(attended)
output = tf.reshape(output, [batch_size, height, width, channels])
output = inputs + output
if return_attention:
attention_spatial = tf.reshape(attention_weights, [batch_size, height, width, height * width])
attention_map = self.attention_visualizer(attention_spatial)
return output, attention_map
return output
def get_config(self):
config = super(ExplainableSelfAttention, self).get_config()
config.update({'units': self.units, 'return_attention': self.return_attention})
return config
class MaxProbLayer(Layer):
def __init__(self, **kwargs):
super(MaxProbLayer, self).__init__(**kwargs)
def call(self, inputs):
return tf.reduce_max(inputs, axis=1, keepdims=True)
CUSTOM_OBJECTS = {
'AttentionVisualizer': AttentionVisualizer,
'MCDropout': MCDropout,
'EvidentialLoss': EvidentialLoss,
'EvidentialLayer': EvidentialLayer,
'ExplainableSelfAttention': ExplainableSelfAttention,
'MaxProbLayer': MaxProbLayer,
}
# ============================================================
# TIEN XU LY ANH - GIONG HET LOGIC TRONG GENERATOR TRAINING
# ============================================================
def load_slice_from_h5(path):
"""Doc 1 slice tu file .h5 (dataset ten 'image')."""
with h5py.File(path, 'r') as f:
if 'image' not in f:
raise ValueError(f"File h5 khong co dataset 'image': {path}")
img = f['image'][:].astype(np.float32)
if img.ndim == 3:
img = img[..., 0]
elif img.ndim != 2:
raise ValueError(f"Shape anh khong hop le: {img.shape}")
return img
def load_slice_from_image(path):
"""Doc 1 slice tu file anh thuong (.png/.jpg), chuyen ve grayscale."""
img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
if img is None:
raise ValueError(f"Khong doc duoc anh: {path}")
return img.astype(np.float32)
def preprocess_slice(img_slice):
"""Resize, chuan hoa mean/std, clip, tao 3-channel stack, roi ap dung
efficientnetv2_preprocess - dung y nhu trong _load_slice/_generate_batch
cua ImprovedBrainTumorGenerator ban goc."""
if img_slice.shape != IMG_SIZE:
img_slice = cv2.resize(img_slice, IMG_SIZE, interpolation=cv2.INTER_LINEAR)
mean = np.mean(img_slice)
std = np.std(img_slice) + 1e-8
img_slice = (img_slice - mean) / std
img_slice = np.clip(img_slice, -3, 3)
# gia lap 3 slice lan can bang cach lap lai slice trung tam
stack = np.stack([img_slice, img_slice, img_slice], axis=-1)
X = np.expand_dims(stack, axis=0).astype(np.float32) # (1, 299, 299, 3)
X = efficientnetv2_preprocess(X)
return X
def load_and_preprocess(path):
if path.lower().endswith(('.h5', '.hdf5')):
img_slice = load_slice_from_h5(path)
else:
img_slice = load_slice_from_image(path)
return preprocess_slice(img_slice)
# ============================================================
# CHAY INFERENCE
# ============================================================
def run_inference(model_path, input_path):
print(f"Dang load model: {model_path}")
model = tf.keras.models.load_model(model_path, custom_objects=CUSTOM_OBJECTS)
print(f"Dang doc va tien xu ly file: {input_path}")
X = load_and_preprocess(input_path)
print("Dang du doan...")
outputs = model.predict(X, verbose=0)
# model tra ve dict cac output (classification, evidential_prob, confidence_score, ...)
probs = outputs['classification'][0]
pred_idx = int(np.argmax(probs))
pred_label = CLASS_NAMES[pred_idx]
print("\n" + "=" * 50)
print("KET QUA DU DOAN")
print("=" * 50)
print(f"Nhan du doan: {pred_label}")
for i, name in enumerate(CLASS_NAMES):
print(f" P({name}) = {probs[i]:.4f}")
if 'confidence_score' in outputs:
print(f"Confidence score: {float(outputs['confidence_score'][0][0]):.4f}")
if 'epistemic_uncertainty' in outputs:
print(f"Epistemic uncertainty: {float(outputs['epistemic_uncertainty'][0][0]):.4f}")
if 'aleatoric_uncertainty' in outputs:
print(f"Aleatoric uncertainty: {float(outputs['aleatoric_uncertainty'][0][0]):.4f}")
print("=" * 50)
return outputs
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Test model phan loai u nao voi 1 file don le")
parser.add_argument("--model", required=True, help="Duong dan file model .keras")
parser.add_argument("--input", required=True, help="Duong dan file .h5 hoac anh (.png/.jpg) can test")
args = parser.parse_args()
run_inference(args.model, args.input)