-
Notifications
You must be signed in to change notification settings - Fork 919
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
best practices / example for parameter sweep with batch runner #1915
Comments
This is my solution. I also did not find a good documentation. I wrote a small script that runs the batch through several parameter ranges. The result is saved as a JSONL. All parameters are part of the reported result. Depending on how you configure the data-collector you get the additional output from the model run, see e.g. the letter pruning here. So you could run the analysis on the reported data-collector outputs, or select a subset of the results via e.g. pandas.query() and run the analysis only on that. The script takes quite a while to run, to I put it on a server in a tmux session and come back a day later.. #!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Run historicallettters model for data creation."""
from datetime import datetime
import pandas as pd
import mesa
from scicom.historicalletters.model import HistoricalLetters
for population in [400,300,200,100]:
starttime = datetime.now()
params = {
"population": population,
"similarityThreshold": 1,
"longRangeNetworkFactor": 0.3,
"shortRangeNetworkFactor": 0.8,
"updateTopic": 0.1,
"moveRange": [0.25, 0.5, 0.75, 0.9],
"letterRange": [0.25, 0.5, 0.75, 0.9],
"useActivation": [True, False],
"useSocialNetwork": [True, False],
"tempfolder": "./initialConditions/"
}
results = mesa.batch_run(
HistoricalLetters,
parameters=params,
iterations=10,
max_steps=200,
number_processes=48,
data_collection_period=-1,
display_progress=True,
)
now = datetime.now()
date_time = now.strftime("%d_%m_%Y-%H_%M_%S")
print(f"Runtime: {(now - starttime)}")
df = pd.DataFrame(results)
df.to_json(f"./Run_N_{population}_{date_time}.json", orient="records", lines=True) The output dataframe has the columns: ['RunId', 'iteration', 'Step', 'population', 'similarityThreshold',
'longRangeNetworkFactor', 'shortRangeNetworkFactor', 'updateTopic',
'moveRange', 'letterRange', 'useActivation', 'useSocialNetwork',
'tempfolder', 'Ledger'] where the last one is the actual output of the model run. |
@maltevogl thanks for sharing your approach, super helpful! I hadn't thought about including important parameters in the model data collection - they would be redundant if you're collecting multiple rounds, but if you only collect data at the end it would be a good solution. Running a large number of batches is pretty slow for me too, even though it doesn't take so many rounds for individual simulations to stabilize. It would be nice if there was a way to chunk out the different parameter combinations and run mulltiple jobs on a HPC cluster. Using jsonl would make it pretty easy to combine the results, too. |
Have you checked out the batch run section of the tutorial? That's a good start. There's also the Bank Reserves Model has an example of the
If you're going into the direction of global sensitivity analysis / robustness, checkout the EMAworkbench! Here's an example of using it with a Mesa model.
Still in the draft stage, but I'm working on a built-in function for local sensitivity analysis. See #1908.
Still experimental, but the EMAworkbench supports this (using our brand new MPIEvaluator). |
@EwoutH thanks for the response and the links. Yes, I started with the batch run tutorial in the mesa documentation and I have been using it successfully, but now I want to scale up a bit in terms of the number of options for several parameters. My problem is that the batch runner doesn't generate a report of which combination of parameters were used for specific runs. I'll check out your EMAworkbench. |
@rlskoeser I ran into this same issue, and what I ended up doing is reading a parameters dictionary from a json file rather than writing to it. Basically, I write a json config file that gives an experiment name and an output directory, along with the parameters for that experiment, then I create that directory and store a copy of the config file with all the output. Here's my run.py. Hope that's helpful. I'd be happy to answer any questions if you want to reach out. |
Thanks for chiming in, @shanedicks - good to know others have run into this and worked around it, and to have another example to look at. (FYI: your link has a typo if you want to edit and correct) I made some progress on this last week and figured some things out, but I didn't get the time to write up any notes here. I figured out that the default data collection does include the configured parameters in the output (which I feel like I should have noticed, but I think it wasn't documented; certainly I wasn't looking for it there when I was first doing data analysis). It also smashes together agent data, model, data, and parameters - which results in significant duplication even if you're only collecting data once for each simulation when it ends. The default batch runner holds all of this smushed-up collected data in memory until it can be returned, so it's simply not scalable for the number of parameters and runs I'm trying to do. I've written a custom batch runner, which uses some of the internal/undocumented mesa batch run methods for constructing parameter combinations and borrows some of the multiprocessing approach, but writes out to separate model and agent data files as it runs the simulations. I'm following the mesa approach and including the parameters in the model data for convenience. I've successfully run my new script on our HPC cluster, and am testing now running it as an array job so I can easily increase the number of iterations for each configuration by spreading it across multiple tasks/runs. I've been thinking about how the mesa batch runner could be refactored to make it more extensible - a class-based batch runner would make it easier to reuse some of the existing internal functionality (generating parameters combinations, multiprocessing, model+parameter data) and allow customizing e.g. how the data is collected and output. |
Would love to see a proposal or draft implementation for a more efficient, flexible and/or performant batch runner. Personally I would find a way to do other configurations than full-factorial very useful. The batch runner was already redesigned once and a lot of discussion has been around it, so it might be nice to make sure those lessons learned are not forgotten. |
@rlskoeser thanks for pointing out the typo. I have corrected it. I have definitely run into the "all results stored in memory problem" so I made a Controller class to run a custom batch runner and a Manager class which is a sort of custom data collector, only it writes to a database instead of holding data in memory. I've been up and running on our HPC cluster for about a year now. I foolishly started with Sqlite as a database because I didn't want to bother with setting up Postgres or something, but I have really struggled with the inability to have concurrent connections. |
|
On 1 and 2: Agreed, but Mesa should support a (clearly documented) end-to-end workflow in my opinion. It doesn't need to rebuilt everything itself, but it doesn't need to connect to the most used components in the modelling world and have clearly documented how to use them. (but this is an separate topic, so I will open a new discussion for it) |
I agree that it would be good to have an example in the documentation showing an entire workflow, including some other tools that can be used as part of this (e.g., SALib). On data storage of experiments (so separate from individual-run data collection), I would be fine with providing a well-documented hook that users could use to do their custom preferred data storage. |
My question would be, if I want to follow best practice regarding with sensitivity analysis, I should use EMA workbench or SALib instead of |
I have a slightly different take. Current batch_run implicitly uses a factorial design over the specified parameters. Rather than implementing other experimental designs within MESA, generate the design with other tools and pass it to |
I'm going to dump an email here I recently send to some TU Delft professors/teachers, which is somewhat relevant to this disucssions.
|
In case another example is useful, here's my custom batch run code: https://github.com/Princeton-CDH/simulating-risk/blob/main/simulatingrisk/hawkdovemulti/batch_run.py I reused a couple of the internal The main change is that my batch runner outputs data as model runs complete rather than storing everything in memory and writing it all out at the end. I also have a slurm script for this batch runner here https://github.com/Princeton-CDH/simulating-risk/blob/main/simulatingrisk/hawkdovemulti/simrisk_batch.slurm I set it up so I can use arrays of tasks to generate data for multiple runs of the same set of parameters and then combine the resulting csvs when doing data analysis. I started wanting to do pairwise statistical testing on specific parameters, so I came up with named options for parameter combinations with smaller subsets - those sets of parameter combinations are small enough I can run locally, but it's nice to have an option to run it in an HPC environment. I think that making the batch runner class-based with a few built in options or mixins, e.g. for generating parameter options and saving results, would make it easier to extend and customize. |
Having a spec for parameter ranges might help and be sort-of related to this issue: Would like some good docs for this for 3.0. |
Are there any examples or best practices for doing parameter sweep type analysis with the batch runner?
I'd like to be able to do a large batch run with a number of varying parameters, and then run some analyses on the results - some across all variants, but also isolating specific runs where a single parameter varies.
I don't see any to get the parameters out of the batch runner so that I can figure out which runs where initialized with which parameters. Is this not yet supported? I think a simple csv of run id and parameters would be sufficient for the analysis I want to do.
I found this example of running a parameter sweep in a notebook, but it wasn't quite enough to get me to what I want to do: https://github.com/projectmesa/mesa-schelling-example/blob/master/analysis.ipynb
I also see there are some other open issues related to batch running and parameters, but I'm not sure how related they are to what I'm asking for. This one seems the most similar:
The text was updated successfully, but these errors were encountered: