guarin/fsx-backup / tipsv2 /update_repo.py
guarin's picture
download
raw
14.9 kB
"""Merge backbone vision encoder weights into DPT repo safetensors files.
For each DPT repo, downloads the current model.safetensors and the corresponding
backbone's model.safetensors, prepends the backbone's vision_encoder.* weights,
and pushes the merged file back to the DPT repo.
"""
import json
from huggingface_hub import (
delete_file,
hf_hub_download,
list_repo_files,
upload_file,
upload_folder,
)
from safetensors.torch import load_file, save_file
from transformers import (
AutoConfig,
Tipsv2Config,
Tipsv2DptConfig,
Tipsv2DptImageProcessor,
Tipsv2DptForDensePrediction,
Tipsv2ImageProcessor,
Tipsv2Model,
Tipsv2Processor,
Tipsv2Tokenizer,
)
import tempfile
from pathlib import Path
# (dpt_repo, backbone_repo, vision_fn). vision_fn keys into VISION_PRESETS and is carried here
# rather than read from the backbone config, because once a config has been converted to the
# transformers format it no longer stores the raw vision_fn key.
REPOS = [
("guarin/tipsv2-b14-dpt", "guarin/tipsv2-b14", "vit_base"),
("guarin/tipsv2-l14-dpt", "guarin/tipsv2-l14", "vit_large"),
("guarin/tipsv2-so400m14-dpt", "guarin/tipsv2-so400m14", "vit_so400m"),
("guarin/tipsv2-g14-dpt", "guarin/tipsv2-g14", "vit_giant2"),
]
# Remote code files (saved next to this script) pushed to each DPT repo so the
# trust_remote_code implementation matches the new config.
REMOTE_CODE_FILES = [
"configuration_dpt.py",
"modeling_dpt.py",
"dpt_head.py",
"image_encoder.py",
]
# Remote code files (saved next to this script) pushed to each backbone repo so the
# trust_remote_code implementation matches the new config.
BACKBONE_REMOTE_CODE_FILES = [
"configuration_tips.py",
]
# trust_remote_code auto_map carried over from the backbone repos' original config so the pushed
# config keeps pointing at the custom configuration/modeling classes.
BACKBONE_AUTO_MAP = {
"AutoConfig": "configuration_tips.TIPSv2Config",
"AutoModel": "modeling_tips.TIPSv2Model",
}
# Per-architecture vision encoder sizes, keyed by the backbone's vision_fn. block_indices select
# which backbone stages feed the DPT neck.
VISION_PRESETS = {
"vit_base": {"num_hidden_layers": 12, "num_attention_heads": 12, "mlp_ratio": 4, "block_indices": [2, 5, 8, 11]},
"vit_large": {"num_hidden_layers": 24, "num_attention_heads": 16, "mlp_ratio": 4, "block_indices": [5, 11, 17, 23]},
"vit_so400m": {"num_hidden_layers": 27, "num_attention_heads": 16, "mlp_ratio": 4304 / 1152, "block_indices": [6, 13, 20, 26]},
"vit_giant2": {"num_hidden_layers": 40, "num_attention_heads": 24, "mlp_ratio": 4, "block_indices": [9, 19, 29, 39]},
}
def merge_weights(dpt_repo: str, backbone_repo: str, vision_fn: str) -> None:
print(f"\n=== {dpt_repo} ===")
# print(f" Downloading {dpt_repo}/model.safetensors ...")
# dpt_path = hf_hub_download(dpt_repo, "model.safetensors")
# print(f" Downloading {backbone_repo}/model.safetensors ...")
# backbone_path = hf_hub_download(backbone_repo, "model.safetensors")
# print(" Loading weights ...")
# dpt_weights = load_file(dpt_path)
# backbone_weights = load_file(backbone_path)
# vision_weights = {
# key: tensor
# for key, tensor in backbone_weights.items()
# if key.startswith("vision_encoder.")
# }
# print(f" Vision encoder keys: {len(vision_weights)}")
# print(f" DPT head keys: {len(dpt_weights)}")
# merged = {**vision_weights, **dpt_weights}
# print(f" Merged total keys: {len(merged)}")
# with tempfile.TemporaryDirectory() as tmp_dir:
# out_path = Path(tmp_dir) / "model.safetensors"
# save_file(merged, out_path)
# print(f" Pushing merged weights to {dpt_repo} ...")
# upload_file(
# path_or_fileobj=str(out_path),
# path_in_repo="model.safetensors",
# repo_id=dpt_repo,
# repo_type="model",
# commit_message="Add vision encoder weights from backbone",
# )
push_remote_code(dpt_repo)
# push_model_config(dpt_repo, backbone_repo, vision_fn)
# push_preprocessor_config(dpt_repo)
# push_backbone_remote_code(backbone_repo)
# push_backbone_model_config(backbone_repo, vision_fn)
# push_backbone_preprocessor_config(backbone_repo)
print(f" Done.")
def push_remote_code(dpt_repo: str) -> None:
this_dir = Path(__file__).parent
for file_name in REMOTE_CODE_FILES:
print(f" Pushing {file_name} to {dpt_repo} ...")
upload_file(
path_or_fileobj=str(this_dir / file_name),
path_in_repo=file_name,
repo_id=dpt_repo,
repo_type="model",
commit_message=f"Update {file_name} for new config",
)
def push_backbone_remote_code(backbone_repo: str) -> None:
this_dir = Path(__file__).parent
for file_name in BACKBONE_REMOTE_CODE_FILES:
print(f" Pushing {file_name} to {backbone_repo} ...")
upload_file(
path_or_fileobj=str(this_dir / file_name),
path_in_repo=file_name,
repo_id=backbone_repo,
repo_type="model",
commit_message=f"Update {file_name} for new config",
)
def load_ade20k_labels() -> dict[int, str]:
"""Load the canonical ADE20K (150 classes) id -> label name mapping."""
labels_path = hf_hub_download(
"huggingface/label-files", "ade20k-id2label.json", repo_type="dataset"
)
id2label = json.loads(Path(labels_path).read_text())
return {int(class_id): label_name for class_id, label_name in id2label.items()}
def sort_json_file(path: Path) -> None:
"""Rewrite a JSON file in place with all keys (recursively) in alphabetical order."""
data = json.loads(path.read_text())
path.write_text(json.dumps(data, indent=2, sort_keys=True) + "\n")
def build_vision_config_dict(backbone_repo: str, vision_fn: str) -> tuple[dict, dict]:
"""Rebuild the vision encoder config from a backbone repo, keyed by its architecture preset.
Returns the raw backbone config dict and the corresponding Tipsv2VisionConfig dict. The layer
geometry comes from VISION_PRESETS[vision_fn] and is always applied. The remaining values
(hidden size, register tokens, image/patch size, ...) are only available from a raw
pre-conversion backbone config; an already-converted config carries them correctly so the
loaded values are kept.
"""
backbone_dict = json.loads(Path(hf_hub_download(backbone_repo, "config.json")).read_text())
preset = VISION_PRESETS[vision_fn]
vision_config = AutoConfig.from_pretrained(backbone_repo).vision_config.to_dict()
vision_config["num_hidden_layers"] = preset["num_hidden_layers"]
vision_config["num_attention_heads"] = preset["num_attention_heads"]
vision_config["mlp_ratio"] = preset["mlp_ratio"]
if "vision_fn" in backbone_dict:
vision_config["hidden_size"] = backbone_dict["embed_dim"]
vision_config["use_swiglu_ffn"] = backbone_dict["ffn_layer"] in ("swiglu", "swiglufused")
vision_config["num_register_tokens"] = backbone_dict["num_register_tokens"]
vision_config["layerscale_value"] = backbone_dict.get("init_values", 1.0)
vision_config["image_size"] = backbone_dict["img_size"]
vision_config["patch_size"] = backbone_dict["patch_size"]
vision_config.pop("out_features")
vision_config.pop("out_indices")
vision_config.pop("stage_names")
return backbone_dict, vision_config
def push_backbone_model_config(backbone_repo: str, vision_fn: str) -> None:
print(f" Building config for {backbone_repo} ...")
backbone_dict, vision_config = build_vision_config_dict(backbone_repo, vision_fn)
loaded_config = AutoConfig.from_pretrained(backbone_repo)
text_config = loaded_config.text_config.to_dict()
temperature_init_value = loaded_config.temperature_init_value
# Already-converted configs carry these directly; only raw configs need the overrides.
if "vision_fn" in backbone_dict:
text_config["vocab_size"] = backbone_dict["vocab_size"]
text_config["hidden_size"] = backbone_dict["text_hidden_size"]
text_config["intermediate_size"] = backbone_dict["text_mlp_dim"]
text_config["num_attention_heads"] = backbone_dict["text_num_heads"]
text_config["num_hidden_layers"] = backbone_dict["text_num_layers"]
text_config["max_position_embeddings"] = backbone_dict["max_len"]
temperature_init_value = backbone_dict["temperature"]
config = Tipsv2Config(
vision_config=vision_config,
text_config=text_config,
temperature_init_value=temperature_init_value,
)
config.architectures = ["TIPSv2Model"]
print(f" Initializing model ...")
model = Tipsv2Model(config)
with tempfile.TemporaryDirectory() as tmp_dir:
model.config.save_pretrained(tmp_dir)
out_path = Path(tmp_dir) / "config.json"
# Preserve the trust_remote_code auto_map from the original config so the repo keeps
# pointing at its custom configuration/modeling classes.
saved_config = json.loads(out_path.read_text())
saved_config["auto_map"] = BACKBONE_AUTO_MAP
out_path.write_text(json.dumps(saved_config, indent=2, sort_keys=True) + "\n")
print(f" Pushing config.json to {backbone_repo} ...")
upload_file(
path_or_fileobj=str(out_path),
path_in_repo="config.json",
repo_id=backbone_repo,
repo_type="model",
commit_message="Add model config",
)
def push_model_config(dpt_repo: str, backbone_repo: str, vision_fn: str) -> None:
print(f" Building config for {dpt_repo} ...")
config_path = hf_hub_download(dpt_repo, "config.json")
config_dict = json.loads(Path(config_path).read_text())
# The model is trained on ADE20K, so the placeholder "LABEL_0, LABEL_1, ..." names are
# replaced with the real 150 ADE20K class names.
id2label = load_ade20k_labels()
config_dict["id2label"] = {
str(class_id): label_name for class_id, label_name in id2label.items()
}
config_dict["label2id"] = {label_name: class_id for class_id, label_name in id2label.items()}
# The stored config does not carry a backbone_config, so the vision encoder config is rebuilt
# from the backbone repo. block_indices select which backbone stages feed the DPT neck (the +1
# offset accounts for the embedding output that precedes the transformer stages).
backbone_dict, backbone_config = build_vision_config_dict(backbone_repo, vision_fn)
preset = VISION_PRESETS[vision_fn]
num_hidden_layers = preset["num_hidden_layers"]
out_indices = [block_index + 1 for block_index in preset["block_indices"]]
stage_names = ["stem"] + [f"stage{index}" for index in range(1, num_hidden_layers + 1)]
backbone_config["stage_names"] = stage_names
backbone_config["out_indices"] = out_indices
backbone_config["out_features"] = [stage_names[out_index] for out_index in out_indices]
backbone_config["apply_layernorm"] = True
backbone_config["reshape_hidden_states"] = False
config_dict["backbone_config"] = backbone_config
config = Tipsv2DptConfig.from_dict(config_dict)
print(f" Initializing model ...")
model = Tipsv2DptForDensePrediction(config)
with tempfile.TemporaryDirectory() as tmp_dir:
model.config.save_pretrained(tmp_dir)
out_path = Path(tmp_dir) / "config.json"
sort_json_file(out_path)
print(f" Pushing config.json to {dpt_repo} ...")
upload_file(
path_or_fileobj=str(out_path),
path_in_repo="config.json",
repo_id=dpt_repo,
repo_type="model",
commit_message="Add model config",
)
def push_preprocessor_config(dpt_repo: str) -> None:
print(f" Creating preprocessor_config.json ...")
image_processor = Tipsv2DptImageProcessor()
with tempfile.TemporaryDirectory() as tmp_dir:
image_processor.save_pretrained(tmp_dir)
config_path = Path(tmp_dir) / "preprocessor_config.json"
sort_json_file(config_path)
print(f" Pushing preprocessor_config.json to {dpt_repo} ...")
upload_file(
path_or_fileobj=str(config_path),
path_in_repo="preprocessor_config.json",
repo_id=dpt_repo,
repo_type="model",
commit_message="Add preprocessor_config.json",
)
def push_backbone_preprocessor_config(backbone_repo: str) -> None:
print(f" Creating processor config for {backbone_repo} ...")
print(f" Downloading {backbone_repo}/tokenizer.model ...")
vocab_path = hf_hub_download(backbone_repo, "tokenizer.model")
image_processor = Tipsv2ImageProcessor()
tokenizer = Tipsv2Tokenizer(vocab_file=vocab_path)
processor = Tipsv2Processor(image_processor=image_processor, tokenizer=tokenizer)
with tempfile.TemporaryDirectory() as tmp_dir:
processor.save_pretrained(tmp_dir)
# tokenizer.json must not be published, so it is removed before upload.
tokenizer_json_path = Path(tmp_dir) / "tokenizer.json"
if tokenizer_json_path.exists():
tokenizer_json_path.unlink()
for json_path in Path(tmp_dir).glob("*.json"):
sort_json_file(json_path)
generated_files = sorted(path.name for path in Path(tmp_dir).iterdir())
print(f" Generated config files: {generated_files}")
print(f" Pushing processor config files to {backbone_repo} ...")
upload_folder(
folder_path=tmp_dir,
repo_id=backbone_repo,
repo_type="model",
commit_message="Add processor config files",
)
repo_files = list_repo_files(backbone_repo, repo_type="model")
if "preprocessor_config.json" in repo_files:
print(f" Deleting obsolete preprocessor_config.json from {backbone_repo} ...")
delete_file(
path_in_repo="preprocessor_config.json",
repo_id=backbone_repo,
repo_type="model",
commit_message="Remove obsolete preprocessor_config.json (merged into processor_config.json)",
)
if "tokenizer.json" in repo_files:
print(f" Deleting tokenizer.json from {backbone_repo} ...")
delete_file(
path_in_repo="tokenizer.json",
repo_id=backbone_repo,
repo_type="model",
commit_message="Remove tokenizer.json",
)
def main() -> None:
for dpt_repo, backbone_repo, vision_fn in REPOS:
merge_weights(dpt_repo, backbone_repo, vision_fn)
if __name__ == "__main__":
main()

Xet Storage Details

Size:
14.9 kB
·
Xet hash:
f3c8c9d5809c134bdab73d0497f32f8469afd748fc81d21096c7ad22da8a778e

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.