Bimaldo commited on
Commit
ed2140a
·
verified ·
1 Parent(s): 42f5d04

Upload inference.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. inference.py +136 -0
inference.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Inference script for Diffusion-LM Riddle Solver (Hugging Face model).
2
+
3
+ Usage:
4
+ python3 inference.py --riddle "i speak without a mouth what am i"
5
+ """
6
+
7
+ import json
8
+ import sys
9
+ import argparse
10
+ import warnings
11
+
12
+
13
+ def load_model(model_dir: str = "."):
14
+ """Load model weights and config from HF model directory."""
15
+ import torch
16
+
17
+ with open(f"{model_dir}/config.json") as f:
18
+ config = json.load(f)
19
+
20
+ with open(f"{model_dir}/vocab.json") as f:
21
+ vocab = json.load(f)
22
+
23
+ inv_vocab = {int(v): k for k, v in vocab.items()}
24
+
25
+ # Add riddle_diffusion.py to Python path if running from HF directory
26
+ sys.path.insert(0, model_dir)
27
+ from riddle_diffusion import DiffusionRiddleModel
28
+ from riddle_diffusion import get_schedule
29
+
30
+ model = DiffusionRiddleModel(
31
+ vocab_size=config["vocab_size"],
32
+ d_model=config["d_model"],
33
+ n_layers=config["n_layers"],
34
+ d_ff=config["d_ff"],
35
+ n_heads=config["n_heads"],
36
+ a_len=config["a_len"],
37
+ q_len=config["q_len"],
38
+ T=config["T"],
39
+ )
40
+
41
+ state = torch.load(f"{model_dir}/model.safetensors", map_location="cpu",
42
+ weights_only=True)
43
+ model.load_state_dict(state)
44
+ model.eval()
45
+
46
+ return model, config, vocab, inv_vocab
47
+
48
+
49
+ def predict(model, config, vocab, inv_vocab, riddle: str, k_samples: int = 10,
50
+ device: str = "cpu"):
51
+ """Run prediction on a single riddle."""
52
+ import torch
53
+ import torch.nn.functional as F
54
+
55
+ model.to(device)
56
+
57
+ # Tokenize
58
+ tokens = [vocab.get(w, vocab.get("<UNK>", 1)) for w in riddle.lower().split()]
59
+ if len(tokens) > config["q_len"]:
60
+ tokens = tokens[:config["q_len"]]
61
+
62
+ q_tokens = torch.tensor([tokens], device=device)
63
+
64
+ # Diffusion schedule (sqrt power law)
65
+ betas = torch.sqrt(torch.linspace(1e-4, 0.02, config["T"])).to(device)
66
+ alphas = 1.0 - betas
67
+ alpha_bars = torch.cumprod(alphas, dim=0)
68
+
69
+ # Reverse diffusion
70
+ x_t = torch.randn(k_samples, config["a_len"], config["d_model"], device=device)
71
+
72
+ for t in reversed(range(config["T"])):
73
+ t_tensor = torch.full((k_samples,), t, device=device, dtype=torch.long)
74
+ pred_x0 = model(x_t, t_tensor, q_tokens)
75
+
76
+ # Euclidean clamping
77
+ logits = 2.0 * F.linear(pred_x0, model.emb.weight)
78
+ logits = logits - model.emb.weight.square().sum(dim=-1).unsqueeze(0).unsqueeze(0)
79
+ x0_tokens = logits.argmax(dim=-1)
80
+ x0_emb = model.emb(x0_tokens)
81
+
82
+ if t > 0:
83
+ alpha_bar = alpha_bars[t]
84
+ alpha_bar_prev = alpha_bars[t - 1]
85
+ beta_tilde = betas[t] * (1 - alpha_bar_prev) / (1 - alpha_bar)
86
+ noise = torch.randn_like(x_t)
87
+ coef1 = torch.sqrt(alpha_bar_prev) * betas[t] / (1 - alpha_bar)
88
+ coef2 = torch.sqrt(alpha_bar) * (1 - alpha_bar_prev) / (1 - alpha_bar)
89
+ mu = coef1 * x0_emb + coef2 * x_t
90
+ x_t = mu + torch.sqrt(beta_tilde) * noise
91
+ else:
92
+ x_t = x0_emb
93
+
94
+ # Decode
95
+ pred_tokens = []
96
+ for b in range(k_samples):
97
+ pred = ""
98
+ for pos in range(config["a_len"]):
99
+ tok_id = x0_tokens[b, pos].item()
100
+ if tok_id == 0:
101
+ break
102
+ pred += inv_vocab.get(tok_id, "?") + " "
103
+ pred_tokens.append(pred.strip())
104
+
105
+ # Majority vote
106
+ from collections import Counter
107
+ counts = Counter(pred_tokens)
108
+ winner = counts.most_common(1)[0][0]
109
+
110
+ return winner, pred_tokens
111
+
112
+
113
+ def main():
114
+ parser = argparse.ArgumentParser()
115
+ parser.add_argument("--riddle", required=True, help="Riddle text")
116
+ parser.add_argument("--model-dir", default=".", help="Model directory")
117
+ parser.add_argument("--device", default="cpu", help="Device (cpu, mps, cuda)")
118
+ parser.add_argument("--k-samples", type=int, default=10)
119
+ args = parser.parse_args()
120
+
121
+ model, config, vocab, inv_vocab = load_model(args.model_dir)
122
+ answer, candidates = predict(model, config, vocab, inv_vocab,
123
+ args.riddle, args.k_samples, args.device)
124
+
125
+ print(f"Riddle: {args.riddle}")
126
+ print(f"Answer: {answer}")
127
+ if len(set(candidates)) > 1:
128
+ from collections import Counter
129
+ counts = Counter(candidates)
130
+ print(f"Candidates ({args.k_samples} samples):")
131
+ for text, count in counts.most_common():
132
+ print(f" {text:<20} ({count} votes)")
133
+
134
+
135
+ if __name__ == "__main__":
136
+ main()