HayrettinIscan commited on
Commit
158177e
·
verified ·
1 Parent(s): 687b68c

Faz2: code/meshai_train/faz2_engine.py

Browse files
Files changed (1) hide show
  1. code/meshai_train/faz2_engine.py +182 -0
code/meshai_train/faz2_engine.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Faz 2 egitim motoru: base safetensors LoRA + preprocessed veri."""
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ import numpy as np
9
+ import torch
10
+ from torch.utils.data import DataLoader, Subset
11
+
12
+ from meshai_train.base_weights import ensure_base_weights
13
+ from meshai_train.dataset import PreprocessedMeshDataset, collate_preprocessed
14
+ from meshai_train.faz2_models import (
15
+ FAZ2_VERSION,
16
+ build_faz2_from_weight_files,
17
+ faz2_loss,
18
+ load_faz2_checkpoint,
19
+ save_faz2_checkpoint,
20
+ )
21
+
22
+
23
+ def start_faz2_training(
24
+ *,
25
+ monitor: Any,
26
+ checkpoint_dir: Path,
27
+ output_dir: Path,
28
+ token: str | None,
29
+ epochs: int,
30
+ resume_from: Path | None,
31
+ validation_every: int,
32
+ val_ratio: float,
33
+ limit: int | None,
34
+ hf_repo: str,
35
+ data_root: Path | None,
36
+ log_fn: Any,
37
+ log_vram_fn: Any,
38
+ clear_gpu_fn: Any,
39
+ load_val_uids_fn: Any,
40
+ checkpoint_every: int = 50,
41
+ lora_rank: int = 8,
42
+ base_cache: Path | None = None,
43
+ ) -> None:
44
+ if not token:
45
+ raise RuntimeError("Faz2 icin HF_TOKEN gerekli (base weight + data)")
46
+
47
+ device = "cuda" if torch.cuda.is_available() else "cpu"
48
+ cache = base_cache or Path("data/base_models")
49
+ log_fn(f"Pipeline surumu: {FAZ2_VERSION}")
50
+ log_fn(f"Veri: {hf_repo}" + (f" | limit={limit}" if limit else ""))
51
+ if torch.cuda.is_available():
52
+ log_fn(f"GPU: {torch.cuda.get_device_name(0)}")
53
+ log_vram_fn("startup")
54
+
55
+ weight_paths = ensure_base_weights(token=token, cache_dir=cache, log_fn=log_fn)
56
+ model = build_faz2_from_weight_files(weight_paths, lora_rank=lora_rank, log_fn=log_fn)
57
+ model = model.to(device)
58
+
59
+ # Faz1 hibrit ckpt varsa hybrid'e yukle
60
+ faz1 = checkpoint_dir / "latest_model.pt"
61
+ if resume_from and resume_from.exists():
62
+ pass
63
+ elif faz1.exists():
64
+ resume_from = faz1
65
+
66
+ dataset = PreprocessedMeshDataset(
67
+ token=token,
68
+ data_root=data_root,
69
+ hf_repo=hf_repo,
70
+ limit=limit,
71
+ )
72
+ val_uids = load_val_uids_fn()
73
+ val_idx = [i for i, o in enumerate(dataset.objects) if str(o.get("uid")) in val_uids]
74
+ if not val_idx:
75
+ val_count = max(1, int(len(dataset) * val_ratio))
76
+ val_idx = list(range(val_count))
77
+ train_idx = [i for i in range(len(dataset)) if i not in set(val_idx)]
78
+ if not train_idx:
79
+ train_idx = list(range(len(dataset)))
80
+ val_idx = train_idx[:1]
81
+
82
+ train_loader = DataLoader(
83
+ Subset(dataset, train_idx),
84
+ batch_size=1 if device == "cuda" else 1,
85
+ shuffle=True,
86
+ pin_memory=device == "cuda",
87
+ collate_fn=collate_preprocessed,
88
+ )
89
+ val_loader = DataLoader(
90
+ Subset(dataset, val_idx),
91
+ batch_size=1,
92
+ shuffle=False,
93
+ pin_memory=device == "cuda",
94
+ collate_fn=collate_preprocessed,
95
+ )
96
+ log_fn(f"Veri: {len(train_idx)} train + {len(val_idx)} val | LoRA rank={lora_rank}")
97
+
98
+ # Egitilebilir: LoRA + proj + hybrid (frozen W buffer)
99
+ trainable = [p for p in model.parameters() if p.requires_grad]
100
+ opt = torch.optim.AdamW(trainable, lr=5e-5, weight_decay=0.01)
101
+ latest = checkpoint_dir / "latest_faz2_model.pt"
102
+ global_step = 0
103
+ if resume_from and resume_from.exists():
104
+ global_step = load_faz2_checkpoint(resume_from, model, device)
105
+ log_fn(f"Resume: {resume_from} step={global_step}")
106
+
107
+ def _eval() -> float:
108
+ model.eval()
109
+ losses: list[float] = []
110
+ with torch.no_grad():
111
+ for batch in val_loader:
112
+ batch = {k: v.to(device) if isinstance(v, torch.Tensor) else v for k, v in batch.items()}
113
+ out = model(batch["geom_in"], batch["views"])
114
+ loss, _ = faz2_loss(out, batch)
115
+ losses.append(float(loss.item()))
116
+ model.train()
117
+ return float(np.mean(losses)) if losses else float("nan")
118
+
119
+ trainable_n = sum(p.numel() for p in trainable)
120
+ frozen_n = sum(p.numel() for p in model.parameters() if not p.requires_grad)
121
+ buffer_n = sum(b.numel() for b in model.buffers())
122
+ log_fn(f"Param: trainable={trainable_n:,} frozen={frozen_n:,} buffers(W)={buffer_n:,}")
123
+
124
+ for epoch in range(1, epochs + 1):
125
+ log_fn(f"--- Faz2 Epoch {epoch}/{epochs} ---")
126
+ model.train()
127
+ epoch_losses: list[float] = []
128
+ for batch in train_loader:
129
+ batch = {k: v.to(device) if isinstance(v, torch.Tensor) else v for k, v in batch.items()}
130
+ opt.zero_grad(set_to_none=True)
131
+ out = model(batch["geom_in"], batch["views"])
132
+ loss, parts = faz2_loss(out, batch)
133
+ if not torch.isfinite(loss):
134
+ monitor.note_nan_skip("faz2")
135
+ continue
136
+ loss.backward()
137
+ torch.nn.utils.clip_grad_norm_(trainable, 1.0)
138
+ opt.step()
139
+ global_step += 1
140
+ epoch_losses.append(float(loss.item()))
141
+ monitor.note_step(global_step, "faz2", parts["voxel"])
142
+ if global_step <= 3 or global_step % 20 == 0:
143
+ log_fn(
144
+ f"step={global_step} loss={float(loss.item()):.6f} "
145
+ f"voxel={parts['voxel']:.4f} trellis={parts['trellis_align']:.4f} "
146
+ f"hunyuan={parts['hunyuan_align']:.4f}"
147
+ )
148
+ if checkpoint_every > 0 and global_step % checkpoint_every == 0:
149
+ save_faz2_checkpoint(latest, epoch=epoch, global_step=global_step, model=model)
150
+ # Ayrica latest_model.pt olarak da yaz (orchestrator uyumu)
151
+ save_faz2_checkpoint(
152
+ checkpoint_dir / "latest_model.pt",
153
+ epoch=epoch,
154
+ global_step=global_step,
155
+ model=model,
156
+ )
157
+ log_fn(
158
+ f"CHECKPOINT_SAVED step={global_step} -> {latest} "
159
+ f"({latest.stat().st_size // (1024 * 1024)} MB)"
160
+ )
161
+ if global_step % validation_every == 0:
162
+ val = _eval()
163
+ monitor.note_validation(global_step, val, val)
164
+ log_vram_fn(f"step_{global_step}")
165
+
166
+ val = _eval()
167
+ mean = float(np.mean(epoch_losses)) if epoch_losses else float("nan")
168
+ monitor.note_epoch_end(epoch, epochs, mean, val, val, val)
169
+ save_faz2_checkpoint(latest, epoch=epoch, global_step=global_step, model=model)
170
+ save_faz2_checkpoint(
171
+ checkpoint_dir / "latest_model.pt",
172
+ epoch=epoch,
173
+ global_step=global_step,
174
+ model=model,
175
+ )
176
+ clear_gpu_fn()
177
+ log_fn(f"Epoch {epoch} kaydedildi -> {latest} ({latest.stat().st_size // (1024 * 1024)} MB)")
178
+ log_fn(f"CHECKPOINT_SAVED step={global_step} epoch={epoch} -> {latest}")
179
+
180
+ monitor.finish(ok=True)
181
+ log_fn("Faz2 TRELLIS/Hunyuan LoRA egitimi tamamlandi.")
182
+ log_fn(f"Cikti: {latest}")