|
import pytest |
|
import io |
|
import os |
|
import yaml |
|
from unittest.mock import patch |
|
|
|
from mlagents.trainers.trainer import TrainerFactory |
|
from mlagents.trainers.cli_utils import load_config, _load_config |
|
from mlagents.trainers.ppo.trainer import PPOTrainer |
|
from mlagents.trainers.exception import TrainerConfigError, UnityTrainerException |
|
from mlagents.trainers.settings import RunOptions |
|
from mlagents.trainers.tests.dummy_config import ppo_dummy_config |
|
from mlagents.trainers.environment_parameter_manager import EnvironmentParameterManager |
|
from mlagents.trainers.directory_utils import ( |
|
validate_existing_directories, |
|
setup_init_path, |
|
) |
|
|
|
|
|
@pytest.fixture |
|
def dummy_config(): |
|
return RunOptions(behaviors={"testbrain": ppo_dummy_config()}) |
|
|
|
|
|
@patch("mlagents_envs.base_env.BehaviorSpec") |
|
def test_initialize_ppo_trainer(BehaviorSpecMock, dummy_config): |
|
brain_name = "testbrain" |
|
training_behaviors = {"testbrain": BehaviorSpecMock()} |
|
output_path = "results_dir" |
|
train_model = True |
|
load_model = False |
|
seed = 11 |
|
expected_reward_buff_cap = 1 |
|
|
|
base_config = dummy_config.behaviors |
|
expected_config = ppo_dummy_config() |
|
|
|
def mock_constructor( |
|
self, |
|
brain, |
|
reward_buff_cap, |
|
trainer_settings, |
|
training, |
|
load, |
|
p_seed, |
|
artifact_path, |
|
): |
|
assert brain == brain_name |
|
assert trainer_settings == expected_config |
|
assert reward_buff_cap == expected_reward_buff_cap |
|
assert training == train_model |
|
assert load == load_model |
|
assert p_seed == seed |
|
assert artifact_path == os.path.join(output_path, brain_name) |
|
|
|
with patch.object(PPOTrainer, "__init__", mock_constructor): |
|
trainer_factory = TrainerFactory( |
|
trainer_config=base_config, |
|
output_path=output_path, |
|
train_model=train_model, |
|
load_model=load_model, |
|
seed=seed, |
|
param_manager=EnvironmentParameterManager(), |
|
) |
|
trainers = {} |
|
for brain_name in training_behaviors.keys(): |
|
trainers[brain_name] = trainer_factory.generate(brain_name) |
|
assert "testbrain" in trainers |
|
assert isinstance(trainers["testbrain"], PPOTrainer) |
|
|
|
|
|
def test_handles_no_config_provided(): |
|
""" |
|
Make sure the trainer setup handles no configs provided at all. |
|
""" |
|
brain_name = "testbrain" |
|
no_default_config = RunOptions().behaviors |
|
|
|
no_default_config.set_config_specified(False) |
|
|
|
trainer_factory = TrainerFactory( |
|
trainer_config=no_default_config, |
|
output_path="output_path", |
|
train_model=True, |
|
load_model=False, |
|
seed=42, |
|
param_manager=EnvironmentParameterManager(), |
|
) |
|
trainer_factory.generate(brain_name) |
|
|
|
|
|
def test_load_config_missing_file(): |
|
with pytest.raises(TrainerConfigError): |
|
load_config("thisFileDefinitelyDoesNotExist.yaml") |
|
|
|
|
|
def test_load_config_valid_yaml(): |
|
file_contents = """ |
|
this: |
|
- is fine |
|
""" |
|
fp = io.StringIO(file_contents) |
|
res = _load_config(fp) |
|
assert res == {"this": ["is fine"]} |
|
|
|
|
|
def test_load_config_invalid_yaml(): |
|
file_contents = """ |
|
you: |
|
- will |
|
- not |
|
- parse |
|
""" |
|
with pytest.raises(TrainerConfigError): |
|
fp = io.StringIO(file_contents) |
|
_load_config(fp) |
|
|
|
|
|
def test_existing_directories(tmp_path): |
|
output_path = os.path.join(tmp_path, "runid") |
|
|
|
validate_existing_directories(output_path, False, False) |
|
|
|
with pytest.raises(UnityTrainerException): |
|
validate_existing_directories(output_path, True, False) |
|
|
|
|
|
os.mkdir(output_path) |
|
|
|
with pytest.raises(UnityTrainerException): |
|
validate_existing_directories(output_path, False, False) |
|
|
|
validate_existing_directories(output_path, True, False) |
|
|
|
validate_existing_directories(output_path, False, True) |
|
|
|
|
|
init_path = os.path.join(tmp_path, "runid2") |
|
with pytest.raises(UnityTrainerException): |
|
validate_existing_directories(output_path, False, True, init_path) |
|
os.mkdir(init_path) |
|
|
|
validate_existing_directories(output_path, False, True, init_path) |
|
|
|
|
|
@pytest.mark.parametrize("dir_exists", [True, False]) |
|
def test_setup_init_path(tmpdir, dir_exists): |
|
""" |
|
|
|
:return: |
|
""" |
|
test_yaml = """ |
|
behaviors: |
|
BigWallJump: |
|
init_path: BigWallJump-6540981.pt #full path |
|
trainer_type: ppo |
|
MediumWallJump: |
|
init_path: {}/test_setup_init_path_results/test_run_id/MediumWallJump/checkpoint.pt |
|
trainer_type: ppo |
|
SmallWallJump: |
|
trainer_type: ppo |
|
checkpoint_settings: |
|
run_id: test_run_id |
|
initialize_from: test_run_id |
|
""".format( |
|
tmpdir |
|
) |
|
run_options = RunOptions.from_dict(yaml.safe_load(test_yaml)) |
|
if dir_exists: |
|
init_path = tmpdir.mkdir("test_setup_init_path_results").mkdir("test_run_id") |
|
big = init_path.mkdir("BigWallJump").join("BigWallJump-6540981.pt") |
|
big.write("content") |
|
med = init_path.mkdir("MediumWallJump").join("checkpoint.pt") |
|
med.write("content") |
|
small = init_path.mkdir("SmallWallJump").join("checkpoint.pt") |
|
small.write("content") |
|
|
|
setup_init_path(run_options.behaviors, init_path) |
|
assert run_options.behaviors["BigWallJump"].init_path == big |
|
assert run_options.behaviors["MediumWallJump"].init_path == med |
|
assert run_options.behaviors["SmallWallJump"].init_path == small |
|
else: |
|
|
|
with pytest.raises(UnityTrainerException): |
|
setup_init_path( |
|
run_options.behaviors, run_options.checkpoint_settings.maybe_init_path |
|
) |
|
|