Repository: mars-project/mars

Issue:
[BUG] LightGBM predicted result has wrong dtype
<!--
Thank you for your contribution!

Please review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.
-->

**Describe the bug**
A clear and concise description of what the bug is.

LightGBM's predicted result's dtype now is the same as label data's dtype, this behavior will lead to wrong dtype in regression model.

**To Reproduce**
``` Python
import mars.tensor as mt
import mars.dataframe as md
from mars.learn.contrib.lightgbm import LGBMRegressor
n_rows = 1000
n_columns = 10
chunk_size = 50
rs = np.random.RandomState(0)
X = md.DataFrame(pd.DataFrame(rs.rand(n_rows, n_columns), columns=['c' + str(i) for i in range(n_columns)]))
y = md.DataFrame(pd.DataFrame(rs.randint(0, 100, n_rows)))
classifier = LGBMRegressor()
classifier.fit(X, y, eval_set=[(X, y)])
prediction = classifier.predict(X)
print(prediction.dtype)

>>> int64
```
the true dtype should be `float64`.



Discussion:

Resolving patch:
diff --git a/mars/learn/contrib/lightgbm/train.py b/mars/learn/contrib/lightgbm/train.py
--- a/mars/learn/contrib/lightgbm/train.py
+++ b/mars/learn/contrib/lightgbm/train.py
@@ -296,7 +296,10 @@ def execute(cls, ctx, op: "LGBMTrain"):
                       eval_set=eval_set, eval_sample_weight=eval_sample_weight,
                       eval_init_score=eval_init_score, **op.kwds)
 
-            if hasattr(label_val, 'dtype'):
+            if op.model_type == LGBMModelType.RANKER or \
+                    op.model_type == LGBMModelType.REGRESSOR:
+                model.set_params(out_dtype_=np.dtype('float'))
+            elif hasattr(label_val, 'dtype'):
                 model.set_params(out_dtype_=label_val.dtype)
             else:
                 model.set_params(out_dtype_=label_val.dtypes[0])


Test patch:
diff --git a/mars/learn/contrib/lightgbm/tests/test_ranker.py b/mars/learn/contrib/lightgbm/tests/test_ranker.py
--- a/mars/learn/contrib/lightgbm/tests/test_ranker.py
+++ b/mars/learn/contrib/lightgbm/tests/test_ranker.py
@@ -54,6 +54,8 @@ def testLocalRanker(self):
         self.assertEqual(prediction.shape[0], len(self.X))
 
         self.assertIsInstance(prediction, mt.Tensor)
+        result = prediction.fetch()
+        self.assertEqual(prediction.dtype, result.dtype)
 
         # test weight
         weight = mt.random.rand(X.shape[0])
@@ -63,3 +65,6 @@ def testLocalRanker(self):
 
         self.assertEqual(prediction.ndim, 1)
         self.assertEqual(prediction.shape[0], len(self.X))
+
+        result = prediction.fetch()
+        self.assertEqual(prediction.dtype, result.dtype)
diff --git a/mars/learn/contrib/lightgbm/tests/test_regressor.py b/mars/learn/contrib/lightgbm/tests/test_regressor.py
--- a/mars/learn/contrib/lightgbm/tests/test_regressor.py
+++ b/mars/learn/contrib/lightgbm/tests/test_regressor.py
@@ -33,7 +33,7 @@ def setUp(self):
         chunk_size = 20
         rs = mt.random.RandomState(0)
         self.X = rs.rand(n_rows, n_columns, chunk_size=chunk_size)
-        self.y = rs.rand(n_rows, chunk_size=chunk_size)
+        self.y = rs.randint(0, 10, n_rows, chunk_size=chunk_size)
 
         self.session = new_session().as_default()
         self._old_executor = self.session._sess._executor
@@ -53,6 +53,8 @@ def testLocalRegressor(self):
         self.assertEqual(prediction.shape[0], len(self.X))
 
         self.assertIsInstance(prediction, mt.Tensor)
+        result = prediction.fetch()
+        self.assertEqual(prediction.dtype, result.dtype)
 
         # test weight
         weight = mt.random.rand(X.shape[0])
@@ -62,3 +64,5 @@ def testLocalRegressor(self):
 
         self.assertEqual(prediction.ndim, 1)
         self.assertEqual(prediction.shape[0], len(self.X))
+        result = prediction.fetch()
+        self.assertEqual(prediction.dtype, result.dtype)