Integrating MongoDB MVP#383
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR integrates a MongoDB-backed MVP for persisting and replaying Ax optimization trials for the Branin benchmark.
- Adds a MongoDB client connection and collections for storing experiments and trials
- Implements loading of existing trials and replaying them before starting new ones
- Persists each completed trial back to MongoDB
Comments suppressed due to low confidence (1)
scripts/mongodbintegrationmvp.py:22
- [nitpick] The variable name
tmongo_clientis unclear. Rename it tomongo_clientfor better readability and consistency.
tmongo_client = MongoClient("mongodb://localhost:27017/")
|
@Gawthaman thanks! Could you have a look at facebook/Ax#2975 (comment)? I think you may need to use the built in serialization function directly. |
…nd enhance MongoDB integration
scripts/mongodbintegrationmvp.py
Outdated
| remaining_sobol = max(SOBOL_TRIALS - n_existing, 0) | ||
|
|
||
| if remaining_sobol > 0: | ||
| generation_strategy = GenerationStrategy([ |
There was a problem hiding this comment.
Please give this a try using Ax's built-in serialization method (save_to_json_file) and using the JSON snapshots that get sent to and reloaded from. In the current implementation, it doesn't preserve the Sobol sequence.
It should follow the same logic in https://colab.research.google.com/drive/1A2p1oUSsD8Edlu2haaB-FBSSjim3HTLS?usp=sharing
Please also add to each MongoDB document the timestamp of when the snapshot is saved and use the timestamp to load the most recent snapshot each time.
sgbaird
left a comment
There was a problem hiding this comment.
Not sure if you were ready for a review, but I took a look and made a couple comments. Makes sense to have the save and load helper functions. Overall the logic looks solid to me
scripts/mongodbintegrationmvp.py
Outdated
|
|
||
| if ax_client is None: | ||
| # Create new experiment | ||
| generation_strategy = create_generation_strategy(SOBOL_TRIALS) |
…ration strategy creation
sgbaird
left a comment
There was a problem hiding this comment.
@Gawthaman nice! Overall seems to capture the right logic. Many print statements and try-excepts can be removed. See other comments.
Once addressed, could you record a video demonstrating the usage of the script and share? (Running the campaign, artificially/ungracefully stopping partway at different points in the script, restarting it, etc.).
Probably upload to YouTube as unlisted and paste here (video would probably be over 100 MB). Narrated or unnarrated is fine.
scripts/mongodbintegrationmvp.py
Outdated
| except errors.ServerSelectionTimeoutError: | ||
| print("Failed to connect to MongoDB. Is MongoDB running?") | ||
| exit(1) | ||
| except Exception as e: |
There was a problem hiding this comment.
Do we need this explicit error handling? Seems like it would just bubble up naturally (unless you found that the error that bubbled up naturally was non-descript).
As a note for later, we'll set this up with a MongoDB Atlas cluster
scripts/mongodbintegrationmvp.py
Outdated
| SOBOL_TRIALS = 5 | ||
|
|
||
|
|
||
| def save_ax_snapshot_to_mongodb(ax_client, experiment_name): |
There was a problem hiding this comment.
Should this return the database ID of the snapshot?
scripts/mongodbintegrationmvp.py
Outdated
| if record: | ||
| # Save snapshot data to temporary file | ||
| temp_file = f"temp_{experiment_name}_snapshot.json" | ||
| with open(temp_file, "w") as f: |
There was a problem hiding this comment.
How does this handle the case where the file already exists? Worth adding the database ID to the filename in addition to experiment name to avoid write conflicts?
scripts/mongodbintegrationmvp.py
Outdated
|
|
||
|
|
||
| # Load existing experiment or create new one | ||
| ax_client = load_ax_snapshot_from_mongodb(obj1_name) |
There was a problem hiding this comment.
Probably don't reuse obj1_name like this. Instead define a separate variable. Could incorporate obj1_name via f-string. Probably good to also add a hard-coded set of 4 characters (randomly generated externally) to it.
scripts/mongodbintegrationmvp.py
Outdated
| max_parallelism=5, | ||
| model_kwargs={"seed": 999}, # For reproducibility | ||
| ), | ||
| GenerationStep( |
There was a problem hiding this comment.
Since you're using snapshots, probably ok to leave to defaults and not specify an explicit generation strategy
scripts/mongodbintegrationmvp.py
Outdated
| print(f"Created new experiment with {SOBOL_TRIALS} Sobol trials") | ||
|
|
||
| # Save initial snapshot | ||
| save_ax_snapshot_to_mongodb(ax_client, obj1_name) |
There was a problem hiding this comment.
Same comment about experiment name
scripts/mongodbintegrationmvp.py
Outdated
| print("Resuming existing experiment") | ||
|
|
||
| # Get current trial count to determine how many more trials to run | ||
| current_trials = ax_client.get_trials_data_frame() |
There was a problem hiding this comment.
Ah, good point about needing to handle max trials in the for loop. I suppose an alternative would be to have a budget variable stored in MongoDB that gets updated, but ignore that for now. More of a note to self/musing.
scripts/mongodbintegrationmvp.py
Outdated
| f"Trial {trial_index}: result={results:.3f} | " | ||
| f"Best so far: {best_value:.3f}" | ||
| ) | ||
| except Exception: |
There was a problem hiding this comment.
Probably remove these try-excepts
scripts/mongodbintegrationmvp.py
Outdated
| print(f"Total trials completed: {len(trials_df)}") | ||
| print(f"Best objective value: {trials_df[obj1_name].min():.6f}") | ||
|
|
||
| except Exception as e: |
There was a problem hiding this comment.
Again, no need for try except here
scripts/mongodbintegrationmvp.py
Outdated
| print(f"Error getting best parameters: {e}") | ||
|
|
||
| # Clean up MongoDB connection | ||
| mongo_client.close() |
There was a problem hiding this comment.
Good to close the client. I suppose a top-level try-except-finally could be implemented at some point if we find that lots of mongo connections are being leftover during restarts, but I think probably not an issue. Again, just a musing
|
Also good to switch to using a free-tier MongoDB Atlas instance prior to recording video, and showing the snapshots in the cloud. Some (not all) bits from https://github.com/ACC-HelloWorld/5-data-logging#mongodb-setup-and-github-secrets can help with setup. Ignore AWS setup for example. |
sgbaird
left a comment
There was a problem hiding this comment.
Nice! Made a few comments. Overall looks clean and minimal. Thanks for making the edits I requested related to unwrapping
Please put this under a subdir called hitl-bo within https://github.com/Gawthaman/ac-training-lab/tree/main/scripts
There was a problem hiding this comment.
Effectively a helper script for your demo video, correct?
| return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length)) | ||
|
|
||
|
|
||
| def get_user_choice(): |
There was a problem hiding this comment.
I.e., if the script gets interrupted after a user has already begun their wetlab experiment, they get to say whether they want to provide an update of results from the previous experiment or not?
There was a problem hiding this comment.
Yes, whenever the script is run, it asks the user if they want to start a new experiment or continue from the last experiment. It mainly works as a workaround since I dont think the script knows if the last experiment was finished or not, so if it was finished, then the user can just start a new experiment. It also gives the user the chance to start a new experiment from scratch if they decide they don't want to start from the last (interrupted) experiment.
|
|
||
|
|
||
| # Get user choice and setup configuration | ||
| user_choice = get_user_choice() |
There was a problem hiding this comment.
Oh, nvm - experiment in the Ax definition (i.e., the whole campaign). This seems fine.
| print(f"Trial {trial_index}: x1={x1:.3f}, x2={x2:.3f}") | ||
|
|
||
| # Save snapshot before running experiment (preserves pending trial) | ||
| save_ax_snapshot_to_mongodb(ax_client, experiment_id) |
There was a problem hiding this comment.
Good that you're doing before and after snapshots. Not sure if we'd want to distinguish this difference in the payload (or filename)
scripts/mongodbintegrationmvp.py
Outdated
| obj1_name = "branin" | ||
| MAX_TRIALS = 19 # Configuration constant | ||
|
|
||
| # Experiment identifier (separate from objective name) |
There was a problem hiding this comment.
| # Experiment identifier (separate from objective name) | |
| # Experiment identifier (separate from objective name) with hardcoded unique ID |
scripts/test_persistence.py
Outdated
|
|
||
| # Start the experiment as a subprocess | ||
| process = subprocess.Popen([ | ||
| sys.executable, "Untitled-1.py" |
There was a problem hiding this comment.
I see lots of references to "Untitled-1.py"
There was a problem hiding this comment.
I was initially working on this in a different temp folder to test out different scenarios, but I forgot to fix file names, I'll fix that asap
…ation scripts - Deleted old persistence testing scripts: simple_test.py and test_persistence.py. - Added new scripts for manual and automated testing of MongoDB persistence: check_persistence.py, mongodbintegration.py, mongodbintegrationmvp.py, simple_test.py, and test_persistence.py. - Enhanced MongoDB connection handling and error reporting in all scripts. - Implemented functionality to save and load experiment snapshots to/from MongoDB. - Added user prompts for experiment continuation or creation in the new integration scripts. - Improved trial management and recovery testing in the new test scripts.
…in persistence testing scripts
sgbaird
left a comment
There was a problem hiding this comment.
We'll also need to think about what happens if someone decides to change the optimization setup later on. For example, adding a new parameter to explore. This script more or less assumes that the campaign doesn't change from start to end, and likewise that it starts from scratch (that's fine, this is meant to be a MWE, and the ability to stop/restart is well-received).
The previously saved AxClient objects will serve as a record regardless. While using these as checkpoints is convenient, it also reduces some of the flexibility.
Integrating MongoDB MVP