Skip to content

Commit

Permalink
Do not directly pass an optimizer to CmpNet
Browse files Browse the repository at this point in the history
An optimizer is a tensorflow object, which (at least in graph mode in
tf1) is not deepcopy-able. Even if we were able to deeocopy it, we
probably wouldn't want to since it contains state. Scikit-learn needs to
be able to deepcopy an estimators arguments so that it can create copies
and derivatives of it.

Instead we pass a function that constructs the optimizer on-demand. This
function is deepcopy-able.
  • Loading branch information
timokau committed May 22, 2020
1 parent b8f8288 commit a4e49c7
Show file tree
Hide file tree
Showing 7 changed files with 22 additions and 17 deletions.
2 changes: 1 addition & 1 deletion csrank/choicefunction/cmpnet_choice.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def __init__(
kernel_regularizer=l2(1e-4),
kernel_initializer="lecun_normal",
activation="relu",
optimizer=SGD(lr=1e-4, nesterov=True, momentum=0.9),
optimizer=lambda: SGD(lr=1e-4, nesterov=True, momentum=0.9),
metrics=["binary_accuracy"],
batch_size=256,
random_state=None,
Expand Down
15 changes: 7 additions & 8 deletions csrank/core/cmpnet_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from keras import backend as K
from keras import Input
from keras import Model
from keras import optimizers
from keras.layers import concatenate
from keras.layers import Dense
from keras.optimizers import SGD
Expand All @@ -30,7 +29,7 @@ def __init__(
kernel_regularizer=l2(1e-4),
kernel_initializer="lecun_normal",
activation="relu",
optimizer=SGD(lr=1e-4, nesterov=True, momentum=0.9),
optimizer=lambda: SGD(lr=1e-4, nesterov=True, momentum=0.9),
metrics=["binary_accuracy"],
batch_size=256,
random_state=None,
Expand All @@ -49,8 +48,7 @@ def __init__(
self.kernel_initializer = kernel_initializer
self.loss_function = loss_function

self.optimizer = optimizers.get(optimizer)
self._optimizer_config = self.optimizer.get_config()
self.optimizer = optimizer

self.n_hidden = n_hidden
self.n_units = n_units
Expand Down Expand Up @@ -104,6 +102,7 @@ def construct_model(self):
model: keras :class:`Model`
Neural network to learn the CmpNet utility score
"""
self.optimizer_ = self.optimizer() # instantiate the optimizer
x1x2 = concatenate([self.x1, self.x2])
x2x1 = concatenate([self.x2, self.x1])
self.logger.debug("Creating the model")
Expand All @@ -117,7 +116,7 @@ def construct_model(self):
merged_output = concatenate([N_g, N_l])
model = Model(inputs=[self.x1, self.x2], outputs=merged_output)
model.compile(
loss=self.loss_function, optimizer=self.optimizer, metrics=self.metrics
loss=self.loss_function, optimizer=self.optimizer_, metrics=self.metrics
)
return model

Expand Down Expand Up @@ -212,7 +211,7 @@ def clear_memory(self, **kwargs):
sess = tf.Session()
K.set_session(sess)

self.optimizer = self.optimizer.from_config(self._optimizer_config)
self.optimizer_ = self.optimizer()
self._construct_layers(
kernel_regularizer=self.kernel_regularizer,
kernel_initializer=self.kernel_initializer,
Expand Down Expand Up @@ -255,8 +254,8 @@ def set_tunable_parameters(
self.n_units = n_units
self.kernel_regularizer = l2(reg_strength)
self.batch_size = batch_size
self.optimizer = self.optimizer.from_config(self._optimizer_config)
K.set_value(self.optimizer.lr, learning_rate)
self.optimizer_ = self.optimizer()
K.set_value(self.optimizer_.lr, learning_rate)
self._construct_layers(
kernel_regularizer=self.kernel_regularizer,
kernel_initializer=self.kernel_initializer,
Expand Down
2 changes: 1 addition & 1 deletion csrank/discretechoice/cmpnet_discrete_choice.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def __init__(
kernel_regularizer=l2(1e-4),
kernel_initializer="lecun_normal",
activation="relu",
optimizer=SGD(lr=1e-4, nesterov=True, momentum=0.9),
optimizer=lambda: SGD(lr=1e-4, nesterov=True, momentum=0.9),
metrics=["binary_accuracy"],
batch_size=256,
random_state=None,
Expand Down
8 changes: 5 additions & 3 deletions csrank/objectranking/cmp_net.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(
kernel_regularizer=l2(1e-4),
kernel_initializer="lecun_normal",
activation="relu",
optimizer=SGD(lr=1e-4, nesterov=True, momentum=0.9),
optimizer=lambda: SGD(lr=1e-4, nesterov=True, momentum=0.9),
metrics=["binary_accuracy"],
batch_size=256,
random_state=None,
Expand Down Expand Up @@ -65,8 +65,10 @@ def __init__(
Regularizer function applied to all the hidden weight matrices.
activation : function or string
Type of activation function to use in each hidden layer
optimizer : function or string
Optimizer to use during stochastic gradient descent
optimizer : function
Constructor for an optimizer to use during stochastic gradient
descent. Must be a function without arguments that returns a
Keras optimizer.
metrics : list
List of metrics to evaluate during training (can be
non-differentiable)
Expand Down
2 changes: 1 addition & 1 deletion csrank/tests/test_choice_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def get_vals(values):
),
CMPNET_CHOICE: (
CmpNetChoiceFunction,
{"optimizer": optimizer},
{"optimizer": lambda: optimizer},
get_vals([0.8554, 0.8649, 0.966]),
),
GLM_CHOICE: (GeneralizedLinearModel, {}, get_vals([0.9567, 0.9955, 1.0])),
Expand Down
2 changes: 1 addition & 1 deletion csrank/tests/test_discrete_choice.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def get_vals(values=[1.0, 1.0]):
),
CMPNET_DC: (
CmpNetDiscreteChoiceFunction,
{"optimizer": optimizer},
{"optimizer": lambda: optimizer},
get_vals([0.994, 1.0]),
),
FATE_DC: (
Expand Down
8 changes: 6 additions & 2 deletions csrank/tests/test_ranking.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
(0.0, 1.0),
),
RANKNET: (RankNet, {"optimizer": optimizer}, (0.0, 1.0)),
CMPNET: (CmpNet, {"optimizer": optimizer}, (0.0, 1.0)),
CMPNET: (CmpNet, {"optimizer": lambda: optimizer}, (0.0, 1.0)),
LISTNET: (ListNet, {"n_top": 3, "optimizer": optimizer}, (0.0, 1.0)),
ERR: (ExpectedRankRegression, {}, (0.0, 1.0)),
RANKSVM: (RankSVM, {}, (0.0, 1.0)),
Expand Down Expand Up @@ -79,7 +79,11 @@ def check_params_tunable(tunable_obj, params, rtol=1e-2, atol=1e-4):
)
else:
assert value == expected
elif key == "learning_rate" and "optimizer" in tunable_obj.__dict__.keys():
elif (
key == "learning_rate"
and "optimizer" in tunable_obj.__dict__.keys()
and hasattr(tunable_obj.optimizer, "get_config")
):
key = (
"learning_rate"
if "learning_rate" in tunable_obj.optimizer.get_config().keys()
Expand Down

0 comments on commit a4e49c7

Please sign in to comment.