feat(training): release V0.9.1 避障训练与基础策略迁移
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled

This commit is contained in:
2026-09-08 10:50:13 +08:00
parent fa5485049a
commit 438e56bcc8
113 changed files with 15027 additions and 539 deletions
+36 -26
View File
@@ -32,7 +32,6 @@ from mjlab.tasks.registry import list_tasks, load_env_cfg, load_rl_cfg, load_run
from mjlab.tasks.velocity.mdp import UniformVelocityCommandCfg
from mjlab.utils.torch import configure_torch_backends
from torch.utils.tensorboard import SummaryWriter
from tuning.schema import apply_reward_configuration, validate_configuration
SCENARIOS = (
@@ -61,6 +60,7 @@ class EvaluateConfig:
checkpoint: str
output: str
reward_config: str | None = None
task_config: str | None = None
num_envs: int = 256
steps_per_seed: int = 1000
seeds: tuple[int, ...] = field(default_factory=lambda: (101, 202, 303))
@@ -163,31 +163,41 @@ def run_evaluation(task_id: str, cfg: EvaluateConfig) -> dict:
checkpoint = Path(cfg.checkpoint).expanduser().resolve(strict=True)
output = Path(cfg.output).expanduser().resolve()
output.parent.mkdir(parents=True, exist_ok=True)
per_seed = [_evaluate_seed(task_id, cfg, seed) for seed in cfg.seeds]
metrics = {
key: fmean(seed_metrics[key] for seed_metrics in per_seed)
for key in (*METRIC_NAMES, "fall_rate")
}
deviations = {
key: pstdev(seed_metrics[key] for seed_metrics in per_seed)
for key in (*METRIC_NAMES, "fall_rate")
}
result = {
"protocolVersion": 1,
"taskId": task_id,
"checkpoint": checkpoint.name,
"checkpointSha256": _sha256(checkpoint),
"seeds": list(cfg.seeds),
"numEnvs": cfg.num_envs,
"stepsPerSeed": cfg.steps_per_seed,
"scenarios": [list(value) for value in SCENARIOS],
"metrics": metrics,
"metricStd": deviations,
"seedMetrics": [
{"seed": seed, "metrics": values}
for seed, values in zip(cfg.seeds, per_seed, strict=True)
],
}
if task_id == "Unitree-Go2-ObstacleAvoidance":
from scripts.evaluate_obstacle import run_obstacle_evaluation
checkpoint_hash = _sha256(checkpoint)
result = run_obstacle_evaluation(task_id, cfg)
if _sha256(checkpoint) != checkpoint_hash:
raise ValueError("Checkpoint changed during evaluation")
result.update({"taskId": task_id, "checkpoint": checkpoint.name,
"checkpointSha256": checkpoint_hash})
metrics = result["metrics"]
else:
per_seed = [_evaluate_seed(task_id, cfg, seed) for seed in cfg.seeds]
metrics = {
key: fmean(seed_metrics[key] for seed_metrics in per_seed)
for key in (*METRIC_NAMES, "fall_rate")
}
deviations = {
key: pstdev(seed_metrics[key] for seed_metrics in per_seed)
for key in (*METRIC_NAMES, "fall_rate")
}
result = {
"protocolVersion": 1,
"taskId": task_id,
"checkpoint": checkpoint.name,
"checkpointSha256": _sha256(checkpoint),
"seeds": list(cfg.seeds),
"numEnvs": cfg.num_envs,
"stepsPerSeed": cfg.steps_per_seed,
"scenarios": [list(value) for value in SCENARIOS],
"metrics": metrics,
"metricStd": deviations,
"seedMetrics": [
{"seed": seed, "metrics": values}
for seed, values in zip(cfg.seeds, per_seed, strict=True)
],
}
output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
writer = SummaryWriter(log_dir=str(output.parent / "evaluation-events"))
try:
@@ -0,0 +1,241 @@
"""Actual checkpoint inference, capturing first-terminal physics before mjlab resets."""
import hashlib
import json
import shutil
import subprocess
import sys
import tempfile
from dataclasses import asdict
from pathlib import Path
from statistics import fmean
from types import SimpleNamespace
# Also executable as a fresh-interpreter seed worker (never fork a CUDA context).
for source in (Path(__file__).resolve().parents[1], Path(__file__).resolve().parents[2]):
if str(source) not in sys.path:
sys.path.insert(0, str(source))
import torch
from mjlab.envs import ManagerBasedRlEnv
from mjlab.rl import MjlabOnPolicyRunner, RslRlVecEnvWrapper
from mjlab.sensor import ContactMatch, ContactSensorCfg
from mjlab.tasks.registry import load_env_cfg, load_rl_cfg, load_runner_cls
from scripts.train import _load_reward_config, _load_task_config
from src.tasks.obstacle_avoidance.env_cfg import apply_obstacle_configuration
from task_config import OBSTACLE_TASK
from tuning.obstacle_scoring import METRICS, STEPS, protocol, score_trajectory
from tuning.schema import apply_reward_configuration
class FirstEpisodeRecorder:
"""The termination hook runs post-physics, before _reset_idx (including first terminal)."""
def __init__(self, env, layout):
self.env = env
self.layout = layout
self.samples = [[] for _ in range(env.num_envs)]
self.previous = torch.zeros((env.num_envs, 12), device=env.device)
self.calls = 0
self.compute = env.termination_manager.compute
env.termination_manager.compute = self.capture
def capture(self):
env = self.env
terminal = self.compute()
robot = env.scene["robot"].data
# Like mjlab termination itself, derived pose is one physics substep old.
position = robot.root_link_pos_w
local_xy = position[:, :2] - env.scene.env_origins[:, :2]
xy = local_xy + torch.tensor(self.layout["spawn"][:2], device=env.device)
boxes = self.layout["boxes"][1:] # Never include the support floor.
clearance = torch.full((env.num_envs,), 0.5, device=env.device)
if boxes:
centers = torch.tensor([b["pos"][:2] for b in boxes], device=env.device)
sizes = torch.tensor([b["size"][:2] for b in boxes], device=env.device)
clearance = (
((xy[:, None, :] - centers).abs() - sizes).clamp(min=0).norm(dim=2).amin(dim=1)
)
clearance = (clearance - 0.3).clamp(min=0) # Conservative body footprint radius.
forces = env.scene["evaluation_obstacles"].data.force_history
collision = forces.norm(dim=-1).flatten(1).amax(dim=1) > 1.0
else:
collision = torch.zeros(env.num_envs, dtype=torch.bool, device=env.device)
nonfoot = env.scene["nonfoot_ground_touch"].data.force_history
collision |= nonfoot.norm(dim=-1).flatten(1).amax(dim=1) > 10.0
fall = (robot.projected_gravity_b[:, 2] > -0.3420201433) | (position[:, 2] < 0.12)
actions = env.action_manager.action
delta = (actions - self.previous).square().mean(dim=1)
self.previous.copy_(actions)
rays = env.scene["forward_scan"].data.distances
hits = (
((rays >= 0) & (rays <= env.scene["forward_scan"].cfg.max_distance)).float().mean(dim=1)
)
distance = env.command_manager.get_term("twist").errors()[1]
values = torch.stack((distance, clearance, delta, hits, collision, fall, terminal), dim=1)
if not torch.isfinite(values).all():
raise ValueError("Nonfinite rollout measurements")
keys = ("distance", "clearance", "action_delta", "ray_hit", "collision", "fall", "terminal")
for samples, row in zip(self.samples, values.cpu().tolist(), strict=True):
if not samples or not samples[-1]["terminal"]:
samples.append(dict(zip(keys, row, strict=True)))
self.calls += 1
return terminal
def metrics(self, horizon=STEPS):
if self.calls != horizon:
raise ValueError("Incomplete rollout horizon")
metrics = [score_trajectory(samples, horizon) for samples in self.samples]
return {key: fmean(item[key] for item in metrics) for key in METRICS}
def configure_seed(task_id, cfg, scenario, reward):
env_cfg = load_env_cfg(task_id, play=False)
env_cfg.seed = scenario["seed"]
env_cfg.scene.num_envs = cfg.num_envs
# Keep the benchmark's declared pair fixed; training itself randomizes every reset.
apply_obstacle_configuration(env_cfg, scenario["taskConfig"], randomize_navigation=False)
apply_reward_configuration(env_cfg, reward, task_id)
env_cfg.curriculum = {}
env_cfg.observations["actor"].enable_corruption = False
env_cfg.events.pop("push_robot", None)
# Primary geom names are literal compiled static terrain geoms, not a regex.
names = tuple(f"terrain_{i}" for i in range(1, len(scenario["terrain"]["boxes"])))
if names:
env_cfg.scene.sensors += (
ContactSensorCfg(
name="evaluation_obstacles",
primary=ContactMatch(mode="geom", pattern=names),
secondary=ContactMatch(mode="subtree", pattern="base_link", entity="robot"),
fields=("force",),
reduce="maxforce",
history_length=env_cfg.decimation,
),
)
return env_cfg
def evaluate_seed(task_id, cfg, scenario, reward):
torch.manual_seed(scenario["seed"])
env_cfg = configure_seed(task_id, cfg, scenario, reward)
agent_cfg = load_rl_cfg(task_id)
env = ManagerBasedRlEnv(env_cfg, device=cfg.device)
wrapped = RslRlVecEnvWrapper(env, clip_actions=agent_cfg.clip_actions)
try:
runner_cls = load_runner_cls(task_id) or MjlabOnPolicyRunner
runner = runner_cls(wrapped, asdict(agent_cfg), log_dir=None, device=wrapped.device)
# rsl_rl actor state_dict includes observation_normalizer buffers. Strict load
# rejects incompatible architecture/statistics; no fixture or zero-action fallback.
runner.load(
str(Path(cfg.checkpoint).resolve(strict=True)),
load_cfg={"actor": True},
strict=True,
map_location=str(wrapped.device),
)
policy = runner.get_inference_policy(device=str(wrapped.device))
obs, _ = env.reset(seed=scenario["seed"])
recorder = FirstEpisodeRecorder(env, scenario["terrain"])
with torch.inference_mode():
for _ in range(STEPS):
actions = policy(obs)
if not torch.isfinite(actions).all():
raise ValueError("Nonfinite policy actions")
obs, _, _, _ = wrapped.step(actions)
return {
"seed": scenario["seed"],
"metrics": recorder.metrics(),
"episodes": cfg.num_envs,
"rolloutSteps": STEPS,
}
finally:
wrapped.close()
def run_obstacle_evaluation(task_id, cfg):
from tuning.obstacle_scoring import SEEDS
if tuple(cfg.seeds) != SEEDS or cfg.steps_per_seed != STEPS:
raise ValueError("Obstacle evaluation seeds/horizon are fixed")
if not cfg.task_config or not cfg.reward_config:
raise ValueError("Obstacle evaluation requires task and parameter configurations")
raw = json.loads(Path(cfg.task_config).read_text())
custom = _load_task_config(task_id, cfg.task_config, raw["seed"])
reward = _load_reward_config(cfg.reward_config, None, OBSTACLE_TASK)
fixed = protocol(custom, cfg.num_envs)
seeds = evaluate_isolated_seeds(task_id, cfg, fixed, reward)
return {
"protocol": fixed,
"seedMetrics": seeds,
"metrics": {key: fmean(item["metrics"][key] for item in seeds) for key in METRICS},
}
def file_hash(path):
digest = hashlib.sha256()
with Path(path).open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def evaluate_isolated_seeds(task_id, cfg, fixed, reward):
from tuning.obstacle_scoring import validate_evaluation
seeds = []
# Inherit the evaluation parent's process group: manager cancellation kills
# both parent and the active seed. subprocess.run kills/reaps on timeout too.
with tempfile.TemporaryDirectory(prefix="go2-obstacle-eval-") as directory:
root = Path(directory)
checkpoint = root / "checkpoint.pt"
source_hash = file_hash(cfg.checkpoint)
shutil.copyfile(cfg.checkpoint, checkpoint)
if file_hash(checkpoint) != source_hash:
raise ValueError("Checkpoint changed during snapshot")
for scenario in fixed["scenarios"]:
request = root / f"request-{scenario['seed']}.json"
output = root / f"result-{scenario['seed']}.json"
request.write_text(
json.dumps(
{
"taskId": task_id,
"config": {**asdict(cfg), "checkpoint": str(checkpoint)},
"scenario": scenario,
"reward": reward,
"checkpointSha256": source_hash,
},
allow_nan=False,
)
)
subprocess.run(
[sys.executable, str(Path(__file__).resolve()), str(request), str(output)],
check=True,
timeout=1200,
shell=False,
)
result = json.loads(output.read_text())
if (
result.pop("checkpointSha256", None) != source_hash
or file_hash(checkpoint) != source_hash
):
raise ValueError("Seed checkpoint identity mismatch")
seeds.append(result)
candidate = {
"protocol": fixed,
"seedMetrics": seeds,
"metrics": {key: fmean(item["metrics"][key] for item in seeds) for key in METRICS},
}
validate_evaluation(candidate, fixed)
return seeds
if __name__ == "__main__":
import src.tasks # noqa: F401
request_path, output_path = map(Path, sys.argv[1:])
request = json.loads(request_path.read_text())
config = SimpleNamespace(**request["config"])
if file_hash(config.checkpoint) != request["checkpointSha256"]:
raise ValueError("Worker checkpoint identity mismatch")
result = evaluate_seed(request["taskId"], config, request["scenario"], request["reward"])
result["checkpointSha256"] = file_hash(config.checkpoint)
output_path.write_text(json.dumps(result, allow_nan=False))
+117 -10
View File
@@ -7,7 +7,7 @@ import sys
from dataclasses import asdict, dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Literal, cast
from typing import Literal
# 训练器作为仓库内置子集直接从 scripts/ 启动,不要求额外执行 pip install -e。
TRAINER_ROOT = Path(__file__).resolve().parents[1]
@@ -34,7 +34,7 @@ from mjlab.utils.gpu import select_gpus
from mjlab.utils.os import dump_yaml, get_checkpoint_path
from mjlab.utils.torch import configure_torch_backends
from mjlab.utils.wrappers import VideoRecorder
from task_config import OBSTACLE_TASK, deployment_metadata, validate_task_config
from tuning.schema import apply_reward_configuration, validate_configuration
@@ -51,8 +51,14 @@ class TrainConfig:
gpu_ids: list[int] | Literal["all"] | None = field(default_factory=lambda: [0])
output_dir: str | None = None
resume_checkpoint: str | None = None
pretrained_checkpoint: str | None = None
pretrained_upload_manifest: str | None = None
pretrained_onnx: str | None = None
pretrained_source_id: str | None = None
pretrained_allowed_roots: list[str] = field(default_factory=list)
reward_config: str | None = None
reward_config_json: str | None = None
task_config: str | None = None
@staticmethod
def from_task(task_id: str) -> "TrainConfig":
@@ -61,27 +67,96 @@ class TrainConfig:
return TrainConfig(env=env_cfg, agent=agent_cfg)
def _load_reward_config(path: str | None, inline: str | None) -> dict | None:
def _load_reward_config(path: str | None, inline: str | None, task_id="Unitree-Go2-Flat") -> dict | None:
if path is not None and inline is not None:
raise ValueError("Use only one of reward_config and reward_config_json")
if inline is not None:
if len(inline.encode("utf-8")) > 64 * 1024:
raise ValueError("Reward configuration is larger than 64 KiB")
return validate_configuration(json.loads(inline))
return validate_configuration(json.loads(inline), task_id)
if path is None:
return None
source = Path(path).expanduser().resolve(strict=True)
if source.stat().st_size > 64 * 1024:
raise ValueError("Reward configuration is larger than 64 KiB")
with source.open(encoding="utf-8") as stream:
return validate_configuration(json.load(stream))
return validate_configuration(json.load(stream), task_id)
def _load_task_config(task_id: str, path: str | None, seed: int) -> dict | None:
if path is None:
return validate_task_config(task_id, {}, seed)
source = Path(path).expanduser().resolve(strict=True)
if source.stat().st_size > 128 * 1024:
raise ValueError("Task configuration is larger than 128 KiB")
payload = json.loads(source.read_text(encoding="utf-8"))
allowed = {"terrainPreset", "terrainParams", "sensorCfg", "seed", "customTerrainBoxes"}
if not isinstance(payload, dict) or payload.keys() - allowed:
raise ValueError("Unknown task configuration fields")
if (
isinstance(payload.get("seed"), bool)
or not isinstance(payload.get("seed"), int)
or payload.get("seed") != seed
):
raise ValueError("Task configuration seed must equal the agent seed")
if payload.get("sensorCfg") is None:
payload.pop("sensorCfg", None)
return validate_task_config(task_id, payload, seed)
def _configure_task_and_rewards(task_id: str, cfg: TrainConfig):
custom = _load_task_config(task_id, cfg.task_config, cfg.agent.seed)
if custom is not None:
from src.tasks.obstacle_avoidance.env_cfg import apply_obstacle_configuration
from src.tasks.obstacle_avoidance.terrain import apply_terrain_configuration
if task_id == OBSTACLE_TASK:
apply_obstacle_configuration(cfg.env, custom)
else:
apply_terrain_configuration(cfg.env, custom)
deployment = deployment_metadata(task_id, custom, cfg.agent.seed)
reward_config = _load_reward_config(cfg.reward_config, cfg.reward_config_json, task_id)
if reward_config is not None:
apply_reward_configuration(cfg.env, reward_config, task_id)
if task_id == OBSTACLE_TASK:
deployment["navigation"]["speed"] = reward_config["params"]["target_velocity"]
deployment["sensorCfg"]["avoidanceWeight"] = reward_config["weights"]["avoidance_weight"]
return deployment, reward_config
def _load_pretrained(cfg: TrainConfig):
if cfg.pretrained_checkpoint is None:
if (cfg.pretrained_onnx is not None or cfg.pretrained_allowed_roots
or cfg.pretrained_source_id or cfg.pretrained_upload_manifest):
raise ValueError("Pretrained options require --pretrained-checkpoint (.pt), not ONNX alone")
return None
if cfg.resume_checkpoint is not None or cfg.agent.resume:
raise ValueError("Pretrained warm-start and resume are mutually exclusive")
from pretrained import read_pretrained_source
options = {"onnx_path": cfg.pretrained_onnx}
if cfg.pretrained_upload_manifest is not None:
if cfg.pretrained_onnx is not None:
raise ValueError("Uploaded actor cannot use adjacent ONNX sidecars")
from pretrained_upload import read_uploaded_source
read_pretrained_source = read_uploaded_source
options = {"manifest_path": cfg.pretrained_upload_manifest}
source = read_pretrained_source(
cfg.pretrained_checkpoint,
allowed_roots=cfg.pretrained_allowed_roots,
**options,
target_env=asdict(cfg.env),
target_agent=asdict(cfg.agent),
)
if cfg.pretrained_source_id is not None and source.manifest["source_id"] != cfg.pretrained_source_id:
raise ValueError("基础策略SHA身份变化,拒绝初始化")
return source
def run_train(task_id: str, cfg: TrainConfig, log_dir: Path) -> None:
reward_config = _load_reward_config(cfg.reward_config, cfg.reward_config_json)
if reward_config is not None:
apply_reward_configuration(cfg.env, reward_config)
deployment, reward_config = _configure_task_and_rewards(task_id, cfg)
# Validate source identity/semantics before allocating a simulation or optimizer.
pretrained = _load_pretrained(cfg)
cuda_visible = os.environ.get("CUDA_VISIBLE_DEVICES", "")
if cuda_visible == "":
device = "cpu"
@@ -135,6 +210,17 @@ def run_train(task_id: str, cfg: TrainConfig, log_dir: Path) -> None:
cfg=cfg.env, device=device, render_mode="rgb_array" if cfg.video else None
)
if pretrained is not None:
from pretrained import validate_runtime_contract
try:
validate_runtime_contract(env)
except Exception:
env.close()
raise
# The ONNX runner exports this exact snapshot beside and inside policy.onnx.
env.platform_deployment = deployment
log_root_path = log_dir.parent # Go up from specific run dir to experiment dir.
resume_path: Path | None = None
@@ -171,9 +257,30 @@ def run_train(task_id: str, cfg: TrainConfig, log_dir: Path) -> None:
runner = runner_cls(env, agent_cfg, str(log_dir), device, **runner_kwargs)
runner.add_git_repo_to_log(__file__)
if pretrained is not None:
from pretrained import initialize_runner
initialization = initialize_runner(runner, pretrained)
env.unwrapped.platform_initialization = initialization
if rank == 0:
(log_dir / "initialization.json").write_text(
json.dumps(initialization, ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
)
print(f"[INFO] Warm-start actor from source {initialization['source_id']}; fresh critic/optimizer, iteration=0")
if resume_path is not None:
print(f"[INFO]: Loading model checkpoint from: {resume_path}")
runner.load(str(resume_path), map_location=device)
# Promotion copies the origin manifest, never the original actor weights.
origin_path = resume_path.parent / "initialization.json"
if origin_path.is_file():
if origin_path.stat().st_size > 64 * 1024:
raise ValueError("Initialization provenance exceeds 64 KiB")
origin = json.loads(origin_path.read_text(encoding="utf-8"))
if not isinstance(origin, dict) or origin.get("mode") != "pretrained-warm-start":
raise ValueError("Invalid initialization provenance")
env.unwrapped.platform_initialization = origin
if rank == 0:
(log_dir / "initialization.json").write_text(json.dumps(origin, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
if explicit_resume:
# RSL-RL stores the last completed zero-based iteration and otherwise
# repeats it after load. Explicit tuning promotion uses an absolute target.
@@ -260,7 +367,7 @@ def main():
# Parse first argument to choose the task.
# Import tasks to populate the registry.
import mjlab.tasks # noqa: F401
import src.tasks
import src.tasks # noqa: F401
all_tasks = list_tasks()
chosen_task, remaining_args = tyro.cli(
@@ -0,0 +1,48 @@
"""Service-owned CPU validator; JSON stdin is never accepted directly from HTTP."""
import json
import sys
from dataclasses import asdict
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
for root in (ROOT, ROOT.parent):
sys.path.insert(0, str(root))
def main():
from pretrained import read_pretrained_source
from task_config import OBSTACLE_TASK
from src.tasks.velocity.config.go2.env_cfgs import unitree_go2_flat_env_cfg
from src.tasks.velocity.config.go2.rl_cfg import unitree_go2_ppo_runner_cfg
payload = json.loads(sys.stdin.read(128 * 1024))
task_id = payload["taskId"]
if task_id == "Unitree-Go2-Flat":
cfg = unitree_go2_flat_env_cfg()
elif task_id == OBSTACLE_TASK:
from src.tasks.obstacle_avoidance.env_cfg import unitree_go2_obstacle_env_cfg, apply_obstacle_configuration
cfg = unitree_go2_obstacle_env_cfg()
if payload.get("taskConfig") is not None:
apply_obstacle_configuration(cfg, payload["taskConfig"])
else:
raise ValueError("基础策略不兼容该任务;支持Flat47与Obstacle81/97,不支持Rough")
directory = Path(payload["directory"])
options = {}
if payload.get("uploaded"):
from pretrained_upload import read_uploaded_source
read_pretrained_source = read_uploaded_source
options["manifest_path"] = directory / "upload.json"
source = read_pretrained_source(
directory / payload["checkpoint"], allowed_roots=[directory], target_env=asdict(cfg),
target_agent=asdict(unitree_go2_ppo_runner_cfg()), **options,
)
print(json.dumps(source.manifest))
if __name__ == "__main__":
try:
main()
except Exception as error:
print(f"基础策略不兼容或缺少配套文件/依赖:{error}", file=sys.stderr)
sys.exit(1)
@@ -0,0 +1,43 @@
"""Resource-limited CPU single-file decoder; no network or neighboring model lookup."""
import contextlib
import json
import os
import resource
import sys
from pathlib import Path
# Apply limits before importing tensor/protobuf runtimes. Thread env also set by parent.
resource.setrlimit(resource.RLIMIT_CPU, (40, 40))
resource.setrlimit(resource.RLIMIT_AS, (8 * 1024**3, 8 * 1024**3))
resource.setrlimit(resource.RLIMIT_FSIZE, (16 * 1024**2, 16 * 1024**2))
resource.setrlimit(resource.RLIMIT_NOFILE, (64, 64))
resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
os.environ["CUDA_VISIBLE_DEVICES"] = ""
ROOT = Path(__file__).resolve().parents[1]
for root in (ROOT, ROOT.parent):
sys.path.insert(0, str(root))
if __name__ == "__main__":
try:
payload = json.loads(sys.stdin.read(4096))
with contextlib.redirect_stdout(sys.stderr):
import torch
from pretrained_upload import import_upload
torch.set_num_threads(1)
manifest = import_upload(
payload["path"], payload["format"], payload["template"], payload["directory"]
)
print(json.dumps(manifest))
except Exception as error:
# Do not echo untrusted pickle/tensor names or absolute local paths.
from pretrained import PretrainedError
message = (
str(error)
if isinstance(error, PretrainedError)
else "文件解析失败;请提供受支持的自包含Go2 legacy47 actor"
)
print(json.dumps({"error": message[:500]}))
sys.exit(1)