lfm3-vl / .meta /create_model.py
yujiepan's picture
Upload folder using huggingface_hub
bd2c39a verified
Raw History Blame Contribute Delete
3.63 kB
def create_model(source_model_id: str, source_revision: str, save_folder: str) -> str:
import json
from pathlib import Path
import torch
from huggingface_hub import hf_hub_download
from transformers import AutoConfig, AutoModelForImageTextToText, set_seed
folder = Path(save_folder)
folder.mkdir(parents=True, exist_ok=True)
# LFM3-VL is not registered in the installed Transformers release; ship its pinned remote code (text backbone,
# vision tower and the image/video processors) so the checkpoint loads without a newer package.
for name in (
"configuration_lfm3.py",
"configuration_lfm3_vl.py",
"modeling_lfm3.py",
"modeling_lfm3_vl.py",
"lfm3_conv.py",
"processing_lfm3_vl.py",
"image_processing_lfm3_vl.py",
"video_processing_lfm3_vl.py",
"chat_template.jinja",
"generation_config.json",
"processor_config.json",
"tokenizer.json",
"tokenizer_config.json",
"LICENSE",
):
(folder / name).write_bytes(Path(hf_hub_download(source_model_id, name, revision=source_revision)).read_bytes())
config = json.loads(Path(hf_hub_download(source_model_id, "config.json", revision=source_revision)).read_text())
# Text backbone: residual/MLP widths 64/128 like tiny-random/lfm3, with at least four KV heads for TP=4 and
# the source 4:1 Q/KV ratio (16 Q heads, 4 KV heads, rectangular projections at head dim 32), the two-conv
# prefix plus two complete full/conv/sliding/conv cycles.
text_config = config["text_config"]
text_config.update(
hidden_size=64,
intermediate_size=128,
num_hidden_layers=10,
num_attention_heads=16,
num_key_value_heads=4,
head_dim=32,
max_position_embeddings=128000,
)
text_config["layer_types"] = text_config["layer_types"][:10]
# Vision tower: at least four KV heads with the source 2:1 Q/KV ratio (8 Q heads, 4 KV heads). Its head dim is
# derived as hidden // num_heads, so width 256 keeps head dim 32 and axial RoPE; patch 16 / temporal patch 2 /
# spatial merge 2. Six layers retain the conv/conv prefix and two full/conv cycles.
vision_config = config["vision_config"]
vision_config.update(
hidden_size=256,
intermediate_size=640,
num_hidden_layers=6,
num_attention_heads=8,
num_key_value_heads=4,
)
vision_config["layer_types"] = ["conv", "conv", "full_attention", "conv", "full_attention", "conv"]
# Projector: LayerNorm + GELU MLP merging 2x2 vision blocks; keep bias and the downsample contract.
config["projector_hidden_size"] = 128
(folder / "config.json").write_text(json.dumps(config, indent=2) + "\n")
set_seed(42)
model = AutoModelForImageTextToText.from_config(
AutoConfig.from_pretrained(folder, trust_remote_code=True), trust_remote_code=True, dtype=torch.bfloat16
)
model.save_pretrained(folder)
# `save_pretrained()` rewrites these two files with this environment's versions; restore the pinned source bytes
# plus the deliberate config edits so only the intended fields differ from the source.
(folder / "config.json").write_text(json.dumps(config, indent=2) + "\n")
(folder / "generation_config.json").write_bytes(
Path(hf_hub_download(source_model_id, "generation_config.json", revision=source_revision)).read_bytes()
)
return str(model)
create_model(
source_model_id="LiquidAI/LFM3-VL-2B",
source_revision="ba44bd51db20a2753f28c273a1571ccaa210d954",
save_folder="/tmp/lfm3-vl",
)