We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
REST API error with sklearn classifier
### System information
- **Have I written custom code (as opposed to using a stock example script provided in MLflow)**: I wrote a simple random forest classifier using sklearn and the iris dataset to test the mlflow workflow from scratch.
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: macOS 10.14.1
- **MLflow installed from (source or binary)**: source (pip install mlflow)
- **MLflow version (run ``mlflow --version``)**: 0.8.2
- **Python version**: 3.7
- **npm version (if running the dev UI):
- **Exact command to reproduce**:curl -X POST -H "Content-Type:application/json; format=pandas-split" --data '{"columns":["sepal_length_cm", "sepal_width_cm", "petal_length_cm", "petal_width_cm"],"data":[[5.3, 1.7, 3.5, 0.5]]}' http://127.0.0.1:1234/invocations

### Describe the problem
If sklearn classifier.predict() method returns a categorical variable, then there is a 500 internal server error returned after the above http request.

The error is from the following function in mlflow/utils/__init.py__:

def ndarray2list(ndarray):
    """
    Convert n-dimensional numpy array into nested lists and convert the elements types to native
    python so that the list is json-able using standard json library.
    :param ndarray: numpy array
    :return: list representation of the numpy array with element types convereted to native python
    """
    if len(ndarray.shape) <= 1:
        return [x.item() for x in ndarray]
    return [ndarray2list(ndarray[i, :]) for i in range(0, ndarray.shape[0])]

x.item() fails if the array returned from the sklearn classifier.predict() method contains strings, as in the case of the iris dataset (labels are 'setosa', etc). I corrected the error on my local install by changing the function as below:

def ndarray2list(ndarray):
    """
    Convert n-dimensional numpy array into nested lists and convert the elements types to native
    python so that the list is json-able using standard json library.
    :param ndarray: numpy array
    :return: list representation of the numpy array with element types convereted to native python
    """
    if len(ndarray.shape) <= 1:
        try:
            return [x.item() for x in ndarray]
        except:
            return [x for x in ndarray]
    return [ndarray2list(ndarray[i, :]) for i in range(0, ndarray.shape[0])]

### Source code / logs
server side error traceback:
[2019-02-19 10:57:20,668] ERROR in app: Exception on /invocations [POST]
Traceback (most recent call last):
  File "/anaconda3/lib/python3.7/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/anaconda3/lib/python3.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/anaconda3/lib/python3.7/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/anaconda3/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/anaconda3/lib/python3.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/anaconda3/lib/python3.7/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/anaconda3/lib/python3.7/site-packages/mlflow/server/handlers.py", line 68, in wrapper
    return func(*args, **kwargs)
  File "/anaconda3/lib/python3.7/site-packages/mlflow/pyfunc/scoring_server.py", line 185, in transformation
    predictions = get_jsonable_obj(raw_predictions, pandas_orient="records")
  File "/anaconda3/lib/python3.7/site-packages/mlflow/utils/__init__.py", line 34, in get_jsonable_obj
    return ndarray2list(data)
  File "/anaconda3/lib/python3.7/site-packages/mlflow/utils/__init__.py", line 20, in ndarray2list
    return [x.item() for x in ndarray]
  File "/anaconda3/lib/python3.7/site-packages/mlflow/utils/__init__.py", line 20, in <listcomp>
    return [x.item() for x in ndarray]
AttributeError: 'str' object has no attribute 'item'

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mlflow/utils/__init__.py`
Content:
```
1 from sys import version_info
2 
3 import numpy as np
4 import pandas as pd
5 
6 
7 PYTHON_VERSION = "{major}.{minor}.{micro}".format(major=version_info.major,
8                                                   minor=version_info.minor,
9                                                   micro=version_info.micro)
10 
11 
12 def ndarray2list(ndarray):
13     """
14     Convert n-dimensional numpy array into nested lists and convert the elements types to native
15     python so that the list is json-able using standard json library.
16     :param ndarray: numpy array
17     :return: list representation of the numpy array with element types convereted to native python
18     """
19     if len(ndarray.shape) <= 1:
20         return [x.item() for x in ndarray]
21     return [ndarray2list(ndarray[i, :]) for i in range(0, ndarray.shape[0])]
22 
23 
24 def get_jsonable_obj(data, pandas_orient="records"):
25     """Attempt to make the data json-able via standard library.
26     Look for some commonly used types that are not jsonable and convert them into json-able ones.
27     Unknown data types are returned as is.
28 
29     :param data: data to be converted, works with pandas and numpy, rest will be returned as is.
30     :param pandas_orient: If `data` is a Pandas DataFrame, it will be converted to a JSON
31                           dictionary using this Pandas serialization orientation.
32     """
33     if isinstance(data, np.ndarray):
34         return ndarray2list(data)
35     if isinstance(data, pd.DataFrame):
36         return data.to_dict(orient=pandas_orient)
37     if isinstance(data, pd.Series):
38         return pd.DataFrame(data).to_dict(orient=pandas_orient)
39     else:  # by default just return whatever this is and hope for the best
40         return data
41 
42 
43 def get_major_minor_py_version(py_version):
44     return ".".join(py_version.split(".")[:2])
45 
46 
47 def get_unique_resource_id(max_length=None):
48     """
49     Obtains a unique id that can be included in a resource name. This unique id is a valid
50     DNS subname.
51 
52     :param max_length: The maximum length of the identifier
53     :return: A unique identifier that can be appended to a user-readable resource name to avoid
54              naming collisions.
55     """
56     import uuid
57     import base64
58     if max_length is not None and max_length <= 0:
59         raise ValueError(
60                 "The specified maximum length for the unique resource id must be positive!")
61 
62     uuid_bytes = uuid.uuid4().bytes
63     # Use base64 encoding to shorten the UUID length. Note that the replacement of the
64     # unsupported '+' symbol maintains uniqueness because the UUID byte string is of a fixed,
65     # 16-byte length
66     uuid_b64 = base64.b64encode(uuid_bytes)
67     if version_info >= (3, 0):
68         # In Python3, `uuid_b64` is a `bytes` object. It needs to be
69         # converted to a string
70         uuid_b64 = uuid_b64.decode("ascii")
71     unique_id = uuid_b64.rstrip('=\n').replace("/", "-").replace("+", "AB").lower()
72     if max_length is not None:
73         unique_id = unique_id[:int(max_length)]
74     return unique_id
75
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mlflow/utils/__init__.py b/mlflow/utils/__init__.py
--- a/mlflow/utils/__init__.py
+++ b/mlflow/utils/__init__.py
@@ -9,18 +9,6 @@
                                                   micro=version_info.micro)
 
 
-def ndarray2list(ndarray):
-    """
-    Convert n-dimensional numpy array into nested lists and convert the elements types to native
-    python so that the list is json-able using standard json library.
-    :param ndarray: numpy array
-    :return: list representation of the numpy array with element types convereted to native python
-    """
-    if len(ndarray.shape) <= 1:
-        return [x.item() for x in ndarray]
-    return [ndarray2list(ndarray[i, :]) for i in range(0, ndarray.shape[0])]
-
-
 def get_jsonable_obj(data, pandas_orient="records"):
     """Attempt to make the data json-able via standard library.
     Look for some commonly used types that are not jsonable and convert them into json-able ones.
@@ -31,7 +19,7 @@
                           dictionary using this Pandas serialization orientation.
     """
     if isinstance(data, np.ndarray):
-        return ndarray2list(data)
+        return data.tolist()
     if isinstance(data, pd.DataFrame):
         return data.to_dict(orient=pandas_orient)
     if isinstance(data, pd.Series):

{"golden_diff": "diff --git a/mlflow/utils/__init__.py b/mlflow/utils/__init__.py\n--- a/mlflow/utils/__init__.py\n+++ b/mlflow/utils/__init__.py\n@@ -9,18 +9,6 @@\n                                                   micro=version_info.micro)\n \n \n-def ndarray2list(ndarray):\n-    \"\"\"\n-    Convert n-dimensional numpy array into nested lists and convert the elements types to native\n-    python so that the list is json-able using standard json library.\n-    :param ndarray: numpy array\n-    :return: list representation of the numpy array with element types convereted to native python\n-    \"\"\"\n-    if len(ndarray.shape) <= 1:\n-        return [x.item() for x in ndarray]\n-    return [ndarray2list(ndarray[i, :]) for i in range(0, ndarray.shape[0])]\n-\n-\n def get_jsonable_obj(data, pandas_orient=\"records\"):\n     \"\"\"Attempt to make the data json-able via standard library.\n     Look for some commonly used types that are not jsonable and convert them into json-able ones.\n@@ -31,7 +19,7 @@\n                           dictionary using this Pandas serialization orientation.\n     \"\"\"\n     if isinstance(data, np.ndarray):\n-        return ndarray2list(data)\n+        return data.tolist()\n     if isinstance(data, pd.DataFrame):\n         return data.to_dict(orient=pandas_orient)\n     if isinstance(data, pd.Series):\n", "issue": "REST API error with sklearn classifier\n### System information\r\n- **Have I written custom code (as opposed to using a stock example script provided in MLflow)**: I wrote a simple random forest classifier using sklearn and the iris dataset to test the mlflow workflow from scratch.\r\n- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**: macOS 10.14.1\r\n- **MLflow installed from (source or binary)**: source (pip install mlflow)\r\n- **MLflow version (run ``mlflow --version``)**: 0.8.2\r\n- **Python version**: 3.7\r\n- **npm version (if running the dev UI):\r\n- **Exact command to reproduce**:curl -X POST -H \"Content-Type:application/json; format=pandas-split\" --data '{\"columns\":[\"sepal_length_cm\", \"sepal_width_cm\", \"petal_length_cm\", \"petal_width_cm\"],\"data\":[[5.3, 1.7, 3.5, 0.5]]}' http://127.0.0.1:1234/invocations\r\n\r\n### Describe the problem\r\nIf sklearn classifier.predict() method returns a categorical variable, then there is a 500 internal server error returned after the above http request.\r\n\r\nThe error is from the following function in mlflow/utils/__init.py__:\r\n\r\ndef ndarray2list(ndarray):\r\n    \"\"\"\r\n    Convert n-dimensional numpy array into nested lists and convert the elements types to native\r\n    python so that the list is json-able using standard json library.\r\n    :param ndarray: numpy array\r\n    :return: list representation of the numpy array with element types convereted to native python\r\n    \"\"\"\r\n    if len(ndarray.shape) <= 1:\r\n        return [x.item() for x in ndarray]\r\n    return [ndarray2list(ndarray[i, :]) for i in range(0, ndarray.shape[0])]\r\n\r\nx.item() fails if the array returned from the sklearn classifier.predict() method contains strings, as in the case of the iris dataset (labels are 'setosa', etc). I corrected the error on my local install by changing the function as below:\r\n\r\ndef ndarray2list(ndarray):\r\n    \"\"\"\r\n    Convert n-dimensional numpy array into nested lists and convert the elements types to native\r\n    python so that the list is json-able using standard json library.\r\n    :param ndarray: numpy array\r\n    :return: list representation of the numpy array with element types convereted to native python\r\n    \"\"\"\r\n    if len(ndarray.shape) <= 1:\r\n        try:\r\n            return [x.item() for x in ndarray]\r\n        except:\r\n            return [x for x in ndarray]\r\n    return [ndarray2list(ndarray[i, :]) for i in range(0, ndarray.shape[0])]\r\n\r\n### Source code / logs\r\nserver side error traceback:\r\n[2019-02-19 10:57:20,668] ERROR in app: Exception on /invocations [POST]\r\nTraceback (most recent call last):\r\n  File \"/anaconda3/lib/python3.7/site-packages/flask/app.py\", line 2292, in wsgi_app\r\n    response = self.full_dispatch_request()\r\n  File \"/anaconda3/lib/python3.7/site-packages/flask/app.py\", line 1815, in full_dispatch_request\r\n    rv = self.handle_user_exception(e)\r\n  File \"/anaconda3/lib/python3.7/site-packages/flask/app.py\", line 1718, in handle_user_exception\r\n    reraise(exc_type, exc_value, tb)\r\n  File \"/anaconda3/lib/python3.7/site-packages/flask/_compat.py\", line 35, in reraise\r\n    raise value\r\n  File \"/anaconda3/lib/python3.7/site-packages/flask/app.py\", line 1813, in full_dispatch_request\r\n    rv = self.dispatch_request()\r\n  File \"/anaconda3/lib/python3.7/site-packages/flask/app.py\", line 1799, in dispatch_request\r\n    return self.view_functions[rule.endpoint](**req.view_args)\r\n  File \"/anaconda3/lib/python3.7/site-packages/mlflow/server/handlers.py\", line 68, in wrapper\r\n    return func(*args, **kwargs)\r\n  File \"/anaconda3/lib/python3.7/site-packages/mlflow/pyfunc/scoring_server.py\", line 185, in transformation\r\n    predictions = get_jsonable_obj(raw_predictions, pandas_orient=\"records\")\r\n  File \"/anaconda3/lib/python3.7/site-packages/mlflow/utils/__init__.py\", line 34, in get_jsonable_obj\r\n    return ndarray2list(data)\r\n  File \"/anaconda3/lib/python3.7/site-packages/mlflow/utils/__init__.py\", line 20, in ndarray2list\r\n    return [x.item() for x in ndarray]\r\n  File \"/anaconda3/lib/python3.7/site-packages/mlflow/utils/__init__.py\", line 20, in <listcomp>\r\n    return [x.item() for x in ndarray]\r\nAttributeError: 'str' object has no attribute 'item'\n", "before_files": [{"content": "from sys import version_info\n\nimport numpy as np\nimport pandas as pd\n\n\nPYTHON_VERSION = \"{major}.{minor}.{micro}\".format(major=version_info.major,\n                                                  minor=version_info.minor,\n                                                  micro=version_info.micro)\n\n\ndef ndarray2list(ndarray):\n    \"\"\"\n    Convert n-dimensional numpy array into nested lists and convert the elements types to native\n    python so that the list is json-able using standard json library.\n    :param ndarray: numpy array\n    :return: list representation of the numpy array with element types convereted to native python\n    \"\"\"\n    if len(ndarray.shape) <= 1:\n        return [x.item() for x in ndarray]\n    return [ndarray2list(ndarray[i, :]) for i in range(0, ndarray.shape[0])]\n\n\ndef get_jsonable_obj(data, pandas_orient=\"records\"):\n    \"\"\"Attempt to make the data json-able via standard library.\n    Look for some commonly used types that are not jsonable and convert them into json-able ones.\n    Unknown data types are returned as is.\n\n    :param data: data to be converted, works with pandas and numpy, rest will be returned as is.\n    :param pandas_orient: If `data` is a Pandas DataFrame, it will be converted to a JSON\n                          dictionary using this Pandas serialization orientation.\n    \"\"\"\n    if isinstance(data, np.ndarray):\n        return ndarray2list(data)\n    if isinstance(data, pd.DataFrame):\n        return data.to_dict(orient=pandas_orient)\n    if isinstance(data, pd.Series):\n        return pd.DataFrame(data).to_dict(orient=pandas_orient)\n    else:  # by default just return whatever this is and hope for the best\n        return data\n\n\ndef get_major_minor_py_version(py_version):\n    return \".\".join(py_version.split(\".\")[:2])\n\n\ndef get_unique_resource_id(max_length=None):\n    \"\"\"\n    Obtains a unique id that can be included in a resource name. This unique id is a valid\n    DNS subname.\n\n    :param max_length: The maximum length of the identifier\n    :return: A unique identifier that can be appended to a user-readable resource name to avoid\n             naming collisions.\n    \"\"\"\n    import uuid\n    import base64\n    if max_length is not None and max_length <= 0:\n        raise ValueError(\n                \"The specified maximum length for the unique resource id must be positive!\")\n\n    uuid_bytes = uuid.uuid4().bytes\n    # Use base64 encoding to shorten the UUID length. Note that the replacement of the\n    # unsupported '+' symbol maintains uniqueness because the UUID byte string is of a fixed,\n    # 16-byte length\n    uuid_b64 = base64.b64encode(uuid_bytes)\n    if version_info >= (3, 0):\n        # In Python3, `uuid_b64` is a `bytes` object. It needs to be\n        # converted to a string\n        uuid_b64 = uuid_b64.decode(\"ascii\")\n    unique_id = uuid_b64.rstrip('=\\n').replace(\"/\", \"-\").replace(\"+\", \"AB\").lower()\n    if max_length is not None:\n        unique_id = unique_id[:int(max_length)]\n    return unique_id\n", "path": "mlflow/utils/__init__.py"}], "after_files": [{"content": "from sys import version_info\n\nimport numpy as np\nimport pandas as pd\n\n\nPYTHON_VERSION = \"{major}.{minor}.{micro}\".format(major=version_info.major,\n                                                  minor=version_info.minor,\n                                                  micro=version_info.micro)\n\n\ndef get_jsonable_obj(data, pandas_orient=\"records\"):\n    \"\"\"Attempt to make the data json-able via standard library.\n    Look for some commonly used types that are not jsonable and convert them into json-able ones.\n    Unknown data types are returned as is.\n\n    :param data: data to be converted, works with pandas and numpy, rest will be returned as is.\n    :param pandas_orient: If `data` is a Pandas DataFrame, it will be converted to a JSON\n                          dictionary using this Pandas serialization orientation.\n    \"\"\"\n    if isinstance(data, np.ndarray):\n        return data.tolist()\n    if isinstance(data, pd.DataFrame):\n        return data.to_dict(orient=pandas_orient)\n    if isinstance(data, pd.Series):\n        return pd.DataFrame(data).to_dict(orient=pandas_orient)\n    else:  # by default just return whatever this is and hope for the best\n        return data\n\n\ndef get_major_minor_py_version(py_version):\n    return \".\".join(py_version.split(\".\")[:2])\n\n\ndef get_unique_resource_id(max_length=None):\n    \"\"\"\n    Obtains a unique id that can be included in a resource name. This unique id is a valid\n    DNS subname.\n\n    :param max_length: The maximum length of the identifier\n    :return: A unique identifier that can be appended to a user-readable resource name to avoid\n             naming collisions.\n    \"\"\"\n    import uuid\n    import base64\n    if max_length is not None and max_length <= 0:\n        raise ValueError(\n                \"The specified maximum length for the unique resource id must be positive!\")\n\n    uuid_bytes = uuid.uuid4().bytes\n    # Use base64 encoding to shorten the UUID length. Note that the replacement of the\n    # unsupported '+' symbol maintains uniqueness because the UUID byte string is of a fixed,\n    # 16-byte length\n    uuid_b64 = base64.b64encode(uuid_bytes)\n    if version_info >= (3, 0):\n        # In Python3, `uuid_b64` is a `bytes` object. It needs to be\n        # converted to a string\n        uuid_b64 = uuid_b64.decode(\"ascii\")\n    unique_id = uuid_b64.rstrip('=\\n').replace(\"/\", \"-\").replace(\"+\", \"AB\").lower()\n    if max_length is not None:\n        unique_id = unique_id[:int(max_length)]\n    return unique_id\n", "path": "mlflow/utils/__init__.py"}]}