File size: 3,969 Bytes
3a7bf50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Record which experts a MoE onw model picks, per layer and per step, over a set of realistic chats (text, code,
image, multi-turn). Writes a JSON trace that sim_expert_cache.py replays against expert caches of different sizes.
usage: python trace_experts.py MODEL_DIR OUT.json [IMAGE]"""
import json, sys, time
from PIL import Image
from onw.chat import ChatEngine
from onw.runtime import SegmentedModel

TRACE = []                                   # one entry per _bind_experts call: [layer, S, n, [experts...]]
_orig = SegmentedModel._bind_experts


def _spy(self, req, m, idx, w, S, n):
    TRACE.append([m["consume_moe"], S, n, sorted({int(e) for e in idx[:n].reshape(-1)})])
    return _orig(self, req, m, idx, w, S, n)


SegmentedModel._bind_experts = _spy

CODE = '''def load_config(path):
    with open(path) as f:
        data = json.load(f)
    if "name" not in data:
        raise ValueError("missing name")
    data["port"] = int(data.get("port", 8080))
    return data'''
CHATS = [
    ("chat-ja", [["NPUとGPUの違いを、身近なたとえを使って説明してください。"],
                 ["では、ノートPCでLLMを動かすならどちらが向いていますか?理由も。"]]),
    ("code", [[f"次のPython関数に型ヒントとdocstringを付けて、関数全体を書き直してください。\n\n```python\n{CODE}\n```"]]),
    ("summary-ja", [["次の文章を3文で要約してください。\n\n" + "東京は日本の首都であり、政治・経済・文化の中心地である。"
                     "江戸時代には徳川幕府が置かれ、1868年の明治維新で東京と改称された。現在は約1400万人が暮らす世界有数の大都市で、"
                     "多くの企業や大学が集まっている。一方で、人口集中による住宅価格の高騰や通勤の混雑、災害への備えといった課題も抱えている。" * 2]]),
    ("chat-en", [["Explain the difference between a process and a thread, with a short Python example."]]),
    ("math-ja", [["ある商品を定価の2割引きで買うと960円でした。定価はいくらですか?途中の式も書いてください。"]]),
]


def main():
    d, out = sys.argv[1], sys.argv[2]
    img = Image.open(sys.argv[3]) if len(sys.argv) > 3 else None
    e = ChatEngine(d, "NPU", pld=False)
    runs = []
    chats = list(CHATS)
    if img is not None:
        chats.insert(1, ("image", [[{"type": "image", "image": img}, {"type": "text", "text": "この画像について詳しく説明してください。"}],
                                   ["その動物の気持ちを想像して、短い物語にしてください。"]]))
    for name, turns in chats:
        e.checkpoint = None
        msgs = []
        for t in turns:
            content = t[0] if len(t) == 1 and isinstance(t[0], str) else t
            msgs.append({"role": "user", "content": content})
            start = len(TRACE)
            parts, st, t0 = [], None, time.time()
            for x in e.stream_chat([dict(m) for m in msgs], 200):
                if isinstance(x, dict):
                    st = x
                else:
                    parts.append(x)
            text = "".join(parts)
            msgs.append({"role": "assistant", "content": text})
            runs.append({"name": name, "start": start, "end": len(TRACE), "prompt_tokens": st["prompt_tokens"],
                         "cached": st["cached_tokens"], "completion": st["completion_tokens"]})
            print(f"{name}: prompt {st['prompt_tokens']} (reused {st['cached_tokens']}), {st['completion_tokens']} tokens, "
                  f"{time.time() - t0:.1f}s | {text[:50]!r}", flush=True)
    meta = {k: e.model.meta.get(k) for k in ("arch",)}
    json.dump({"E": e.model.E, "K": e.model.K, "meta": meta, "runs": runs, "trace": TRACE}, open(out, "w"))
    print(f"wrote {len(TRACE)} binds to {out}")


if __name__ == "__main__":
    main()