mirror of
https://github.com/wassname/ray.git
synced 2026-07-11 07:30:25 +08:00
428516056a
* Policy-classes cleanup and torch/tf unification. - Make Policy abstract. - Add `action_dist` to call to `extra_action_out_fn` (necessary for PPO torch). - Move some methods and vars to base Policy (from TFPolicy): num_state_tensors, ACTION_PROB, ACTION_LOGP and some more. * Fix `clip_action` import from Policy (should probably be moved into utils altogether). * - Move `is_recurrent()` and `num_state_tensors()` into TFPolicy (from DynamicTFPolicy). - Add config to all Policy c'tor calls (as 3rd arg after obs and action spaces). * Add `config` to c'tor call to TFPolicy. * Add missing `config` to c'tor call to TFPolicy in marvil_policy.py. * Fix test_rollout_worker.py::MockPolicy and BadPolicy classes (Policy base class is now abstract). * Fix LINT errors in Policy classes. * Implement StatefulPolicy abstract methods in test cases: test_multi_agent_env.py. * policy.py LINT errors. * Create a simple TestPolicy to sub-class from when testing Policies (reduces code in some test cases). * policy.py - Remove abstractmethod from `apply_gradients` and `compute_gradients` (these are not required iff `learn_on_batch` implemented). - Fix docstring of `num_state_tensors`. * Make QMIX torch Policy a child of TorchPolicy (instead of Policy). * QMixPolicy add empty implementations of abstract Policy methods. * Store Policy's config in self.config in base Policy c'tor. * - Make only compute_actions in base Policy's an abstractmethod and provide pass implementation to all other methods if not defined. - Fix state_batches=None (most Policies don't have internal states). * Cartpole tf learning. * Cartpole tf AND torch learning (in ~ same ts). * Cartpole tf AND torch learning (in ~ same ts). 2 * Cartpole tf (torch syntax-broken) learning (in ~ same ts). 3 * Cartpole tf AND torch learning (in ~ same ts). 4 * Cartpole tf AND torch learning (in ~ same ts). 5 * Cartpole tf AND torch learning (in ~ same ts). 6 * Cartpole tf AND torch learning (in ~ same ts). Pendulum tf learning. * WIP. * WIP. * SAC torch learning Pendulum. * WIP. * SAC torch and tf learning Pendulum and Cartpole after cleanup. * WIP. * LINT. * LINT. * SAC: Move policy.target_model to policy.device as well. * Fixes and cleanup. * Fix data-format of tf keras Conv2d layers (broken for some tf-versions which have data_format="channels_first" as default). * Fixes and LINT. * Fixes and LINT. * Fix and LINT. * WIP. * Test fixes and LINT. * Fixes and LINT. Co-authored-by: Sven Mika <sven@Svens-MacBook-Pro.local>
238 lines
6.8 KiB
Python
238 lines
6.8 KiB
Python
import logging
|
|
import os
|
|
import sys
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Represents a generic tensor type.
|
|
TensorType = Any
|
|
|
|
|
|
def check_framework(framework="tf"):
|
|
"""
|
|
Checks, whether the given framework is "valid", meaning, whether all
|
|
necessary dependencies are installed. Errors otherwise.
|
|
|
|
Args:
|
|
framework (str): Once of "tf", "torch", or None.
|
|
|
|
Returns:
|
|
str: The input framework string.
|
|
"""
|
|
if framework == "tf":
|
|
if tf is None:
|
|
raise ImportError("Could not import tensorflow.")
|
|
elif framework == "torch":
|
|
if torch is None:
|
|
raise ImportError("Could not import torch.")
|
|
else:
|
|
assert framework is None
|
|
return framework
|
|
|
|
|
|
def try_import_tf(error=False):
|
|
"""
|
|
Args:
|
|
error (bool): Whether to raise an error if tf cannot be imported.
|
|
|
|
Returns:
|
|
The tf module (either from tf2.0.compat.v1 OR as tf1.x.
|
|
"""
|
|
# Make sure, these are reset after each test case
|
|
# that uses them: del os.environ["RLLIB_TEST_NO_TF_IMPORT"]
|
|
if "RLLIB_TEST_NO_TF_IMPORT" in os.environ:
|
|
logger.warning("Not importing TensorFlow for test purposes")
|
|
return None
|
|
|
|
if "TF_CPP_MIN_LOG_LEVEL" not in os.environ:
|
|
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
|
|
|
|
# Try to reuse already imported tf module. This will avoid going through
|
|
# the initial import steps below and thereby switching off v2_behavior
|
|
# (switching off v2 behavior twice breaks all-framework tests for eager).
|
|
if "tensorflow" in sys.modules:
|
|
tf_module = sys.modules["tensorflow"]
|
|
# Try "reducing" tf to tf.compat.v1.
|
|
try:
|
|
tf_module = tf_module.compat.v1
|
|
# No compat.v1 -> return tf as is.
|
|
except AttributeError:
|
|
pass
|
|
return tf_module
|
|
|
|
# Just in case. We should not go through the below twice.
|
|
assert "tensorflow" not in sys.modules
|
|
|
|
try:
|
|
# Try "reducing" tf to tf.compat.v1.
|
|
import tensorflow.compat.v1 as tf
|
|
tf.logging.set_verbosity(tf.logging.ERROR)
|
|
# Disable v2 eager mode.
|
|
tf.disable_v2_behavior()
|
|
return tf
|
|
except ImportError:
|
|
try:
|
|
import tensorflow as tf
|
|
return tf
|
|
except ImportError as e:
|
|
if error:
|
|
raise e
|
|
return None
|
|
|
|
|
|
def tf_function(tf_module):
|
|
"""Conditional decorator for @tf.function.
|
|
|
|
Use @tf_function(tf) instead to avoid errors if tf is not installed."""
|
|
|
|
# The actual decorator to use (pass in `tf` (which could be None)).
|
|
def decorator(func):
|
|
# If tf not installed -> return function as is (won't be used anyways).
|
|
if tf_module is None or tf_module.executing_eagerly():
|
|
return func
|
|
# If tf installed, return @tf.function-decorated function.
|
|
return tf_module.function(func)
|
|
|
|
return decorator
|
|
|
|
|
|
def try_import_tfp(error=False):
|
|
"""
|
|
Args:
|
|
error (bool): Whether to raise an error if tfp cannot be imported.
|
|
|
|
Returns:
|
|
The tfp module.
|
|
"""
|
|
if "RLLIB_TEST_NO_TF_IMPORT" in os.environ:
|
|
logger.warning("Not importing TensorFlow Probability for test "
|
|
"purposes.")
|
|
return None
|
|
|
|
try:
|
|
import tensorflow_probability as tfp
|
|
return tfp
|
|
except ImportError as e:
|
|
if error:
|
|
raise e
|
|
return None
|
|
|
|
|
|
# Fake module for torch.nn.
|
|
class NNStub:
|
|
def __init__(self, *a, **kw):
|
|
# Fake nn.functional module within torch.nn.
|
|
self.functional = None
|
|
self.Module = ModuleStub
|
|
|
|
|
|
# Fake class for torch.nn.Module to allow it to be inherited from.
|
|
class ModuleStub:
|
|
def __init__(self, *a, **kw):
|
|
raise ImportError("Could not import `torch`.")
|
|
|
|
|
|
def try_import_torch(error=False):
|
|
"""
|
|
Args:
|
|
error (bool): Whether to raise an error if torch cannot be imported.
|
|
|
|
Returns:
|
|
tuple: torch AND torch.nn modules.
|
|
"""
|
|
if "RLLIB_TEST_NO_TORCH_IMPORT" in os.environ:
|
|
logger.warning("Not importing Torch for test purposes.")
|
|
return _torch_stubs()
|
|
|
|
try:
|
|
import torch
|
|
import torch.nn as nn
|
|
return torch, nn
|
|
except ImportError as e:
|
|
if error:
|
|
raise e
|
|
return _torch_stubs()
|
|
|
|
|
|
def _torch_stubs():
|
|
nn = NNStub()
|
|
return None, nn
|
|
|
|
|
|
def get_variable(value,
|
|
framework="tf",
|
|
trainable=False,
|
|
tf_name="unnamed-variable",
|
|
torch_tensor=False,
|
|
device=None):
|
|
"""
|
|
Args:
|
|
value (any): The initial value to use. In the non-tf case, this will
|
|
be returned as is.
|
|
framework (str): One of "tf", "torch", or None.
|
|
trainable (bool): Whether the generated variable should be
|
|
trainable (tf)/require_grad (torch) or not (default: False).
|
|
tf_name (str): For framework="tf": An optional name for the
|
|
tf.Variable.
|
|
torch_tensor (bool): For framework="torch": Whether to actually create
|
|
a torch.tensor, or just a python value (default).
|
|
|
|
Returns:
|
|
any: A framework-specific variable (tf.Variable, torch.tensor, or
|
|
python primitive).
|
|
"""
|
|
if framework == "tf":
|
|
import tensorflow as tf
|
|
dtype = getattr(
|
|
value, "dtype", tf.float32
|
|
if isinstance(value, float) else tf.int32
|
|
if isinstance(value, int) else None)
|
|
return tf.compat.v1.get_variable(
|
|
tf_name, initializer=value, dtype=dtype, trainable=trainable)
|
|
elif framework == "torch" and torch_tensor is True:
|
|
torch, _ = try_import_torch()
|
|
var_ = torch.from_numpy(value).to(device)
|
|
var_.requires_grad = trainable
|
|
return var_
|
|
# torch or None: Return python primitive.
|
|
return value
|
|
|
|
|
|
def get_activation_fn(name, framework="tf"):
|
|
"""
|
|
Returns a framework specific activation function, given a name string.
|
|
|
|
Args:
|
|
name (str): One of "relu" (default), "tanh", or "linear".
|
|
framework (str): One of "tf" or "torch".
|
|
|
|
Returns:
|
|
A framework-specific activtion function. e.g. tf.nn.tanh or
|
|
torch.nn.ReLU. Returns None for name="linear".
|
|
"""
|
|
if framework == "torch":
|
|
_, nn = try_import_torch()
|
|
if name == "linear":
|
|
return None
|
|
elif name == "relu":
|
|
return nn.ReLU
|
|
elif name == "tanh":
|
|
return nn.Tanh
|
|
else:
|
|
if name == "linear":
|
|
return None
|
|
tf = try_import_tf()
|
|
fn = getattr(tf.nn, name, None)
|
|
if fn is not None:
|
|
return fn
|
|
|
|
raise ValueError("Unknown activation ({}) for framework={}!".format(
|
|
name, framework))
|
|
|
|
|
|
# This call should never happen inside a module's functions/classes
|
|
# as it would re-disable tf-eager.
|
|
tf = try_import_tf()
|
|
torch, _ = try_import_torch()
|