Source code for pycmtensor.models.MNL
# models.py
from time import perf_counter
from typing import Dict, List, Union
import aesara.tensor as aet
from aesara import function, pprint
from aesara.tensor.var import TensorVariable
from ..functions import log_likelihood, logit
from ..logger import debug
from ..pycmtensor import PyCMTensorModel
from ..utils import time_format
[docs]class MNL(PyCMTensorModel):
def __init__(
self,
db,
params: Dict,
utility: Union[list[TensorVariable], TensorVariable],
av: List[TensorVariable] = None,
**kwargs,
):
"""Defines a Multinomial Logit model
Args:
db (pycmtensor.Data): the database object
params (dict): dictionary of parameters
utility (list or TensorVariable): the vector of utility functions
av (list, optional): list of availability conditions. If `None`, all
availability is set to 1
**kwargs: keyword arguments. Possible options are
`optimizer: pycmtensor.optimizer=Adam` set the optimizer to use. see
:py:mod:`pycmtensor.optimizer` for available options.
"""
start_time = perf_counter()
super().__init__(db, **kwargs)
self.name = "MNL"
# Definition of the symbolic choice output (tensor)
self.y = db.tensor.choice
# symbolic expression for the choice model probability
self.p_y_given_x = logit(utility, av)
# symbolic expression for the likelihood
self.ll = log_likelihood(self.p_y_given_x, self.y)
# the cost function to minimize is the negative loglikelihood
self.cost = -(self.ll / self.y.shape[0])
# symbolic description of how to compute prediction as class whose probability
# is maximal
self.pred = aet.argmax(self.p_y_given_x, axis=0)
# add params to track in self.params and self.betas
self.add_params(params)
# define the optimizer for the model
self.opt = self.optimizer(self.params)
self.updates += self.opt.update(self.cost, self.params, self.learning_rate)
# define the update function to update the parameters wrt to the cost
self.update_wrt_cost = function(
name="update_wrt_cost",
inputs=self.inputs + [self.learning_rate],
outputs=self.cost,
updates=self.updates,
)
# internal functions
self.model_loglikelihood()
self.model_choice_probabilities()
self.model_choice_predictions()
self.model_prediction_error()
self.model_H()
self.model_BHHH()
self.model_gnorm()
build_time = round(perf_counter() - start_time, 3)
self.results.build_time = time_format(build_time)
debug(f"Build time = {self.results.build_time}")
# compute the null loglikelihood
data = db.pandas.inputs(self.inputs, split_type="train")
self.results.null_loglikelihood = self.loglikelihood(*data)
debug(f"Null loglikelihood = {self.results.null_loglikelihood}")
def __str__(self):
return f"{self.name}"
def __repr__(self):
return pprint(self.cost)