diff --git a/bemb/model/bayesian_coefficient.py b/bemb/model/bayesian_coefficient.py index 790c4fe..464c95a 100644 --- a/bemb/model/bayesian_coefficient.py +++ b/bemb/model/bayesian_coefficient.py @@ -6,9 +6,11 @@ """ from typing import Optional, Tuple, Union +import numpy as np import torch import torch.nn as nn from torch.distributions.lowrank_multivariate_normal import LowRankMultivariateNormal +from torch.distributions.gamma import Gamma class BayesianCoefficient(nn.Module): @@ -21,7 +23,8 @@ def __init__(self, num_obs: Optional[int] = None, dim: int = 1, prior_mean: float = 0.0, - prior_variance: Union[float, torch.Tensor] = 1.0 + prior_variance: Union[float, torch.Tensor] = 1.0, + distribution: str = 'gaussian' ) -> None: """The Bayesian coefficient object represents a learnable tensor mu_i in R^k, where i is from a family (e.g., user, item) so there are num_classes * num_obs learnable weights in total. @@ -63,12 +66,34 @@ def __init__(self, If a tensor with shape (num_classes, dim) is supplied, supplying a (num_classes, dim) tensor is amount to specifying a different prior variance for each entry in the coefficient. Defaults to 1.0. + distribution (str, optional): the distribution of the coefficient. Currently we support 'gaussian' and 'gamma'. + Defaults to 'gaussian'. """ super(BayesianCoefficient, self).__init__() # do we use this at all? TODO: drop self.variation. assert variation in ['item', 'user', 'constant', 'category'] self.variation = variation + + assert distribution in ['gaussian', 'gamma'], f'Unsupported distribution {distribution}' + if distribution == 'gamma': + ''' + assert not obs2prior, 'Gamma distribution is not supported for obs2prior at present.' + mean = 1.0 + variance = 10.0 + assert mean > 0, 'Gamma distribution requires mean > 0' + assert variance > 0, 'Gamma distribution requires variance > 0' + # shape (concentration) is mean^2/variance, rate is variance/mean for Gamma distribution. + shape = prior_mean ** 2 / prior_variance + rate = prior_mean / prior_variance + prior_mean = np.log(shape) + prior_variance = rate + ''' + prior_mean = np.log(prior_mean) + prior_variance = prior_variance + + self.distribution = distribution + self.obs2prior = obs2prior if variation == 'constant' or variation == 'category': if obs2prior: @@ -89,13 +114,15 @@ def __init__(self, if self.obs2prior: # the mean of prior distribution depends on observables. # initiate a Bayesian Coefficient with shape (dim, num_obs) standard Gaussian. + prior_H_dist = 'gaussian' self.prior_H = BayesianCoefficient(variation='constant', num_classes=dim, obs2prior=False, dim=num_obs, prior_variance=1.0, H_zero_mask=self.H_zero_mask, - is_H=True) # this is a distribution responsible for the obs2prior H term. + is_H=True, + distribution=prior_H_dist) # this is a distribution responsible for the obs2prior H term. else: self.register_buffer( @@ -114,13 +141,21 @@ def __init__(self, num_classes, dim) * self.prior_variance) # create variational distribution. - self.variational_mean_flexible = nn.Parameter( - torch.randn(num_classes, dim), requires_grad=True) + if self.distribution == 'gaussian': + self.variational_mean_flexible = nn.Parameter( + torch.randn(num_classes, dim), requires_grad=True) + # TOOD(kanodiaayush): initialize the gamma distribution variational mean in a more principled way. + elif self.distribution == 'gamma': + # initialize using uniform distribution between 0.5 and 1.5 + # for a gamma distribution, we store the concentration as log(concentration) = variational_mean_flexible + self.variational_mean_flexible = nn.Parameter( + torch.rand(num_classes, dim) + 0.5, requires_grad=True) if self.is_H and self.H_zero_mask is not None: assert self.H_zero_mask.shape == self.variational_mean_flexible.shape, \ f"The H_zero_mask should have exactly the shape as the H variable, `H_zero_mask`.shape is {self.H_zero_mask.shape}, `H`.shape is {self.variational_mean_flexible.shape} " + # for gamma distribution, we store the rate as log(rate) = variational_logstd self.variational_logstd = nn.Parameter( torch.randn(num_classes, dim), requires_grad=True) @@ -163,6 +198,10 @@ def variational_mean(self) -> torch.Tensor: else: M = self.variational_mean_fixed + self.variational_mean_flexible + if self.distribution == 'gamma': + # M = torch.pow(M, 2) + 0.000001 + M = M.exp() / self.variational_logstd.exp() + if self.is_H and (self.H_zero_mask is not None): # a H-variable with zero-entry restriction. # multiply zeros to entries with H_zero_mask[i, j] = 1. @@ -196,7 +235,11 @@ def log_prior(self, Returns: torch.Tensor: the log prior of the variable with shape (num_seeds, num_classes). """ - # p(sample) + # DEBUG_MARKER + ''' + print(sample) + print('log_prior') + ''' num_seeds, num_classes, dim = sample.shape # shape (num_seeds, num_classes) if self.obs2prior: @@ -211,9 +254,19 @@ def log_prior(self, else: mu = self.prior_zero_mean - out = LowRankMultivariateNormal(loc=mu, - cov_factor=self.prior_cov_factor, - cov_diag=self.prior_cov_diag).log_prob(sample) + + if self.distribution == 'gaussian': + out = LowRankMultivariateNormal(loc=mu, + cov_factor=self.prior_cov_factor, + cov_diag=self.prior_cov_diag).log_prob(sample) + elif self.distribution == 'gamma': + concentration = torch.exp(mu) + rate = self.prior_variance + out = Gamma(concentration=concentration, + rate=rate).log_prob(sample) + # sum over the last dimension + out = torch.sum(out, dim=-1) + assert out.shape == (num_seeds, num_classes) return out @@ -250,6 +303,7 @@ def rsample(self, num_seeds: int = 1) -> Union[torch.Tensor, Tuple[torch.Tensor] """ value_sample = self.variational_distribution.rsample( torch.Size([num_seeds])) + # DEBUG_MARKER if self.obs2prior: # sample obs2prior H as well. H_sample = self.prior_H.rsample(num_seeds=num_seeds) @@ -258,12 +312,22 @@ def rsample(self, num_seeds: int = 1) -> Union[torch.Tensor, Tuple[torch.Tensor] return value_sample @property - def variational_distribution(self) -> LowRankMultivariateNormal: + def variational_distribution(self) -> Union[LowRankMultivariateNormal, Gamma]: """Constructs the current variational distribution of the coefficient from current variational mean and covariance. """ - return LowRankMultivariateNormal(loc=self.variational_mean, - cov_factor=self.variational_cov_factor, - cov_diag=torch.exp(self.variational_logstd)) + if self.distribution == 'gaussian': + return LowRankMultivariateNormal(loc=self.variational_mean, + cov_factor=self.variational_cov_factor, + cov_diag=torch.exp(self.variational_logstd)) + elif self.distribution == 'gamma': + # for a gamma distribution, we store the concentration as log(concentration) = variational_mean_flexible + assert self.variational_mean_fixed == None, 'Gamma distribution does not support fixed mean' + concentration = self.variational_mean_flexible.exp() + # for gamma distribution, we store the rate as log(rate) = variational_logstd + rate = torch.exp(self.variational_logstd) + return Gamma(concentration=concentration, rate=rate) + else: + raise NotImplementedError("Unknown variational distribution type.") @property def device(self) -> torch.device: diff --git a/bemb/model/bemb.py b/bemb/model/bemb.py index be704db..8c2ba56 100644 --- a/bemb/model/bemb.py +++ b/bemb/model/bemb.py @@ -39,21 +39,26 @@ def parse_utility(utility_string: str) -> List[Dict[str, Union[List[str], None]] A helper function parse utility string into a list of additive terms. Example: - utility_string = 'lambda_item + theta_user * alpha_item + gamma_user * beta_item * price_obs' + utility_string = 'lambda_item + theta_user * alpha_item - gamma_user * beta_item * price_obs' output = [ { 'coefficient': ['lambda_item'], - 'observable': None + 'observable': None, + 'sign': 1.0, + }, { 'coefficient': ['theta_user', 'alpha_item'], 'observable': None + 'sign': 1.0, }, { 'coefficient': ['gamma_user', 'beta_item'], 'observable': 'price_obs' + 'sign': -1.0, } ] + Note that 'minus' is allowed in the utility string. If the first term is negative, the minus should be without a space. """ # split additive terms coefficient_suffix = ('_item', '_user', '_constant', '_category') @@ -76,10 +81,16 @@ def is_coefficient(name: str) -> bool: def is_observable(name: str) -> bool: return any(name.startswith(prefix) for prefix in observable_prefix) + utility_string = utility_string.replace(' - ', ' + -') additive_terms = utility_string.split(' + ') additive_decomposition = list() for term in additive_terms: - atom = {'coefficient': [], 'observable': None} + if term.startswith('-'): + sign = -1.0 + term = term[1:] + else: + sign = 1.0 + atom = {'coefficient': [], 'observable': None, 'sign': sign} # split multiplicative terms. for x in term.split(' * '): assert not (is_observable(x) and is_coefficient(x)), f"The element {x} is ambiguous, it follows naming convention of both an observable and a coefficient." @@ -113,6 +124,7 @@ def __init__(self, num_items: int, pred_item: bool, num_classes: int = 2, + coef_dist_dict: Dict[str, str] = {'default' : 'gaussian'}, H_zero_mask_dict: Optional[Dict[str, torch.BoolTensor]] = None, prior_mean: Union[float, Dict[str, float]] = 0.0, prior_variance: Union[float, Dict[str, float]] = 1.0, @@ -140,6 +152,14 @@ def __init__(self, lambda_item + theta_user * alpha_item + gamma_user * beta_item * price_obs See the doc-string of parse_utility for an example. + coef_dist_dict (Dict[str, str]): a dictionary mapping coefficient name to coefficient distribution name. + The coefficient distribution name can be one of the following: + 1. 'gaussian' + 2. 'gamma' - obs2prior is not supported for gamma coefficients + If a coefficient does not appear in the dictionary, it will be assigned the distribution specified + by the 'default' key. By default, the default distribution is 'gaussian'. + For coefficients which have gamma distributions, prior mean and variance MUST be specified in the prior_mean and prior_variance arguments if obs2prior is False for this coefficient. If obs2prior is True, prior_variance is still required + obs2prior_dict (Dict[str, bool]): a dictionary maps coefficient name (e.g., 'lambda_item') to a boolean indicating if observable (e.g., item_obs) enters the prior of the coefficient. @@ -184,6 +204,8 @@ def __init__(self, If no `prior_mean['default']` is provided, the default prior mean will be 0.0 for those coefficients not in the prior_mean.keys(). + For coefficients with gamma distributions, prior_mean specifies the shape parameter of the gamma prior. + Defaults to 0.0. prior_variance (Union[float, Dict[str, float]], Dict[str, torch. Tensor]): the variance of prior distribution @@ -203,6 +225,8 @@ def __init__(self, If no `prior_variance['default']` is provided, the default prior variance will be 1.0 for those coefficients not in the prior_variance.keys(). + For coefficients with gamma distributions, prior_variance specifies the concentration parameter of the gamma prior. + Defaults to 1.0, which means all priors have identity matrix as the covariance matrix. num_users (int, optional): number of users, required only if coefficient or observable @@ -233,6 +257,7 @@ def __init__(self, self.utility_formula = utility_formula self.obs2prior_dict = obs2prior_dict self.coef_dim_dict = coef_dim_dict + self.coef_dist_dict = coef_dist_dict if H_zero_mask_dict is not None: self.H_zero_mask_dict = H_zero_mask_dict else: @@ -325,6 +350,21 @@ def __init__(self, for additive_term in self.formula: for coef_name in additive_term['coefficient']: variation = coef_name.split('_')[-1] + + if coef_name not in self.coef_dist_dict.keys(): + if 'default' in self.coef_dist_dict.keys(): + self.coef_dist_dict[coef_name] = self.coef_dist_dict['default'] + else: + warnings.warn(f"You provided a dictionary of coef_dist_dict, but coefficient {coef_name} is not a key in it. Supply a value for 'default' in the coef_dist_dict dictionary to use that as default value (e.g., coef_dist_dict['default'] = 'gaussian'); now using distribution='gaussian' since this is not supplied.") + self.coef_dist_dict[coef_name] = 'gaussian' + + elif self.coef_dist_dict[coef_name] == 'gamma': + if not self.obs2prior_dict[coef_name]: + assert isinstance(self.prior_mean, dict) and coef_name in self.prior_mean.keys(), \ + f"Prior mean for {coef_name} needs to be provided because it's posterior is estimated as a gamma distribution." + assert isinstance(self.prior_variance, dict) and coef_name in self.prior_variance.keys(), \ + f"Prior variance for {coef_name} needs to be provided because it's posterior is estimated as a gamma distribution." + if isinstance(self.prior_mean, dict): # the user didn't specify prior mean for this coefficient. if coef_name not in self.prior_mean.keys(): @@ -367,7 +407,8 @@ def __init__(self, prior_mean=mean, prior_variance=s2, H_zero_mask=H_zero_mask, - is_H=False) + is_H=False, + distribution=self.coef_dist_dict[coef_name]) self.coef_dict = nn.ModuleDict(coef_dict) # ============================================================================================================== @@ -907,6 +948,7 @@ def reshape_observable(obs, name): sample_dict[coef_name], coef_name) assert coef_sample.shape == (R, P, I, 1) additive_term = coef_sample.view(R, P, I) + additive_term *= term['sign'] # Type II: factorized coefficient, e.g., . elif len(term['coefficient']) == 2 and term['observable'] is None: @@ -922,6 +964,7 @@ def reshape_observable(obs, name): R, P, I, positive_integer) additive_term = (coef_sample_0 * coef_sample_1).sum(dim=-1) + additive_term *= term['sign'] # Type III: single coefficient multiplied by observable, e.g., theta_user * x_obs_item. elif len(term['coefficient']) == 1 and term['observable'] is not None: @@ -935,6 +978,7 @@ def reshape_observable(obs, name): assert obs.shape == (R, P, I, positive_integer) additive_term = (coef_sample * obs).sum(dim=-1) + additive_term *= term['sign'] # Type IV: factorized coefficient multiplied by observable. # e.g., gamma_user * beta_item * price_obs. @@ -965,6 +1009,7 @@ def reshape_observable(obs, name): coef = (coef_sample_0 * coef_sample_1).sum(dim=-1) additive_term = (coef * obs).sum(dim=-1) + additive_term *= term['sign'] else: raise ValueError(f'Undefined term type: {term}') @@ -1138,6 +1183,7 @@ def reshape_observable(obs, name): sample_dict[coef_name], coef_name) assert coef_sample.shape == (R, total_computation, 1) additive_term = coef_sample.view(R, total_computation) + additive_term *= term['sign'] # Type II: factorized coefficient, e.g., . elif len(term['coefficient']) == 2 and term['observable'] is None: @@ -1153,6 +1199,7 @@ def reshape_observable(obs, name): R, total_computation, positive_integer) additive_term = (coef_sample_0 * coef_sample_1).sum(dim=-1) + additive_term *= term['sign'] # Type III: single coefficient multiplied by observable, e.g., theta_user * x_obs_item. elif len(term['coefficient']) == 1 and term['observable'] is not None: @@ -1167,6 +1214,7 @@ def reshape_observable(obs, name): assert obs.shape == (R, total_computation, positive_integer) additive_term = (coef_sample * obs).sum(dim=-1) + additive_term *= term['sign'] # Type IV: factorized coefficient multiplied by observable. # e.g., gamma_user * beta_item * price_obs. @@ -1196,6 +1244,7 @@ def reshape_observable(obs, name): coef = (coef_sample_0 * coef_sample_1).sum(dim=-1) additive_term = (coef * obs).sum(dim=-1) + additive_term *= term['sign'] else: raise ValueError(f'Undefined term type: {term}') diff --git a/bemb/model/bemb_flex_lightning.py b/bemb/model/bemb_flex_lightning.py index 7873aa5..0c7dbee 100644 --- a/bemb/model/bemb_flex_lightning.py +++ b/bemb/model/bemb_flex_lightning.py @@ -79,6 +79,14 @@ def training_step(self, batch, batch_idx): loss = - elbo return loss + # DEBUG_MARKER + ''' + def on_train_batch_end(self, outputs, batch, batch_idx, dataloader_idx): + print(f"Epoch {self.current_epoch} has ended") + breakpoint() + ''' + # DEBUG_MARKER + def _get_performance_dict(self, batch): if self.model.pred_item: log_p = self.model(batch, return_type='log_prob', diff --git a/bemb/utils/run_helper.py b/bemb/utils/run_helper.py index 410745d..0d5637a 100644 --- a/bemb/utils/run_helper.py +++ b/bemb/utils/run_helper.py @@ -17,7 +17,7 @@ def section_print(input_text): print('=' * 20, input_text, '=' * 20) -def run(model: LitBEMBFlex, dataset_list: List[ChoiceDataset], batch_size: int=-1, num_epochs: int=10, num_workers: int=8, **kwargs) -> LitBEMBFlex: +def run(model: LitBEMBFlex, dataset_list: List[ChoiceDataset], batch_size: int=-1, num_epochs: int=10, num_workers: int=8, run_test=True, **kwargs) -> LitBEMBFlex: """A standard pipeline of model training and evaluation. Args: @@ -25,6 +25,7 @@ def run(model: LitBEMBFlex, dataset_list: List[ChoiceDataset], batch_size: int=- dataset_list (List[ChoiceDataset]): train_dataset, validation_test, and test_dataset in a list of length 3. batch_size (int, optional): batch_size for training and evaluation. Defaults to -1, which indicates full-batch training. num_epochs (int, optional): number of epochs for training. Defaults to 10. + run_test (bool, optional): whether to run evaluation on test set. Defaults to True. **kwargs: additional keyword argument for the pytorch-lightning Trainer. Returns: @@ -57,6 +58,7 @@ def run(model: LitBEMBFlex, dataset_list: List[ChoiceDataset], batch_size: int=- trainer.fit(model, train_dataloaders=train, val_dataloaders=validation) print(f'time taken: {time.time() - start_time}') - section_print('test performance') - trainer.test(model, dataloaders=test) + if run_test: + section_print('test performance') + trainer.test(model, dataloaders=test) return model diff --git a/tutorials/supermarket/configs.yaml b/tutorials/supermarket/configs.yaml index ce6b672..7ecc4f7 100644 --- a/tutorials/supermarket/configs.yaml +++ b/tutorials/supermarket/configs.yaml @@ -1,30 +1,36 @@ device: cuda # data_dir: /home/tianyudu/Data/MoreSupermarket/tsv/ -data_dir: /home/tianyudu/Data/MoreSupermarket/20180101-20191231_13/tsv/ +# data_dir: /home/tianyudu/Data/MoreSupermarket/20180101-20191231_13/tsv/ +data_dir: /oak/stanford/groups/athey/MoreSupermarkets/csv/new_data/nf_runs/1904/20180101-20191231_44/tsv # utility: lambda_item # utility: lambda_item + theta_user * alpha_item # utility: lambda_item + theta_user * alpha_item + zeta_user * item_obs -utility: lambda_item + theta_user * alpha_item + gamma_user * beta_item * price_obs +# utility: lambda_item + theta_user * alpha_item + gamma_user * beta_item * price_obs +utility: lambda_item + theta_user * alpha_item + gamma_user * price_obs +# utility: alpha_item * gamma_user * price_obs out_dir: ./output/ # model configuration. +coef_dist_dict: + default: 'gaussian' + gamma_user: 'gamma' obs2prior_dict: - lambda_item: True - theta_user: True - alpha_item: True + lambda_item: False + theta_user: False + alpha_item: False zeta_user: True lota_item: True - gamma_user: True + gamma_user: False beta_item: True coef_dim_dict: lambda_item: 1 theta_user: 10 alpha_item: 10 - gamma_user: 10 - beta_item: 10 + gamma_user: 1 + beta_item: 1 #### optimization. trace_log_q: False shuffle: False batch_size: 100000 -num_epochs: 3 +num_epochs: 100 learning_rate: 0.03 -num_mc_seeds: 1 +num_mc_seeds: 2 diff --git a/tutorials/supermarket/main.py b/tutorials/supermarket/main.py index 7bbb1e4..b7c9548 100644 --- a/tutorials/supermarket/main.py +++ b/tutorials/supermarket/main.py @@ -10,7 +10,8 @@ from termcolor import cprint from example_customized_module import ExampleCustomizedModule from torch_choice.data import ChoiceDataset -from bemb.model import LitBEMBFlex +# from bemb.model import LitBEMBFlex +from bemb.model.bemb_supermarket_lightning import LitBEMBFlex from bemb.utils.run_helper import run @@ -70,28 +71,30 @@ def load_tsv(file_name, data_dir): # ============================================================================================== # user observables # ============================================================================================== - user_obs = pd.read_csv(os.path.join(configs.data_dir, 'obsUser.tsv'), - sep='\t', - index_col=0, - header=None) - # TODO(Tianyu): there could be duplicate information for each user. - # do we need to catch it in some check process? - user_obs = user_obs.groupby(user_obs.index).first().sort_index() - user_obs = torch.Tensor(user_obs.values) - configs.num_user_obs = user_obs.shape[1] - configs.coef_dim_dict['obsuser_item'] = configs.num_user_obs + if configs.obs_user: + user_obs = pd.read_csv(os.path.join(configs.data_dir, 'obsUser.tsv'), + sep='\t', + index_col=0, + header=None) + # TODO(Tianyu): there could be duplicate information for each user. + # do we need to catch it in some check process? + user_obs = user_obs.groupby(user_obs.index).first().sort_index() + user_obs = torch.Tensor(user_obs.values) + configs.num_user_obs = user_obs.shape[1] + configs.coef_dim_dict['obsuser_item'] = configs.num_user_obs # ============================================================================================== # item observables # ============================================================================================== - item_obs = pd.read_csv(os.path.join(configs.data_dir, 'obsItem.tsv'), - sep='\t', - index_col=0, - header=None) - item_obs = item_obs.groupby(item_obs.index).first().sort_index() - item_obs = torch.Tensor(item_obs.values) - configs.num_item_obs = item_obs.shape[1] - configs.coef_dim_dict['obsitem_user'] = configs.num_item_obs + if configs.obs_item: + item_obs = pd.read_csv(os.path.join(configs.data_dir, 'obsItem.tsv'), + sep='\t', + index_col=0, + header=None) + item_obs = item_obs.groupby(item_obs.index).first().sort_index() + item_obs = torch.Tensor(item_obs.values) + configs.num_item_obs = item_obs.shape[1] + configs.coef_dim_dict['obsitem_user'] = configs.num_item_obs # ============================================================================================== # item availability @@ -152,14 +155,22 @@ def load_tsv(file_name, data_dir): # example day of week, random example. session_day_of_week = torch.LongTensor(np.random.randint(0, 7, configs.num_sessions)) - choice_dataset = ChoiceDataset(item_index=label, - user_index=user_index, - session_index=session_index, - item_availability=item_availability, - user_obs=user_obs, - item_obs=item_obs, - price_obs=price_obs, - session_day_of_week=session_day_of_week) + choice_dataset_args = { + "item_index": label, + "user_index": user_index, + "session_index": session_index, + "item_availability": item_availability, + "price_obs": price_obs, + "session_day_of_week": session_day_of_week + } + + if configs.obs_user: + choice_dataset_args["user_obs"] = user_obs + + if configs.obs_item: + choice_dataset_args["item_obs"] = item_obs + + choice_dataset = ChoiceDataset(**choice_dataset_args) dataset_list.append(choice_dataset) @@ -194,9 +205,19 @@ def load_tsv(file_name, data_dir): # ============================================================================================== # pytorch-lightning training # ============================================================================================== + # if prior_mean is in the configs namespace set it to the prior mean + if hasattr(configs, 'prior_mean'): + prior_mean = configs.prior_mean + else: + prior_mean = 0.0 + if hasattr(configs, 'prior_variance'): + prior_variance = configs.prior_variance + else: + prior_variance = 1.0 bemb = LitBEMBFlex( # trainings args. pred_item = configs.pred_item, + coef_dist_dict=configs.coef_dist_dict, learning_rate=configs.learning_rate, num_seeds=configs.num_mc_seeds, # model args, will be passed to BEMB constructor. @@ -208,14 +229,30 @@ def load_tsv(file_name, data_dir): coef_dim_dict=configs.coef_dim_dict, trace_log_q=configs.trace_log_q, category_to_item=category_to_item, - num_user_obs=configs.num_user_obs, - num_item_obs=configs.num_item_obs, - # num_price_obs=configs.num_price_obs, + num_user_obs=configs.num_user_obs if configs.obs_user else None, + num_item_obs=configs.num_item_obs if configs.obs_item else None, + prior_mean = prior_mean, + prior_variance= prior_variance, + num_price_obs=configs.num_price_obs, + preprocess=False, # additional_modules=[ExampleCustomizedModule()] ) bemb = bemb.to(configs.device) - bemb = run(bemb, dataset_list, batch_size=configs.batch_size, num_epochs=configs.num_epochs) + bemb = run(bemb, dataset_list, batch_size=configs.batch_size, num_epochs=configs.num_epochs, run_test=False) + + # ''' + # coeffs = coeffs**2 + # give distribution statistics + if 'gamma_user' in configs.utility: + coeffs_gamma = bemb.model.coef_dict['gamma_user'].variational_mean.detach().cpu().numpy() + print('Coefficients statistics Gamma:') + print(pd.DataFrame(coeffs_gamma).describe()) + if 'nfact_category' in configs.utility: + coeffs_nfact = bemb.model.coef_dict['nfact_category'].variational_mean.detach().cpu().numpy() + print('Coefficients statistics nfact_category:') + print(pd.DataFrame(coeffs_nfact).describe()) + # ''' # ============================================================================================== # inference example