File size: 5,420 Bytes
86f5ee3 | 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 128 129 130 131 132 133 134 135 136 | """
Smoke test for the thinkhong/RAIC checkpoint on the Hugging Face Hub.
Downloads config/model/tokenizers via `trust_remote_code=True` and runs a forward pass on a
single synthetic example covering every continuous and categorical variable the model was
trained with. Raw values for a few named variables are min-max normalized with the provided
`train_continuous_variables_min_max_values.json` to show real preprocessing; all other
variables get a neutral placeholder value since this is a connectivity/parity smoke test,
not a clinical prediction.
Usage:
python test_upload.py [--repo-id thinkhong/RAIC] [--revision main]
"""
import argparse
import json
import sys
import torch
from huggingface_hub import hf_hub_download
from transformers import AutoConfig, AutoModelForSequenceClassification, AutoTokenizer
CLS_ID = 1
SPECIALS = {"[UNK]", "[CLS]", "[SEP]", "[PAD]", "[MASK]"}
def minmax_normalize(x, x_min, x_max, new_min=1.0, new_max=3.0):
return (x - x_min) / (x_max - x_min) * (new_max - new_min) + new_min
def build_example(bias_tok, weights_tok, min_max, raw_values=None):
raw_values = raw_values or {}
bias_vocab = bias_tok.get_vocab()
weight_vocab = weights_tok.get_vocab()
bias_names = [n for n in bias_vocab if n not in SPECIALS]
cont_vars = sorted(n for n in bias_names if n.endswith("_Continuous") or n.endswith("_Integer"))
cat_vars = sorted(n for n in bias_names if n not in cont_vars)
num_input_val = [1.0] # CLS weight
for v in cont_vars:
if v in raw_values and v in min_max:
x_min, x_max = min_max[v]
num_input_val.append(minmax_normalize(raw_values[v], x_min, x_max))
else:
num_input_val.append(2.0) # neutral placeholder (mid of the [1, 3] normalization range)
num_input_ids = [CLS_ID] + [weight_vocab[v] for v in cont_vars]
num_variable_ids = [CLS_ID] + [bias_vocab[v] for v in cont_vars]
cat_value_tokens = []
for cv in cat_vars:
token = f"{cv}_{int(raw_values[cv])}" if cv in raw_values else None
if token is None or token not in weight_vocab:
token = sorted(w for w in weight_vocab if w.startswith(cv + "_"))[0]
cat_value_tokens.append(token)
cat_input_ids = [weight_vocab[t] for t in cat_value_tokens]
cat_variable_ids = [bias_vocab[v] for v in cat_vars]
seq_len = len(num_input_ids) + len(cat_input_ids)
batch = {
"num_input_val": torch.tensor([num_input_val], dtype=torch.float32),
"num_input_ids": torch.tensor([num_input_ids], dtype=torch.long),
"cat_input_ids": torch.tensor([cat_input_ids], dtype=torch.long),
"num_variable_ids": torch.tensor([num_variable_ids], dtype=torch.long),
"cat_variable_ids": torch.tensor([cat_variable_ids], dtype=torch.long),
"attention_mask": torch.tensor([[1] * seq_len], dtype=torch.long),
}
return batch, {"cont_vars": cont_vars, "cat_vars": cat_vars, "seq_len": seq_len}
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--repo-id", default="thinkhong/RAIC")
parser.add_argument("--revision", default=None)
args = parser.parse_args()
print(f"Loading config/model from '{args.repo_id}' (trust_remote_code=True) ...")
config = AutoConfig.from_pretrained(args.repo_id, revision=args.revision, trust_remote_code=True)
model = AutoModelForSequenceClassification.from_pretrained(
args.repo_id, revision=args.revision, trust_remote_code=True
)
model.eval()
print(f" architecture: {config.architectures}")
print(f" num_labels: {config.num_labels} id2label: {config.id2label}")
print(f" params: {sum(p.numel() for p in model.parameters()):,}")
print("Loading tokenizers ...")
bias_tok = AutoTokenizer.from_pretrained(
args.repo_id, revision=args.revision, subfolder="ftt_variable_bias_tokenizer_20240530"
)
weights_tok = AutoTokenizer.from_pretrained(
args.repo_id, revision=args.revision, subfolder="ftt_variable_weights_tokenizer_20240530"
)
print("Loading min/max normalization stats ...")
min_max_path = hf_hub_download(
args.repo_id, "train_continuous_variables_min_max_values.json", revision=args.revision
)
min_max = json.load(open(min_max_path))
# A few named, semi-realistic raw values; every other variable gets a neutral placeholder.
raw_values = {
"AGE_Continuous": 55.0,
"SBP_Continuous": 130.0,
"SEX_Categorical": 0,
}
batch, meta = build_example(bias_tok, weights_tok, min_max, raw_values)
print(
f"Built example: seq_len={meta['seq_len']} "
f"({len(meta['cont_vars'])} continuous + {len(meta['cat_vars'])} categorical + 1 CLS)"
)
with torch.no_grad():
out1 = model(**batch)
out2 = model(**batch) # determinism check
assert torch.allclose(out1.logits, out2.logits), "Model is non-deterministic in eval mode!"
probs = torch.softmax(out1.logits, dim=-1)[0]
pred_id = int(probs.argmax())
print()
print("Logits:", out1.logits.tolist())
print("Probs: ", probs.tolist())
print(f"Predicted class: {pred_id} ({config.id2label[pred_id]})")
print()
print(
"OK: model downloaded from the Hub, loaded with trust_remote_code=True, "
"and produced a deterministic forward pass."
)
if __name__ == "__main__":
sys.exit(main())
|