|
|
| """HF MeshAI-Base-Models (dataset) uzerinden TRELLIS/Hunyuan agirlik indirme + LoRA hedef secimi."""
|
| from __future__ import annotations
|
|
|
| import json
|
| from pathlib import Path
|
| from typing import Any
|
|
|
| import torch
|
| import torch.nn as nn
|
|
|
| BASE_DATASET_REPO = "HayrettinIscan/MeshAI-Base-Models"
|
|
|
|
|
| DEFAULT_WEIGHT_FILES = (
|
| "Microsoft-TRELLIS/ckpts/slat_enc_swin8_B_64l8_fp16.safetensors",
|
| "Microsoft-TRELLIS/ckpts/ss_enc_conv3d_16l8_fp16.safetensors",
|
| "Microsoft-TRELLIS/ckpts/slat_dec_mesh_swin8_B_64l8m256c_fp16.safetensors",
|
| "Tencent-Hunyuan3D/hunyuan3d-dit-v2-0-turbo/model.fp16.safetensors",
|
| )
|
|
|
|
|
| def ensure_base_weights(
|
| *,
|
| token: str,
|
| cache_dir: Path,
|
| files: tuple[str, ...] = DEFAULT_WEIGHT_FILES,
|
| log_fn: Any = print,
|
| ) -> dict[str, Path]:
|
| """Indirilen safetensors yollarini dondurur (HF hub cache veya local_dir)."""
|
| from huggingface_hub import hf_hub_download
|
|
|
| cache_dir.mkdir(parents=True, exist_ok=True)
|
| out: dict[str, Path] = {}
|
| for rel in files:
|
| log_fn(f"[faz2] indiriliyor: {rel}")
|
| path = Path(
|
| hf_hub_download(
|
| repo_id=BASE_DATASET_REPO,
|
| filename=rel,
|
| repo_type="dataset",
|
| token=token,
|
| local_dir=str(cache_dir),
|
| )
|
| )
|
|
|
| candidate = cache_dir / rel
|
| if candidate.exists():
|
| path = candidate
|
| out[rel] = path
|
| log_fn(f"[faz2] hazir: {path} ({path.stat().st_size // (1024 * 1024)} MB)")
|
| meta = cache_dir / "faz2_weight_manifest.json"
|
| meta.write_text(
|
| json.dumps({k: str(v) for k, v in out.items()}, indent=2),
|
| encoding="utf-8",
|
| )
|
| return out
|
|
|
|
|
| def _load_safetensors(path: Path) -> dict[str, torch.Tensor]:
|
| try:
|
| from safetensors.torch import load_file
|
|
|
| return load_file(str(path))
|
| except Exception:
|
|
|
| return torch.load(path, map_location="cpu", weights_only=True)
|
|
|
|
|
| def pick_lora_targets(
|
| state: dict[str, torch.Tensor],
|
| *,
|
| max_matrices: int = 8,
|
| min_in: int = 64,
|
| max_in: int = 8192,
|
| ) -> list[tuple[str, torch.Tensor]]:
|
| """2D weight matrislerinden LoRA adaylarini sec (buyukten kucuge)."""
|
| cands: list[tuple[str, torch.Tensor, int]] = []
|
| for key, tensor in state.items():
|
| if not key.endswith("weight"):
|
| continue
|
| if tensor.ndim != 2:
|
| continue
|
| out_f, in_f = int(tensor.shape[0]), int(tensor.shape[1])
|
| if in_f < min_in or in_f > max_in:
|
| continue
|
| if out_f < 16:
|
| continue
|
| cands.append((key, tensor.detach().float().cpu(), out_f * in_f))
|
| cands.sort(key=lambda x: x[2], reverse=True)
|
| return [(k, t) for k, t, _ in cands[:max_matrices]]
|
|
|
|
|
| class FrozenLinearLoRA(nn.Module):
|
| """W frozen + LoRA(A,B). y = x @ W.T + scale * (x @ A.T) @ B.T"""
|
|
|
| def __init__(
|
| self,
|
| weight: torch.Tensor,
|
| *,
|
| rank: int = 8,
|
| scale: float = 1.0,
|
| name: str = "",
|
| ) -> None:
|
| super().__init__()
|
| out_f, in_f = weight.shape
|
| self.name = name
|
| self.in_features = in_f
|
| self.out_features = out_f
|
| self.scale = scale
|
| self.register_buffer("weight", weight.contiguous())
|
| self.lora_A = nn.Parameter(torch.zeros(rank, in_f))
|
| self.lora_B = nn.Parameter(torch.zeros(out_f, rank))
|
| nn.init.kaiming_uniform_(self.lora_A, a=5**0.5)
|
| nn.init.zeros_(self.lora_B)
|
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| base = nn.functional.linear(x, self.weight)
|
| delta = (x @ self.lora_A.T) @ self.lora_B.T
|
| return base + self.scale * delta
|
|
|
|
|
| class BaseLoRATower(nn.Module):
|
| """Birden fazla frozen+LoRA katmani; giris projesi ile dim hizalama."""
|
|
|
| def __init__(
|
| self,
|
| targets: list[tuple[str, torch.Tensor]],
|
| *,
|
| input_dim: int,
|
| rank: int = 8,
|
| out_dim: int = 512,
|
| ) -> None:
|
| super().__init__()
|
| if not targets:
|
| raise ValueError("LoRA hedefi yok")
|
| layers = nn.ModuleList()
|
|
|
| first_in = int(targets[0][1].shape[1])
|
| self.proj_in = nn.Linear(input_dim, first_in)
|
| prev_out = first_in
|
| for name, w in targets:
|
| w = w.contiguous()
|
|
|
| if prev_out != int(w.shape[1]):
|
| layers.append(nn.Linear(prev_out, int(w.shape[1])))
|
| layers.append(nn.GELU())
|
| layers.append(FrozenLinearLoRA(w, rank=rank, name=name))
|
| layers.append(nn.GELU())
|
| prev_out = int(w.shape[0])
|
| self.layers = layers
|
| self.proj_out = nn.Linear(prev_out, out_dim)
|
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| h = self.proj_in(x)
|
| for layer in self.layers:
|
| h = layer(h)
|
| return self.proj_out(h)
|
|
|
| def lora_parameters(self) -> list[nn.Parameter]:
|
| params: list[nn.Parameter] = []
|
| for m in self.modules():
|
| if isinstance(m, FrozenLinearLoRA):
|
| params.extend([m.lora_A, m.lora_B])
|
| return params
|
|
|