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

exposed unity random seed to python api surface #823

Merged
merged 7 commits into from
Aug 9, 2021
Merged

Conversation

sagadre
Copy link

@sagadre sagadre commented Jul 20, 2021

Main change is on L3057 in BaseFPSAgentController.cs

@mattdeitke
Copy link
Member

To add to some context, I much prefer this solution, of setting the random seed globally, rather than every random action including a random seed, which then sets the seed inside of the action.

So, instead of:

controller.step(action="RandomizeMaterials", randomSeed=42)
controller.step(action="RandomizeMaterials", randomSeed=42)

you have

controller.step(action="SetRandomSeed", seed=42)
controller.step(action="RandomizeMaterials")
controller.step(action="RandomizeMaterials")

This is much more consistent with how one expects to use a random seed in other libraries. For instance, in NumPy, one doesn't use:

np.random.randn((125, 125), random_seed=42)
np.random.randn((125, 125), random_seed=45)
np.random.randn((125, 125), random_seed=46)

You instead set the seed once globally and keep sampling from it.

Copy link
Member

@mattdeitke mattdeitke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Added a test for it, and visually tested in Colab: https://colab.research.google.com/drive/1QjMJrlXKS-b_o1N2MMbcf_0oTGdaaYCi?usp=sharing

@sagadre
Copy link
Author

sagadre commented Jul 20, 2021

Thanks Matt, I like your suggestion!

In the interim, one thing to note is the following sequences of actions will set the random seed twice.

controller.step(action="SetRandomSeed", seed=42)
controller.step(action="InitialRandomSpawn") # default randomSeed=0, so the previous line didn't really do anything

However, this case should not really cause issues for the end user (behavior should still be reproducible even though the seed switches).

@sagadre
Copy link
Author

sagadre commented Jul 20, 2021

Another thing to consider is adding a random seed param to the Controller constructor and to reset so someone could set the seed from there and then not worry about having to call SetRandomSeed. Would still be useful to have this function for some use cases.

@mattdeitke
Copy link
Member

mattdeitke commented Jul 20, 2021

Another thing to consider is adding a random seed param to the Controller constructor and to reset so someone could set the seed from there and then not worry about having to call SetRandomSeed. Would still be useful to have this function for some use cases.

I tend to prefer against having a seed in reset/initialize. Some reasons:

  1. Supporting 2 ways to do something (i.e., setting a random seed as an action or upon initialization) is a bit dangerous. If one reads the documentation, and sees that there's a seed parameter upon initialize, it doesn't seem likely that one would also expect it to be a standalone action, and vice-versa. The idea of having 2 ways to do something is also unpythonic.
  2. One common use case that can only be supported with the action is the ability to call reset right before some random action, so as to reset the seed when that action is reached. For instance:
import random

controller.step(action="SetRandomSeed", seed=42)
controller.step(action="RandomizeMaterials")

# --- some code that may utilize unity's random function, but the scene has not reset ---
random_n_steps = random.choice(range(5, 20))
for _ in range(random_n_steps):
     # assume this is a stochastic movement that randomly samples moveMagnitude in Unity
    controller.step(action="MoveAhead")

controller.step(action="SetRandomSeed", seed=42)
controller.step(action="RandomizeLighting")

If one only knew about SetRandomSeed in reset, then one would not be able to guarantee that the random numbers generated for the RandomizeLighting action were reproducible, since the seed set upon reset would be sampled from an nondeterministic amount of times.

  1. Initialization is super large right now. Between all agent types, it is probably the hardest thing to maintain because it is so messy with so many existing parameters.
  2. Using actionFinishedEmit is extremely fast since it can largely cache the previously returned Event (without having to pass back new object metadata or image frames). Thus, keeping SetRandomSeed as a standalone action incurs a largely negligible performance cost.

@sagadre
Copy link
Author

sagadre commented Jul 20, 2021

oops this case could be an issue with the current codebase

controller.step(action="SetRandomSeed", seed=42)
e1 = controller.step(action="InitialRandomSpawn") # default randomSeed=0, so the previous line didn't really do anything
controller.step(action="SetRandomSeed", seed=41)
e2 = controller.step(action="InitialRandomSpawn")

A user might expect e1 != e2 but this is not the case. Would not be an issue for RandomizeMaterials etc. tho.

@mattdeitke
Copy link
Member

I think this is more of a fault of InitialRandomSpawn, which is getting deprecated soon, having a randomSeed parameter, rather than it being a fault of the current setup.

Copy link

@ekolve ekolve left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with the change with one caveat that we need to ensure that we make users aware that the order that actions are executed in will change the result if the actions invoke the RNG. For example,

SetRandomSeed
RandomizeMaterials
RandomizeMaterials

vs

SetRandomSeed
RandomizeMaterials
SomeOtherRandomAction
RandomizeMaterials

The latter will result in a different set of random materials. This behavior is not different from say using Python's random and explicitly setting the seed. There is also the issue of an action on the Unity side indirectly using the RNG through some other API that we call, which will affect downstream calls to the RNG. This only matters when someone is say generating a dataset and then adds an additional call to an action within a sequence of other actions that use the RNG.

Copy link
Collaborator

@Lucaweihs Lucaweihs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments but I'm otherwise happy with this update.

ai2thor/tests/test_unity.py Outdated Show resolved Hide resolved
Comment on lines 3058 to 3061
public void SetRandomSeed(int seed) {
UnityEngine.Random.InitState(seed);
actionFinishedEmit(true);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the System.Random seed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a new systemRandom variable to BaseFPSAgentController, which is now used in place of always constructing a new System.Random() object.

Some notes:

  1. There was no way to set the seed of System.Random globally.
  2. System.Random is necessary for generating doubles, instead of just floats (which can be done with UnityEngine.Random).
  3. It is intentionally not used with a few actions that have randomSeed as a parameter. The only public one is InitialRandomSpawn, which is being reworked, such that, among other things, it will no longer have a random seed parameter.
  4. After calling controller.reset(), there are no guarantees that the seeds remain in tact.

Copy link
Collaborator

@Lucaweihs Lucaweihs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@mattdeitke mattdeitke merged commit 323f517 into main Aug 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants