File size: 8,845 Bytes
ff0fadf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | import os
from pathlib import Path
from types import SimpleNamespace
import yaml
PROJECT_ROOT = Path(__file__).resolve().parents[1]
torch = None
get_data = None
DistributedManager = None
replace_function = None
get_model = None
DerivLoss = None
L2Loss = None
def load_config():
with open(PROJECT_ROOT / "config" / "config.yaml", "r", encoding="utf-8") as f:
return yaml.safe_load(f)
def load_onescience():
global torch
global get_data
global DistributedManager
global replace_function
global get_model
global DerivLoss
global L2Loss
if torch is not None:
return
import torch as torch_module
from onescience.datapipes.cfd_benchmark.data_factory import get_data as get_data_fn
from onescience.distributed.manager import DistributedManager as DistributedManager_cls
from onescience.memory.checkpoint import replace_function as replace_function_fn
from onescience.models.cfd_benchmark.model_factory import get_model as get_model_fn
from onescience.utils.cfd_benchmark.loss import DerivLoss as DerivLoss_cls
from onescience.utils.cfd_benchmark.loss import L2Loss as L2Loss_cls
torch = torch_module
get_data = get_data_fn
DistributedManager = DistributedManager_cls
replace_function = replace_function_fn
get_model = get_model_fn
DerivLoss = DerivLoss_cls
L2Loss = L2Loss_cls
def build_args(cfg):
data = cfg["data"]
model = cfg["model"]
train = cfg["train"]
return SimpleNamespace(
lr=train["lr"],
epochs=train["epochs"],
weight_decay=train["weight_decay"],
pct_start=train["pct_start"],
batch_size=data["batch_size"],
gpu=0,
max_grad_norm=train["max_grad_norm"],
derivloss=train["derivloss"],
optimizer=train["optimizer"],
scheduler=train["scheduler"],
step_size=100,
gamma=0.5,
find_unused_parameters=False,
use_checkpoint=train["use_checkpoint"],
checkpoint_layers=train["checkpoint_layers"],
resume=False,
data_path=cfg["paths"]["data_dir"],
loader=data["loader"],
config_name="config_name",
train_ratio=0.8,
ntrain=data["ntrain"],
ntest=data["ntest"],
normalize=data["normalize"],
norm_type=data["norm_type"],
geotype=data["geotype"],
time_input=False,
space_dim=data["space_dim"],
fun_dim=data["fun_dim"],
out_dim=data["out_dim"],
shapelist=None,
downsamplex=data["downsamplex"],
downsampley=data["downsampley"],
downsamplez=1,
radius=0.2,
task=data["task"],
T_in=10,
T_out=10,
model=model["name"],
n_hidden=model["n_hidden"],
n_layers=model["n_layers"],
n_heads=model["n_heads"],
act=model["act"],
mlp_ratio=model["mlp_ratio"],
dropout=model["dropout"],
unified_pos=model["unified_pos"],
ref=model["ref"],
slice_num=model["slice_num"],
modes=12,
psi_dim=8,
attn_type="nystrom",
mwt_k=3,
branch_depth=5,
trunk_depth=6,
hidden_channels=[],
kernel_size=5,
emb_dims=128,
eval=0,
save_name=train["save_name"],
vis_num=cfg["inference"]["vis_num"],
vis_bound=None,
)
def get_device(args, dist):
if dist.world_size == 1 and torch.cuda.is_available():
return torch.device(f"cuda:{args.gpu}")
if dist.world_size > 1 and torch.cuda.is_available():
return dist.device
return torch.device("cpu")
def make_optimizer(args, model):
if args.optimizer == "AdamW":
return torch.optim.AdamW(
model.parameters(), lr=args.lr, weight_decay=args.weight_decay
)
if args.optimizer == "Adam":
return torch.optim.Adam(
model.parameters(), lr=args.lr, weight_decay=args.weight_decay
)
raise ValueError("Optimizer only AdamW or Adam")
def make_scheduler(args, optimizer, train_loader):
if args.scheduler == "OneCycleLR":
return torch.optim.lr_scheduler.OneCycleLR(
optimizer,
max_lr=args.lr,
epochs=args.epochs,
steps_per_epoch=len(train_loader),
pct_start=args.pct_start,
)
if args.scheduler == "CosineAnnealingLR":
return torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=args.epochs)
if args.scheduler == "StepLR":
return torch.optim.lr_scheduler.StepLR(
optimizer, step_size=args.step_size, gamma=args.gamma
)
raise ValueError("Scheduler only OneCycleLR, CosineAnnealingLR or StepLR")
def evaluate(model, test_loader, dataset, args, device):
myloss = L2Loss(size_average=False)
model.eval()
rel_err = 0.0
with torch.no_grad():
for pos, fx, y in test_loader:
x, fx, y = pos.to(device), fx.to(device), y.to(device)
if args.fun_dim == 0:
fx = None
out = model(x, fx)
if args.normalize:
out = dataset.y_normalizer.decode(out)
rel_err += myloss(out, y).item()
return rel_err / args.ntest
def train(args):
load_onescience()
DistributedManager.initialize()
dist = DistributedManager()
dataset, train_loader, test_loader, args.shapelist = get_data(args, dist)
device = get_device(args, dist)
model = get_model(args, device).to(device)
if hasattr(dataset, "x_normalizer"):
dataset.x_normalizer = dataset.x_normalizer.to(device)
if hasattr(dataset, "y_normalizer"):
dataset.y_normalizer = dataset.y_normalizer.to(device)
optimizer = make_optimizer(args, model)
scheduler = make_scheduler(args, optimizer, train_loader)
checkpoint_dir = PROJECT_ROOT / "checkpoints"
checkpoint_dir.mkdir(parents=True, exist_ok=True)
checkpoint_path = checkpoint_dir / f"{args.save_name}.pt"
checkpoint_layers = (
[layer.strip() for layer in args.checkpoint_layers.split(",") if layer.strip()]
if args.use_checkpoint and args.checkpoint_layers
else []
)
myloss = L2Loss(size_average=False)
regloss = DerivLoss(size_average=False, shapelist=args.shapelist) if args.derivloss else None
best_test_loss = float("inf")
best_epoch = 0
print(args)
print(model)
print(f"Use device: {device}")
for epoch in range(args.epochs):
model.train()
train_loss = 0.0
for pos, fx, y in train_loader:
x, fx, y = pos.to(device), fx.to(device), y.to(device)
if args.fun_dim == 0:
fx = None
with replace_function(
module=model,
replace_layers_list=checkpoint_layers,
ddp_flag=(dist.world_size > 1),
):
out = model(x, fx)
if args.normalize:
out = dataset.y_normalizer.decode(out)
y = dataset.y_normalizer.decode(y)
loss = myloss(out, y)
if regloss is not None:
loss = loss + 0.1 * regloss(out, y)
train_loss += loss.item()
optimizer.zero_grad()
loss.backward()
if args.max_grad_norm is not None:
torch.nn.utils.clip_grad_norm_(model.parameters(), args.max_grad_norm)
optimizer.step()
if args.scheduler == "OneCycleLR":
scheduler.step()
if args.scheduler in {"CosineAnnealingLR", "StepLR"}:
scheduler.step()
train_loss = train_loss / args.ntrain
rel_err = evaluate(model, test_loader, dataset, args, device)
if rel_err < best_test_loss:
best_test_loss = rel_err
best_epoch = epoch
torch.save(
{
"epoch": epoch,
"model_state": model.state_dict(),
"optimizer_state": optimizer.state_dict(),
"scheduler_state": scheduler.state_dict(),
"best_test_loss": best_test_loss,
"best_epoch": best_epoch,
"args": vars(args),
},
checkpoint_path,
)
if epoch % 10 == 0:
print("Epoch {} Train loss : {:.5f}".format(epoch, train_loss))
print("rel_err:{}".format(rel_err))
print(
"Training completed. Best model saved at epoch {} with rel_err: {:.5f}".format(
best_epoch, best_test_loss
)
)
def main():
os.chdir(PROJECT_ROOT)
cfg = load_config()
if cfg.get("runtime", {}).get("device", "cpu") == "cpu":
os.environ["CUDA_VISIBLE_DEVICES"] = ""
train(build_args(cfg))
if __name__ == "__main__":
main()
|