giakhuyendihoc commited on
Commit
8dff78c
·
verified ·
1 Parent(s): d2fa8ac

Upload UR5 full fine-tuned checkpoint: pi05_pour_full configs/serve_policy_from_checkpoint.py

Browse files
configs/serve_policy_from_checkpoint.py ADDED
@@ -0,0 +1,335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import dataclasses
2
+ import json
3
+ import logging
4
+ import pathlib
5
+ import socket
6
+ from typing import Any
7
+
8
+ import torch
9
+ import tyro
10
+
11
+ from openpi.policies import policy as _policy
12
+ from openpi.policies import policy_config as _policy_config
13
+ from openpi.serving import websocket_policy_server
14
+ from openpi.shared import normalize as _normalize
15
+ from openpi.training import config as _config
16
+
17
+
18
+ def _torch_load_metadata(metadata_path: pathlib.Path) -> dict[str, Any]:
19
+ try:
20
+ return torch.load(metadata_path, map_location="cpu", weights_only=False)
21
+ except TypeError:
22
+ return torch.load(metadata_path, map_location="cpu")
23
+
24
+
25
+ def load_train_config_from_checkpoint(
26
+ checkpoint_dir: pathlib.Path,
27
+ *,
28
+ config_name: str | None = None,
29
+ lora_rank: int | None = None,
30
+ lora_alpha: float | None = None,
31
+ ) -> _config.TrainConfig:
32
+ """Load a TrainConfig and restore checkpoint-time model overrides when available."""
33
+
34
+ checkpoint_dir = checkpoint_dir.resolve()
35
+ metadata_path = checkpoint_dir / "metadata.pt"
36
+ metadata_config: dict[str, Any] | None = None
37
+
38
+ if metadata_path.exists():
39
+ metadata = _torch_load_metadata(metadata_path)
40
+ metadata_config = metadata.get("config")
41
+ if not isinstance(metadata_config, dict):
42
+ metadata_config = None
43
+
44
+ resolved_config_name = config_name
45
+ if resolved_config_name is None and metadata_config is not None:
46
+ resolved_config_name = metadata_config.get("name")
47
+ if resolved_config_name is None:
48
+ raise ValueError(
49
+ "Could not infer config name. Pass --config-name, or use a checkpoint with metadata.pt."
50
+ )
51
+
52
+ train_config = _config.get_config(resolved_config_name)
53
+
54
+ if metadata_config is not None:
55
+ model_config = metadata_config.get("model")
56
+ if isinstance(model_config, dict):
57
+ for key, value in model_config.items():
58
+ if hasattr(train_config.model, key):
59
+ object.__setattr__(train_config.model, key, value)
60
+
61
+ if lora_rank is not None:
62
+ object.__setattr__(train_config.model, "lora_rank", lora_rank)
63
+ if lora_alpha is not None:
64
+ object.__setattr__(train_config.model, "lora_alpha", lora_alpha)
65
+
66
+ return train_config
67
+
68
+
69
+ @dataclasses.dataclass
70
+ class Args:
71
+ checkpoint_dir: pathlib.Path
72
+ config_name: str | None = None
73
+ default_prompt: str | None = None
74
+ port: int = 8000
75
+ pytorch_device: str | None = None
76
+ num_denoise_steps: int = 10
77
+ model_label: str | None = None
78
+ reference_metrics_json: pathlib.Path | None = None
79
+ norm_stats_path: pathlib.Path | None = None
80
+ lora_rank: int | None = None
81
+ lora_alpha: float | None = None
82
+ record: bool = False
83
+
84
+
85
+ def main(args: Args) -> None:
86
+ checkpoint_metadata = _load_checkpoint_metadata(args.checkpoint_dir)
87
+ train_config = load_train_config_from_checkpoint(
88
+ args.checkpoint_dir,
89
+ config_name=args.config_name,
90
+ lora_rank=args.lora_rank,
91
+ lora_alpha=args.lora_alpha,
92
+ )
93
+ logging.info("Serving config: %s", train_config.name)
94
+ logging.info("Serving checkpoint: %s", args.checkpoint_dir)
95
+
96
+ norm_stats = _load_norm_stats_override(args.norm_stats_path)
97
+ policy = _policy_config.create_trained_policy(
98
+ train_config,
99
+ args.checkpoint_dir,
100
+ default_prompt=args.default_prompt,
101
+ sample_kwargs={"num_steps": args.num_denoise_steps},
102
+ norm_stats=norm_stats,
103
+ pytorch_device=args.pytorch_device,
104
+ )
105
+ _log_policy_efficiency_context(policy, args.checkpoint_dir)
106
+ policy_metadata = dict(policy.metadata or {})
107
+ policy_metadata.update(
108
+ _server_metadata(
109
+ policy,
110
+ args,
111
+ train_config.name,
112
+ checkpoint_metadata,
113
+ )
114
+ )
115
+ logging.info("Server metadata: %s", policy_metadata)
116
+
117
+ if args.record:
118
+ policy = _policy.PolicyRecorder(policy, "policy_records")
119
+
120
+ hostname = socket.gethostname()
121
+ local_ip = socket.gethostbyname(hostname)
122
+ logging.info("Creating server (host: %s, ip: %s, port: %d)", hostname, local_ip, args.port)
123
+
124
+ server = websocket_policy_server.WebsocketPolicyServer(
125
+ policy=policy,
126
+ host="0.0.0.0",
127
+ port=args.port,
128
+ metadata=policy_metadata,
129
+ )
130
+ server.serve_forever()
131
+
132
+
133
+ def _load_norm_stats_override(path: pathlib.Path | None):
134
+ if path is None:
135
+ return None
136
+ path = pathlib.Path(path).expanduser().resolve()
137
+ if path.is_file():
138
+ norm_stats = _normalize.deserialize_json(path.read_text(encoding="utf-8"))
139
+ logging.info("Loaded explicit norm stats from %s", path)
140
+ return norm_stats
141
+ norm_stats = _normalize.load(path)
142
+ logging.info("Loaded explicit norm stats from %s", path / "norm_stats.json")
143
+ return norm_stats
144
+
145
+
146
+ def _load_checkpoint_metadata(checkpoint_dir: pathlib.Path) -> dict[str, Any]:
147
+ metadata_path = pathlib.Path(checkpoint_dir) / "metadata.pt"
148
+ if not metadata_path.exists():
149
+ return {}
150
+ return _torch_load_metadata(metadata_path)
151
+
152
+
153
+ def _server_metadata(
154
+ policy: _policy.Policy,
155
+ args: Args,
156
+ config_name: str,
157
+ checkpoint_metadata: dict[str, Any],
158
+ ) -> dict[str, Any]:
159
+ global_step = checkpoint_metadata.get("global_step")
160
+ if global_step is None:
161
+ try:
162
+ global_step = int(pathlib.Path(args.checkpoint_dir).name)
163
+ except ValueError:
164
+ global_step = None
165
+
166
+ reference = _load_reference_metrics(args.reference_metrics_json)
167
+ metadata = {
168
+ "model_label": args.model_label or config_name,
169
+ "config_name": config_name,
170
+ "checkpoint_dir": str(pathlib.Path(args.checkpoint_dir).resolve()),
171
+ "global_step": global_step,
172
+ "num_denoise_steps": args.num_denoise_steps,
173
+ "pytorch_device": args.pytorch_device,
174
+ "reference_metrics_json": str(args.reference_metrics_json) if args.reference_metrics_json else None,
175
+ "norm_stats_path": str(args.norm_stats_path.resolve()) if args.norm_stats_path else None,
176
+ "reference_inference": reference,
177
+ "model": _collect_model_metadata(policy, args.checkpoint_dir, checkpoint_metadata),
178
+ "training_efficiency": _collect_training_efficiency(checkpoint_metadata),
179
+ }
180
+ return metadata
181
+
182
+
183
+ def _load_reference_metrics(path: pathlib.Path | None) -> dict[str, Any]:
184
+ if path is None:
185
+ return {}
186
+ path = pathlib.Path(path)
187
+ if not path.exists():
188
+ logging.warning("Reference metrics JSON does not exist: %s", path)
189
+ return {}
190
+ metrics = json.loads(path.read_text(encoding="utf-8"))
191
+ latency = metrics.get("latency") or {}
192
+ frequency = metrics.get("frequency") or {}
193
+ model_infer = latency.get("model_infer_ms") or {}
194
+ wall = latency.get("wall_ms") or {}
195
+ flop_profile = metrics.get("flop_profile") or {}
196
+ return {
197
+ "source": str(path),
198
+ "warmup_runs": _number_or_none(metrics.get("warmup_runs")),
199
+ "timed_runs": _number_or_none(metrics.get("timed_runs")),
200
+ "num_denoise_steps": _number_or_none(metrics.get("num_denoise_steps")),
201
+ "action_horizon": _number_or_none(metrics.get("action_horizon")),
202
+ "actions_shape": _jsonable_value(metrics.get("actions_shape")),
203
+ "model_infer_ms": _jsonable_value(model_infer),
204
+ "wall_ms": _jsonable_value(wall),
205
+ "model_infer_per_action_ms": _jsonable_value(latency.get("model_infer_per_action_ms") or {}),
206
+ "wall_per_action_ms": _jsonable_value(latency.get("wall_per_action_ms") or {}),
207
+ "latency_decomposition": _jsonable_value(metrics.get("latency_decomposition") or {}),
208
+ "approx_gflops": _number_or_none(metrics.get("approx_gflops")),
209
+ "flop_profile": {
210
+ key: _jsonable_value(value)
211
+ for key, value in flop_profile.items()
212
+ },
213
+ "profiler_gflops": _number_or_none(flop_profile.get("profiler_gflops")),
214
+ "manual_conv2d_gflops": _number_or_none(flop_profile.get("manual_conv2d_gflops")),
215
+ "manual_conv2d_gflops_added": _number_or_none(flop_profile.get("manual_conv2d_gflops_added")),
216
+ "profiler_conv2d_gflops": _number_or_none(flop_profile.get("profiler_conv2d_gflops")),
217
+ "model_infer_ms_median": _number_or_none(model_infer.get("median")),
218
+ "model_infer_ms_p95": _number_or_none(model_infer.get("p95")),
219
+ "wall_ms_median": _number_or_none(wall.get("median")),
220
+ "model_query_hz": _number_or_none(frequency.get("model_query_hz")),
221
+ "model_action_generation_hz": _number_or_none(frequency.get("model_action_generation_hz")),
222
+ "wall_query_hz": _number_or_none(frequency.get("wall_query_hz")),
223
+ "wall_action_generation_hz": _number_or_none(frequency.get("wall_action_generation_hz")),
224
+ }
225
+
226
+
227
+ def _collect_model_metadata(
228
+ policy: _policy.Policy,
229
+ checkpoint_dir: pathlib.Path,
230
+ checkpoint_metadata: dict[str, Any],
231
+ ) -> dict[str, Any]:
232
+ metrics = dict(checkpoint_metadata.get("efficiency_metrics") or {})
233
+ model = getattr(policy, "_model", None)
234
+ if model is not None:
235
+ if hasattr(model, "configure_trainable_parameters"):
236
+ model.configure_trainable_parameters()
237
+ params = list(model.parameters())
238
+ total_params = sum(param.numel() for param in params)
239
+ trainable_params = sum(param.numel() for param in params if param.requires_grad)
240
+ metrics.update(
241
+ {
242
+ "model_total_params": total_params,
243
+ "model_trainable_params": trainable_params,
244
+ "model_trainable_pct": trainable_params / total_params * 100 if total_params else 0.0,
245
+ "model_total_params_b": total_params / 1e9,
246
+ "model_trainable_params_m": trainable_params / 1e6,
247
+ }
248
+ )
249
+
250
+ weight_path = pathlib.Path(checkpoint_dir) / "model.safetensors"
251
+ if weight_path.exists():
252
+ checkpoint_size_bytes = weight_path.stat().st_size
253
+ metrics["checkpoint_size_bytes"] = checkpoint_size_bytes
254
+ metrics["checkpoint_size_gb"] = checkpoint_size_bytes / 1024**3
255
+
256
+ return {key: _jsonable_scalar(value) for key, value in metrics.items()}
257
+
258
+
259
+ def _collect_training_efficiency(checkpoint_metadata: dict[str, Any]) -> dict[str, Any]:
260
+ metrics = dict(checkpoint_metadata.get("efficiency_metrics") or {})
261
+ for key in (
262
+ "elapsed_training_hours",
263
+ "elapsed_after_first_step_hours",
264
+ "elapsed_run_hours",
265
+ "steps_per_second",
266
+ "steps_per_second_after_first_step",
267
+ "samples_per_second",
268
+ "samples_per_second_after_first_step",
269
+ "cuda_peak_allocated_gb",
270
+ "cuda_peak_reserved_gb",
271
+ ):
272
+ if key in checkpoint_metadata and key not in metrics:
273
+ metrics[key] = checkpoint_metadata[key]
274
+ return {key: _jsonable_scalar(value) for key, value in metrics.items()}
275
+
276
+
277
+ def _jsonable_scalar(value: Any) -> Any:
278
+ number = _number_or_none(value)
279
+ if number is not None:
280
+ return number
281
+ if value is None or isinstance(value, (str, bool)):
282
+ return value
283
+ return str(value)
284
+
285
+
286
+ def _jsonable_value(value: Any) -> Any:
287
+ if isinstance(value, dict):
288
+ return {str(key): _jsonable_value(item) for key, item in value.items()}
289
+ if isinstance(value, (list, tuple)):
290
+ return [_jsonable_value(item) for item in value]
291
+ return _jsonable_scalar(value)
292
+
293
+
294
+ def _number_or_none(value: Any) -> float | int | None:
295
+ try:
296
+ if hasattr(value, "item"):
297
+ value = value.item()
298
+ number = float(value)
299
+ except (TypeError, ValueError):
300
+ return None
301
+ if not torch.isfinite(torch.tensor(number)):
302
+ return None
303
+ return int(number) if number.is_integer() else number
304
+
305
+
306
+ def _log_policy_efficiency_context(policy: _policy.Policy, checkpoint_dir: pathlib.Path) -> None:
307
+ model = getattr(policy, "_model", None)
308
+ if model is not None:
309
+ total_params = sum(param.numel() for param in model.parameters())
310
+ trainable_params = sum(param.numel() for param in model.parameters() if param.requires_grad)
311
+ logging.info(
312
+ "Model parameters: total=%.2fM trainable=%.2fM trainable_pct=%.2f%%",
313
+ total_params / 1e6,
314
+ trainable_params / 1e6,
315
+ trainable_params / total_params * 100 if total_params else 0.0,
316
+ )
317
+
318
+ weight_path = pathlib.Path(checkpoint_dir) / "model.safetensors"
319
+ if weight_path.exists():
320
+ logging.info("Checkpoint weight size: %.2fGB", weight_path.stat().st_size / 1024**3)
321
+
322
+ if torch.cuda.is_available():
323
+ device = torch.device(getattr(policy, "_pytorch_device", "cuda"))
324
+ if device.type == "cuda":
325
+ torch.cuda.reset_peak_memory_stats(device)
326
+ logging.info(
327
+ "CUDA memory after model load: allocated=%.2fGB reserved=%.2fGB",
328
+ torch.cuda.memory_allocated(device) / 1024**3,
329
+ torch.cuda.memory_reserved(device) / 1024**3,
330
+ )
331
+
332
+
333
+ if __name__ == "__main__":
334
+ logging.basicConfig(level=logging.INFO, force=True)
335
+ main(tyro.cli(Args))