Vladprime commited on
Commit
449ddfe
·
verified ·
1 Parent(s): 4fd4e82

Add trained Unity PPO policy, ONNX export and independent evaluation

Browse files
README.md CHANGED
@@ -1,3 +1,29 @@
1
  ---
2
  license: apache-2.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: apache-2.0
3
+ tags:
4
+ - reinforcement-learning
5
+ - ml-agents
6
+ - ML-Agents-SnowballTarget
7
+ - deep-rl-class
8
+ model-index:
9
+ - name: SnowballTarget
10
+ results:
11
+ - task:
12
+ type: reinforcement-learning
13
+ name: Reinforcement Learning
14
+ dataset:
15
+ name: ML-Agents-SnowballTarget
16
+ type: ML-Agents-SnowballTarget
17
+ metrics:
18
+ - type: mean_reward
19
+ name: mean_reward
20
+ value: 6.6364 +/- 2.0790
21
  ---
22
+
23
+ # SnowballTarget
24
+
25
+ Trained from random initialization for Vlad Kolomiiets (`Vladprime`) using the official Hugging Face Deep RL Course Unity environment and Unity ML-Agents 1.1.0. No pretrained weights were used. This is an initial training run; it is not claimed to have converged. Units 5 and 7 require publishing a trained model without a reward threshold.
26
+
27
+ Training: ppo, 30000 configured environment steps, seed 20261101. Export: genuine ML-Agents ONNX plus PyTorch checkpoint and trainer configuration. Separate ONNX evaluation process: seed 1000000; 44 completed agent episodes, mean 6.6364, standard deviation 2.0790. Original environment, action and observation spaces were retained. See results.json for individual returns and evaluation limitations.
28
+
29
+ The .onnx file is compatible with the course Unity browser viewer. Training/evaluation code is in train-unity.py; the exact run configuration is config.yaml.
SHA256SUMS.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "SnowballTarget-checkpoint.pt": "83b5a60eb00fe520f410e7ced9eb5084663eccbf1e34403ebd91c6db51888a36",
3
+ "README.md": "b485bb2e9cf53f40dd557ac0936a950c5f6d62f3922dd357d722859f1ac57b09",
4
+ "events.out.tfevents.1793538312.e5108cd25e8e.9150.0": "36a5560ceededfa54e1c897c3260cf4c61b46a769d70f9d80aad9a9518b72abd",
5
+ "results.json": "2a91a74f6bd1c305e17e985b7b4d98688fecf43aad7f23f0cd412d8baa888f48",
6
+ "SnowballTarget.onnx": "badddcb40788d91b862314b5c0478a20c8d4306e1eaa92b18dc7ea51883b0272",
7
+ "config.yaml": "20bfc88158086a8458c13de10f3fc86790d7969cb44b63b3add2ee6ae2b85350",
8
+ "train-unity.py": "dce2bd5169df6422af51eecf3ef243b4af8aa08444152598fefa600859462b59"
9
+ }
SnowballTarget-checkpoint.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:83b5a60eb00fe520f410e7ced9eb5084663eccbf1e34403ebd91c6db51888a36
3
+ size 1542626
SnowballTarget.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:badddcb40788d91b862314b5c0478a20c8d4306e1eaa92b18dc7ea51883b0272
3
+ size 264598
config.yaml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ behaviors:
2
+ SnowballTarget:
3
+ trainer_type: ppo
4
+ hyperparameters:
5
+ learning_rate: 0.0003
6
+ learning_rate_schedule: linear
7
+ batch_size: 128
8
+ buffer_size: 2048
9
+ beta: 0.005
10
+ epsilon: 0.2
11
+ lambd: 0.95
12
+ num_epoch: 3
13
+ network_settings:
14
+ normalize: false
15
+ hidden_units: 128
16
+ num_layers: 2
17
+ reward_signals:
18
+ extrinsic:
19
+ gamma: 0.99
20
+ strength: 1.0
21
+ max_steps: 30000
22
+ time_horizon: 64
23
+ summary_freq: 2000
24
+ checkpoint_interval: 10000
25
+ keep_checkpoints: 2
events.out.tfevents.1793538312.e5108cd25e8e.9150.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:36a5560ceededfa54e1c897c3260cf4c61b46a769d70f9d80aad9a9518b72abd
3
+ size 29080
results.json ADDED
@@ -0,0 +1,321 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "mean_reward": 6.636363636363637,
3
+ "std_reward": 2.0790175683689585,
4
+ "mean_minus_std": 4.557346067994678,
5
+ "n_eval_agent_episodes": 44,
6
+ "episode_rewards": [
7
+ 6.0,
8
+ 7.0,
9
+ 10.0,
10
+ 10.0,
11
+ 5.0,
12
+ 4.0,
13
+ 4.0,
14
+ 4.0,
15
+ 8.0,
16
+ 4.0,
17
+ 7.0,
18
+ 7.0,
19
+ 9.0,
20
+ 5.0,
21
+ 9.0,
22
+ 6.0,
23
+ 7.0,
24
+ 6.0,
25
+ 6.0,
26
+ 6.0,
27
+ 7.0,
28
+ 5.0,
29
+ 5.0,
30
+ 4.0,
31
+ 9.0,
32
+ 0.0,
33
+ 7.0,
34
+ 6.0,
35
+ 6.0,
36
+ 11.0,
37
+ 10.0,
38
+ 5.0,
39
+ 7.0,
40
+ 9.0,
41
+ 7.0,
42
+ 9.0,
43
+ 7.0,
44
+ 5.0,
45
+ 6.0,
46
+ 9.0,
47
+ 7.0,
48
+ 7.0,
49
+ 6.0,
50
+ 8.0
51
+ ],
52
+ "records": [
53
+ {
54
+ "behavior": "SnowballTarget?team=0",
55
+ "agent_id": 0,
56
+ "reward": 6.0,
57
+ "interrupted": true
58
+ },
59
+ {
60
+ "behavior": "SnowballTarget?team=0",
61
+ "agent_id": 1,
62
+ "reward": 7.0,
63
+ "interrupted": true
64
+ },
65
+ {
66
+ "behavior": "SnowballTarget?team=0",
67
+ "agent_id": 2,
68
+ "reward": 10.0,
69
+ "interrupted": true
70
+ },
71
+ {
72
+ "behavior": "SnowballTarget?team=0",
73
+ "agent_id": 3,
74
+ "reward": 10.0,
75
+ "interrupted": true
76
+ },
77
+ {
78
+ "behavior": "SnowballTarget?team=0",
79
+ "agent_id": 4,
80
+ "reward": 5.0,
81
+ "interrupted": true
82
+ },
83
+ {
84
+ "behavior": "SnowballTarget?team=0",
85
+ "agent_id": 5,
86
+ "reward": 4.0,
87
+ "interrupted": true
88
+ },
89
+ {
90
+ "behavior": "SnowballTarget?team=0",
91
+ "agent_id": 6,
92
+ "reward": 4.0,
93
+ "interrupted": true
94
+ },
95
+ {
96
+ "behavior": "SnowballTarget?team=0",
97
+ "agent_id": 7,
98
+ "reward": 4.0,
99
+ "interrupted": true
100
+ },
101
+ {
102
+ "behavior": "SnowballTarget?team=0",
103
+ "agent_id": 8,
104
+ "reward": 8.0,
105
+ "interrupted": true
106
+ },
107
+ {
108
+ "behavior": "SnowballTarget?team=0",
109
+ "agent_id": 9,
110
+ "reward": 4.0,
111
+ "interrupted": true
112
+ },
113
+ {
114
+ "behavior": "SnowballTarget?team=0",
115
+ "agent_id": 10,
116
+ "reward": 7.0,
117
+ "interrupted": true
118
+ },
119
+ {
120
+ "behavior": "SnowballTarget?team=0",
121
+ "agent_id": 0,
122
+ "reward": 7.0,
123
+ "interrupted": true
124
+ },
125
+ {
126
+ "behavior": "SnowballTarget?team=0",
127
+ "agent_id": 1,
128
+ "reward": 9.0,
129
+ "interrupted": true
130
+ },
131
+ {
132
+ "behavior": "SnowballTarget?team=0",
133
+ "agent_id": 2,
134
+ "reward": 5.0,
135
+ "interrupted": true
136
+ },
137
+ {
138
+ "behavior": "SnowballTarget?team=0",
139
+ "agent_id": 3,
140
+ "reward": 9.0,
141
+ "interrupted": true
142
+ },
143
+ {
144
+ "behavior": "SnowballTarget?team=0",
145
+ "agent_id": 4,
146
+ "reward": 6.0,
147
+ "interrupted": true
148
+ },
149
+ {
150
+ "behavior": "SnowballTarget?team=0",
151
+ "agent_id": 5,
152
+ "reward": 7.0,
153
+ "interrupted": true
154
+ },
155
+ {
156
+ "behavior": "SnowballTarget?team=0",
157
+ "agent_id": 6,
158
+ "reward": 6.0,
159
+ "interrupted": true
160
+ },
161
+ {
162
+ "behavior": "SnowballTarget?team=0",
163
+ "agent_id": 7,
164
+ "reward": 6.0,
165
+ "interrupted": true
166
+ },
167
+ {
168
+ "behavior": "SnowballTarget?team=0",
169
+ "agent_id": 8,
170
+ "reward": 6.0,
171
+ "interrupted": true
172
+ },
173
+ {
174
+ "behavior": "SnowballTarget?team=0",
175
+ "agent_id": 9,
176
+ "reward": 7.0,
177
+ "interrupted": true
178
+ },
179
+ {
180
+ "behavior": "SnowballTarget?team=0",
181
+ "agent_id": 10,
182
+ "reward": 5.0,
183
+ "interrupted": true
184
+ },
185
+ {
186
+ "behavior": "SnowballTarget?team=0",
187
+ "agent_id": 0,
188
+ "reward": 5.0,
189
+ "interrupted": true
190
+ },
191
+ {
192
+ "behavior": "SnowballTarget?team=0",
193
+ "agent_id": 1,
194
+ "reward": 4.0,
195
+ "interrupted": true
196
+ },
197
+ {
198
+ "behavior": "SnowballTarget?team=0",
199
+ "agent_id": 2,
200
+ "reward": 9.0,
201
+ "interrupted": true
202
+ },
203
+ {
204
+ "behavior": "SnowballTarget?team=0",
205
+ "agent_id": 3,
206
+ "reward": 0.0,
207
+ "interrupted": true
208
+ },
209
+ {
210
+ "behavior": "SnowballTarget?team=0",
211
+ "agent_id": 4,
212
+ "reward": 7.0,
213
+ "interrupted": true
214
+ },
215
+ {
216
+ "behavior": "SnowballTarget?team=0",
217
+ "agent_id": 5,
218
+ "reward": 6.0,
219
+ "interrupted": true
220
+ },
221
+ {
222
+ "behavior": "SnowballTarget?team=0",
223
+ "agent_id": 6,
224
+ "reward": 6.0,
225
+ "interrupted": true
226
+ },
227
+ {
228
+ "behavior": "SnowballTarget?team=0",
229
+ "agent_id": 7,
230
+ "reward": 11.0,
231
+ "interrupted": true
232
+ },
233
+ {
234
+ "behavior": "SnowballTarget?team=0",
235
+ "agent_id": 8,
236
+ "reward": 10.0,
237
+ "interrupted": true
238
+ },
239
+ {
240
+ "behavior": "SnowballTarget?team=0",
241
+ "agent_id": 9,
242
+ "reward": 5.0,
243
+ "interrupted": true
244
+ },
245
+ {
246
+ "behavior": "SnowballTarget?team=0",
247
+ "agent_id": 10,
248
+ "reward": 7.0,
249
+ "interrupted": true
250
+ },
251
+ {
252
+ "behavior": "SnowballTarget?team=0",
253
+ "agent_id": 0,
254
+ "reward": 9.0,
255
+ "interrupted": true
256
+ },
257
+ {
258
+ "behavior": "SnowballTarget?team=0",
259
+ "agent_id": 1,
260
+ "reward": 7.0,
261
+ "interrupted": true
262
+ },
263
+ {
264
+ "behavior": "SnowballTarget?team=0",
265
+ "agent_id": 2,
266
+ "reward": 9.0,
267
+ "interrupted": true
268
+ },
269
+ {
270
+ "behavior": "SnowballTarget?team=0",
271
+ "agent_id": 3,
272
+ "reward": 7.0,
273
+ "interrupted": true
274
+ },
275
+ {
276
+ "behavior": "SnowballTarget?team=0",
277
+ "agent_id": 4,
278
+ "reward": 5.0,
279
+ "interrupted": true
280
+ },
281
+ {
282
+ "behavior": "SnowballTarget?team=0",
283
+ "agent_id": 5,
284
+ "reward": 6.0,
285
+ "interrupted": true
286
+ },
287
+ {
288
+ "behavior": "SnowballTarget?team=0",
289
+ "agent_id": 6,
290
+ "reward": 9.0,
291
+ "interrupted": true
292
+ },
293
+ {
294
+ "behavior": "SnowballTarget?team=0",
295
+ "agent_id": 7,
296
+ "reward": 7.0,
297
+ "interrupted": true
298
+ },
299
+ {
300
+ "behavior": "SnowballTarget?team=0",
301
+ "agent_id": 8,
302
+ "reward": 7.0,
303
+ "interrupted": true
304
+ },
305
+ {
306
+ "behavior": "SnowballTarget?team=0",
307
+ "agent_id": 9,
308
+ "reward": 6.0,
309
+ "interrupted": true
310
+ },
311
+ {
312
+ "behavior": "SnowballTarget?team=0",
313
+ "agent_id": 10,
314
+ "reward": 8.0,
315
+ "interrupted": true
316
+ }
317
+ ],
318
+ "evaluation_seed": 1000000,
319
+ "eval_datetime": "2026-11-01T13:06:49.043568+00:00",
320
+ "evaluation_kind": "separate Unity process, ONNX policy, completed agent episodes (soccer episodes within a match are correlated)"
321
+ }
train-unity.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Own from-scratch course ML-Agents training, export, independent ONNX evaluation.
2
+ Run inside the compatible Linux environment prepared by colab-setup.py.
3
+ """
4
+ from pathlib import Path
5
+ from datetime import datetime,timezone
6
+ import os,sys,json,subprocess,zipfile,urllib.request,hashlib,shutil,time
7
+ import numpy as np
8
+ import yaml
9
+ ROOT=Path('/content/vlad-deep-rl-unity');ROOT.mkdir(exist_ok=True)
10
+ os.environ['OMP_NUM_THREADS']='1';os.environ['MKL_NUM_THREADS']='1'
11
+ SEED=20261101
12
+
13
+ def download(url,target):
14
+ print('Downloading official course environment',target.name,flush=True)
15
+ urllib.request.urlretrieve(url,target)
16
+ def unzip(path,target):
17
+ target.mkdir(exist_ok=True)
18
+ with zipfile.ZipFile(path) as z:
19
+ for item in z.infolist():
20
+ if not (target/item.filename).resolve().is_relative_to(target.resolve()):raise ValueError('Unsafe zip member')
21
+ z.extractall(target)
22
+ def evaluate_onnx(model,executable,output):
23
+ from mlagents_envs.environment import UnityEnvironment
24
+ from mlagents_envs.base_env import ActionTuple
25
+ from mlagents_envs.side_channel.engine_configuration_channel import EngineConfigurationChannel
26
+ import onnxruntime as ort
27
+ opts=ort.SessionOptions();opts.intra_op_num_threads=1;opts.inter_op_num_threads=1
28
+ session=ort.InferenceSession(str(model),sess_options=opts,providers=['CPUExecutionProvider'])
29
+ print('ONNX inputs',[(i.name,i.shape) for i in session.get_inputs()],flush=True)
30
+ channel=EngineConfigurationChannel();channel.set_configuration_parameters(time_scale=20)
31
+ env=UnityEnvironment(file_name=str(executable),seed=1000000,no_graphics=True,side_channels=[channel],worker_id=10,timeout_wait=120)
32
+ totals={};records=[]
33
+ try:
34
+ env.reset()
35
+ for iteration in range(30000):
36
+ for behavior in env.behavior_specs:
37
+ decision,terminal=env.get_steps(behavior)
38
+ for agent_id in terminal.agent_id:
39
+ step=terminal[agent_id];key=(behavior,int(agent_id));total=totals.pop(key,0.0)+float(step.reward)+float(step.group_reward)
40
+ records.append({'behavior':behavior,'agent_id':int(agent_id),'reward':total,'interrupted':bool(step.interrupted)})
41
+ for agent_id in decision.agent_id:
42
+ step=decision[agent_id];key=(behavior,int(agent_id));totals[key]=totals.get(key,0.0)+float(step.reward)+float(step.group_reward)
43
+ if len(decision):
44
+ inputs={}
45
+ for spec in session.get_inputs():
46
+ if spec.name.startswith('obs_'):inputs[spec.name]=decision.obs[int(spec.name.split('_')[-1])].astype(np.float32)
47
+ elif spec.name=='action_masks':
48
+ inputs[spec.name]=np.concatenate([1-m.astype(np.float32) for m in decision.action_mask],axis=1)
49
+ else:raise ValueError('Unexpected ONNX input '+spec.name)
50
+ values=session.run(None,inputs);values={spec.name:value for spec,value in zip(session.get_outputs(),values)}
51
+ action=ActionTuple()
52
+ if 'continuous_actions' in values:action.add_continuous(values['continuous_actions'].astype(np.float32))
53
+ if 'discrete_actions' in values:action.add_discrete(values['discrete_actions'].astype(np.int32))
54
+ env.set_actions(behavior,action)
55
+ if len(records)>=40:break
56
+ env.step()
57
+ finally:env.close()
58
+ if not records:raise RuntimeError('Independent evaluation completed no episodes')
59
+ rewards=[r['reward'] for r in records];mean=float(np.mean(rewards));std=float(np.std(rewards))
60
+ result={'mean_reward':mean,'std_reward':std,'mean_minus_std':mean-std,'n_eval_agent_episodes':len(rewards),'episode_rewards':rewards,'records':records,'evaluation_seed':1000000,'eval_datetime':datetime.now(timezone.utc).isoformat(),'evaluation_kind':'separate Unity process, ONNX policy, completed agent episodes (soccer episodes within a match are correlated)'}
61
+ output.write_text(json.dumps(result,indent=2));print('EVALUATED',model.name,mean,std,flush=True);return result
62
+
63
+ def card(folder,name,env_tag,result,config):
64
+ meta={'license':'apache-2.0','tags':['reinforcement-learning','ml-agents',env_tag,'deep-rl-class'],'model-index':[{'name':name,'results':[{'task':{'type':'reinforcement-learning','name':'Reinforcement Learning'},'dataset':{'name':env_tag,'type':env_tag},'metrics':[{'type':'mean_reward','name':'mean_reward','value':f"{result['mean_reward']:.4f} +/- {result['std_reward']:.4f}"}]}]}]}
65
+ text='---\n'+yaml.safe_dump(meta,sort_keys=False)+'---\n\n# '+name+'\n\n'
66
+ text+='Trained from random initialization for Vlad Kolomiiets (`Vladprime`) using the official Hugging Face Deep RL Course Unity environment and Unity ML-Agents 1.1.0. No pretrained weights were used. This is an initial training run; it is not claimed to have converged. Units 5 and 7 require publishing a trained model without a reward threshold.\n\n'
67
+ text+=f"Training: {config['trainer_type']}, {config['max_steps']} configured environment steps, seed {SEED}. Export: genuine ML-Agents ONNX plus PyTorch checkpoint and trainer configuration. Separate ONNX evaluation process: seed 1000000; {result['n_eval_agent_episodes']} completed agent episodes, mean {result['mean_reward']:.4f}, standard deviation {result['std_reward']:.4f}. Original environment, action and observation spaces were retained. See results.json for individual returns and evaluation limitations.\n\n"
68
+ if 'Soccer' in env_tag:text+='Offline MA-POCA self-play training and local evaluation only. No ranked AI-vs-AI challenge result is claimed; the course online leaderboard is unavailable. Player episodes within a soccer match are correlated.\n\n'
69
+ text+='The .onnx file is compatible with the course Unity browser viewer. Training/evaluation code is in train-unity.py; the exact run configuration is config.yaml.\n'
70
+ (folder/'README.md').write_text(text)
71
+ (folder/'SHA256SUMS.json').write_text(json.dumps({str(p.relative_to(folder)):hashlib.sha256(p.read_bytes()).hexdigest() for p in folder.rglob('*') if p.is_file() and p.name!='SHA256SUMS.json'},indent=2))
72
+
73
+ def main():
74
+ sources=[('SnowballTarget','https://github.com/huggingface/Snowball-Target/raw/main/SnowballTarget.zip','SnowballTarget/SnowballTarget.x86_64','SnowballTarget.yaml','ML-Agents-SnowballTarget'),('Pyramids','https://huggingface.co/spaces/unity/ML-Agents-Pyramids/resolve/main/Pyramids.zip','Pyramids/Pyramids','PyramidsRND-local.yaml','ML-Agents-Pyramids'),('SoccerTwos','gdrive:1KuqBKYiXiIcU4kNMqEzhgypuFP5_45CL','SoccerTwos.x86_64','SoccerTwos-local.yaml','ML-Agents-SoccerTwos')]
75
+ results=[]
76
+ for name,url,relative,config_name,env_tag in sources:
77
+ zip_path=ROOT/(name+'.zip')
78
+ if not zip_path.exists():
79
+ if url.startswith('gdrive:'):
80
+ import gdown;gdown.download(id=url.split(':')[1],output=str(zip_path),quiet=False)
81
+ else:download(url,zip_path)
82
+ target=ROOT/('env-'+name);unzip(zip_path,target);executable=target/relative;executable.chmod(executable.stat().st_mode|0o111)
83
+ config_path=ROOT/config_name;config=yaml.safe_load(config_path.read_text());behavior=next(iter(config['behaviors'].values()));run_id='vlad-'+name
84
+ command=[str(Path(sys.executable).parent/'mlagents-learn'),str(config_path),'--env='+str(executable),'--run-id='+run_id,'--no-graphics','--seed='+str(SEED),'--torch-device=cpu','--results-dir='+str(ROOT/'training')]
85
+ print('TRAINING',name,' '.join(command),flush=True)
86
+ subprocess.run(command,check=True,env={**os.environ,'OMP_NUM_THREADS':'1','MKL_NUM_THREADS':'1'})
87
+ trained=ROOT/'training'/run_id;model=list(trained.rglob('*.onnx'))
88
+ # Final exported model is at the run root; step checkpoints live in behavior subdirectories.
89
+ model=next((p for p in model if p.parent==trained),model[-1])
90
+ output=ROOT/('publish-'+name);output.mkdir(exist_ok=True)
91
+ shutil.copy2(model,output/(name+'.onnx'))
92
+ for p in trained.rglob('*.pt'):
93
+ if p.name=='checkpoint.pt':shutil.copy2(p,output/(name+'-checkpoint.pt'));break
94
+ shutil.copy2(config_path,output/'config.yaml');shutil.copy2(__file__,output/'train-unity.py')
95
+ logs=list(trained.rglob('events.out.tfevents*'))
96
+ if logs:shutil.copy2(logs[0],output/logs[0].name)
97
+ result=evaluate_onnx(output/(name+'.onnx'),executable,output/'results.json');card(output,name,env_tag,result,behavior)
98
+ shutil.make_archive(str(ROOT/('publish-'+name)),'zip',output)
99
+ results.append({'name':name,**{k:result[k] for k in ['mean_reward','std_reward','mean_minus_std','n_eval_agent_episodes']}})
100
+ (ROOT/'summary.json').write_text(json.dumps(results,indent=2));print('ALL_UNITY_TRAINING_COMPLETE',json.dumps(results),flush=True)
101
+ if __name__=='__main__':main()