Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 23 additions & 14 deletions glmdisc/_fitNN.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@
from tensorflow.keras.layers import concatenate


def _layer_output(layer, x):
"""
Applies a layer to an array of inputs.

:param layer: a Keras layer
:param numpy.ndarray x: the inputs of the layer
:returns: a list holding the numpy array of outputs (the format returned by
:code:`tf.keras.backend.function`, which does not exist in Keras 3)
:rtype: list
"""
return [np.asarray(layer(tf.convert_to_tensor(x, dtype=tf.float32)))]


def _plot_cont_soft_quant(self):
"""
Plots the soft quantization while fitting
Expand Down Expand Up @@ -128,15 +141,13 @@ def on_epoch_end(self, batch, logs=None):
for j in range(self.d_cont):
self.best_weights.append(self.current_weights[j])
self.best_outputs.append(
tf.keras.backend.function([self.glmdisc_object.model_nn["liste_layers_quant"][j].input],
[self.glmdisc_object.model_nn["liste_layers_quant"][j].output])(
[self.glmdisc_object.predictors_cont[:, j, np.newaxis]]))
_layer_output(self.glmdisc_object.model_nn["liste_layers_quant"][j],
self.glmdisc_object.predictors_cont[:, j, np.newaxis]))
for j in range(self.d_qual):
self.best_weights.append(self.current_weights[j + self.d_cont])
self.best_outputs.append(
tf.keras.backend.function([self.glmdisc_object.model_nn["liste_layers_qual"][j].input],
[self.glmdisc_object.model_nn["liste_layers_qual"][j].output])(
[self.glmdisc_object.predictors_qual_dummy[j]]))
_layer_output(self.glmdisc_object.model_nn["liste_layers_qual"][j],
self.glmdisc_object.predictors_qual_dummy[j]))

if self.plot_fit:
_plot_cont_soft_quant(self)
Expand Down Expand Up @@ -208,14 +219,12 @@ def _from_layers_to_proba_training(d_cont, d_qual, glmdisc_object):
results = [None] * (d_cont + d_qual)

for j in range(d_cont):
results[j] = tf.keras.backend.function([glmdisc_object.model_nn["liste_layers_quant"][j].input],
[glmdisc_object.model_nn["liste_layers_quant"][j].output])(
[glmdisc_object.predictors_cont[glmdisc_object.train_rows, j, np.newaxis]])
results[j] = _layer_output(glmdisc_object.model_nn["liste_layers_quant"][j],
glmdisc_object.predictors_cont[glmdisc_object.train_rows, j, np.newaxis])

for j in range(d_qual):
results[j + d_cont] = tf.keras.backend.function([glmdisc_object.model_nn["liste_layers_qual"][j].input],
[glmdisc_object.model_nn["liste_layers_qual"][j].output])(
[glmdisc_object.predictors_qual_dummy[j][glmdisc_object.train_rows]])
results[j + d_cont] = _layer_output(glmdisc_object.model_nn["liste_layers_qual"][j],
glmdisc_object.predictors_qual_dummy[j][glmdisc_object.train_rows])

return results

Expand Down Expand Up @@ -285,7 +294,7 @@ def _evaluate_disc(history, d_cont: int, d_qual: int, glmdisc_object):
results = [None] * (d_cont + d_qual)
for j in range(d_cont + d_qual):
results[j] = np.argmax(proba[j][0], axis=1)
encoders.append(sk.preprocessing.OneHotEncoder(categories='auto', sparse=False, handle_unknown="ignore"))
encoders.append(sk.preprocessing.OneHotEncoder(categories='auto', sparse_output=False, handle_unknown="ignore"))
X_transformed = np.concatenate(
(X_transformed,
encoders[j].fit_transform(
Expand Down Expand Up @@ -429,7 +438,7 @@ def _parse_kwargs(self, **kwargs):
if "optimizer" in kwargs:
optim = kwargs['optimizer']
else:
optim = tensorflow.keras.optimizers.RMSprop(lr=0.5, rho=0.9, epsilon=None, decay=0.0)
optim = tensorflow.keras.optimizers.RMSprop(learning_rate=0.5, rho=0.9, epsilon=1e-7)

self.model_nn["callbacks"] = [
ReduceLROnPlateau(
Expand Down
32 changes: 26 additions & 6 deletions tests/tests_NN/test_NN_fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
import random
import glmdisc
import sklearn as sk
from glmdisc._fitNN import _initialize_neural_net
import tensorflow
from glmdisc._fitNN import _initialize_neural_net, _layer_output, _parse_kwargs
from tensorflow.keras.optimizers import Adagrad
from tensorflow.keras.callbacks import EarlyStopping

Expand All @@ -24,10 +23,8 @@ def test_kwargs(caplog):
plot=True,
optimizer=Adagrad(),
callbacks=EarlyStopping())
assert isinstance(model.model_nn['tensorflow_model'].optimizer,
tensorflow.python.keras.optimizer_v2.adagrad.Adagrad)
assert isinstance(model.model_nn['callbacks'][-1],
tensorflow.python.keras.callbacks.EarlyStopping)
assert isinstance(model.model_nn['tensorflow_model'].optimizer, Adagrad)
assert isinstance(model.model_nn['callbacks'][-1], EarlyStopping)
with pytest.raises(ValueError):
model.fit(predictors_cont=x,
predictors_qual=None,
Expand Down Expand Up @@ -228,3 +225,26 @@ def fail_fit(self, y):

monkeypatch.setattr(sk.preprocessing.LabelEncoder, "fit", fail_fit)
_initialize_neural_net(model)


def test_default_optimizer():
model = glmdisc.Glmdisc(algorithm="NN")
optim = _parse_kwargs(model)
assert float(optim.learning_rate) == pytest.approx(0.5)


def test_layer_output():
from tensorflow.keras.layers import Dense, Input
layer = Dense(3, activation='softmax')
layer(Input((1, )))
kernel = np.array([[1., -1., 0.]])
bias = np.array([0., .5, 0.])
layer.set_weights([kernel, bias])
x = np.array([[0.], [2.]])

output = _layer_output(layer, x)

logits = x @ kernel + bias
expected = np.exp(logits) / np.exp(logits).sum(axis=1, keepdims=True)
assert isinstance(output, list) and len(output) == 1
np.testing.assert_allclose(output[0], expected, rtol=1e-5)