Skip to content

Commit

Permalink
ADD: few simple LC model tests (#191)
Browse files Browse the repository at this point in the history
* ADD: basic LC model tests

* ADD: eagerly exec for SimpleMNL

* ADD: call to compute_report()

* ENH: remove dependecy to tfp for Normal cdf

* ENH: make latent class tests faster
  • Loading branch information
VincentAuriau authored Nov 30, 2024
1 parent a9db338 commit 5213a05
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 18 deletions.
9 changes: 6 additions & 3 deletions choice_learn/models/conditional_logit.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Conditional MNL model."""

import logging
import math

import numpy as np
import pandas as pd
Expand Down Expand Up @@ -645,10 +646,12 @@ def compute_report(self, choice_dataset):
pandas.DataFrame
A DF with estimation, Std Err, z_value and p_value for each coefficient.
"""
import tensorflow_probability as tfp

def phi(x):
"""Cumulative distribution function for the standard normal distribution."""
return (1.0 + math.erf(x / math.sqrt(2.0))) / 2.0

weights_std = self.get_weights_std(choice_dataset)
dist = tfp.distributions.Normal(loc=0.0, scale=1.0)

names = []
z_values = []
Expand All @@ -663,7 +666,7 @@ def compute_report(self, choice_dataset):
names.append(f"{weight.name[:-2]}")
estimations.append(weight.numpy()[0][j])
z_values.append(weight.numpy()[0][j] / weights_std[i].numpy())
p_z.append(2 * (1 - dist.cdf(tf.math.abs(z_values[-1])).numpy()))
p_z.append(2 * (1 - phi(tf.math.abs(z_values[-1]).numpy())))
i += 1

return pd.DataFrame(
Expand Down
2 changes: 2 additions & 0 deletions choice_learn/models/latent_class_base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ def instantiate(self, **kwargs):
for model in self.models:
model.instantiate(**kwargs)

self.instantiated = True

# @tf.function
def batch_predict(
self,
Expand Down
9 changes: 6 additions & 3 deletions choice_learn/models/simple_mnl.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""

import logging
import math

import pandas as pd
import tensorflow as tf
Expand Down Expand Up @@ -254,10 +255,12 @@ def compute_report(self, choice_dataset):
pandas.DataFrame
A DF with estimation, Std Err, z_value and p_value for each coefficient.
"""
import tensorflow_probability as tfp

def phi(x):
"""Cumulative distribution function for the standard normal distribution."""
return (1.0 + math.erf(x / math.sqrt(2.0))) / 2.0

weights_std = self.get_weights_std(choice_dataset)
dist = tfp.distributions.Normal(loc=0.0, scale=1.0)

names = []
z_values = []
Expand All @@ -272,7 +275,7 @@ def compute_report(self, choice_dataset):
names.append(f"{weight.name[:-2]}")
estimations.append(weight.numpy()[j])
z_values.append(weight.numpy()[j] / weights_std[i].numpy())
p_z.append(2 * (1 - dist.cdf(tf.math.abs(z_values[-1])).numpy()))
p_z.append(2 * (1 - phi(tf.math.abs(z_values[-1]).numpy())))
i += 1

return pd.DataFrame(
Expand Down
4 changes: 2 additions & 2 deletions notebooks/introduction/3_model_clogit.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1361,7 +1361,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "tf_env",
"language": "python",
"name": "python3"
},
Expand All @@ -1375,7 +1375,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.14"
"version": "3.11.4"
}
},
"nbformat": 4,
Expand Down
3 changes: 2 additions & 1 deletion tests/integration_tests/models/test_conditional_logit.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ def test_mode_canada_fit():
canada_dataset = load_modecanada(as_frame=False, preprocessing="tutorial")

model = ConditionalLogit(coefficients=coefficients)
model.fit(canada_dataset, get_report=True)
model.fit(canada_dataset)
model.compute_report(canada_dataset)

total_nll = model.evaluate(canada_dataset) * len(canada_dataset)
assert total_nll <= 1874.4, f"Got NLL: {total_nll}"
Expand Down
25 changes: 21 additions & 4 deletions tests/integration_tests/models/test_latent_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_latent_simple_mnl():
"""Test the simple latent class model fit() method."""
tf.config.run_functions_eagerly(True)
lc_model = LatentClassSimpleMNL(
n_latent_classes=3, fit_method="mle", optimizer="lbfgs", epochs=1000, lbfgs_tolerance=1e-20
n_latent_classes=3, fit_method="mle", optimizer="lbfgs", epochs=1000, lbfgs_tolerance=1e-8
)
_, _ = lc_model.fit(elec_dataset)

Expand All @@ -25,7 +25,7 @@ def test_latent_clogit():
"""Test the conditional logit latent class model fit() method."""
tf.config.run_functions_eagerly(True)
lc_model = LatentClassConditionalLogit(
n_latent_classes=3, fit_method="mle", optimizer="lbfgs", epochs=1000, lbfgs_tolerance=1e-12
n_latent_classes=3, fit_method="mle", optimizer="lbfgs", epochs=40, lbfgs_tolerance=1e-8
)
lc_model.add_shared_coefficient(
coefficient_name="pf", feature_name="pf", items_indexes=[0, 1, 2, 3]
Expand Down Expand Up @@ -58,11 +58,28 @@ def test_manual_lc():
model_parameters={"add_exit_choice": False},
n_latent_classes=3,
fit_method="mle",
epochs=1000,
epochs=40,
optimizer="lbfgs",
lbfgs_tolerance=1e-12,
lbfgs_tolerance=1e-8,
)

manual_lc.instantiate(n_items=4, n_shared_features=0, n_items_features=6)
_ = manual_lc.fit(elec_dataset)
assert manual_lc.evaluate(elec_dataset) < 1.15


def test_manual_lc_gd():
"""Test manual specification of Latent Class Simple MNL model with gradient descent."""
tf.config.run_functions_eagerly(True)
manual_lc = BaseLatentClassModel(
model_class=SimpleMNL,
model_parameters={"add_exit_choice": False},
n_latent_classes=3,
fit_method="mle",
epochs=10,
optimizer="Adam",
)
manual_lc.instantiate(n_items=4, n_shared_features=0, n_items_features=6)
nll_before = manual_lc.evaluate(elec_dataset)
_ = manual_lc.fit(elec_dataset)
assert manual_lc.evaluate(elec_dataset) < nll_before
13 changes: 8 additions & 5 deletions tests/integration_tests/models/test_simple_mnl.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
dataset = load_swissmetro()


def test_simple_mnl_lbfgs_fit_with_lbfgs():
def test_simple_mnl_fit_with_lbfgs():
"""Tests that SimpleMNL can fit with LBFGS."""
tf.config.run_functions_eagerly(True)
global dataset

model = SimpleMNL(epochs=20)
model.fit(dataset)
model.evaluate(dataset)
_ = model.fit(dataset, get_report=True)
_ = model.evaluate(dataset)
assert model.evaluate(dataset) < 1.0


Expand All @@ -24,7 +25,7 @@ def test_simple_mnl_lbfgs_fit_with_adam():
global dataset

model = SimpleMNL(epochs=20, optimizer="adam", batch_size=256)
model.fit(dataset, get_report=True)
model.fit(dataset)
model.evaluate(dataset)
assert model.evaluate(dataset) < 1.0

Expand All @@ -34,10 +35,12 @@ def test_that_endpoints_run():
No verification of results.
"""
tf.config.run_functions_eagerly(True)
global dataset

model = SimpleMNL(epochs=20)
model.fit(dataset)
_ = model.fit(dataset)
_ = model.compute_report(dataset)
model.evaluate(dataset)
model.predict_probas(dataset)
assert True

0 comments on commit 5213a05

Please sign in to comment.