| |
| """Evaluate the official test set and weights on a single DCU.""" |
|
|
| import argparse |
| import pickle |
| import time |
| from pathlib import Path |
|
|
| from _bootstrap import DATA_DIR, WEIGHT_DIR |
|
|
| import numpy as np |
| import tensorflow as tf |
| import tf_keras |
| from sklearn.metrics import average_precision_score, roc_auc_score |
|
|
|
|
| DEFAULT_DATA_DIR = DATA_DIR / "features" / "cdr_kmer3_ag_kmer1" |
| DEFAULT_WEIGHT_PATH = WEIGHT_DIR / "cdr_kmer3_ag_kmer1" / "Model99.h5" |
|
|
|
|
| def parse_args(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument("--data-dir", type=Path, default=DEFAULT_DATA_DIR) |
| parser.add_argument("--weights", type=Path, default=DEFAULT_WEIGHT_PATH) |
| parser.add_argument("--batch-size", type=int, default=64) |
| parser.add_argument( |
| "--output", |
| type=Path, |
| help="optional .npz path for labels and predictions", |
| ) |
| return parser.parse_args() |
|
|
|
|
| def load_test_set(data_dir): |
| |
| import model |
|
|
| with (data_dir / "cdr_features_te.pickle").open("rb") as reader: |
| cdr_features = pickle.load(reader) |
| with (data_dir / "ag_features_te.pickle").open("rb") as reader: |
| ag_features = pickle.load(reader) |
|
|
| if not cdr_features: |
| raise ValueError("The test set is empty") |
| if len(cdr_features) != len(ag_features): |
| raise ValueError( |
| f"Unpaired test features: CDR={len(cdr_features)}, AG={len(ag_features)}" |
| ) |
|
|
| cdr_labels = np.asarray([item.label_id for item in cdr_features], dtype=np.int32) |
| ag_labels = np.asarray([item.label_id for item in ag_features], dtype=np.int32) |
| if not np.array_equal(cdr_labels, ag_labels): |
| mismatch_count = int(np.count_nonzero(cdr_labels != ag_labels)) |
| raise ValueError(f"CDR/AG labels differ for {mismatch_count} test samples") |
| if set(np.unique(cdr_labels).tolist()) != {0, 1}: |
| raise ValueError(f"Expected binary labels 0/1, got {np.unique(cdr_labels)}") |
|
|
| inputs = [ |
| np.asarray([item.input_ids for item in cdr_features], dtype=np.int32), |
| np.asarray([item.cdr_number_ids for item in cdr_features], dtype=np.int32), |
| np.asarray([item.input_ids for item in ag_features], dtype=np.int32), |
| ] |
| sample_count = len(cdr_features) |
| expected_shapes = ((sample_count, 24), (sample_count, 24), (sample_count, 2371)) |
| actual_shapes = tuple(array.shape for array in inputs) |
| if actual_shapes != expected_shapes: |
| raise ValueError(f"Unexpected input shapes: {actual_shapes}, expected {expected_shapes}") |
|
|
| return inputs, cdr_labels |
|
|
|
|
| def main(): |
| args = parse_args() |
| if args.batch_size < 1: |
| raise ValueError(f"--batch-size must be positive, got {args.batch_size}") |
| if not args.data_dir.is_dir(): |
| raise FileNotFoundError(f"Data directory not found: {args.data_dir}") |
| if not args.weights.is_file(): |
| raise FileNotFoundError(f"Weight file not found: {args.weights}") |
|
|
| gpus = tf.config.list_physical_devices("GPU") |
| if not gpus: |
| raise RuntimeError( |
| "TensorFlow cannot see a DCU. Set HIP_VISIBLE_DEVICES and " |
| "CUDA_VISIBLE_DEVICES before starting Python." |
| ) |
| for gpu in gpus: |
| tf.config.experimental.set_memory_growth(gpu, True) |
|
|
| inputs, labels = load_test_set(args.data_dir) |
|
|
| |
| from model import get_model |
|
|
| with tf.device("/GPU:0"): |
| model = get_model() |
| model.load_weights(str(args.weights)) |
| start_time = time.perf_counter() |
| predictions = model.predict( |
| inputs, |
| batch_size=args.batch_size, |
| verbose=1, |
| ).reshape(-1) |
| elapsed_seconds = time.perf_counter() - start_time |
|
|
| if predictions.shape != labels.shape: |
| raise ValueError( |
| f"Prediction/label shape mismatch: {predictions.shape} vs {labels.shape}" |
| ) |
| if not np.all(np.isfinite(predictions)): |
| raise FloatingPointError("Test predictions contain NaN or Inf") |
| if np.any((predictions < 0.0) | (predictions > 1.0)): |
| raise ValueError("Test predictions are outside [0, 1]") |
|
|
| auroc = float(roc_auc_score(labels, predictions)) |
| aupr = float(average_precision_score(labels, predictions)) |
| positive_count = int(labels.sum()) |
| negative_count = int(labels.size - positive_count) |
| dcu_variable_count = sum("GPU:0" in variable.device for variable in model.variables) |
|
|
| print("\nFull official test-set evaluation") |
| print(f"TensorFlow: {tf.__version__}") |
| print(f"tf_keras: {tf_keras.__version__}") |
| print(f"DCU: {gpus[0]}") |
| print(f"Samples: {labels.size}") |
| print(f"Positive/negative: {positive_count}/{negative_count}") |
| print(f"Batch size: {args.batch_size}") |
| print(f"Variables placed on GPU:0: {dcu_variable_count}/{len(model.variables)}") |
| print(f"Prediction min/max/mean: {predictions.min():.10g} / " |
| f"{predictions.max():.10g} / {predictions.mean():.10g}") |
| print(f"Elapsed seconds: {elapsed_seconds:.3f}") |
| print(f"Throughput samples/s: {labels.size / elapsed_seconds:.3f}") |
| print(f"AUROC: {auroc:.10f}") |
| print(f"AUPR: {aupr:.10f}") |
| print("Paper reference AUROC/AUPR: 0.926 / 0.952") |
|
|
| if dcu_variable_count == 0: |
| raise RuntimeError("No model variables were placed on GPU:0") |
|
|
| if args.output is not None: |
| args.output.parent.mkdir(parents=True, exist_ok=True) |
| np.savez_compressed( |
| args.output, |
| labels=labels, |
| predictions=predictions, |
| auroc=np.asarray(auroc), |
| aupr=np.asarray(aupr), |
| ) |
| print(f"Saved predictions: {args.output}") |
|
|
| print("Full test-set inference: PASS") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|