Diffusers
Safetensors
AR / train_v1.py
xfcghj's picture
Upload folder using huggingface_hub
f0fc238 verified
Raw History Blame Contribute Delete
6.3 kB
import os
import torch
import torch.nn as nn
import argparse
import torch.optim as optim
import logging
from datetime import datetime
from tqdm import tqdm
# 导入您定义的组件
from models.auto import TemporalTrajectoryPredictor, SpatialGraphRefiner
from models.vae import TripoSGVaeWrapper
from models.clip import FrozenCLIPTextEncoder
from utils.autodataloader import get_mesh_dataloader
from utils.loss import TokenL2Loss
class TextProjectionLayer(nn.Module):
"""两层线性层 + 残差连接:将 512 维投影至 64 维"""
def __init__(self, in_dim=512, out_dim=64):
super().__init__()
self.net = nn.Sequential(
nn.Linear(in_dim, in_dim),
nn.ReLU(),
nn.Linear(in_dim, out_dim)
)
self.residual = nn.Linear(in_dim, out_dim) if in_dim != out_dim else nn.Identity()
def forward(self, x):
return self.net(x) + self.residual(x)
def setup_logger(save_dir):
os.makedirs(save_dir, exist_ok=True)
time_str = datetime.now().strftime("%Y%m%d_%H%M%S")
log_file = os.path.join(save_dir, f"train_{time_str}.log")
logger = logging.getLogger("TrainLogger")
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(message)s')
file_handler = logging.FileHandler(log_file)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
return logger
def count_parameters(model, name, logger):
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
logger.info(f"[{name}] 可训练参数量: {trainable:,}")
return trainable
def train(args):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
logger = setup_logger(args.save_dir)
# 1. 初始化模型
vae = TripoSGVaeWrapper(device=device)
vae.eval()
for param in vae.parameters():
param.requires_grad = False
text_encoder = FrozenCLIPTextEncoder(args.clip_model_path, device=device)
text_proj = TextProjectionLayer(in_dim=512, out_dim=args.token_dim).to(device)
temporal_model = TemporalTrajectoryPredictor(num_tokens=args.num_tokens, token_dim=args.token_dim, max_seq_len=16).to(device)
spatial_model = SpatialGraphRefiner(num_tokens=args.num_tokens, token_dim=args.token_dim, text_dim=args.token_dim).to(device)
logger.info("--- 参数统计 ---")
count_parameters(temporal_model, "TemporalModel", logger)
count_parameters(spatial_model, "SpatialModel", logger)
count_parameters(text_proj, "TextProjection", logger)
criterion = TokenL2Loss()
optimizer = optim.Adam(
list(temporal_model.parameters()) + list(spatial_model.parameters()) + list(text_proj.parameters()),
lr=args.lr
)
dataloader = get_mesh_dataloader(batch_size=args.batch_size, num_workers=4)
# 定义美观的进度条格式
bar_format = '{l_bar}{bar:30}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}{postfix}]'
# 3. 训练循环
for epoch in range(args.epochs):
temporal_model.train()
spatial_model.train()
text_proj.train()
pbar = tqdm(dataloader, desc=f"Epoch {epoch+1:03d}/{args.epochs:03d}",
ncols=300, bar_format=bar_format)
for batch in pbar:
optimizer.zero_grad()
vertices = batch['vertices'].to(device)
faces = batch['faces'].to(device)
max_v_idx = vertices.shape[2] - 1
if faces.max() > max_v_idx:
pbar.write(f"\n[!] 发现非法数据,索引 {faces.max()} 超出范围,跳过Batch")
continue
with torch.no_grad():
B, T, V, C = vertices.shape
latents = []
for t in range(T):
l = vae.encode_mesh(vertices[:, t].squeeze(0), faces.squeeze(0))
latents.append(l)
latents = torch.stack(latents, dim=1)
text_embed = text_proj(text_encoder(list(batch['caption']))).unsqueeze(1)
t_idx = torch.randint(1, 16, (1,)).item()
history = latents[:, :t_idx, :, :]
target = latents[:, t_idx, :, :]
next_t_init = temporal_model(history)
next_t_refined = spatial_model(next_t_init, text_embed, torch.zeros((args.batch_size, args.num_tokens, args.num_tokens)).to(device))
loss_t = criterion(next_t_init, target)
loss_s = criterion(next_t_refined, target)
total_loss = loss_t + loss_s
total_loss.backward()
optimizer.step()
pbar.set_postfix({
"Loss": f"{total_loss.item():.3f}",
"Temp": f"{loss_t.item():.3f}",
"Spat": f"{loss_s.item():.3f}"
})
logger.info(f"Epoch {epoch+1}/{args.epochs} 完成 - Total Loss: {total_loss.item():.4f}")
if (epoch + 1) % args.save_every_k_epochs == 0:
ckpt_path = os.path.join(args.save_dir, f"model_epoch_{epoch+1}.pth")
torch.save({
'temporal': temporal_model.state_dict(),
'spatial': spatial_model.state_dict(),
'proj': text_proj.state_dict()
}, ckpt_path)
logger.info(f"模型已保存至: {ckpt_path}")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--num_tokens", type=int, default=2048)
parser.add_argument("--token_dim", type=int, default=64)
parser.add_argument("--batch_size", type=int, default=4)
parser.add_argument("--lr", type=float, default=1e-4)
parser.add_argument("--epochs", type=int, default=100)
parser.add_argument("--save_every_k_epochs", type=int, default=10)
parser.add_argument("--save_dir", type=str, default="./checkpoints")
parser.add_argument("--clip_model_path", type=str, default="/home/dataset-assist-0/usr/lh/ysh/dw/RL/AR/pretrain/clip-vit-base-patch32")
args = parser.parse_args()
train(args)