sacred icon indicating copy to clipboard operation
sacred copied to clipboard

Call captured functions from code & nicer interface to mongodb

Open berleon opened this issue 8 years ago • 2 comments

Hello developers of sacred! First, I want to thank you for this awesome project. It is so helpful for keeping track of your experiments.

I currently run my experiments from the commandline and want to access the results using an ipython notebook.

However, it is not clear to me how to load an experiment and how to call captured functions. Here, my current approach and why it does not work.

My experiment file:

ex = Experiment()

@ex.capture
def create_model(model_hyperparams, datset):
    // build model
    return model

....

In the Jupyter Notebook, I would like to create the model from the save config:

from my_experiment import create_model

client = MongoClient()
db = client.get_database('sacred')
exps = db.runs.find({
    'experiment.name': {'$eq': 'train_area_models_4x4'},
    'config.use_conditional': False,
    'status': 'COMPLETED',
})
ex = next(exps)

create_model(
    model_hyperparams=ex['config']['model_hyperparams'],
    dataset=ex['config']['dataset'],
    ...
)

Then I get this error:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-40-e5e56d899fb3> in <module>()
      3     dataset=ex['config']['dataset'],
      4     use_conditional=ex['config']['use_conditional'],
----> 5     encoder=ex['config']['encoder'],
      6 )

~/.local/lib/python3.5/site-packages/wrapt/wrappers.py in __call__(self, *args, **kwargs)
    521
    522         return self._self_wrapper(self.__wrapped__, self._self_instance,
--> 523                 args, kwargs)
    524
    525 class BoundFunctionWrapper(_FunctionWrapperBase):

~/.local/lib/python3.5/site-packages/sacred/config/captured_function.py in captured_function(wrapped, instance, args, kwargs)
     42     args, kwargs = wrapped.signature.construct_arguments(args, kwargs, options,
     43                                                          bound)
---> 44     wrapped.logger.debug("Started")
     45     start_time = time.time()
     46     # =================== run actual function =================================

AttributeError: 'NoneType' object has no attribute 'debug'

It seems to me that the experiment must be running to call captured functions. I guess a simple fix to this problem would be to change sacred/config/captured_function.py:

@wrapt.decorator
def captured_function(wrapped, instance, args, kwargs):
    options = FallbackDict(
        wrapped.config,
        _config=wrapped.config,
        _log=wrapped.logger,
        _run=wrapped.run
    )
    if wrapped.uses_randomness:  # only generate _seed and _rnd if needed
        options['_seed'] = get_seed(wrapped.rnd)
        options['_rnd'] = create_rnd(options['_seed'])

    bound = (instance is not None)
    args, kwargs = wrapped.signature.construct_arguments(args, kwargs, options,
                                                         bound)
    if wrapped.logger is not None:
        wrapped.logger.debug("Started")
        start_time = time.time()
    # =================== run actual function =================================
    result = wrapped(*args, **kwargs)
    # =========================================================================
    if wrapped.logger is not None:
        stop_time = time.time()
        elapsed_time = timedelta(seconds=round(stop_time - start_time))
        wrapped.logger.debug("Finished after %s.", elapsed_time)

    return result

A even nicer thing would be to have a sacred interface to MongoDB like this:

// wrapper of mongodb
db = SacredMongoDB()
db.find({
    'experiment.name': {'$eq': 'train_area_models_4x4'},
    'config.use_conditional': False,
})

# get one experiment
ex = next(db)

# access config
ex.config.use_conditional           # -> False

# access experiment values
ex.experiment.name                  # -> train_area_models_4x4

# call captured functions with the config from the database
ex.create_model()

# raises Exception. Experiment already run
ex.run()

I probably could create such a wrapper for mongodb if you think this is useful to have for sacred.

berleon avatar Oct 13 '17 06:10 berleon

My workflow for doing exactly this is the following:

from my_experiment import ex, create_model
from pymongo import MongoClient

client = MongoClient()
run_entry = client.sacred.find_one({
    'experiment.name': 'train_area_models_4x4',
    'config.use_conditional': False,
    'status': 'COMPLETED',
})

# create a run object 
run = ex._create_run(config_updates=run_entry['config'])

# Access config and lots of other things:
run.config['use_conditional']  # -> False

# now you can call captured functions that will use that configuration
create_model()

# or execute the main function:
run()

I agree that having a better interface to instantiate runs from the database would be nice. But this goes hand in hand with providing a more object-oriented interface for sacred (see #193 ) and involves quite a bit of refactoring. It is on my long-term roadmap, but will probably take a while.

Qwlouse avatar Oct 31 '17 19:10 Qwlouse

Thanks, for your awnser. For now, I will go with your proposed solution.

berleon avatar Nov 07 '17 16:11 berleon