zeechimp commited on
Commit
6f1ff07
·
verified ·
1 Parent(s): e3cbc95

Update evaluate.py

Browse files
Files changed (1) hide show
  1. evaluate.py +114 -55
evaluate.py CHANGED
@@ -1,76 +1,135 @@
1
  """
2
  evaluate.py
3
 
4
- Reproduce the reported test metrics on the intent router.
 
 
 
 
5
  """
6
 
7
  import numpy as np
8
- from hv_intent import HVIntentRouter
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
 
11
- TEST_DATA = {
12
- "weather": ["what is the temperature", "is it cloudy", "how windy is it"],
13
- "time": ["how many hours until noon",
14
- "when does the sun set",
15
- "when does the store close"],
16
- "reminder": ["remind me to water the plants",
17
- "set an alarm for morning",
18
- "remind me to pick up kids"],
19
- "math": ["what is one hundred minus forty",
20
- "calculate the sum of one and two",
21
- "what is the product"],
22
- "greeting": ["what is going on", "hello friend", "good afternoon"],
23
- }
24
 
25
 
26
- def main(model_path="."):
27
- router = HVIntentRouter.from_pretrained(model_path)
 
 
 
 
28
 
29
- print(f"Model loaded: {router.D}-bit, {len(router.labels)} intents")
30
- print(f"Labels: {router.labels}")
31
- print()
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  correct = 0
34
  total = 0
35
- per_intent = {label: [0, 0] for label in router.labels}
36
 
37
- print(f"{'phrase':<40} | {'pred':<10} | {'true':<10} | ok")
38
- print("-" * 76)
 
 
 
 
 
39
 
40
- for true_label, phrases in TEST_DATA.items():
41
- for phrase in phrases:
42
- pred = router.predict(phrase)
43
- ok = pred == true_label
44
- correct += int(ok)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  total += 1
46
- per_intent[true_label][0] += int(ok)
47
- per_intent[true_label][1] += 1
48
- print(f"{phrase:<40} | {pred:<10} | {true_label:<10} | "
49
- f"{'OK' if ok else 'ERR'}")
50
-
51
- print()
52
- print(f"Overall: {correct}/{total} = {correct/total:.1%}")
53
- print()
54
- print("Per-intent accuracy:")
55
- for label in router.labels:
56
- c, n = per_intent[label]
57
- print(f" {label:<10}: {c}/{n} = {c/n:.1%}")
58
-
59
- print()
60
- print("Latency benchmark:")
61
- import time
62
- phrases = [p for phrases in TEST_DATA.values() for p in phrases]
63
- router.predict(phrases[0]) # warmup
64
- t0 = time.perf_counter()
65
- for _ in range(100):
66
- for p in phrases:
67
- router.predict(p)
68
- dt = (time.perf_counter() - t0) / (100 * len(phrases)) * 1000
69
- print(f" avg prediction: {dt:.3f} ms")
70
- print(f" throughput: {1000/dt:.0f} predictions/second")
71
 
72
 
73
  if __name__ == "__main__":
74
  import sys
75
- path = sys.argv[1] if len(sys.argv) > 1 else "."
76
- main(path)
 
1
  """
2
  evaluate.py
3
 
4
+ Reproduce the 5-fold cross-validation accuracy.
5
+
6
+ Reconstructs the fold splits from phrases.txt, retrains on each
7
+ 4/5 subset, and reports the held-out accuracy. Result should be
8
+ around 76%.
9
  """
10
 
11
  import numpy as np
12
+ from hv_intent import load_v2, predict, _encode_phrase
13
+ import json
14
+ from pathlib import Path
15
+
16
+
17
+ N_FOLDS = 5
18
+ D = 2048
19
+ N_AUG = 12
20
+ N_CODEBOOKS = 2
21
+ AUG_SEED = 42
22
+ DROP_THRESHOLD = 0.15
23
+ K_NEIGHBOURS = 7
24
+
25
+
26
+ def compute_word_weights(train_data, n_classes):
27
+ counts = {}
28
+ for phrase, t in train_data:
29
+ for w in phrase.lower().split():
30
+ counts.setdefault(w, np.zeros(n_classes))[t] += 1
31
+ floor = 1.0 / n_classes
32
+ return {
33
+ w: float((c.max() / c.sum() - floor) / (1.0 - floor))
34
+ for w, c in counts.items() if c.sum() >= 1
35
+ }
36
+
37
+
38
+ def augment_drop(phrase, rng):
39
+ words = phrase.split()
40
+ n = len(words)
41
+ if n <= 2:
42
+ return phrase
43
+ op = int(rng.integers(0, 3))
44
+ if op == 0:
45
+ i = int(rng.integers(0, n))
46
+ return " ".join(w for j, w in enumerate(words) if j != i)
47
+ elif op == 1:
48
+ if n <= 3:
49
+ return phrase
50
+ idx = rng.choice(n, size=2, replace=False)
51
+ return " ".join(w for j, w in enumerate(words) if j not in idx)
52
+ else:
53
+ return " ".join(words[:-1] if rng.random() < 0.5 else words[1:])
54
 
55
 
56
+ def build_bank(train_data, n_aug, seed):
57
+ rng = np.random.default_rng(seed)
58
+ bank = list(train_data)
59
+ for phrase, label in train_data:
60
+ for _ in range(n_aug):
61
+ bank.append((augment_drop(phrase, rng), label))
62
+ return bank
 
 
 
 
 
 
63
 
64
 
65
+ def main(path="."):
66
+ path = Path(path)
67
+ with open(path / "config.json") as f:
68
+ config = json.load(f)
69
+ intent_labels = config["intent_labels"]
70
+ l2i = {l: i for i, l in enumerate(intent_labels)}
71
 
72
+ # Load phrases
73
+ data = np.load(path / "codebooks.npz", allow_pickle=True)
74
+ phrases = list(data["phrases"])
75
+ labels = data["phrase_labels"]
76
+
77
+ # Group by intent
78
+ by_intent = {l: [] for l in intent_labels}
79
+ for phrase, t in zip(phrases[:50], labels[:50]): # first 50 are originals
80
+ by_intent[intent_labels[t]].append(phrase)
81
+
82
+ # Make folds
83
+ folds = [[] for _ in range(N_FOLDS)]
84
+ for label, phrases_list in by_intent.items():
85
+ for i, p in enumerate(phrases_list):
86
+ folds[i % N_FOLDS].append((p, l2i[label]))
87
 
88
  correct = 0
89
  total = 0
 
90
 
91
+ for fold_i in range(N_FOLDS):
92
+ test_data = folds[fold_i]
93
+ train_data = [item for j, f in enumerate(folds) if j != fold_i
94
+ for item in f]
95
+
96
+ ww = compute_word_weights(train_data, len(intent_labels))
97
+ bank = build_bank(train_data, N_AUG, AUG_SEED + fold_i)
98
 
99
+ # Vocab
100
+ vocab = sorted({w for p, _ in train_data for w in p.lower().split()})
101
+ vocab_index = {w: i for i, w in enumerate(vocab)}
102
+
103
+ codebooks = []
104
+ for cb in range(N_CODEBOOKS):
105
+ rng = np.random.default_rng(AUG_SEED * 100 + fold_i * 10 + cb)
106
+ codebooks.append(rng.choice(np.array([-1, 1], dtype=np.int8),
107
+ size=(len(vocab), D)))
108
+ banks = []
109
+ for K in codebooks:
110
+ H = np.stack([_encode_phrase(p, K, vocab_index, ww)
111
+ for p, _ in bank])
112
+ Y = np.array([y for _, y in bank], dtype=np.int32)
113
+ banks.append((H, Y))
114
+
115
+ for phrase, t in test_data:
116
+ vote = np.zeros(len(intent_labels), dtype=np.float32)
117
+ for K, (H, Y) in zip(codebooks, banks):
118
+ q = _encode_phrase(phrase, K, vocab_index, ww)
119
+ s = H @ q
120
+ k_eff = min(K_NEIGHBOURS, s.shape[0])
121
+ top = np.argpartition(-s, k_eff - 1)[:k_eff]
122
+ for j in top:
123
+ vote[Y[j]] += max(s[j], 0.0)
124
+ if int(vote.argmax()) == t:
125
+ correct += 1
126
  total += 1
127
+
128
+ print(f"5-fold CV accuracy: {correct}/{total} = {correct/total:.1%}")
129
+ print(f"Config: D={D}, aug={N_AUG}, codebooks={N_CODEBOOKS}, "
130
+ f"k={K_NEIGHBOURS}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
 
133
  if __name__ == "__main__":
134
  import sys
135
+ main(sys.argv[1] if len(sys.argv) > 1 else ".")