Initializing new random instances for each estimator instead of passing around the same one

### Describe the issue linked to the documentation

After reading the detailed and helpful section on ["Controlling randomness"](https://scikit-learn.org/stable/common_pitfalls.html#controlling-randomness), my understanding is that the benefit of passing an `np.random.RandomState` instance to each estimator is the different randomness for different folds during the CV process, which passing an integer would not provide. In the sample code, the random instance is defined globally and then passed to the `random_state` parameter of all function calls. The drawback of this is that the order of operations now matters (as mentioned in the documentation) and the addition of any code that uses the same globally defined random instance variable will advance the random number generator and change the results of any code that is run afterwards, which can be quite confusing and not ideal for reproducibility.

Instead of defining this random state globally and re-using it, would it not be more beneficial to pass a new instance to each estimator? It seems to me that this would combine the benefits of the CV exploring different randomness in each fold, and having the code reproducible regardless of insertion of new code that uses the same random instance.


### Suggest a potential alternative/fix

Instead of the current recommendation in the docs:

```py
rng = RandomState(0)  # from numpy.random import RandomState
X, y = make_classification(random_state=rng)
rf = RandomForestClassifier(random_state=rng)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=rng)
rf.fit(X_train, y_train).score(X_test, y_test)
```

I would expect this to combine the benefits I mentioned above:

```py
rng_init = 0
X, y = make_classification(random_state=RandomState(rng_init))
rf = RandomForestClassifier(random_state=RandomState(rng_init))
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=RandomState(rng_init))
rf.fit(X_train, y_train).score(X_test, y_test)
```

Apart from the increased verbosity, is there any downside to this approach (or is it even the same as passing an `int` so it has the same downsides)? If not, would you be open to a PR mentioning this in the docs? I am not that well-versed with pseudo random number generators in general, so while I know that the sequence generated by the number generator can be beneficial for some application, I don't know if there are any benefits to advancing this sequence as a global variable between different estimator call or if it is more important within each estimator and it is therefore fine to reinitialize the sequence as I suggest above.

And in this particular example, isn't only to the estimator we need to pass the `RandomState` instance (so the call to `make_classification` and `train_test_split` could receive an `int` instead which would make it less verbose)?

COMMENT:
> Trying to bring us back on track to a concrete suggestion for a change (so we can decide yes or no on it)

@betatim I am afraid that the current "Controlling randomness" reference does not truthfully describe the current behaviour. I am bringing this up to prevent making a decision based on a wrong premise. To be specific, this premise:

> After reading the detailed and helpful section on ["Controlling randomness"](https://scikit-learn.org/stable/common_pitfalls.html#controlling-randomness), my understanding is that the benefit of passing an `np.random.RandomState` instance to each estimator is the different randomness for different folds during the CV process, which passing an integer would not provide.

I believe that estimator instances used for different folds do _not_ have different randomness (therefore, there is no difference in that regard between passing an RNG instance or an integer as `random_state`). Here is the code that I believe demonstrates that:

```python
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_validate
from sklearn.ensemble import RandomForestClassifier
import numpy as np

rng = np.random.RandomState(0)  # from numpy.random import RandomState
X, y = make_classification(random_state=rng)
rf = RandomForestClassifier(random_state=rng)

d = cross_validate(rf, X, y, return_estimator=True, cv=2)
rngs = [e.random_state for e in d['estimator']]
# estimators corresponding to different CV runs have DIFFERENT but IDENTICAL RNGs:
print(rngs[0] is rngs[1]) # False
print(all(rngs[0].randint(10, size=10) == rngs[1].randint(10, size=10))) # True
```

Labels: Documentation