Repository: dmlc/gluon-nlp

Issue:
[reproducibility] add util function to enhance reproducibility
There are a few seeds that may lead to nondeterministic training loss: 
```
random.seed(seed)
mx.random.seed(seed)
numpy.random.seed(seed)
```
Sometimes our users miss one of them. Shall we add an API `gluonnlp.utils.seed` to set the seed for all of them? 



Discussion:
We should add one. Actually, even if all these are set, we cannot guarantee reproducibility due to the issues here: https://github.com/apache/incubator-mxnet/issues/16705.

However, we still should add `gluonnlp.utils.seed` and enhance the MXNet side to ensure reproducibility (or teach the user how to tweak the environmental variables for reproducibility concerns).
Hi @eric-haibin-lin 
I can take this up. Can you provide me little more context on how the function should be??

Resolving patch:
diff --git a/src/gluonnlp/utils/__init__.py b/src/gluonnlp/utils/__init__.py
--- a/src/gluonnlp/utils/__init__.py
+++ b/src/gluonnlp/utils/__init__.py
@@ -18,10 +18,11 @@
 # pylint: disable=wildcard-import, arguments-differ
 """Module for utility functions."""
 
-from . import files, parallel, parameter, version
+from . import files, parallel, parameter, version, seed
 from .files import *
 from .parallel import *
 from .parameter import *
 from .version import *
+from .seed import *
 
-__all__ = parallel.__all__ + parameter.__all__ + files.__all__ + version.__all__
+__all__ = parallel.__all__ + parameter.__all__ + files.__all__ + version.__all__ + seed.__all__
diff --git a/src/gluonnlp/utils/seed.py b/src/gluonnlp/utils/seed.py
new file mode 100644
--- /dev/null
+++ b/src/gluonnlp/utils/seed.py
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Utility function for setting seed."""
+import random
+import numpy as np
+import mxnet as mx
+
+__all__ = ['set_seed']
+
+def set_seed(seed=0):
+    """Sets the seed for reproducibility
+
+	Parameters
+	----------
+	seed : int
+	    Value of the seed to set
+	"""
+    random.seed(seed)
+    np.random.seed(seed)
+    mx.random.seed(seed)


Test patch:
diff --git a/tests/unittest/test_utils.py b/tests/unittest/test_utils.py
--- a/tests/unittest/test_utils.py
+++ b/tests/unittest/test_utils.py
@@ -197,3 +197,16 @@ def test_train_valid_split():
 
     assert np.all(np.ceil(.05*num_class).astype(int) == valid_num_class) and \
            (len(train_dataset) + len(valid_dataset) == len(dataset))
+
+@pytest.mark.parametrize('seed', [42, 42])
+def test_set_seed(seed):
+    contexts = [mx.cpu(0), mx.cpu(1)]
+    for ctx in contexts: 
+        nlp.utils.seed.set_seed(seed)
+        x = mx.ndarray.random.randn(2, 2, loc=0, scale=1, ctx=ctx)
+        nlp.utils.seed.set_seed(seed)
+        y = mx.ndarray.random.randn(2, 2, loc=0, scale=1, ctx=ctx)
+        assert x[0][0]==y[0][0]
+        assert x[0][1]==y[0][1]
+        assert x[1][0]==y[1][0]
+        assert x[1][1]==y[1][1]