diff --git a/com.unity.ml-agents/CHANGELOG.md b/com.unity.ml-agents/CHANGELOG.md index 1c8a8c23a0..5874a41832 100755 --- a/com.unity.ml-agents/CHANGELOG.md +++ b/com.unity.ml-agents/CHANGELOG.md @@ -24,6 +24,7 @@ terminated teammates. (#5441) - Fixed conflicting CLI and yaml options regarding resume & initialize_from (#5495) - Fixed failing tests for gym-unity due to gym 0.20.0 release - Fixed a bug in VAIL where the variational bottleneck was not properly passing gradients (#5546) +- Added minimal analytics collection to LL-API (#5511) ## [2.1.0-exp.1] - 2021-06-09 ### Minor Changes #### com.unity.ml-agents / com.unity.ml-agents.extensions (C#) diff --git a/ml-agents-envs/mlagents_envs/environment.py b/ml-agents-envs/mlagents_envs/environment.py index 88dc254b8f..776c5d1030 100644 --- a/ml-agents-envs/mlagents_envs/environment.py +++ b/ml-agents-envs/mlagents_envs/environment.py @@ -10,6 +10,7 @@ from mlagents_envs.logging_util import get_logger from mlagents_envs.side_channel.side_channel import SideChannel +from mlagents_envs.side_channel import DefaultTrainingAnalyticsSideChannel from mlagents_envs.side_channel.side_channel_manager import SideChannelManager from mlagents_envs import env_utils @@ -186,6 +187,16 @@ def __init__( self._timeout_wait: int = timeout_wait self._communicator = self._get_communicator(worker_id, base_port, timeout_wait) self._worker_id = worker_id + if side_channels is None: + side_channels = [] + default_training_side_channel: Optional[ + DefaultTrainingAnalyticsSideChannel + ] = None + if DefaultTrainingAnalyticsSideChannel.CHANNEL_ID not in [ + _.channel_id for _ in side_channels + ]: + default_training_side_channel = DefaultTrainingAnalyticsSideChannel() + side_channels.append(default_training_side_channel) self._side_channel_manager = SideChannelManager(side_channels) self._log_folder = log_folder self.academy_capabilities: UnityRLCapabilitiesProto = None # type: ignore @@ -246,6 +257,8 @@ def __init__( self._is_first_message = True self._update_behavior_specs(aca_output) self.academy_capabilities = aca_params.capabilities + if default_training_side_channel is not None: + default_training_side_channel.environment_initialized() @staticmethod def _get_communicator(worker_id, base_port, timeout_wait): diff --git a/ml-agents-envs/mlagents_envs/side_channel/__init__.py b/ml-agents-envs/mlagents_envs/side_channel/__init__.py index d6ccfdf573..c9a1f5f0f7 100644 --- a/ml-agents-envs/mlagents_envs/side_channel/__init__.py +++ b/ml-agents-envs/mlagents_envs/side_channel/__init__.py @@ -2,3 +2,6 @@ from mlagents_envs.side_channel.outgoing_message import OutgoingMessage # noqa from mlagents_envs.side_channel.side_channel import SideChannel # noqa +from mlagents_envs.side_channel.default_training_analytics_side_channel import ( # noqa + DefaultTrainingAnalyticsSideChannel, # noqa +) # noqa diff --git a/ml-agents-envs/mlagents_envs/side_channel/default_training_analytics_side_channel.py b/ml-agents-envs/mlagents_envs/side_channel/default_training_analytics_side_channel.py new file mode 100644 index 0000000000..a53e686709 --- /dev/null +++ b/ml-agents-envs/mlagents_envs/side_channel/default_training_analytics_side_channel.py @@ -0,0 +1,49 @@ +import sys +import uuid +import mlagents_envs + +from mlagents_envs.exception import UnityCommunicationException +from mlagents_envs.side_channel import SideChannel, IncomingMessage, OutgoingMessage +from mlagents_envs.communicator_objects.training_analytics_pb2 import ( + TrainingEnvironmentInitialized, +) +from google.protobuf.any_pb2 import Any + + +class DefaultTrainingAnalyticsSideChannel(SideChannel): + """ + Side channel that sends information about the training to the Unity environment so it can be logged. + """ + + CHANNEL_ID = uuid.UUID("b664a4a9-d86f-5a5f-95cb-e8353a7e8356") + + def __init__(self) -> None: + # >>> uuid.uuid5(uuid.NAMESPACE_URL, "com.unity.ml-agents/TrainingAnalyticsSideChannel") + # UUID('b664a4a9-d86f-5a5f-95cb-e8353a7e8356') + # We purposefully use the SAME side channel as the TrainingAnalyticsSideChannel + + super().__init__(DefaultTrainingAnalyticsSideChannel.CHANNEL_ID) + + def on_message_received(self, msg: IncomingMessage) -> None: + raise UnityCommunicationException( + "The DefaultTrainingAnalyticsSideChannel received a message from Unity, " + + "this should not have happened." + ) + + def environment_initialized(self) -> None: + # Tuple of (major, minor, patch) + vi = sys.version_info + + msg = TrainingEnvironmentInitialized( + python_version=f"{vi[0]}.{vi[1]}.{vi[2]}", + mlagents_version="Custom", + mlagents_envs_version=mlagents_envs.__version__, + torch_version="Unknown", + torch_device_type="Unknown", + ) + any_message = Any() + any_message.Pack(msg) + + env_init_msg = OutgoingMessage() + env_init_msg.set_raw_bytes(any_message.SerializeToString()) # type: ignore + super().queue_message_to_send(env_init_msg) diff --git a/ml-agents/mlagents/training_analytics_side_channel.py b/ml-agents/mlagents/training_analytics_side_channel.py index f964f13fac..4b1f1c2dd5 100644 --- a/ml-agents/mlagents/training_analytics_side_channel.py +++ b/ml-agents/mlagents/training_analytics_side_channel.py @@ -1,12 +1,15 @@ import sys from typing import Optional -import uuid import mlagents_envs import mlagents.trainers from mlagents import torch_utils from mlagents.trainers.settings import RewardSignalType from mlagents_envs.exception import UnityCommunicationException -from mlagents_envs.side_channel import SideChannel, IncomingMessage, OutgoingMessage +from mlagents_envs.side_channel import ( + IncomingMessage, + OutgoingMessage, + DefaultTrainingAnalyticsSideChannel, +) from mlagents_envs.communicator_objects.training_analytics_pb2 import ( TrainingEnvironmentInitialized, TrainingBehaviorInitialized, @@ -16,7 +19,7 @@ from mlagents.trainers.settings import TrainerSettings, RunOptions -class TrainingAnalyticsSideChannel(SideChannel): +class TrainingAnalyticsSideChannel(DefaultTrainingAnalyticsSideChannel): """ Side channel that sends information about the training to the Unity environment so it can be logged. """ @@ -24,13 +27,14 @@ class TrainingAnalyticsSideChannel(SideChannel): def __init__(self) -> None: # >>> uuid.uuid5(uuid.NAMESPACE_URL, "com.unity.ml-agents/TrainingAnalyticsSideChannel") # UUID('b664a4a9-d86f-5a5f-95cb-e8353a7e8356') - super().__init__(uuid.UUID("b664a4a9-d86f-5a5f-95cb-e8353a7e8356")) + # Use the same uuid as the parent side channel + super().__init__() self.run_options: Optional[RunOptions] = None def on_message_received(self, msg: IncomingMessage) -> None: raise UnityCommunicationException( "The TrainingAnalyticsSideChannel received a message from Unity, " - + "this should not have happened." + "this should not have happened." ) def environment_initialized(self, run_options: RunOptions) -> None: