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())