File size: 4,709 Bytes
df41178
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
"""実験23-A: 層ごとの「痛み」を測る。再量子化なし・順伝播なし。

引き継ぎ書6-1「層ごとのビット配分」の第一歩。
252層すべてに一律1.15bitを配っているが、層によって誤差は2倍以上違うはず。
まずそれを実測する。

材料は既にある:
  - data/ckpt/unified/*.npy  ... 1.154bitで量子化済みの252層
  - data/calib/real_imatrix.npz ... 本物の活性化の2乗平均(対角)
  - 元のGGUF ................... 元の重み

測るもの(出力空間の相対誤差。重み空間の誤差ではない):
    err = ‖(W−Ŵ)·diag(√imat)‖_F / ‖W·diag(√imat)‖_F

RESULTS.mdの最大の教訓「重み誤差はモデル品質を保証しない」を踏まえ、
活性化で重みづけした**出力側**の誤差で見る。これが層の痛みの指標。
"""
import json, os, sys, time
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.insert(0, os.path.join(ROOT, "lib"))
import numpy as np
import forward

MODEL = (os.environ.get("MODEL_BLOB") or "/path/to/localai/ollama-models/blobs/"
         "sha256-4a188102020e9c9530b687fd6400f775c45e90a0d7baafe65bd0a36963fbb7ba")
CKPT = os.path.join(ROOT, "data", "ckpt", os.environ.get("NAME", "unified"))
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                   "results", os.environ.get("NAME", "unified") + "_sens.json")


def say(m):
    print(f"[{time.strftime('%H:%M:%S')}] {m}", flush=True)


imat = np.load(os.path.join(ROOT, "data/calib/real_imatrix.npz"))
m = forward.Model(MODEL)
done = json.load(open(OUT)) if os.path.exists(OUT) else {}

names = sorted(f[:-4] for f in os.listdir(CKPT) if f.endswith(".npy"))
say(f"{len(names)}層を評価する(済み{len(done)}層)")

for i, n in enumerate(names):
    if n in done:
        continue
    if n not in imat:
        say(f"  skip {n}: imatrixに無い")
        continue
    W = m.T(n).astype(np.float32)
    Wh = np.load(os.path.join(CKPT, n + ".npy")).astype(np.float32)
    if W.shape != Wh.shape:
        say(f"  skip {n}: 形が違う {W.shape} vs {Wh.shape}")
        continue
    s = np.sqrt(np.maximum(imat[n].astype(np.float32), 0.0))
    if s.shape[0] != W.shape[1]:
        say(f"  skip {n}: imatrixの長さが違う {s.shape[0]} vs {W.shape[1]}")
        continue
    D = (W - Wh) * s[None, :]
    B = W * s[None, :]
    num = float(np.sqrt((D.astype(np.float64) ** 2).sum()))
    den = float(np.sqrt((B.astype(np.float64) ** 2).sum())) or 1.0
    # 重み空間の誤差も並べて記録する(比較用。これが当てにならないことの証拠になる)
    wnum = float(np.sqrt(((W - Wh).astype(np.float64) ** 2).sum()))
    wden = float(np.sqrt((W.astype(np.float64) ** 2).sum())) or 1.0
    blk = int(n.split(".")[1])
    kind = n.split(".")[2]
    done[n] = dict(blk=blk, kind=kind, size=int(W.size),
                   out=int(W.shape[0]), inn=int(W.shape[1]),
                   err=num / den, werr=wnum / wden)
    m.drop(n)
    del W, Wh, D, B
    if (i + 1) % 20 == 0:
        json.dump(done, open(OUT, "w"), ensure_ascii=False, indent=1)
        say(f"  {i+1}/{len(names)}")

json.dump(done, open(OUT, "w"), ensure_ascii=False, indent=1)
say(f"保存: {OUT}")

# --- 要約 ---
v = sorted(done.values(), key=lambda d: -d["err"])
tot = sum(d["size"] for d in v)
glob = float(np.sqrt(sum((d["err"] ** 2) * d["size"] for d in v) / tot))
print(f"\n全体の出力誤差(サイズ加重RMS) = {glob:.4f}")
print(f"最悪 {v[0]['err']:.4f} / 最良 {v[-1]['err']:.4f} = {v[0]['err']/max(v[-1]['err'],1e-12):.1f}倍の開き\n")

print("--- 種類ごとの平均 ---")
kinds = {}
for d in v:
    kinds.setdefault(d["kind"], []).append(d["err"])
for k, xs in sorted(kinds.items(), key=lambda x: -np.mean(x[1])):
    print(f"  {k:<14} 出力誤差 平均{np.mean(xs):.4f}  最悪{max(xs):.4f}  最良{min(xs):.4f}")

print("\n--- 痛い層 上位15 ---")
for d in v[:15]:
    print(f"  blk.{d['blk']:>2}.{d['kind']:<14} 出力{d['err']:.4f}  重み{d['werr']:.4f}")

print("\n--- 深さ方向(ブロックごとの平均出力誤差)---")
byb = {}
for d in v:
    byb.setdefault(d["blk"], []).append(d["err"])
for b in sorted(byb):
    e = float(np.mean(byb[b]))
    print(f"  blk.{b:>2} {e:.4f} " + "#" * int(e * 120))

# 出力誤差と重み誤差の順位が一致するか(一致しないなら重み誤差で判断してはいけない)
a = np.array([d["err"] for d in v]); b = np.array([d["werr"] for d in v])
ra = np.argsort(np.argsort(a)); rb = np.argsort(np.argsort(b))
print(f"\n出力誤差 vs 重み誤差 の順位相関 = {np.corrcoef(ra, rb)[0,1]:.3f}")