def _inverse(self, y): return mult(y, pinv(self.sf)) + self.avg
def _train(self, x, block_size=None, train_mode=None, node_weights=None, edge_weights=None, **argv): self.input_dim = x.shape[1] if self.output_dim is None: self.output_dim = self.input_dim print "Training iGSFANode..." if (not self.reconstruct_with_sfa) and (self.offsetting_mode in [None, "data_dependent"]): self.multiple_train(x, block_size=block_size, train_mode=train_mode, node_weights=node_weights, edge_weights=edge_weights) # scheduler = None, n_parallel=None return if (not self.reconstruct_with_sfa) and (self.offsetting_mode not in [None, "data_dependent"]): er = "'reconstruct_with_sfa' (" + str(self.reconstruct_with_sfa) + ") must be True when the scaling" + \ "method (" + str(self.offsetting_mode) + ") is neither 'None' not 'data_dependent'" raise Exception(er) # else use regular method: # Remove mean before expansion self.x_mean = x.mean(axis=0) x_zm = x - self.x_mean # Reorder or pre-process the data before it is expanded, but only if there is really an expansion if self.pre_expansion_node_class and self.exp_node: self.pre_expansion_node = self.pre_expansion_node_class( output_dim=self.pre_expansion_output_dim) # GSFANode() or a WhitheningNode() self.pre_expansion_node.train(x_zm, block_size=block_size, train_mode=train_mode) # Some arguments might not be necessary self.pre_expansion_node.stop_training() x_pre_exp = self.pre_expansion_node.execute(x_zm) else: x_pre_exp = x_zm # Expand data if self.exp_node: print "expanding x..." exp_x = self.exp_node.execute(x_pre_exp) # x_zm else: exp_x = x_pre_exp self.expanded_dim = exp_x.shape[1] if self.max_lenght_slow_part is None: sfa_output_dim = min(self.expanded_dim, self.output_dim) else: sfa_output_dim = min(self.max_lenght_slow_part, self.expanded_dim, self.output_dim) # Apply SFA to expanded data self.sfa_node = GSFANode(output_dim=sfa_output_dim) self.sfa_node.train_params(exp_x, params={"block_size": block_size, "train_mode": train_mode, "node_weights": node_weights, "edge_weights": edge_weights}) # , node_weights=None, edge_weights=None, scheduler = None, n_parallel=None) self.sfa_node.stop_training() print "self.sfa_node.d", self.sfa_node.d # Decide how many slow features are preserved (either use Delta_T=max_preserved_sfa when # max_preserved_sfa is a float, or preserve max_preserved_sfa features when max_preserved_sfa is an integer) if isinstance(self.max_preserved_sfa, float): # here self.max_lenght_slow_part should be considered self.num_sfa_features_preserved = (self.sfa_node.d <= self.max_preserved_sfa).sum() elif isinstance(self.max_preserved_sfa, int): # here self.max_lenght_slow_part should be considered self.num_sfa_features_preserved = self.max_preserved_sfa else: ex = "Cannot handle type of self.max_preserved_sfa" print ex raise Exception(ex) if self.num_sfa_features_preserved > self.output_dim: self.num_sfa_features_preserved = self.output_dim SFANode_reduce_output_dim(self.sfa_node, self.num_sfa_features_preserved) print "sfa execute..." sfa_x = self.sfa_node.execute(exp_x) # Truncate leaving only slowest features (this might be redundant) # sfa_x = sfa_x[:,0:self.num_sfa_features_preserved].copy() #WARNING, MODIFIED # normalize sfa_x self.sfa_x_mean = sfa_x.mean(axis=0) self.sfa_x_std = sfa_x.std(axis=0) print "self.sfa_x_mean=", self.sfa_x_mean print "self.sfa_x_std=", self.sfa_x_std if (self.sfa_x_std == 0).any(): er = "zero-component detected" raise Exception(er) n_sfa_x = (sfa_x - self.sfa_x_mean) / self.sfa_x_std if self.reconstruct_with_sfa: # Compress input if self.compress_input_with_pca: self.compress_node = mdp.nodes.PCANode(output_dim=self.compression_out_dim) self.compress_node.train(x_zm) x_pca = self.compress_node.execute(x_zm) print "compress: %d components out of %d sufficed for the desired compression_out_dim" % \ (x_pca.shape[1], x_zm.shape[1]), self.compression_out_dim else: x_pca = x_zm # approximate input linearly, done inline to preserve node for future use print "training linear regression..." self.lr_node = mdp.nodes.LinearRegressionNode() self.lr_node.train(n_sfa_x, x_pca) # Notice that the input "x"=n_sfa_x and the output to learn is "y" = x_pca self.lr_node.stop_training() x_pca_app = self.lr_node.execute(n_sfa_x) if self.compress_input_with_pca: x_app = self.compress_node.inverse(x_pca_app) else: x_app = x_pca_app else: x_app = numpy.zeros_like(x_zm) # Remove linear approximation sfa_removed_x = x_zm - x_app # print "Data_variance(x_zm)=", data_variance(x_zm) # print "Data_variance(x_app)=", data_variance(x_app) # print "Data_variance(sfa_removed_x)=", data_variance(sfa_removed_x) # print "x_app.mean(axis=0)=", x_app # TODO:Compute variance removed by linear approximation print "ranking method..." # AKA Laurenz method for feature scaling( +rotation) if self.reconstruct_with_sfa and self.offsetting_mode == "QR_decomposition": M = self.lr_node.beta[1:, :].T # bias is used by default, we do not need to consider it Q, R = numpy.linalg.qr(M) self.Q = Q self.R = R self.Rpinv = pinv(R) s_n_sfa_x = numpy.dot(n_sfa_x, R.T) # AKA my method for feature scaling (no rotation) elif self.reconstruct_with_sfa and self.offsetting_mode == "sensitivity_based_pure": beta = self.lr_node.beta[1:, :] # bias is used by default, we do not need to consider it sens = (beta ** 2).sum(axis=1) self.magn_n_sfa_x = sens s_n_sfa_x = n_sfa_x * self.magn_n_sfa_x ** self.exponent_variance print "method: sensitivity_based_pure enforced" # AKA alternative method for feature scaling (no rotation) elif self.reconstruct_with_sfa and self.offsetting_mode == "sensitivity_based_normalized": beta = self.lr_node.beta[1:, :] # bias is used by default, we do not need to consider it sens = (beta ** 2).sum(axis=1) self.magn_n_sfa_x = sens * ((x_pca_app ** 2).sum(axis=1).mean() / sens.sum()) s_n_sfa_x = n_sfa_x * self.magn_n_sfa_x ** self.exponent_variance print "method: sensitivity_based_normalized enforced" elif self.offsetting_mode is None: self.magn_n_sfa_x = 1.0 s_n_sfa_x = n_sfa_x * self.magn_n_sfa_x print "method: constant amplitude for all slow features" elif self.offsetting_mode == "data_dependent": self.magn_n_sfa_x = 0.01 * numpy.min(x_zm.std(axis=0)) + 1e-1 # SFA components have the variance of the weakest PCA feature # self.magn_n_sfa_x = 0.01 * numpy.min( # x_zm.var(axis=0)) # SFA components have a variance 1/10000 times the smallest data variance s_n_sfa_x = n_sfa_x * self.magn_n_sfa_x # Scale according to ranking print "method: data dependent (setting magn_n_fa_x later)" else: er = "unknown feature offsetting mode=" + str(self.offsetting_mode) + "for reconstruct_with_sfa=" + \ str(self.reconstruct_with_sfa) raise Exception(er) print "training PCA..." pca_output_dim = self.output_dim - self.num_sfa_features_preserved # if pca_output_dim == 0: # self.pca_node = mdp.nodes.IdentityNode() # else: # # WARNING: WHY WAS I EXTRACTING ALL PCA COMPONENTS!!?? INEFFICIENT!!!! self.pca_node = mdp.nodes.PCANode(output_dim=pca_output_dim) # reduce=True) #output_dim = pca_out_dim) self.pca_node.train(sfa_removed_x) self.pca_node.stop_training() # TODO:check that pca_out_dim > 0 print "executing PCA..." pca_x = self.pca_node.execute(sfa_removed_x) if self.pca_node.output_dim + self.num_sfa_features_preserved < self.output_dim: er = "Error, the number of features computed is SMALLER than the output dimensionality of the node: " + \ "self.pca_node.output_dim=" + str(self.pca_node.output_dim) + ", self.num_sfa_features_preserved=" + \ str(self.num_sfa_features_preserved) + ", self.output_dim=" + str(self.output_dim) raise Exception(er) # Finally output is the concatenation of scaled slow features and remaining pca components sfa_pca_x = numpy.concatenate((s_n_sfa_x, pca_x), axis=1) sfa_pca_x_truncated = sfa_pca_x[:, 0:self.output_dim] # Compute explained variance from amplitudes of output compared to amplitudes of input # Only works because amplitudes of SFA are scaled to be equal to explained variance, because PCA is # a rotation, and because data has zero mean self.evar = (sfa_pca_x_truncated ** 2).sum() / (x_zm ** 2).sum() print "Variance(output) / Variance(input) is ", self.evar self.stop_training()
def _inverse(self, y): """Return inverse of the slow feature response. :return: The inverse of the slow feature response. """ return mult(y, pinv(self.sf)) + self.avgnode.avg