Repository: keras-team/autokeras

Issue:
Add a class to generate an MLP
<!---
Please label your issue with `new_task_module`.
-->

### Suggested Name
<!---
-->
MlpGenerator

### Task Description
<!---
A clear and concise description of the machine learning task to be added, its problem statement and learning outcome.
-->
Add a class named MlpGenerator. Create a superclass that would be inherited by CnnGenerator and MlpGenerator.



Discussion:

Resolving patch:
diff --git a/autokeras/constant.py b/autokeras/constant.py
--- a/autokeras/constant.py
+++ b/autokeras/constant.py
@@ -18,9 +18,12 @@ class Constant:
 
     DENSE_DROPOUT_RATE = 0.5
     CONV_DROPOUT_RATE = 0.25
+    MLP_DROPOUT_RATE = 0.25
     CONV_BLOCK_DISTANCE = 2
     DENSE_BLOCK_DISTANCE = 1
     MODEL_LEN = 3
+    MLP_MODEL_LEN = 3
+    MLP_MODEL_WIDTH = 5
     MODEL_WIDTH = 64
 
     # ModelTrainer
diff --git a/autokeras/nn/generator.py b/autokeras/nn/generator.py
--- a/autokeras/nn/generator.py
+++ b/autokeras/nn/generator.py
@@ -2,12 +2,22 @@
 from autokeras.nn.graph import Graph
 from autokeras.nn.layers import StubBatchNormalization, StubConv, StubDropout, StubPooling, StubDense, StubFlatten, \
     StubReLU, StubGlobalPooling
+from abc import abstractmethod
 
 
-class CnnGenerator:
+class NetworkGenerator:
     def __init__(self, n_output_node, input_shape):
         self.n_output_node = n_output_node
         self.input_shape = input_shape
+
+    @abstractmethod
+    def generate(self, model_len, model_width):
+        pass
+
+
+class CnnGenerator(NetworkGenerator):
+    def __init__(self, n_output_node, input_shape):
+        super(CnnGenerator, self).__init__(n_output_node, input_shape)
         if len(self.input_shape) > 4:
             raise ValueError('The input dimension is too high.')
         if len(self.input_shape) < 2:
@@ -33,3 +43,28 @@ def generate(self, model_len=Constant.MODEL_LEN, model_width=Constant.MODEL_WIDT
         output_node_id = graph.add_layer(StubReLU(), output_node_id)
         graph.add_layer(StubDense(model_width, self.n_output_node), output_node_id)
         return graph
+
+
+class MlpGenerator(NetworkGenerator):
+    def __init__(self, n_output_node, input_shape):
+        super(MlpGenerator, self).__init__(n_output_node, input_shape)
+        if len(self.input_shape) > 1:
+            raise ValueError('The input dimension is too high.')
+
+    def generate(self, model_len=Constant.MLP_MODEL_LEN, model_width=Constant.MLP_MODEL_WIDTH):
+        if type(model_width) is list and not len(model_width) == model_len:
+            raise ValueError('The length of \'model_width\' does not match \'model_len\'')
+        elif type(model_width) is int:
+            model_width = [model_width] * model_len
+
+        graph = Graph(self.input_shape[0], False)
+        output_node_id = 0
+        n_nodes_prev_layer = self.input_shape[0]
+        for width in model_width:
+            output_node_id = graph.add_layer(StubDense(n_nodes_prev_layer, width), output_node_id)
+            output_node_id = graph.add_layer(StubDropout(Constant.MLP_DROPOUT_RATE), output_node_id)
+            output_node_id = graph.add_layer(StubReLU(), output_node_id)
+            n_nodes_prev_layer = width
+
+        graph.add_layer(StubDense(n_nodes_prev_layer, self.n_output_node), output_node_id)
+        return graph


Test patch:
diff --git a/tests/nn/test_generator.py b/tests/nn/test_generator.py
--- a/tests/nn/test_generator.py
+++ b/tests/nn/test_generator.py
@@ -4,10 +4,18 @@
 import torch
 
 
-def test_default_generator():
+def test_default_cnn_generator():
     generator = CnnGenerator(3, (28, 28, 1))
     graph = generator.generate()
     model = graph.produce_model()
     inputs = torch.Tensor(np.ones((100, 1, 28, 28)))
     print(model(inputs).size())
     assert isinstance(model, TorchModel)
+
+
+def test_default_mlp_generator():
+    generator = MlpGenerator(5, (4,))
+
+    graph = generator.generate(3, 5)
+    model = graph.produce_model()
+    assert isinstance(model, TorchModel)