def update(self, feed_dict=None): """Run one iteration of inference. Any derived class of `Inference` **must** implement this method. Args: feed_dict: dict, optional. Feed dictionary for a TensorFlow session run. It is used to feed placeholders that are not fed during initialization. Returns: dict. Dictionary of algorithm-specific information. """ if feed_dict is None: feed_dict = {} for key, value in six.iteritems(self.data): if isinstance(key, tf.Tensor) and "Placeholder" in key.op.type: feed_dict[key] = value sess = get_session() t = sess.run(self.increment_t) if self.debug: sess.run(self.op_check, feed_dict) if self.logging and self.n_print != 0: if t == 1 or t % self.n_print == 0: summary = sess.run(self.summarize, feed_dict) self.train_writer.add_summary(summary, t) return {'t': t}
def update(self, feed_dict=None): """Run one iteration of optimization. Args: feed_dict: dict, optional. Feed dictionary for a TensorFlow session run. It is used to feed placeholders that are not fed during initialization. Returns: dict. Dictionary of algorithm-specific information. In this case, the loss function value after one iteration. """ if feed_dict is None: feed_dict = {} for key, value in six.iteritems(self.data): if isinstance(key, tf.Tensor) and "Placeholder" in key.op.type: feed_dict[key] = value sess = get_session() _, t, loss = sess.run([self.train, self.increment_t, self.loss], feed_dict) if self.debug: sess.run(self.op_check, feed_dict) if self.logging and self.n_print != 0: if t == 1 or t % self.n_print == 0: summary = sess.run(self.summarize, feed_dict) self.train_writer.add_summary(summary, t) return {'t': t, 'loss': loss}
def update(self, feed_dict=None, variables=None): info_dict = super(WGANInference, self).update(feed_dict, variables) sess = get_session() if self.clip_op is not None and variables in (None, "Disc"): sess.run(self.clip_op) return info_dict
def update(self, feed_dict=None, variables=None): """Run one iteration of optimization. Args: feed_dict: dict, optional. Feed dictionary for a TensorFlow session run. It is used to feed placeholders that are not fed during initialization. variables: str, optional. Which set of variables to update. Either "Disc" or "Gen". Default is both. Returns: dict. Dictionary of algorithm-specific information. In this case, the iteration number and generative and discriminative losses. #### Notes The outputted iteration number is the total number of calls to `update`. Each update may include updating only a subset of parameters. """ if feed_dict is None: feed_dict = {} for key, value in six.iteritems(self.data): if isinstance(key, tf.Tensor) and "Placeholder" in key.op.type: feed_dict[key] = value sess = get_session() if variables is None: _, _, t, loss, loss_d = sess.run([ self.train, self.train_d, self.increment_t, self.loss, self.loss_d ], feed_dict) elif variables == "Gen": _, t, loss = sess.run([self.train, self.increment_t, self.loss], feed_dict) loss_d = 0.0 elif variables == "Disc": _, t, loss_d = sess.run( [self.train_d, self.increment_t, self.loss_d], feed_dict) loss = 0.0 else: raise NotImplementedError( "variables must be None, 'Gen', or 'Disc'.") if self.debug: sess.run(self.op_check, feed_dict) if self.logging and self.n_print != 0: if t == 1 or t % self.n_print == 0: summary = sess.run(self.summarize, feed_dict) self.train_writer.add_summary(summary, t) return {'t': t, 'loss': loss, 'loss_d': loss_d}
def __init__(self, latent_vars=None, data=None): """Create an inference algorithm. Args: latent_vars: dict, optional. Collection of latent variables (of type `RandomVariable` or `tf.Tensor`) to perform inference on. Each random variable is binded to another random variable; the latter will infer the former conditional on data. data: dict, optional. Data dictionary which binds observed variables (of type `RandomVariable` or `tf.Tensor`) to their realizations (of type `tf.Tensor`). It can also bind placeholders (of type `tf.Tensor`) used in the model to their realizations; and prior latent variables (of type `RandomVariable`) to posterior latent variables (of type `RandomVariable`). """ sess = get_session() if latent_vars is None: latent_vars = {} if data is None: data = {} check_latent_vars(latent_vars) self.latent_vars = latent_vars check_data(data) self.data = {} for key, value in six.iteritems(data): if isinstance(key, tf.Tensor) and "Placeholder" in key.op.type: self.data[key] = value elif isinstance(key, (RandomVariable, tf.Tensor)): if isinstance(value, (RandomVariable, tf.Tensor)): self.data[key] = value elif isinstance(value, (float, list, int, np.ndarray, np.number, str)): # If value is a Python type, store it in the graph. # Assign its placeholder with the key's data type. with tf.variable_scope(None, default_name="data"): ph = tf.placeholder(key.dtype, np.shape(value)) var = tf.Variable(ph, trainable=False, collections=[]) sess.run(var.initializer, {ph: value}) self.data[key] = var
def finalize(self, feed_dict=None): """Function to call after convergence. Computes the Hessian at the mode. Args: feed_dict: dict, optional. Feed dictionary for a TensorFlow session run during evaluation of Hessian. It is used to feed placeholders that are not fed during initialization. """ if feed_dict is None: feed_dict = {} for key, value in six.iteritems(self.data): if isinstance(key, tf.Tensor) and "Placeholder" in key.op.type: feed_dict[key] = value sess = get_session() sess.run(self.finalize_ops, feed_dict) super(Laplace, self).finalize()
def update(self, feed_dict=None): """Run one iteration of sampling. Args: feed_dict: dict, optional. Feed dictionary for a TensorFlow session run. It is used to feed placeholders that are not fed during initialization. Returns: dict. Dictionary of algorithm-specific information. In this case, the acceptance rate of samples since (and including) this iteration. #### Notes We run the increment of `t` separately from other ops. Whether the others op run with the `t` before incrementing or after incrementing depends on which is run faster in the TensorFlow graph. Running it separately forces a consistent behavior. """ if feed_dict is None: feed_dict = {} for key, value in six.iteritems(self.data): if isinstance(key, tf.Tensor) and "Placeholder" in key.op.type: feed_dict[key] = value sess = get_session() _, accept_rate = sess.run([self.train, self.n_accept_over_t], feed_dict) t = sess.run(self.increment_t) if self.debug: sess.run(self.op_check, feed_dict) if self.logging and self.n_print != 0: if t == 1 or t % self.n_print == 0: summary = sess.run(self.summarize, feed_dict) self.train_writer.add_summary(summary, t) return {'t': t, 'accept_rate': accept_rate}
def ppc(T, data, latent_vars=None, n_samples=100): """Posterior predictive check [@rubin1984bayesianly; @meng1994posterior; @gelman1996posterior]. PPC's form an empirical distribution for the predictive discrepancy, $p(T\mid x) = \int p(T(x^{\\text{rep}})\mid z) p(z\mid x) dz$ by drawing replicated data sets $x^{\\text{rep}}$ and calculating $T(x^{\\text{rep}})$ for each data set. Then it compares it to $T(x)$. If `data` is inputted with the prior predictive distribution, then it is a prior predictive check [@box1980sampling]. Args: T: function. Discrepancy function, which takes a dictionary of data and dictionary of latent variables as input and outputs a `tf.Tensor`. data: dict. Data to compare to. It binds observed variables (of type `RandomVariable` or `tf.Tensor`) to their realizations (of type `tf.Tensor`). It can also bind placeholders (of type `tf.Tensor`) used in the model to their realizations. latent_vars: dict, optional. Collection of random variables (of type `RandomVariable` or `tf.Tensor`) binded to their inferred posterior. This argument is used when the discrepancy is a function of latent variables. n_samples: int, optional. Number of replicated data sets. Returns: list of np.ndarray. List containing the reference distribution, which is a NumPy array with `n_samples` elements, $(T(x^{{\\text{rep}},1}, z^{1}), ..., T(x^{\\text{rep,nsamples}}, z^{\\text{nsamples}}))$ and the realized discrepancy, which is a NumPy array with `n_samples` elements, $(T(x, z^{1}), ..., T(x, z^{\\text{nsamples}})).$ #### Examples ```python # build posterior predictive after inference: # it is parameterized by a posterior sample x_post = ed.copy(x, {z: qz, beta: qbeta}) # posterior predictive check # T is a user-defined function of data, T(data) T = lambda xs, zs: tf.reduce_mean(xs[x_post]) ed.ppc(T, data={x_post: x_train}) # in general T is a discrepancy function of the data (both response and # covariates) and latent variables, T(data, latent_vars) T = lambda xs, zs: tf.reduce_mean(zs[z]) ed.ppc(T, data={y_post: y_train, x_ph: x_train}, latent_vars={z: qz, beta: qbeta}) # prior predictive check # run ppc on original x ed.ppc(T, data={x: x_train}) ``` """ sess = get_session() if not callable(T): raise TypeError("T must be a callable function.") check_data(data) if latent_vars is None: latent_vars = {} check_latent_vars(latent_vars) if not isinstance(n_samples, int): raise TypeError("n_samples must have type int.") # Build replicated latent variables. zrep = { key: tf.convert_to_tensor(value) for key, value in six.iteritems(latent_vars) } # Build replicated data. xrep = { x: (x.value() if isinstance(x, RandomVariable) else obs) for x, obs in six.iteritems(data) } # Create feed_dict for data placeholders that the model conditions # on; it is necessary for all session runs. feed_dict = { key: value for key, value in six.iteritems(data) if isinstance(key, tf.Tensor) and "Placeholder" in key.op.type } # Calculate discrepancy over many replicated data sets and latent # variables. Trep = T(xrep, zrep) Tobs = T(data, zrep) Treps = [] Ts = [] for _ in range(n_samples): # Take a forward pass (session run) to get new samples for # each calculation of the discrepancy. # Alternatively, we could unroll the graph by registering this # operation `n_samples` times, each for different parent nodes # representing `xrep` and `zrep`. But it's expensive. Treps += [sess.run(Trep, feed_dict)] Ts += [sess.run(Tobs, feed_dict)] return [np.stack(Treps), np.stack(Ts)]
def evaluate(metrics, data, n_samples=500, output_key=None): """Evaluate fitted model using a set of metrics. A metric, or scoring rule [@winkler1994evaluating], is a function of observed data under the posterior predictive distribution. For example in supervised metrics such as classification accuracy, the observed data (true output) is compared to the posterior predictive's mean (predicted output). In unsupervised metrics such as log-likelihood, the probability of observing the data is calculated under the posterior predictive's log-density. Args: metrics: list of str or str. List of metrics or a single metric: `'binary_accuracy'`, `'categorical_accuracy'`, `'sparse_categorical_accuracy'`, `'log_loss'` or `'binary_crossentropy'`, `'categorical_crossentropy'`, `'sparse_categorical_crossentropy'`, `'hinge'`, `'squared_hinge'`, `'mse'` or `'MSE'` or `'mean_squared_error'`, `'mae'` or `'MAE'` or `'mean_absolute_error'`, `'mape'` or `'MAPE'` or `'mean_absolute_percentage_error'`, `'msle'` or `'MSLE'` or `'mean_squared_logarithmic_error'`, `'poisson'`, `'cosine'` or `'cosine_proximity'`, `'log_lik'` or `'log_likelihood'`. data: dict. Data to evaluate model with. It binds observed variables (of type `RandomVariable` or `tf.Tensor`) to their realizations (of type `tf.Tensor`). It can also bind placeholders (of type `tf.Tensor`) used in the model to their realizations. n_samples: int, optional. Number of posterior samples for making predictions, using the posterior predictive distribution. output_key: RandomVariable or tf.Tensor, optional. It is the key in `data` which corresponds to the model's output. Returns: list of float or float. A list of evaluations or a single evaluation. Raises: NotImplementedError. If an input metric does not match an implemented metric in Edward. #### Examples ```python # build posterior predictive after inference: it is # parameterized by a posterior sample x_post = ed.copy(x, {z: qz, beta: qbeta}) # log-likelihood performance ed.evaluate('log_likelihood', data={x_post: x_train}) # classification accuracy # here, `x_ph` is any features the model is defined with respect to, # and `y_post` is the posterior predictive distribution ed.evaluate('binary_accuracy', data={y_post: y_train, x_ph: x_train}) # mean squared error ed.evaluate('mean_squared_error', data={y: y_data, x: x_data}) ``` """ sess = get_session() if isinstance(metrics, str): metrics = [metrics] elif not isinstance(metrics, list): raise TypeError("metrics must have type str or list.") check_data(data) if not isinstance(n_samples, int): raise TypeError("n_samples must have type int.") if output_key is None: # Default output_key to the only data key that isn't a placeholder. keys = [ key for key in six.iterkeys(data) if not isinstance(key, tf.Tensor) or "Placeholder" not in key.op.type ] if len(keys) == 1: output_key = keys[0] else: raise KeyError("User must specify output_key.") elif not isinstance(output_key, RandomVariable): raise TypeError("output_key must have type RandomVariable.") # Create feed_dict for data placeholders that the model conditions # on; it is necessary for all session runs. feed_dict = { key: value for key, value in six.iteritems(data) if isinstance(key, tf.Tensor) and "Placeholder" in key.op.type } # Form true data. y_true = data[output_key] # Make predictions (if there are any supervised metrics). if metrics != ['log_lik'] and metrics != ['log_likelihood']: binary_discrete = (Bernoulli, Binomial) categorical_discrete = (Categorical, Multinomial, OneHotCategorical) if isinstance(output_key, binary_discrete + categorical_discrete): # Average over realizations of their probabilities, then predict # via argmax over probabilities. probs = [ sess.run(output_key.probs, feed_dict) for _ in range(n_samples) ] probs = tf.add_n(probs) / tf.cast(n_samples, probs[0].dtype) if isinstance(output_key, binary_discrete): # make random prediction whenever probs is exactly 0.5 random = tf.random_uniform(shape=tf.shape(probs)) y_pred = tf.round(tf.where(tf.equal(0.5, probs), random, probs)) else: y_pred = tf.argmax(probs, len(probs.shape) - 1) else: # Monte Carlo estimate the mean of the posterior predictive. y_pred = [ sess.run(output_key, feed_dict) for _ in range(n_samples) ] y_pred = tf.cast(tf.add_n(y_pred), y_pred[0].dtype) / \ tf.cast(n_samples, y_pred[0].dtype) # Evaluate y_true (according to y_pred if supervised) for all metrics. evaluations = [] for metric in metrics: if metric == 'accuracy' or metric == 'crossentropy': # automate binary or sparse cat depending on its support support = sess.run(tf.reduce_max(y_true), feed_dict) if support <= 1: metric = 'binary_' + metric else: metric = 'sparse_categorical_' + metric if metric == 'binary_accuracy': evaluations += [binary_accuracy(y_true, y_pred)] elif metric == 'categorical_accuracy': evaluations += [categorical_accuracy(y_true, y_pred)] elif metric == 'sparse_categorical_accuracy': evaluations += [sparse_categorical_accuracy(y_true, y_pred)] elif metric == 'log_loss' or metric == 'binary_crossentropy': evaluations += [binary_crossentropy(y_true, y_pred)] elif metric == 'categorical_crossentropy': evaluations += [categorical_crossentropy(y_true, y_pred)] elif metric == 'sparse_categorical_crossentropy': evaluations += [sparse_categorical_crossentropy(y_true, y_pred)] elif metric == 'hinge': evaluations += [hinge(y_true, y_pred)] elif metric == 'squared_hinge': evaluations += [squared_hinge(y_true, y_pred)] elif (metric == 'mse' or metric == 'MSE' or metric == 'mean_squared_error'): evaluations += [mean_squared_error(y_true, y_pred)] elif (metric == 'mae' or metric == 'MAE' or metric == 'mean_absolute_error'): evaluations += [mean_absolute_error(y_true, y_pred)] elif (metric == 'mape' or metric == 'MAPE' or metric == 'mean_absolute_percentage_error'): evaluations += [mean_absolute_percentage_error(y_true, y_pred)] elif (metric == 'msle' or metric == 'MSLE' or metric == 'mean_squared_logarithmic_error'): evaluations += [mean_squared_logarithmic_error(y_true, y_pred)] elif metric == 'poisson': evaluations += [poisson(y_true, y_pred)] elif metric == 'cosine' or metric == 'cosine_proximity': evaluations += [cosine_proximity(y_true, y_pred)] elif metric == 'log_lik' or metric == 'log_likelihood': # Monte Carlo estimate the log-density of the posterior predictive. tensor = tf.reduce_mean(output_key.log_prob(y_true)) log_pred = [sess.run(tensor, feed_dict) for _ in range(n_samples)] log_pred = tf.add_n(log_pred) / tf.cast(n_samples, tensor.dtype) evaluations += [log_pred] else: raise NotImplementedError( "Metric is not implemented: {}".format(metric)) if len(evaluations) == 1: return sess.run(evaluations[0], feed_dict) else: return sess.run(evaluations, feed_dict)
def update(self, feed_dict=None): """Run one iteration of sampling. Args: feed_dict: dict, optional. Feed dictionary for a TensorFlow session run. It is used to feed placeholders that are not fed during initialization. Returns: dict. Dictionary of algorithm-specific information. In this case, the acceptance rate of samples since (and including) this iteration. """ sess = get_session() if not self.feed_dict: # Initialize feed for all conditionals to be the draws at step 0. samples = OrderedDict(self.latent_vars) inits = sess.run([qz.params[0] for qz in six.itervalues(samples)]) for z, init in zip(six.iterkeys(samples), inits): self.feed_dict[z] = init for key, value in six.iteritems(self.data): if isinstance(key, tf.Tensor) and "Placeholder" in key.op.type: self.feed_dict[key] = value elif isinstance(key, RandomVariable) and \ isinstance(value, (tf.Tensor, tf.Variable)): self.feed_dict[key] = sess.run(value) if feed_dict is None: feed_dict = {} feed_dict.update(self.feed_dict) # Determine scan order. if self.scan_order == 'random': scan_order = list(six.iterkeys(self.latent_vars)) random.shuffle(scan_order) else: # list scan_order = self.scan_order # Fetch samples by iterating over complete conditional draws. for z in scan_order: if isinstance(z, RandomVariable): draw = sess.run(self.proposal_vars[z], feed_dict) feed_dict[z] = draw self.feed_dict[z] = draw else: # list draws = sess.run([self.proposal_vars[zz] for zz in z], feed_dict) for zz, draw in zip(z, draws): feed_dict[zz] = draw self.feed_dict[zz] = draw # Assign the samples to the Empirical random variables. _, accept_rate = sess.run([self.train, self.n_accept_over_t], feed_dict) t = sess.run(self.increment_t) if self.debug: sess.run(self.op_check, feed_dict) if self.logging and self.n_print != 0: if t == 1 or t % self.n_print == 0: summary = sess.run(self.summarize, feed_dict) self.train_writer.add_summary(summary, t) return {'t': t, 'accept_rate': accept_rate}