|
|
"""Validate the cracked OneOCRFeatureExtract structure by replacing the op with standard Gemm.""" |
|
|
import onnx |
|
|
from onnx import numpy_helper, TensorProto |
|
|
import numpy as np |
|
|
from pathlib import Path |
|
|
import onnxruntime as ort |
|
|
|
|
|
models_dir = Path("oneocr_extracted/onnx_models") |
|
|
|
|
|
|
|
|
model_path = list(models_dir.glob("model_11_*"))[0] |
|
|
model = onnx.load(str(model_path)) |
|
|
|
|
|
|
|
|
for init in model.graph.initializer: |
|
|
if init.name == "feature/config": |
|
|
blob = bytes(init.string_data[0]) |
|
|
break |
|
|
|
|
|
be_arr = np.frombuffer(blob, dtype='>f4').copy() |
|
|
print(f"Config blob: {len(be_arr)} floats total") |
|
|
print(f"First 30 values: {be_arr[:30]}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
W_0 = be_arr[:1050].reshape(21, 50) |
|
|
b_0 = be_arr[1050:1100] |
|
|
tail = be_arr[1100:] |
|
|
print(f"\n--- No header: W[21×50] + b[50] + tail[{len(tail)}] ---") |
|
|
print(f" W: range=[{W_0.min():.4f}, {W_0.max():.4f}], mean={W_0.mean():.4f}, std={W_0.std():.4f}") |
|
|
print(f" b: range=[{b_0.min():.4f}, {b_0.max():.4f}], mean={b_0.mean():.4f}") |
|
|
print(f" tail: {tail}") |
|
|
|
|
|
|
|
|
W_t = be_arr[:1050].reshape(50, 21) |
|
|
b_t = be_arr[1050:1100] |
|
|
print(f"\n--- No header: W[50×21] + b[50] + tail[{len(tail)}] ---") |
|
|
print(f" W: range=[{W_t.min():.4f}, {W_t.max():.4f}], mean={W_t.mean():.4f}, std={W_t.std():.4f}") |
|
|
print(f" b: range=[{b_t.min():.4f}, {b_t.max():.4f}], mean={b_t.mean():.4f}") |
|
|
|
|
|
|
|
|
header = be_arr[:23] |
|
|
W_h = be_arr[23:23+1050].reshape(21, 50) |
|
|
b_h = be_arr[23+1050:] |
|
|
print(f"\n--- Header=23: W[21×50] + b[50] ---") |
|
|
print(f" Header: {header}") |
|
|
print(f" W: range=[{W_h.min():.4f}, {W_h.max():.4f}], mean={W_h.mean():.4f}, std={W_h.std():.4f}") |
|
|
print(f" b: range=[{b_h.min():.4f}, {b_h.max():.4f}], mean={b_h.mean():.4f}") |
|
|
|
|
|
|
|
|
print(f"\n--- Values > 10 ---") |
|
|
for i, v in enumerate(be_arr): |
|
|
if abs(v) > 10: |
|
|
print(f" [{i}] = {v}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
add_const = None |
|
|
div_const = None |
|
|
for node in model.graph.node: |
|
|
if node.op_type == "Constant": |
|
|
name = node.output[0] |
|
|
for attr in node.attribute: |
|
|
if attr.type == 4: |
|
|
t = attr.t |
|
|
data = numpy_helper.to_array(t) |
|
|
if name == '26': |
|
|
add_const = data |
|
|
elif name == '28': |
|
|
div_const = data |
|
|
|
|
|
print(f"\nNormalization: add={add_const.shape}, div={div_const.shape}") |
|
|
|
|
|
|
|
|
test_input = np.random.randn(1, 21, 1, 1).astype(np.float32) |
|
|
|
|
|
|
|
|
x = test_input.reshape(1, 21)[:, :21] |
|
|
x = (x + add_const) / div_const |
|
|
|
|
|
|
|
|
for name, W, b in [("no_header_21x50", W_0, b_0), |
|
|
("no_header_50x21_T", W_t.T, b_t), |
|
|
("header23_21x50", W_h, b_h)]: |
|
|
feat = x @ W + b |
|
|
print(f"\n{name}:") |
|
|
print(f" Feature output: range=[{feat.min():.4f}, {feat.max():.4f}], mean={feat.mean():.4f}") |
|
|
|
|
|
|
|
|
for init in model.graph.initializer: |
|
|
if init.name == "learned_2": |
|
|
W2 = numpy_helper.to_array(init) |
|
|
elif init.name == "learned_3": |
|
|
b2 = numpy_helper.to_array(init) |
|
|
elif init.name == "1.layers.5.weight": |
|
|
W5 = numpy_helper.to_array(init) |
|
|
elif init.name == "1.layers.5.bias": |
|
|
b5 = numpy_helper.to_array(init) |
|
|
elif init.name == "1.layers.7.weight": |
|
|
W7 = numpy_helper.to_array(init) |
|
|
elif init.name == "1.layers.7.bias": |
|
|
b7 = numpy_helper.to_array(init) |
|
|
|
|
|
h1 = np.maximum(0, feat @ W2.T + b2) |
|
|
h2 = np.maximum(0, h1 @ W5.T + b5) |
|
|
logits = h2 @ W7.T + b7 |
|
|
probs = np.exp(logits) / np.exp(logits).sum(axis=1, keepdims=True) |
|
|
print(f" Final softmax: {probs}") |
|
|
|