class ConstructableNetwork(SupervisedLearning, BaseNetwork): """ Class contains functionality that helps work with network that have constructable layers architecture. Parameters ---------- connection : list, tuple or object Network architecture. That variables could be described in different ways. The simples one is a list or tuple that contains integers. Each integer describe layer input size. For example, ``(2, 4, 1)`` means that network will have 3 layers with 2 input units, 4 hidden units and 1 output unit. The one limitation of that method is that all layers automaticaly would with sigmoid actiavtion function. Other way is just a list of ``layers.BaseLayer``` class instances. For example: ``[Input(2), Tanh(4), Relu(1)]``. And the most readable one is pipeline ``Input(2) > Tanh(4) > Relu(1)``. error : {{'mse', 'rmse', 'mae', 'categorical_crossentropy', \ 'binary_crossentropy'}} or function Function that calculate prediction error. Defaults to ``mse``. * ``mae`` - Mean Absolute Error. * ``mse`` - Mean Squared Error. * ``rmse`` - Root Mean Squared Error. * ``msle`` - Mean Squared Logarithmic Error. * ``rmsle`` - Root Mean Squared Logarithmic Error. * ``categorical_crossentropy`` - Categorical cross entropy. * ``binary_crossentropy`` - Binary cross entropy. * Custom function that accept two mandatory arguments. The first one is expected value and the second one is predicted value. Example: ``custom_func(expected, predicted)`` {BaseNetwork.step} {BaseNetwork.show_epoch} {BaseNetwork.shuffle_data} {BaseNetwork.epoch_end_signal} {BaseNetwork.train_end_signal} {Verbose.verbose} Attributes ---------- {BaseNetwork.errors} {BaseNetwork.train_errors} {BaseNetwork.validation_errors} {BaseNetwork.last_epoch} """ error = ErrorFunctionProperty(default='mse', choices={ 'mae': errors.mae, 'mse': errors.mse, 'rmse': errors.rmse, 'msle': errors.msle, 'rmsle': errors.rmsle, 'binary_crossentropy': errors.binary_crossentropy, 'categorical_crossentropy': errors.categorical_crossentropy, }) def __init__(self, connection, *args, **kwargs): self.connection = clean_layers(connection) self.layers = list(self.connection) self.input_layer = self.layers[0] self.hidden_layers = self.layers[1:] self.output_layer = self.layers[-1] self.init_layers() super(ConstructableNetwork, self).__init__(*args, **kwargs) self.logs.message("THEANO", "Initializing Theano variables and " "functions.") start_init_time = time.time() self.variables = AttributeKeyDict( network_input=create_input_variable( self.input_layer, variable_name='x' ), network_output=create_output_variable( self.error, variable_name='y' ), ) self.methods = AttributeKeyDict() self.init_variables() self.init_methods() finish_init_time = time.time() self.logs.message("THEANO", "Initialization finished sucessfully. " "It took {:.2f} seconds" "".format(finish_init_time - start_init_time)) def init_variables(self): """ Initialize Theano variables. """ network_input = self.variables.network_input network_output = self.variables.network_output train_prediction = self.connection.output(network_input) with self.connection.disable_training_state(): prediction = self.connection.output(network_input) self.variables.update( step=theano.shared(name='step', value=asfloat(self.step)), epoch=theano.shared(name='epoch', value=asfloat(self.last_epoch)), prediction_func=prediction, train_prediction_func=train_prediction, error_func=self.error(network_output, train_prediction), validation_error_func=self.error(network_output, prediction), ) def init_methods(self): """ Initialize all methods that needed for prediction and training procedures. """ network_input = self.variables.network_input network_output = self.variables.network_output self.methods.predict = theano.function( inputs=[self.variables.network_input], outputs=self.variables.prediction_func ) self.methods.train_epoch = theano.function( inputs=[network_input, network_output], outputs=self.variables.error_func, updates=self.init_train_updates(), ) self.methods.prediction_error = theano.function( inputs=[network_input, network_output], outputs=self.variables.validation_error_func ) def init_layers(self): """ Initialize layers in the same order as they were list in network initialization step. """ self.connection.initialize() def init_train_updates(self): """ Initialize train function update in Theano format that would be trigger after each training epoch. """ updates = [] for layer in self.layers: updates.extend(self.init_layer_updates(layer)) return updates def init_layer_updates(self, layer): """ Initialize train function update in Theano format that would be trigger after each training epoch for each layer. Parameters ---------- layer : object Any layer that inherit from layers.BaseLayer class. Returns ------- list Update that excaptable by ``theano.function``. There should be a lits that contains tuples with 2 elements. First one should be parameter that would be updated after epoch and the second one should update rules for this parameter. For example parameter could be a layer's weight and bias. """ updates = [] for parameter in layer.parameters: updates.extend(self.init_param_updates(layer, parameter)) updates.extend(layer.updates) return updates def init_param_updates(self, layer, parameter): """ Initialize parameter updates. Parameters ---------- layer : object Any layer that inherit from BaseLayer class. parameter : object Usualy it is a weight or bias. Returns ------- list List of updates related to the specified parameter. """ return [] def format_input_data(self, input_data): """ Input data format is depend on the input layer structure. Parameters ---------- input_data : array-like or None Returns ------- array-like or None Function returns formatted array. """ if input_data is not None: is_feature1d = does_layer_accept_1d_feature(self.input_layer) return format_data(input_data, is_feature1d) def format_target_data(self, target_data): """ Target data format is depend on the output layer structure. Parameters ---------- target_data : array-like or None Returns ------- array-like or None Function returns formatted array. """ if target_data is not None: is_feature1d = does_layer_accept_1d_feature(self.output_layer) return format_data(target_data, is_feature1d) def prediction_error(self, input_data, target_data): """ Calculate prediction accuracy for input data. Parameters ---------- input_data : array-like target_data : array-like Returns ------- float Prediction error. """ return self.methods.prediction_error( self.format_input_data(input_data), self.format_target_data(target_data) ) def predict(self, input_data): """ Return prediction results for the input data. Parameters ---------- input_data : array-like Returns ------- array-like """ input_data = self.format_input_data(input_data) return self.methods.predict(input_data) def on_epoch_start_update(self, epoch): """ Function would be trigger before run all training procedure related to the current epoch. Parameters ---------- epoch : int Current epoch number. """ super(ConstructableNetwork, self).on_epoch_start_update(epoch) self.variables.epoch.set_value(epoch) def train(self, input_train, target_train, input_test=None, target_test=None, *args, **kwargs): """ Trains neural network. """ return super(ConstructableNetwork, self).train( self.format_input_data(input_train), self.format_target_data(target_train), self.format_input_data(input_test), self.format_target_data(target_test), *args, **kwargs ) def train_epoch(self, input_train, target_train): """ Trains neural network over one epoch. Parameters ---------- input_data : array-like target_data : array-like Returns ------- float Prediction error. """ return self.methods.train_epoch(input_train, target_train) def architecture(self): """ Shows network's architecture in the terminal if ``verbose`` parameter is equal to ``True``. """ self.logs.title("Network's architecture") values = [] for index, layer in enumerate(self.layers, start=1): input_shape = preformat_layer_shape(layer.input_shape) output_shape = preformat_layer_shape(layer.output_shape) classname = layer.__class__.__name__ values.append((index, input_shape, classname, output_shape)) table.TableBuilder.show_full_table( columns=[ table.Column(name="#"), table.Column(name="Input shape"), table.Column(name="Layer Type"), table.Column(name="Output shape"), ], values=values, stdout=self.logs.write, ) self.logs.newline() def __repr__(self): n_layers = len(self.connection) if n_layers > 5: connection = '[... {} layers ...]'.format(n_layers) else: connection = self.connection return "{}({}, {})".format(self.class_name(), connection, self._repr_options())