diff --git a/csrank/choicefunction/baseline.py b/csrank/choicefunction/baseline.py index a3c69439..165e4395 100644 --- a/csrank/choicefunction/baseline.py +++ b/csrank/choicefunction/baseline.py @@ -19,6 +19,7 @@ def __init__(self, **kwargs): def fit(self, X, Y, **kwd): self._pre_fit() + return self def _predict_scores_fixed(self, X, Y, **kwargs): return np.zeros_like(Y) + Y.mean() diff --git a/csrank/choicefunction/cmpnet_choice.py b/csrank/choicefunction/cmpnet_choice.py index 4bde0cd7..8c1d1562 100644 --- a/csrank/choicefunction/cmpnet_choice.py +++ b/csrank/choicefunction/cmpnet_choice.py @@ -179,3 +179,4 @@ def fit( else: super().fit(X, Y, epochs, callbacks, validation_split, verbose, **kwd) self.threshold_ = 0.5 + return self diff --git a/csrank/choicefunction/fate_choice.py b/csrank/choicefunction/fate_choice.py index 2154468d..8f033606 100644 --- a/csrank/choicefunction/fate_choice.py +++ b/csrank/choicefunction/fate_choice.py @@ -180,3 +180,4 @@ def fit( else: super().fit(X, Y, **kwargs) self.threshold_ = 0.5 + return self diff --git a/csrank/choicefunction/fatelinear_choice.py b/csrank/choicefunction/fatelinear_choice.py index a36666aa..b6473071 100644 --- a/csrank/choicefunction/fatelinear_choice.py +++ b/csrank/choicefunction/fatelinear_choice.py @@ -98,3 +98,4 @@ def fit( else: super().fit(X, Y, epochs, callbacks, validation_split, verbose, **kwd) self.threshold_ = 0.5 + return self diff --git a/csrank/choicefunction/feta_choice.py b/csrank/choicefunction/feta_choice.py index e5a2a738..dc5f689c 100644 --- a/csrank/choicefunction/feta_choice.py +++ b/csrank/choicefunction/feta_choice.py @@ -311,6 +311,7 @@ def fit( else: super().fit(X, Y, epochs, callbacks, validation_split, verbose, **kwd) self.threshold_ = 0.5 + return self def sub_sampling(self, X, Y): if self.n_objects_fit_ <= self.max_number_of_objects: diff --git a/csrank/choicefunction/fetalinear_choice.py b/csrank/choicefunction/fetalinear_choice.py index ec24015b..d07b5191 100644 --- a/csrank/choicefunction/fetalinear_choice.py +++ b/csrank/choicefunction/fetalinear_choice.py @@ -96,3 +96,4 @@ def fit( else: super().fit(X, Y, epochs, callbacks, validation_split, verbose, **kwd) self.threshold_ = 0.5 + return self diff --git a/csrank/choicefunction/generalized_linear_model.py b/csrank/choicefunction/generalized_linear_model.py index 10ba751e..3ddb3df5 100644 --- a/csrank/choicefunction/generalized_linear_model.py +++ b/csrank/choicefunction/generalized_linear_model.py @@ -256,6 +256,7 @@ def fit( **kwargs, ) self.threshold_ = 0.5 + return self def _fit( self, diff --git a/csrank/choicefunction/pairwise_choice.py b/csrank/choicefunction/pairwise_choice.py index 36450e32..6aebf7b0 100644 --- a/csrank/choicefunction/pairwise_choice.py +++ b/csrank/choicefunction/pairwise_choice.py @@ -114,3 +114,4 @@ def fit(self, X, Y, tune_size=0.1, thin_thresholds=1, verbose=0, **kwd): else: super().fit(X, Y, **kwd) self.threshold_ = 0.5 + return self diff --git a/csrank/choicefunction/ranknet_choice.py b/csrank/choicefunction/ranknet_choice.py index c687fb87..1aa8bb88 100644 --- a/csrank/choicefunction/ranknet_choice.py +++ b/csrank/choicefunction/ranknet_choice.py @@ -166,3 +166,4 @@ def fit( else: super().fit(X, Y, epochs, callbacks, validation_split, verbose, **kwd) self.threshold_ = 0.5 + return self diff --git a/csrank/core/cmpnet_core.py b/csrank/core/cmpnet_core.py index 40c8e74c..932a2ee3 100644 --- a/csrank/core/cmpnet_core.py +++ b/csrank/core/cmpnet_core.py @@ -157,12 +157,15 @@ def fit( """ self._pre_fit() _n_instances, self.n_objects_fit_, self.n_object_features_fit_ = X.shape - x1, x2, y_double = self._convert_instances_(X, Y) - logger.debug("Instances created {}".format(x1.shape[0])) self._construct_layers() self.model_ = self.construct_model() + if self.n_objects_fit_ < 2: + # Nothing to learn here, no pairwise comparisons can be generated. + return self + x1, x2, y_double = self._convert_instances_(X, Y) + logger.debug("Instances created {}".format(x1.shape[0])) logger.debug("Finished Creating the model, now fitting started") self.model_.fit( [x1, x2], @@ -175,6 +178,7 @@ def fit( **kwd, ) logger.debug("Fitting Complete") + return self def predict_pair(self, a, b, **kwargs): return self.model_.predict([a, b], **kwargs) diff --git a/csrank/core/fate_linear.py b/csrank/core/fate_linear.py index 6b2f83dc..8c60debf 100644 --- a/csrank/core/fate_linear.py +++ b/csrank/core/fate_linear.py @@ -112,6 +112,7 @@ def fit( self.bias1_ = tf_session.run(self.b1) self.weight2_ = tf_session.run(self.W2) self.bias2_ = tf_session.run(self.b2) + return self def _fit_(self, X, Y, epochs, n_instances, tf_session, verbose): try: diff --git a/csrank/core/fate_network.py b/csrank/core/fate_network.py index 1de78df1..2948762d 100644 --- a/csrank/core/fate_network.py +++ b/csrank/core/fate_network.py @@ -140,7 +140,9 @@ def join_input_layers(self, input_layer, *layers, n_layers, n_objects): for j in range(self.n_hidden_joint_layers): joint = self.joint_layers[j](joint) scores.append(self.scorer(joint)) - scores = concatenate(scores, name="final_scores") + scores = ( + concatenate(scores, name="final_scores") if len(scores) > 1 else scores[0] + ) logger.debug("Done") return scores @@ -518,6 +520,7 @@ def fit( refit=refit, **kwargs, ) + return self def fit_generator( self, diff --git a/csrank/core/feta_linear.py b/csrank/core/feta_linear.py index 1e5f38a8..789709cd 100644 --- a/csrank/core/feta_linear.py +++ b/csrank/core/feta_linear.py @@ -162,6 +162,10 @@ def fit( self._pre_fit() # Global Variables Initializer n_instances, self.n_objects_fit_, self.n_object_features_fit_ = X.shape + if self.n_objects_fit_ < 2: + # Nothing to learn here, model cannot be constructed without any + # instance pairs. + return self self._construct_model_(self.n_objects_fit_) init = tf.global_variables_initializer() @@ -179,6 +183,7 @@ def fit( self.weight2_ = tf_session.run(self.W2) self.bias2_ = tf_session.run(self.b2) self.W_last_ = tf_session.run(self.W_out_) + return self def _fit_(self, X, Y, epochs, n_instances, tf_session, verbose): try: diff --git a/csrank/core/feta_network.py b/csrank/core/feta_network.py index 6c66eaf2..7c441a97 100644 --- a/csrank/core/feta_network.py +++ b/csrank/core/feta_network.py @@ -303,6 +303,9 @@ def fit( logger.debug("Enter fit function...") X, Y = self.sub_sampling(X, Y) + if self.n_objects_fit_ < 2: + # Nothing to learn, can't construct a model. + return self self.model_ = self.construct_model() logger.debug("Starting gradient descent...") @@ -316,6 +319,7 @@ def fit( verbose=verbose, **kwd, ) + return self def sub_sampling(self, X, Y): if self.n_objects_fit_ > self.max_number_of_objects: diff --git a/csrank/core/pairwise_svm.py b/csrank/core/pairwise_svm.py index 4e6eac94..c3f68acd 100644 --- a/csrank/core/pairwise_svm.py +++ b/csrank/core/pairwise_svm.py @@ -75,7 +75,6 @@ def fit(self, X, Y, **kwargs): """ self._pre_fit() _n_instances, self.n_objects_fit_, self.n_object_features_fit_ = X.shape - x_train, y_single = self._convert_instances_(X, Y) if self.use_logistic_regression: self.model_ = LogisticRegression( C=self.C, @@ -93,6 +92,10 @@ def fit(self, X, Y, **kwargs): ) logger.info("Linear SVC model ") + if self.n_objects_fit_ < 2: + # Nothing to learn, cannot create pairwise instances. + return self + x_train, y_single = self._convert_instances_(X, Y) if self.normalize: std_scalar = StandardScaler() x_train = std_scalar.fit_transform(x_train) @@ -103,6 +106,7 @@ def fit(self, X, Y, **kwargs): if self.fit_intercept: self.weights_ = np.append(self.weights_, self.model_.intercept_) logger.debug("Fitting Complete") + return self def _predict_scores_fixed(self, X, **kwargs): assert X.shape[-1] == self.n_object_features_fit_ diff --git a/csrank/core/ranknet_core.py b/csrank/core/ranknet_core.py index 0d0908fe..88344fff 100644 --- a/csrank/core/ranknet_core.py +++ b/csrank/core/ranknet_core.py @@ -147,15 +147,19 @@ def fit( """ self._pre_fit() _n_instances, self.n_objects_fit_, self.n_object_features_fit_ = X.shape - X1, X2, Y_single = self._convert_instances_(X, Y) - - logger.debug("Instances created {}".format(X1.shape[0])) logger.debug("Creating the model") self._construct_layers() # Model with input as two objects and output as probability of x1>x2 self.model_ = self.construct_model() + + if self.n_objects_fit_ < 2: + # Nothing to learn, cannot create pairwise comparisons. + return self + X1, X2, Y_single = self._convert_instances_(X, Y) + + logger.debug("Instances created {}".format(X1.shape[0])) logger.debug("Finished Creating the model, now fitting started") self.model_.fit( @@ -170,6 +174,7 @@ def fit( ) logger.debug("Fitting Complete") + return self @property def scoring_model(self): diff --git a/csrank/dataset_reader/objectranking/util.py b/csrank/dataset_reader/objectranking/util.py index c7e5f7a1..b1306840 100644 --- a/csrank/dataset_reader/objectranking/util.py +++ b/csrank/dataset_reader/objectranking/util.py @@ -109,7 +109,9 @@ def complete_linear_regression_dataset(X, rankings): Y_single = [] for features, rank in zip(X, rankings): X1.extend(features) - norm_ranks = rank / np.max(rank, axis=0) + min_rank = np.min(rank, axis=0) + max_rank = np.max(rank, axis=0) + norm_ranks = (rank - min_rank + 1) / (max_rank - min_rank + 1) Y_single.extend(norm_ranks) X1 = np.array(X1) Y_single = np.array(Y_single) diff --git a/csrank/discretechoice/baseline.py b/csrank/discretechoice/baseline.py index 6f8585b6..5b098f21 100644 --- a/csrank/discretechoice/baseline.py +++ b/csrank/discretechoice/baseline.py @@ -24,6 +24,7 @@ def _pre_fit(self): def fit(self, X, Y, **kwd): self._pre_fit() + return self def _predict_scores_fixed(self, X, **kwargs): n_instances, n_objects, n_features = X.shape diff --git a/csrank/discretechoice/generalized_nested_logit.py b/csrank/discretechoice/generalized_nested_logit.py index 7ba6cdb4..6830976b 100644 --- a/csrank/discretechoice/generalized_nested_logit.py +++ b/csrank/discretechoice/generalized_nested_logit.py @@ -337,6 +337,7 @@ def fit( self.n_nests = self.n_objects_fit_ + int(self.n_objects_fit_ / 2) self.construct_model(X, Y) fit_pymc3_model(self, sampler, draws, tune, vi_params, **kwargs) + return self def _predict_scores_fixed(self, X, **kwargs): mean_trace = dict(pm.summary(self.trace_)["mean"]) diff --git a/csrank/discretechoice/mixed_logit_model.py b/csrank/discretechoice/mixed_logit_model.py index a1dc687a..08259f7c 100644 --- a/csrank/discretechoice/mixed_logit_model.py +++ b/csrank/discretechoice/mixed_logit_model.py @@ -218,6 +218,7 @@ def fit( _n_instances, self.n_objects_fit_, self.n_object_features_fit_ = X.shape self.construct_model(X, Y) fit_pymc3_model(self, sampler, draws, tune, vi_params, **kwargs) + return self def _predict_scores_fixed(self, X, **kwargs): summary = dict(pm.summary(self.trace_)["mean"]) diff --git a/csrank/discretechoice/model_selector.py b/csrank/discretechoice/model_selector.py index 078d9719..4c6f137e 100644 --- a/csrank/discretechoice/model_selector.py +++ b/csrank/discretechoice/model_selector.py @@ -105,6 +105,7 @@ def fit(self, X, Y): self.parameter_ind[j][1], ) self.fit_learner(X, Y, key) + return self def fit_learner(self, X, Y, key): learner = self.learner_cls(**self.model_params) diff --git a/csrank/discretechoice/multinomial_logit_model.py b/csrank/discretechoice/multinomial_logit_model.py index 7db65b46..0415e3c1 100644 --- a/csrank/discretechoice/multinomial_logit_model.py +++ b/csrank/discretechoice/multinomial_logit_model.py @@ -217,6 +217,7 @@ def fit( _n_instances, self.n_objects_fit_, self.n_object_features_fit_ = X.shape self.construct_model(X, Y) fit_pymc3_model(self, sampler, draws, tune, vi_params, **kwargs) + return self def _predict_scores_fixed(self, X, **kwargs): d = dict(pm.summary(self.trace_)["mean"]) diff --git a/csrank/discretechoice/nested_logit_model.py b/csrank/discretechoice/nested_logit_model.py index 5b4e1d46..be9c4862 100644 --- a/csrank/discretechoice/nested_logit_model.py +++ b/csrank/discretechoice/nested_logit_model.py @@ -392,6 +392,7 @@ def fit( self.random_state_ = check_random_state(self.random_state) self.construct_model(X, Y) fit_pymc3_model(self, sampler, draws, tune, vi_params, **kwargs) + return self def _predict_scores_fixed(self, X, **kwargs): y_nests = self.create_nests(X) diff --git a/csrank/discretechoice/paired_combinatorial_logit.py b/csrank/discretechoice/paired_combinatorial_logit.py index 54d90269..f6a13d7f 100644 --- a/csrank/discretechoice/paired_combinatorial_logit.py +++ b/csrank/discretechoice/paired_combinatorial_logit.py @@ -332,6 +332,7 @@ def fit( self.n_nests = len(self.nests_indices) self.construct_model(X, Y) fit_pymc3_model(self, sampler, draws, tune, vi_params, **kwargs) + return self def _predict_scores_fixed(self, X, **kwargs): mean_trace = dict(pm.summary(self.trace_)["mean"]) diff --git a/csrank/discretechoice/pairwise_discrete_choice.py b/csrank/discretechoice/pairwise_discrete_choice.py index b50d678f..28211704 100644 --- a/csrank/discretechoice/pairwise_discrete_choice.py +++ b/csrank/discretechoice/pairwise_discrete_choice.py @@ -76,4 +76,4 @@ def _convert_instances_(self, X, Y): def fit(self, X, Y, **kwd): self._pre_fit() _n_instances, self.n_objects_fit_, self.n_object_features_fit_ = X.shape - super().fit(X, Y, **kwd) + return super().fit(X, Y, **kwd) diff --git a/csrank/dyadranking/fate_dyad_ranker.py b/csrank/dyadranking/fate_dyad_ranker.py index 37c10fcd..59d00244 100644 --- a/csrank/dyadranking/fate_dyad_ranker.py +++ b/csrank/dyadranking/fate_dyad_ranker.py @@ -6,6 +6,7 @@ class FATEDyadRanker(FATENetwork, DyadRanker): def fit(self, Xo, Xc, Y, **kwargs): self._pre_fit() + return self def predict_scores(self, Xo, Xc, **kwargs): return self.model_.predict([Xo, Xc], **kwargs) diff --git a/csrank/layers.py b/csrank/layers.py index e92b9625..a5e971c9 100644 --- a/csrank/layers.py +++ b/csrank/layers.py @@ -134,7 +134,8 @@ def _create_model(self, shape): set_mappings.append((i, curr)) # TODO: is feature_repr used outside? - feature_repr = average([x for (j, x) in set_mappings]) + x_values = [x for (j, x) in set_mappings] + feature_repr = average(x_values) if len(x_values) > 1 else x_values[0] self.cached_models[n_objects] = Model(inputs=input_layer, outputs=feature_repr) diff --git a/csrank/objectranking/baseline.py b/csrank/objectranking/baseline.py index acb5a6b1..4593966a 100644 --- a/csrank/objectranking/baseline.py +++ b/csrank/objectranking/baseline.py @@ -16,7 +16,7 @@ def __init__(self, random_state=None, **kwargs): :param kwargs: Keyword arguments for the algorithms """ - self.random_state = (random_state,) + self.random_state = random_state def _pre_fit(self): super()._pre_fit() @@ -24,6 +24,7 @@ def _pre_fit(self): def fit(self, X, Y, **kwd): self._pre_fit() + return self def _predict_scores_fixed(self, X, **kwargs): n_instances, n_objects, n_features = X.shape diff --git a/csrank/objectranking/expected_rank_regression.py b/csrank/objectranking/expected_rank_regression.py index 71dfde37..cb83f293 100644 --- a/csrank/objectranking/expected_rank_regression.py +++ b/csrank/objectranking/expected_rank_regression.py @@ -127,6 +127,7 @@ def fit(self, X, Y, **kwargs): if self.fit_intercept: self.weights_ = np.append(self.weights_, self.model_.intercept_) logger.debug("Fitting Complete") + return self def _predict_scores_fixed(self, X, **kwargs): n_instances, n_objects, n_features = X.shape diff --git a/csrank/objectranking/list_net.py b/csrank/objectranking/list_net.py index c5fc19cd..8f5bac6a 100644 --- a/csrank/objectranking/list_net.py +++ b/csrank/objectranking/list_net.py @@ -199,6 +199,7 @@ def fit( **kwd, ) logger.debug("Fitting Complete") + return self def construct_model(self): """ @@ -215,7 +216,7 @@ def construct_model(self): for hidden_layer in self.hidden_layers: hid = [hidden_layer(x) for x in hid] outputs = [self.output_node(x) for x in hid] - merged = concatenate(outputs) + merged = concatenate(outputs) if len(outputs) > 1 else outputs[0] model = Model(inputs=self.input_layer, outputs=merged) model.compile( loss=self.loss_function, diff --git a/csrank/objectranking/rank_svm.py b/csrank/objectranking/rank_svm.py index 89a56339..8be3c68f 100644 --- a/csrank/objectranking/rank_svm.py +++ b/csrank/objectranking/rank_svm.py @@ -60,7 +60,7 @@ def __init__( def fit(self, X, Y, **kwargs): self._pre_fit() _n_instances, self.n_objects_fit_, self.n_object_features_fit_ = X.shape - super().fit(X, Y, **kwargs) + return super().fit(X, Y, **kwargs) def _convert_instances_(self, X, Y): logger.debug("Creating the Dataset") diff --git a/csrank/tests/test_fate.py b/csrank/tests/test_fate.py index 7ce2cc69..96023824 100644 --- a/csrank/tests/test_fate.py +++ b/csrank/tests/test_fate.py @@ -27,7 +27,7 @@ def predict(self, *args, **kwargs): pass def fit(self, *args, **kwargs): - pass + return self grc = MockClass() grc._initialize_optimizer()