(architecture=None, prediction_periods=1)
return neural_net
Create and compile a Keras Sequential model based on the model's architecture dataframe.
        
        The architecture dataframe should define the layers and compilation parameters for a Keras sequential model.
        Each layer has to be defined across three columns: layer, args, kwargs.
        The first layer should define the input_shape. This SSE does not support the alternative input_dim argument.
        The final row of the dataframe should define the compilation parameters.

        For example:
          layer        args    kwargs
        0 Dense        [12]    {'input_shape': (8,), activation': 'relu'}
        1 Dropout      [0.25]  {}
        2 Dense        [8]     {'activation': 'relu'}
        3 Dense        [1]     {'activation': 'sigmoid'}
        4 Compilation  []      {'loss': 'binary_crossentropy', 'optimizer': 'adam', 'metrics': ['accuracy']}

        If you want to specify parameters for the optimizer, you can add that as the second last row of the architecture:
        ...
        4 SGD          []      {'lr': 0.01, 'clipvalue': 0.5}
        5 Compilation  []      {'loss': 'binary_crossentropy'}

        Layer wrappers can be used by including them in the layer name followed by a space:
        0 TimeDistributed Dense ...
        ...
        For the Bidirectional wrapper the merge_mode parameter can be specified in the kwargs.

        When using recurrent or convolutional layers you will need to pass a valid input_shape to reshape the data.
        Additionally, the feature definitions should include an 'identifier' variable that will be used for reshaping.
        E.g. The identifier is a date field and you want to use a LSTM with 10 time steps on a dataset with 20 features.
        0 LSTM         [64]    {'input_shape': (10,20), 'activation': 'relu''
        
        For further information on the columns refer to the project documentation: 
        https://github.com/nabeel-oz/qlik-py-tools
Create and compile a Keras Sequential model based on the model's architecture dataframe.
        
        The architecture dataframe should define the layers and compilation parameters for a Keras sequential model.
        Each layer has to be defined across three columns: layer, args, kwargs.
        The first layer should define the input_shape. This SSE does not support the alternative input_dim argument.
        The final row of the dataframe should define the compilation parameters.
Create and compile a Keras Sequential model based on the model s architecture dataframe . The architecture dataframe should define the layers and compilation parameters for a Keras sequential model . Each layer has to be defined across three columns : layer args kwargs . The first layer should define the input_shape . This SSE does not support the alternative input_dim argument . The final row of the dataframe should define the compilation parameters .
def _keras_build_fn(architecture=None, prediction_periods=1):
        """
        Create and compile a Keras Sequential model based on the model's architecture dataframe.
        
        The architecture dataframe should define the layers and compilation parameters for a Keras sequential model.
        Each layer has to be defined across three columns: layer, args, kwargs.
        The first layer should define the input_shape. This SSE does not support the alternative input_dim argument.
        The final row of the dataframe should define the compilation parameters.

        For example:
          layer        args    kwargs
        0 Dense        [12]    {'input_shape': (8,), activation': 'relu'}
        1 Dropout      [0.25]  {}
        2 Dense        [8]     {'activation': 'relu'}
        3 Dense        [1]     {'activation': 'sigmoid'}
        4 Compilation  []      {'loss': 'binary_crossentropy', 'optimizer': 'adam', 'metrics': ['accuracy']}

        If you want to specify parameters for the optimizer, you can add that as the second last row of the architecture:
        ...
        4 SGD          []      {'lr': 0.01, 'clipvalue': 0.5}
        5 Compilation  []      {'loss': 'binary_crossentropy'}

        Layer wrappers can be used by including them in the layer name followed by a space:
        0 TimeDistributed Dense ...
        ...
        For the Bidirectional wrapper the merge_mode parameter can be specified in the kwargs.

        When using recurrent or convolutional layers you will need to pass a valid input_shape to reshape the data.
        Additionally, the feature definitions should include an 'identifier' variable that will be used for reshaping.
        E.g. The identifier is a date field and you want to use a LSTM with 10 time steps on a dataset with 20 features.
        0 LSTM         [64]    {'input_shape': (10,20), 'activation': 'relu''
        
        For further information on the columns refer to the project documentation: 
        https://github.com/nabeel-oz/qlik-py-tools
        """
        
        # List of optimizers that can be specified in the architecture
        optimizers = ['Adadelta', 'Adagrad', 'Adam', 'Adamax', 'Nadam', 'RMSprop', 'SGD']
        
        # The model definition should contain at least one layer and the compilation parameters
        if architecture is None or len(architecture) < 2:
            err = "Invalid Keras architecture. Expected at least one layer and compilation parameters."
            raise Exception(err)
        # The last row of the model definition should contain compilation parameters
        elif not architecture.iloc[-1,0].capitalize() in ['Compile', 'Compilation']:
            err = "Invalid Keras architecture. The last row of the model definition should provide 'Compile' parameters."
            raise Exception(err)
        
        # sys.stdout.write("Architecture Data Frame in _keras_build_fn:\n{}\n\n".format(architecture.to_string()))

        neural_net = keras.models.Sequential()

        for i in architecture.index:
            # Name items in the row for easy access
            name, args, kwargs = architecture.iloc[i,0], architecture.iloc[i,1], architecture.iloc[i,2]

            # The last row of the DataFrame should provide compilation keyword arguments
            if i == max(architecture.index):
                # Check if an optimizer with custom parameters has been defined
                try:
                    kwargs = kwargs.copy() # Copy so that we don't modify the architecture dataframe
                    kwargs['optimizer'] = opt
                except UnboundLocalError:
                    pass
                
                # Compile the model
                neural_net.compile(**kwargs)
            # Watch out for a row providing optimizer parameters
            elif name in optimizers:
                opt = getattr(keras.optimizers, name)(**kwargs) 
            # All other rows of the DataFrame define the model architecture
            else:
                # Check if the name includes a layer wrapper e.g. TimeDistributed Dense
                names = name.split(' ')
                if len(names) == 2:
                    wrapper = names[0]
                    name = names[1]
                    
                    # Get wrapper kwargs
                    wrapper_kwargs = dict()
                    if 'merge_mode' in kwargs:
                        wrapper_kwargs['merge_mode'] = kwargs.pop('merge_mode')
                else:
                    wrapper = None

                # Create a keras layer of the required type with the provided positional and keyword arguments
                layer = getattr(keras.layers, name)(*args, **kwargs)

                if wrapper:
                    # Create the layer wrapper
                    wrapper = getattr(keras.layers, wrapper)(layer, **wrapper_kwargs)
                    # Add the layer wrapper to the model
                    neural_net.add(wrapper)    
                else:
                    # Add the layer to the model
                    neural_net.add(layer)
        
        # Get the number of nodes for the final layer
        output_features = neural_net.layers[-1].get_config()['units']
        assert prediction_periods == output_features, "The number of nodes in the final layer of the network must match the prediction_periods execution argument. Expected {} nodes but got {}.".format(prediction_periods, output_features)
        
        return neural_net
def _keras_build_fn ( architecture = None , prediction_periods = 1 ) : # List of optimizers that can be specified in the architecture optimizers = [ 'Adadelta' , 'Adagrad' , 'Adam' , 'Adamax' , 'Nadam' , 'RMSprop' , 'SGD' ] # The model definition should contain at least one layer and the compilation parameters if architecture is None or len ( architecture ) < 2 : err = "Invalid Keras architecture. Expected at least one layer and compilation parameters." raise Exception ( err ) # The last row of the model definition should contain compilation parameters elif not architecture . iloc [ - 1 , 0 ] . capitalize ( ) in [ 'Compile' , 'Compilation' ] : err = "Invalid Keras architecture. The last row of the model definition should provide 'Compile' parameters." raise Exception ( err ) # sys.stdout.write("Architecture Data Frame in _keras_build_fn:\n{}\n\n".format(architecture.to_string())) neural_net = keras . models . Sequential ( ) for i in architecture . index : # Name items in the row for easy access name , args , kwargs = architecture . iloc [ i , 0 ] , architecture . iloc [ i , 1 ] , architecture . iloc [ i , 2 ] # The last row of the DataFrame should provide compilation keyword arguments if i == max ( architecture . index ) : # Check if an optimizer with custom parameters has been defined try : kwargs = kwargs . copy ( ) # Copy so that we don't modify the architecture dataframe kwargs [ 'optimizer' ] = opt except UnboundLocalError : pass # Compile the model neural_net . compile ( * * kwargs ) # Watch out for a row providing optimizer parameters elif name in optimizers : opt = getattr ( keras . optimizers , name ) ( * * kwargs ) # All other rows of the DataFrame define the model architecture else : # Check if the name includes a layer wrapper e.g. TimeDistributed Dense names = name . split ( ' ' ) if len ( names ) == 2 : wrapper = names [ 0 ] name = names [ 1 ] # Get wrapper kwargs wrapper_kwargs = dict ( ) if 'merge_mode' in kwargs : wrapper_kwargs [ 'merge_mode' ] = kwargs . pop ( 'merge_mode' ) else : wrapper = None # Create a keras layer of the required type with the provided positional and keyword arguments layer = getattr ( keras . layers , name ) ( * args , * * kwargs ) if wrapper : # Create the layer wrapper wrapper = getattr ( keras . layers , wrapper ) ( layer , * * wrapper_kwargs ) # Add the layer wrapper to the model neural_net . add ( wrapper ) else : # Add the layer to the model neural_net . add ( layer ) # Get the number of nodes for the final layer output_features = neural_net . layers [ - 1 ] . get_config ( ) [ 'units' ] assert prediction_periods == output_features , "The number of nodes in the final layer of the network must match the prediction_periods execution argument. Expected {} nodes but got {}." . format ( prediction_periods , output_features ) return neural_net