sceneseg-p150 / code /scripts /diag /st2_probe.py
changh95's picture
tt-model push sceneseg-p150 (container)
e0adbab verified
Raw History Blame Contribute Delete
4.47 kB
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
"""ST2 per-layer probe: every stacked [W_hi | W_lo] conv / transposed conv vs its two separate term convs, inside one
eager forward of the real graph on the shipped sample (EPI on), bit for bit per layer.
bin/devrun -t 900 -- python -u code/scripts/diag/st2_probe.py
Each wrapped layer runs both forms on the same input; the stacked result continues the forward. Prints one line per
layer call (identical / number of differing elements / max abs diff) and the conv config of the layer.
"""
from __future__ import annotations
import sys
from pathlib import Path
import numpy as np
def main() -> int:
import ttnn
from tt_sceneseg.device import close_device, open_device
from tt_sceneseg.host.preprocess import prepare_image
from tt_sceneseg.reference import assets, config
from tt_sceneseg.reference.weights import load_weights
from tt_sceneseg.tt import layers as L
from tt_sceneseg.tt.model import INPUT_SHAPE, TtSceneSeg
from tt_sceneseg.ttaw.tensors import to_numpy
code = Path(__file__).resolve().parents[2]
wdir = assets.test_weights_dir()
onnx = assets.find_in(wdir, config.ONNX_FILE) if wdir is not None else None
if onnx is None:
from huggingface_hub import snapshot_download
from tt_sceneseg.api import SceneSeg
d = snapshot_download(SceneSeg.DEFAULT_REPO, revision=SceneSeg.DEFAULT_REVISION,
allow_patterns=SceneSeg.ALLOW_PATTERNS)
onnx = assets.find_in(Path(d), config.ONNX_FILE)
dev = open_device(l1_small_size=98304) # both forms of every layer prepared: more conv config tensors
model = TtSceneSeg(load_weights(onnx), "bgr", epi=True, term_stack=True)
stats = []
def cmp(name, a, b):
x, y = to_numpy(a), to_numpy(b)
same = x.shape == y.shape and np.array_equal(x.view(np.uint8), y.view(np.uint8))
nd = 0 if same else int((x != y).sum())
md = 0.0 if same else float(np.abs(x.astype(np.float64) - y.astype(np.float64)).max())
stats.append((name, same))
print(f"{'SAME' if same else 'DIFF'} {name} {tuple(x.shape)} diff {nd} max {md:.3g}", flush=True)
orig_conv = L.TermConv2d.__call__
def conv_call(self, x, *, act_terms=1, output_dtype=None):
if self.stacked is None:
return orig_conv(self, x, act_terms=act_terms, output_dtype=output_dtype)
xin = x.with_tensor(ttnn.clone(x.tensor))
y1 = orig_conv(self, x, act_terms=act_terms, output_dtype=output_dtype)
st, self.stacked = self.stacked, None
y2 = orig_conv(self, xin, act_terms=act_terms, output_dtype=output_dtype)
self.stacked = st
cmp(f"conv {self.name} {x.height}x{x.width} {self.in_channels}->{self.out_channels} at={act_terms}",
y1.tensor, y2.tensor)
ttnn.deallocate(y2.tensor)
ttnn.deallocate(xin.tensor)
return y1
orig_views = L.TermConvTranspose2d.term_views
def views(self, x):
if self.stacked is None:
return orig_views(self, x)
parts, cols, first = orig_views(self, x)
st, self.stacked = self.stacked, None
p2, _, _ = orig_views(self, x)
self.stacked = st
c = self.out_channels
shp = [int(v) for v in parts[0].shape]
for i, off in enumerate((0, c)):
h = ttnn.slice(parts[0], [0, 0, 0, off], [shp[0], shp[1], shp[2], off + c])
cmp(f"deconv {self.name} term {i} {x.height}x{x.width} {self.in_channels}->{c}", h, p2[i])
ttnn.deallocate(h)
for t in p2:
ttnn.deallocate(t)
return parts, cols, first
L.TermConv2d.__call__ = conv_call
L.TermConvTranspose2d.term_views = views
canvas = prepare_image(str(code / "tt_sceneseg" / "samples" / "highway_normal_1.png")).device_input()
import torch
image = ttnn.from_torch(torch.from_numpy(np.ascontiguousarray(canvas.reshape(INPUT_SHAPE))), dtype=ttnn.uint8,
layout=ttnn.ROW_MAJOR_LAYOUT, device=dev, memory_config=ttnn.DRAM_MEMORY_CONFIG)
out = model.forward(image)
print("class map", np.bincount(to_numpy(out["class_map"]).astype(np.int64).ravel(), minlength=3).tolist())
n_diff = sum(not s for _, s in stats)
print(f"{len(stats)} layer calls, {n_diff} differ")
model.release()
close_device(dev)
return 0
if __name__ == "__main__":
sys.exit(main())