TextModel-v1 / build_png_release.py
TobiasLogic's picture
Add model weights, tokenizer, and training/data-generation code
e16ecc9 verified
Raw
History Blame Contribute Delete
3.76 kB
import json
import math
import os
import shutil
import numpy as np
import torch
from PIL import Image
from safetensors.torch import load_file
from transformers import LlamaConfig, LlamaForCausalLM
from huggingface_hub import hf_hub_download, HfApi
def build_model(c):
return LlamaForCausalLM(LlamaConfig(**{k: c[k] for k in [
"vocab_size", "hidden_size", "intermediate_size", "num_hidden_layers",
"num_attention_heads", "num_key_value_heads", "max_position_embeddings",
"rms_norm_eps", "tie_word_embeddings",
]}))
def encode(weights, png_path, cfg_path, config="config.json"):
c = json.load(open(config))
model = build_model(c)
model.load_state_dict(load_file(weights), strict=False)
parts, manifest = [], []
for name, p in model.named_parameters():
a = p.detach().to(torch.float16).contiguous().view(-1).numpy()
parts.append(a)
manifest.append({"name": name, "shape": list(p.shape), "numel": int(a.size)})
flat = np.concatenate(parts)
N = flat.size
side = math.ceil(math.sqrt(N))
u16 = flat.view(np.uint16)
img = np.zeros((side * side, 3), dtype=np.uint8)
img[:N, 0] = (u16 >> 8).astype(np.uint8)
img[:N, 1] = (u16 & 0xFF).astype(np.uint8)
Image.fromarray(img.reshape(side, side, 3), "RGB").save(png_path)
total = sum(m["numel"] for m in manifest)
json.dump({"cfg": c, "params": manifest, "total_parameters": total,
"side": side, "dtype": "float16", "channels": "R=hi,G=lo,B=unused"},
open(cfg_path, "w"))
mb = os.path.getsize(png_path) / 1e6
print(f"[png] encoded {total:,} params -> {side}x{side} PNG ({mb:.1f} MB)")
return total, side
def load_model_png(png_path, cfg_path, device="cpu"):
meta = json.load(open(cfg_path))
model = build_model(meta["cfg"])
arr = np.asarray(Image.open(png_path).convert("RGB")).reshape(-1, 3)
total = meta["total_parameters"]
hi = arr[:total, 0].astype(np.uint16)
lo = arr[:total, 1].astype(np.uint16)
flat = ((hi << 8) | lo).astype(np.uint16).view(np.float16)
sd = dict(model.named_parameters())
off = 0
with torch.no_grad():
for m in meta["params"]:
n = m["numel"]
chunk = flat[off:off + n].astype(np.float16)
sd[m["name"]].copy_(torch.from_numpy(chunk.copy()).view(*m["shape"]).to(torch.float32))
off += n
return model.to(device).eval()
def verify(weights, png_path, cfg_path, config="config.json"):
model = load_model_png(png_path, cfg_path)
ref = load_file(weights)
worst, name = 0.0, ""
for k, p in model.named_parameters():
d = (p.detach() - ref[k].float()).abs().max().item()
if d > worst:
worst, name = d, k
rel = worst / max(1e-12, ref[name].float().abs().max().item())
print(f"[png] round-trip max abs err {worst:.3e} on {name} (relative {rel:.2e})")
return worst
repo = "TobiasLogic/textmodel-gci-scratch-ckpt"
rev = "step50000"
cfg_path = hf_hub_download(repo_id=repo, revision=rev, filename="config.json")
w_path = hf_hub_download(repo_id=repo, revision=rev, filename="model.safetensors")
shutil.copy(cfg_path, "config.json")
shutil.copy(w_path, "model.safetensors")
total, side = encode("model.safetensors", "model.png", "model_png.json", "config.json")
worst = verify("model.safetensors", "model.png", "model_png.json", "config.json")
print("total params", total, "side", side, "worst abs err", worst)
api = HfApi()
api.upload_file(path_or_fileobj="model.png", path_in_repo="model.png", repo_id="TobiasLogic/TextModel-v1")
api.upload_file(path_or_fileobj="model_png.json", path_in_repo="model_png.json", repo_id="TobiasLogic/TextModel-v1")
print("uploaded model.png + model_png.json")