[tune] multiple runs with same hyperparameter, different random seed

I want to re-run the same experiment multiple times with the same hyperparameters and different random seeds, so I can average the results over multiple runs. How can I do this? I can’t use a Repeater because that requires a Search algorithm (which I don’t need, I’m just randomly sampling 40 hyperparameter sets from the search space).

Maybe try tune.run(config={"seed": tune.grid_search([1, 2, 3, 4,5])})

You’ll have to average the results manually though.

I want tune to generate the other hyperparameters for me. Image

from ray import tune

config = dict(
    feature_dim=tune.choice([128, 256]),
    lr=tune.choice([0.001, 0.0001]),
    seed=tune.grid_search([1, 2, 3, 4, 5])
)

def trainable(config):
    print(config)
    tune.report(score=0)

tune.run(trainable, config=config, num_samples=10)

When running this, while seed is grid-searched over, the other hyperparameters are sampled randomly. Instead, I would want the same hyperparameters repeated 5 times except for the seed which changes. I would want to generate 10 hyperparameter sets (except for the seed), resulting in 50 runs in total. I can handle the averaging myself post-hoc.

Also, the Repeater search won’t work for me because I report two different metrics at different points, and no search algorithm that can be wrapped by repeater works with that setup… And I don’t understand how to subclass the Searcher class to generate configurations (not well documented and code for the search algorithms changes a lot).

Alright, after a lot of thinking I ended up deciding that, for my use case, I can generate the hyperparameter variants myself and ask ray to run them in parallel by just using a nested config with a grid search. I just do a grid search over all the generated variants to let ray handle parallelism, gpu assignment, etc.

from ray import tune
import copy

def generate_variants(config, num_seeds=1, num_samples=1, seed_generator = lambda seed: seed):
    """
    Requires all elements to have a .sample method, like tune.choice etc. 
    cannot be grid_search.
    Given a config of hyperparameters, generates num_samples of them by 
    calling .sample on each hyperparameter seach space. Then makes 
    num_seeds copies of these, each with a different random seed.
    """
    variants = []
    for sample in range(num_samples):
        variant = {param:space.sample() for param, space in config.items()}
        for seed in range(num_seeds):
            seeded_variant = copy.deepcopy(variant)
            seeded_variant['seed'] = seed_generator(seed)
            variants.append(seeded_variant)
    return tune.grid_search(variants)

hyperparams = dict(
    feature_dim=tune.choice([128, 256]),
    lr=tune.choice([0.001, 0.0001]),
)

config = generate_variants(hyperparams, num_seeds=3, num_samples=2)

def trainable(config):
    tune.report(**config)

tune.run(trainable, config=config)