Skip to content
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

Ray integration yaml serialization error #586

Closed
nacef-labidi opened this issue Sep 30, 2019 · 11 comments
Closed

Ray integration yaml serialization error #586

nacef-labidi opened this issue Sep 30, 2019 · 11 comments
Labels
ty:bug type of the issue is a bug

Comments

@nacef-labidi
Copy link

wandb --version && python --version && uname

  • Weights and Biases version: 0.8.12
  • Python version: 3.7.3
  • Operating System: Linux

Description

Tried to use the wandb Ray tune integration. Training fails when serializing config with following error:
yaml.representer.RepresenterError: cannot represent an object: <function main.. at 0x7fd0fc4bfbf8>

What I Did

  1. Started from simple cartpole example provided by Ray: https://raw.githubusercontent.com/ray-project/ray/master/rllib/examples/multiagent_cartpole.py

  2. Imported wandb
    from wandb.ray import WandbLogger

  3. Included wandb configuration and set it as logger

    tune.run(
        "PPO",
        stop={"training_iteration": args.num_iters},
        config={
            "env": "multi_cartpole",
            "monitor": True,
            "env_config": {
                "wandb": {"project": "rl", "monitor_gym": True}
            },
            "log_level": "DEBUG",
            "simple_optimizer": args.simple,
            "num_sgd_traceiter": 10,
            "multiagent": {
                "policies": policies,
                "policy_mapping_fn": (
                    lambda agent_id: random.choice(policy_ids)),
            },
        },
        loggers=[WandbLogger],
    )
  1. Fails with following stacktrace:
2019-09-30 17:45:01,241	ERROR trial_runner.py:560 -- Error processing event.
Traceback (most recent call last):
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/ray/tune/trial_runner.py", line 540, in _process_trial
    result, terminate=(decision == TrialScheduler.STOP))
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/ray/tune/trial.py", line 386, in update_last_result
    self.result_logger.on_result(self.last_result)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/ray/tune/logger.py", line 333, in on_result
    _logger.on_result(result)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/wandb/ray/__init__.py", line 24, in on_result
    wandb.config[k] = config[k]
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/wandb/wandb_config.py", line 184, in __setitem__
    self.persist()
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/wandb/wandb_config.py", line 171, in persist
    conf_file.write(str(self))
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/wandb/wandb_config.py", line 274, in __str__
    allow_unicode=True, encoding='utf-8')
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/__init__.py", line 200, in dump
    return dump_all([data], stream, Dumper=Dumper, **kwds)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/__init__.py", line 188, in dump_all
    dumper.represent(data)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 26, in represent
    node = self.represent_data(data)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 47, in represent_data
    node = self.yaml_representers[data_types[0]](self, data)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 205, in represent_dict
    return self.represent_mapping('tag:yaml.o
wandb: Waiting for W&B process to finish, PID 20314
rg,2002:map', data)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 116, in represent_mapping
    node_value = self.represent_data(item_value)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 47, in represent_data
    node = self.yaml_representers[data_types[0]](self, data)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 205, in represent_dict
    return self.represent_mapping('tag:yaml.org,2002:map', data)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 116, in represent_mapping
    node_value = self.represent_data(item_value)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 47, in represent_data
    node = self.yaml_representers[data_types[0]](self, data)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 205, in represent_dict
    return self.represent_mapping('tag:yaml.org,2002:map', data)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 116, in represent_mapping
    node_value = self.represent_data(item_value)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 57, in represent_data
    node = self.yaml_representers[None](self, data)
  File "/home/nacef/tools/anaconda3/envs/rl/lib/python3.7/site-packages/yaml/representer.py", line 229, in represent_undefined
    raise RepresenterError("cannot represent an object: %s" % data)
yaml.representer.RepresenterError: cannot represent an object: <function main.<locals>.<lambda> at 0x7fd0fc4bfbf8>
@issue-label-bot
Copy link

Issue-Label Bot is automatically applying the label bug to this issue, with a confidence of 0.93. Please mark this comment with 👍 or 👎 to give our bot feedback!

Links: app homepage, dashboard and code for this bot.

@issue-label-bot issue-label-bot bot added the ty:bug type of the issue is a bug label Sep 30, 2019
@cvphelps
Copy link
Contributor

cvphelps commented Oct 3, 2019

Thanks for letting me know about this! I'll go over this issue in our weekly meeting.

@AGKhalil
Copy link

AGKhalil commented Feb 6, 2020

Any update on this? I'm facing similar issues.

@drozzy
Copy link

drozzy commented Mar 17, 2020

Having the same bug just now - this is specific to multi agent training in rllib only.

The issue here is that the config contains a python lambda function. Your parser should probably just skip it.

The relevant part is:

 "policy_mapping_fn": (
                    lambda agent_id: random.choice(policy_ids)),
            },

See the docs here:
https://ray.readthedocs.io/en/latest/rllib-env.html#multi-agent-and-hierarchical

and search for policy_mapping_fn.

The OP provided a link to fully standalone example - you just need to add wandb instrumentation to it:
https://github.com/ray-project/ray/blob/master/rllib/examples/multi_agent_cartpole.py

@drozzy
Copy link

drozzy commented Mar 17, 2020

FYI: This should be fixed in next version of PyYAML 5.1.
yaml/pyyaml#222

You guys are using an old version PyYAML==4.2b4. Seems like they have released PyYAML 5.3:
https://pypi.org/project/PyYAML/5.3/

But there were some issues with 5.1 being backward compatible. So yeah...

@drozzy
Copy link

drozzy commented Mar 31, 2020

This is kind of required to log from rllib for more advanced scenarios...

@free-soellingeraj
Copy link

free-soellingeraj commented May 20, 2020

I have another one similar sounding error.

wandb, version 0.8.36
Python 3.6.9 :: Anaconda, Inc.
Linux

initializing like this, inside a KFold loop:

...
wandb.init(
        project=rootname.replace('/', '-').lower(),
        name='run_{}_fr{}_ufr{}_{}of{}_fr'.format(
            exp_uuid, froze_cycles, unfroze_cycles, run_i, n_splits
        ),
        config=config,
        group='fr_'+exp_uuid,
        save_code=True,
        tags=[
            'parent_{}'.format(run),
            'sklearn.model_selection.StratifiedKFold',
            str(mod).split(' ')[1]
        ],
        job_type='eval'
    )
...

getting

---------------------------------------------------------------------------
RepresenterError                          Traceback (most recent call last)
<ipython-input-37-80069da565fb> in <module>
     70             str(mod).split(' ')[1]
     71         ],
---> 72         job_type='eval'
     73     )
     74     learn.fit_one_cycle(froze_cycles)

/opt/conda/lib/python3.6/site-packages/wandb/__init__.py in init(job_type, dir, config, project, entity, reinit, tags, group, allow_val_change, resume, force, tensorboard, sync_tensorboard, monitor_gym, name, notes, id, magic, anonymous, config_exclude_keys, config_include_keys, save_code)
   1142                 include_keys=config_include_keys,
   1143                 allow_val_change=allow_val_change,
-> 1144                 as_defaults=not allow_val_change)
   1145 
   1146     # Access history to ensure resumed is set when resuming

/opt/conda/lib/python3.6/site-packages/wandb/wandb_config.py in _update(self, params, allow_val_change, as_defaults, exclude_keys, include_keys)
    309                 continue
    310             self._items[key] = val
--> 311         self.persist()
    312 
    313     def update(self, params, allow_val_change=False, exclude_keys=None, include_keys=None):

/opt/conda/lib/python3.6/site-packages/wandb/wandb_config.py in persist(self)
    214             return
    215         with open(path, "w") as conf_file:
--> 216             conf_file.write(str(self))
    217         if wandb.run and wandb.run._jupyter_agent:
    218             wandb.run._jupyter_agent.start()

/opt/conda/lib/python3.6/site-packages/wandb/wandb_config.py in __str__(self)
    350         if as_dict:  # adding an empty dictionary here causes a parse error
    351             s += b'\n\n' + yaml.dump(as_dict, Dumper=yaml.SafeDumper, default_flow_style=False,
--> 352                                      allow_unicode=True, encoding='utf-8')
    353         return s.decode("utf-8")
    354 

/opt/conda/lib/python3.6/site-packages/yaml/__init__.py in dump(data, stream, Dumper, **kwds)
    288     If stream is None, return the produced string instead.
    289     """
--> 290     return dump_all([data], stream, Dumper=Dumper, **kwds)
    291 
    292 def safe_dump_all(documents, stream=None, **kwds):

/opt/conda/lib/python3.6/site-packages/yaml/__init__.py in dump_all(documents, stream, Dumper, default_style, default_flow_style, canonical, indent, width, allow_unicode, line_break, encoding, explicit_start, explicit_end, version, tags, sort_keys)
    276         dumper.open()
    277         for data in documents:
--> 278             dumper.represent(data)
    279         dumper.close()
    280     finally:

/opt/conda/lib/python3.6/site-packages/yaml/representer.py in represent(self, data)
     25 
     26     def represent(self, data):
---> 27         node = self.represent_data(data)
     28         self.serialize(node)
     29         self.represented_objects = {}

/opt/conda/lib/python3.6/site-packages/yaml/representer.py in represent_data(self, data)
     46         data_types = type(data).__mro__
     47         if data_types[0] in self.yaml_representers:
---> 48             node = self.yaml_representers[data_types[0]](self, data)
     49         else:
     50             for data_type in data_types:

/opt/conda/lib/python3.6/site-packages/yaml/representer.py in represent_dict(self, data)
    205 
    206     def represent_dict(self, data):
--> 207         return self.represent_mapping('tag:yaml.org,2002:map', data)
    208 
    209     def represent_set(self, data):

/opt/conda/lib/python3.6/site-packages/yaml/representer.py in represent_mapping(self, tag, mapping, flow_style)
    116         for item_key, item_value in mapping:
    117             node_key = self.represent_data(item_key)
--> 118             node_value = self.represent_data(item_value)
    119             if not (isinstance(node_key, ScalarNode) and not node_key.style):
    120                 best_style = False

/opt/conda/lib/python3.6/site-packages/yaml/representer.py in represent_data(self, data)
     46         data_types = type(data).__mro__
     47         if data_types[0] in self.yaml_representers:
---> 48             node = self.yaml_representers[data_types[0]](self, data)
     49         else:
     50             for data_type in data_types:

/opt/conda/lib/python3.6/site-packages/yaml/representer.py in represent_dict(self, data)
    205 
    206     def represent_dict(self, data):
--> 207         return self.represent_mapping('tag:yaml.org,2002:map', data)
    208 
    209     def represent_set(self, data):

/opt/conda/lib/python3.6/site-packages/yaml/representer.py in represent_mapping(self, tag, mapping, flow_style)
    116         for item_key, item_value in mapping:
    117             node_key = self.represent_data(item_key)
--> 118             node_value = self.represent_data(item_value)
    119             if not (isinstance(node_key, ScalarNode) and not node_key.style):
    120                 best_style = False

/opt/conda/lib/python3.6/site-packages/yaml/representer.py in represent_data(self, data)
     56                     node = self.yaml_multi_representers[None](self, data)
     57                 elif None in self.yaml_representers:
---> 58                     node = self.yaml_representers[None](self, data)
     59                 else:
     60                     node = ScalarNode(None, str(data))

/opt/conda/lib/python3.6/site-packages/yaml/representer.py in represent_undefined(self, data)
    229 
    230     def represent_undefined(self, data):
--> 231         raise RepresenterError("cannot represent an object", data)
    232 
    233 SafeRepresenter.add_representer(type(None),

RepresenterError: ('cannot represent an object', <function resnet50 at 0x7f6c5b49aa60>)

YAML:

import yaml
yaml.__version__
# >> '5.3'

@free-soellingeraj
Copy link

free-soellingeraj commented May 20, 2020

I figured out the problem for me was that I was trying to add the model to the wandb.config attribute.

config = {
    'exp_id': exp_uuid,
    'input_size': 224,
    'n_splits': n_splits,
    'exp_method': StratifiedKFold,
    'parent_run': run,
    'model': models.resnet50}

So I converted it to a string from the function name and it worked.

config = {
    'exp_id': exp_uuid,
    'input_size': 224,
    'n_splits': n_splits,
    'exp_method': StratifiedKFold,
    'parent_run': run,
    'model': str(models.resnet50).split(' ')[1]}

@AGKhalil
Copy link

I ran across this issue myself when trying to run ray with wandb. The yaml SafeDumper can't dump the callbacks object and Dumper tries to reconstruct the object, but fails.

My solution is a little tacky but it works. I edited wandb/wandb_config.py, specifically the __str__() function under the Config class. It looks like this:

def __str__(self):                                                                                                                                                                                                                    
    s = b"wandb_version: 1"                                                                                                                                                                                                           
    as_dict = self.as_dict()                                                                                                                                                                                                          
    if "callbacks" in as_dict:                                                                                                                                                                                                        
        as_dict['callbacks']['value'] = 'CustomCallbacks'                                                                                                                                                                             
    if as_dict:  # adding an empty dictionary here causes a parse error                                                                                                                                                               
        s += b'\n\n' + yaml.dump(as_dict, Dumper=yaml.SafeDumper, default_flow_style=False, allow_unicode=True, encoding='utf-8')                                                                                                                                                                
    return s.decode("utf-8")

Basically, I found the callbacks object and replaced its value with a string. The reason I didn't just replace the object with a string early on in my main code is because I need the object. Only wandb struggles with its representation.

If anyone has a better way or any guidance on how to do this properly, and more generally, please let me know. I'd be happy to contribute to this repo if I can.

@AGKhalil
Copy link

Actually found a better way:

def __str__(self):                                                                                                                                                                                                                    
    s = b"wandb_version: 1"                                                                                                                                                                                                           
    as_dict = self.as_dict()                                                                                                                                                                                                          
    for k, v in as_dict.items():
        if inspect.isclass(v['value']):
            as_dict[k]['value'] = str(v['value'])                                                                                                                                                                          
    if as_dict:  # adding an empty dictionary here causes a parse error                                                                                                                                                               
        s += b'\n\n' + yaml.dump(as_dict, Dumper=yaml.SafeDumper, default_flow_style=False, allow_unicode=True, encoding='utf-8')                                                                                                                                                                
    return s.decode("utf-8")

This looks through the dictionary of config objects we wish to upload to wandb. If an object is a class instance, I replace the value with a string of the instance.

@AGKhalil
Copy link

Found related commit. It is 4 behind master though. So for anyone looking, until this is fully merged, my solution and the commit's solution should work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ty:bug type of the issue is a bug
Projects
None yet
Development

No branches or pull requests

6 participants