Exemplo n.º 1
0
 def rejection_loglike(z, ll_args):
     if ll_args == 'logistic':
         z = ilogit(z)
     if z.min() < min_mu or z.max() > max_mu or (z[1:] -
                                                 z[:-1]).max() > 0:
         return -np.inf
     return log_likelihood(z, ll_args)
Exemplo n.º 2
0
    def _resample_R(self, data):
        '''Random-walk MCMC for R'''
        self.R = self.R[..., None]
        logR = np.log(self.R)

        # Current success probability
        P = ilogit(np.einsum('nk,mtk->nmt', self.W,
                             self.V).clip(-10, 10))[..., None]

        # Run a fixed number of random walk Metropolis-Hastings steps
        for mcmc_step in range(self.nmetropolis):
            # Sample a candidate R values
            candidate_logR = logR + np.random.normal(
                0, self.rpropstdev, size=logR.shape)
            candidate_R = np.exp(candidate_logR)

            # Get the prior log-likelihood ratio
            accept_prior = norm.logpdf(
                candidate_logR, loc=0, scale=self.rstdev) - norm.logpdf(
                    logR, loc=0, scale=self.rstdev)

            # Get the likelihood log-likelihood ratio
            accept_likelihood = (
                gammaln(data + candidate_R) - gammaln(candidate_R) -
                gammaln(data + self.R) + gammaln(self.R)  # combinatorial bit
                + (candidate_R - self.R) * np.log(1 - P)
            )  # failure prob bit; success prob bit does not use R

            # Multiply replicates and any other aggregated likelihoods
            for dim in self.rdims:
                accept_likelihood = np.nansum(accept_likelihood, axis=dim)

            # Get rid of aggregated prior dims; make sure to go back to 1 for any size-1 unaggregated dims
            accept_prior = np.squeeze(accept_prior).reshape(
                accept_likelihood.shape)

            # Get the acceptance probability for each R
            accept_probs = np.exp(
                np.clip(accept_prior + accept_likelihood, -10,
                        1)).reshape(self.R.shape)

            # Accept/reject the samples
            accept_indices = np.random.random(
                size=accept_probs.shape) <= accept_probs

            accept_indices = accept_indices & (candidate_R > 1)  # TEMP
            accepted_logR = candidate_logR[accept_indices]
            logR[accept_indices] = accepted_logR
            self.R[accept_indices] = np.exp(accepted_logR)

        # Update the pseudo-counts for the PG-augmentation step
        self.N = np.nansum(data + self.R,
                           axis=-1)  # Binomial N can sum independent trials
        self.R = self.R[..., 0]
Exemplo n.º 3
0
def benchmarks():
    '''Benchmarking GASS vs.
    1) naive ESS + rejection sampling
    2) logistic ESS + rejection sampling for monotonicity
    3) logistic ESS + posterior projection'''
    import matplotlib
    import matplotlib.pyplot as plt
    from functionalmf.elliptical_slice import elliptical_slice as ess
    from functionalmf.utils import ilogit, pav
    from scipy.stats import gamma
    np.random.seed(42)
    ntrials = 100
    nmethods = 5
    nobs = 3
    sample_sizes = np.array([100, 500, 1000, 5000, 10000], dtype=int)
    nsizes = len(sample_sizes)
    nburn = nsamples = sample_sizes.max()
    verbose = True


    mu_prior = np.array([0.95, 0.8, 0.75, 0.5, 0.29, 0.2, 0.17, 0.15, 0.01, 0.0001]) # monotonic curve prior
    T = len(mu_prior)
    b = 3
    min_mu, max_mu = 0.0, 1
    sigma_prior = 0.1*np.array([np.exp(-0.5*(i - np.arange(T))**2 / b) for i in range(T)]) # Squared exponential kernel
    
    # Get an empirical estimate of the logit-transformed covariance
    print('Building empirical covariance matrix for logit transformed model')
    mu_samples = np.zeros((1000, len(mu_prior)))
    for i in range(mu_samples.shape[0]):
        if i % 1 == 0:
            print('\t', i)
        mu_samples[i] = np.random.multivariate_normal(mu_prior, sigma_prior)
        while mu_samples[i].min() < min_mu or mu_samples[i].max() > max_mu or (mu_samples[i][1:] - mu_samples[i][:-1]).max() > 0:
            mu_samples[i] = np.random.multivariate_normal(mu_prior, sigma_prior)
    mu_samples_logit = np.log(mu_samples / (1-mu_samples))
    sigma_prior_logit = np.einsum('ni,nj->nij', mu_samples_logit, mu_samples_logit).mean(axis=0)
    mu_prior_logit = np.log(mu_prior / (1-mu_prior))

    mse = np.zeros((ntrials,nsizes,nmethods))
    coverage = np.zeros((ntrials, nsizes, nmethods,T), dtype=bool)
    for trial in range(ntrials):
        print('Trial {}'.format(trial))
        
        # Sample the true mean via rejection sampling
        mu_truth = np.random.multivariate_normal(mu_prior, sigma_prior)
        while mu_truth.min() < min_mu or mu_truth.max() > max_mu or (mu_truth[1:] - mu_truth[:-1]).max() > 0:
            mu_truth = np.random.multivariate_normal(mu_prior, sigma_prior)
        
        print(mu_truth)

        
        
        # Plot some data points using the true scale
        data = np.array([np.random.gamma(100, scale=mu_truth) for _ in range(nobs)]).T
        samples = np.zeros((nsamples, nmethods, T))

        xobs = np.tile(np.arange(T), (nobs, 1)).T

        # Linear constraints requiring monotonicity and [0, 1] intervals
        C_zero = np.concatenate([np.eye(T), np.zeros((T,1))+min_mu], axis=1)
        C_one = np.concatenate([np.eye(T)*-1, np.ones((T,1))*-1 ], axis=1)
        C_mono = np.array([np.concatenate([np.zeros(i), [1,-1], np.zeros(T-i-2), [0]]) for i in range(T-1)])

        # Setup the lower bound inequalities
        C_lower = np.concatenate([C_zero, C_one, C_mono], axis=0)

        # Initialize to be a simple line trending downward (i.e. reasonable guess)
        x = ((T - np.arange(T)) / T).clip(min_mu+0.01, max_mu-0.01)
        x = np.tile(x, nmethods).reshape(nmethods, T)
        
        # Convert to logits for the logistic models
        x[2] = np.log(x[2] / (1-x[2]))
        x[4] = np.log(x[4] / (1-x[4]))

        # Simple iid N(y | mu, sigma^2) likelihood
        def log_likelihood(z, ll_args):
            if len(z.shape) == len(data.shape):
                return gamma.logpdf(data[None], 100, scale=z[...,None]).sum(axis=-1).sum(axis=-1)
            return gamma.logpdf(data, 100, scale=z[...,None]).sum()

        # Log likelihood where we don't assume everything is valid
        def rejection_loglike(z, ll_args):
            if ll_args == 'logistic':
                z = ilogit(z)
            if z.min() < min_mu or z.max() > max_mu or (z[1:] - z[:-1]).max() > 0:
                return -np.inf
            return log_likelihood(z, ll_args)

        # Generalized analytic slice sampling
        cur_ll = [None]*nmethods
        for step in range(nburn+nsamples):
            if verbose and step % 1000 == 0:
                print(step)
            import warnings
            warnings.simplefilter("ignore")
            # Generalized analytic slice sampling
            x[0], cur_ll[0] = gass(x[0], sigma_prior, log_likelihood, C_lower, cur_ll=cur_ll[0], mu=mu_prior)

            # Naive ESS + rejection sampling
            x[1], cur_ll[1] = ess(x[1], sigma_prior, rejection_loglike, cur_log_like=cur_ll[1], mu=mu_prior)

            # Logistic ESS + rejection sampling
            x[2], cur_ll[2] = ess(x[2], sigma_prior_logit, rejection_loglike, cur_log_like=cur_ll[2], mu=mu_prior_logit, ll_args='logistic')

            # Naive ESS + posterior projection
            x[3], cur_ll[3] = ess(x[3], sigma_prior, log_likelihood, cur_log_like=cur_ll[3], mu=mu_prior)

            # Logistic ESS + posterior projection
            x[4], cur_ll[4] = ess(x[4], sigma_prior_logit, lambda x_prop, ll_args: log_likelihood(ilogit(x_prop), ll_args),
                                    cur_log_like=cur_ll[4], mu=mu_prior_logit)

            # Save posterior samples
            if step >= nburn:
                samples[step-nburn] = x

        # Pass the results for logistic models through the logistic function
        samples[:,(2,4)] = ilogit(samples[:,(2,4)])

        # Project the posteriors using PAV
        print('Projecting third and fourth method posteriors')
        for i in range(nsamples):
            samples[i,3] = pav(samples[i,3][::-1]).clip(0,1)[::-1]
            samples[i,4] = pav(samples[i,4][::-1]).clip(0,1)[::-1]

        for size_idx, sample_size in enumerate(sample_sizes):
            mu_hat = samples[:sample_size].mean(axis=0)
            mu_lower = np.percentile(samples[:sample_size], 5, axis=0)
            mu_upper = np.percentile(samples[:sample_size], 95, axis=0)

            np.set_printoptions(precision=2, suppress=True)
            # Calculate the mean squared error
            mse[trial,size_idx] = ((mu_truth[None] - mu_hat)**2).mean(axis=-1)

            # Calculate the 90th credible interval coverage
            coverage[trial,size_idx] = (mu_truth[None] >= mu_lower) & (mu_truth[None] <= mu_upper)
            
            print('Samples={} MSE={} Coverage={}'.format(sample_size,
                                                        mse[:trial+1,size_idx].mean(axis=0) * 1e3,
                                                        coverage[:trial+1,size_idx].mean(axis=0).mean(axis=1)))
        print()

            
        if trial == 0:
            np.save('data/gass-benchmark-samples.npy', samples)
            xobs = np.tile(np.arange(T), (nobs, 1)).T
            # plt.scatter(xobs, data)
            plt.plot(xobs[:,0], mu_hat[1], color='0.25', ls='--', label='ESS+Rejection')
            plt.plot(xobs[:,0], mu_hat[2], color='0.4', ls=':', label='ESS+Link+Rejection')
            plt.plot(xobs[:,0], mu_hat[3], color='0.65', ls='--', label='ESS+Projection')
            plt.plot(xobs[:,0], mu_hat[4], color='0.8', ls=':', label='ESS+Link+Projection')
            plt.plot(xobs[:,0], mu_hat[0], color='orange', label='GASS')
            plt.plot(xobs[:,0], mu_truth, color='black', label='Truth')
            # plt.fill_between(xobs[:,0], mu_lower, mu_upper, color='orange', alpha=0.5,)
            plt.axhline(1, color='purple', ls='--', label='Upper bound')
            plt.axhline(0, color='red', ls='--', label='Lower bound')
            plt.legend(loc='lower left', ncol=2)
            plt.savefig('plots/gass-benchmark.pdf', bbox_inches='tight')
            plt.close()

            import seaborn as sns
            methods = ['GASS', 'RS', 'LRS', 'PP', 'LPP']
            for midx, method in enumerate(methods):
                with sns.axes_style('white'):
                    plt.rc('font', weight='bold')
                    plt.rc('grid', lw=3)
                    plt.rc('lines', lw=3)
                    matplotlib.rcParams['pdf.fonttype'] = 42
                    matplotlib.rcParams['ps.fonttype'] = 42
                    # plt.scatter(xobs, data, color='gray', alpha=0.5)
                    plt.plot(xobs[:,0], mu_truth, color='black', label='Truth')
                    plt.plot(xobs[:,0], mu_hat[midx], color='orange', label=method)
                    plt.fill_between(xobs[:,0], mu_lower[midx], mu_upper[midx], color='orange', alpha=0.5)
                    plt.axhline(max_mu, color='red', ls='--', label='Upper bound')
                    plt.axhline(min_mu, color='red', ls='--', label='Lower bound')
                    plt.ylim([-0.1,1.1])
                    plt.ylabel('Gamma scale', fontsize=14)
                    plt.savefig('plots/gass-example-{}.pdf'.format(method.lower()), bbox_inches='tight')
                    plt.close()
        
            
    np.save('data/gass-benchmark-mse.npy', mse)
    np.save('data/gass-benchmark-coverage.npy', coverage)

    mse = mse * 1e3
    mse_mean, mse_stderr = mse.mean(axis=0), mse.std(axis=0) / np.sqrt(mse.shape[0])
    coverage_mean, coverage_stderr = coverage.mean(axis=-1).mean(axis=0), coverage.mean(axis=-1).std(axis=0) / np.sqrt(coverage.shape[0])
    for i in range(nmethods):
        print(' & '.join(['${:.2f} \\pm {:.2f}$'.format(m,s) for m,s in zip(mse_mean[:,i], mse_stderr[:,i])]))
    print()
    for i in range(nmethods):
        print(' & '.join(['${:.2f} \\pm {:.2f}$'.format(m,s) for m,s in zip(coverage_mean[:,i], coverage_stderr[:,i])]))
Exemplo n.º 4
0
####### Run the Gibbs sampler #######
results = model.run_gibbs(Y_missing,
                          nburn=nburn,
                          nthin=nthin,
                          nsamples=nsamples,
                          print_freq=100,
                          verbose=True)
Ws = results['W']
Vs = results['V']
Rs = results['R']
Tau2s = results['Tau2']
lam2s = results['lam2']
sigma2s = results['sigma2']

# Get the Bayes estimate
Ps = ilogit(np.einsum('znk,zmtk->znmt', Ws, Vs).clip(-10, 10))
Mu_hat = Rs * Ps / (1 - Ps)
Mu_hat_mean = Mu_hat.mean(axis=0)
Mu_hat_upper = np.percentile(Mu_hat, 95, axis=0)
Mu_hat_lower = np.percentile(Mu_hat, 5, axis=0)

###### Plot the true curves, the noisy observations, and the fits ######
print('Plotting results')
X = np.arange(ndepth)
fig, axarr = plt.subplots(nrows,
                          ncols,
                          figsize=(5 * ncols, 5 * nrows),
                          sharex=True,
                          sharey=True)
for i in range(nrows):
    for j in range(ncols):
Exemplo n.º 5
0
 def fun(x):
     logit = np.einsum('k,nk,t->nt', x[1:], W,
                       concentrations) + x[0] + a[:, None]
     return np.nansum((Y[:, j] - ilogit(logit))**
                      2) + regularizer * (x**2).mean()
Exemplo n.º 6
0
 def fun(x):
     logit = np.einsum('k,mk,t->mt', x[1:], V,
                       concentrations) + x[0] + b[:, None]
     return np.nansum(
         (Y[i] - ilogit(logit))**2) + regularizer * (x**2).mean()
     bounds = [(-10, 10) for _ in range(nembeds + 1)]
Exemplo n.º 7
0
def fit_logistic_factors(Y,
                         nembeds,
                         max_steps=100,
                         concentrations=None,
                         verbose=False,
                         tol=1e-4,
                         regularizer=1e-4):
    from scipy.optimize import minimize
    if concentrations is None:
        concentrations = np.arange(Y.shape[2])
    W = np.random.normal(0, 0.1, size=(Y.shape[0], nembeds))
    V = np.random.normal(0, 0.1, size=(Y.shape[1], nembeds))
    a, b = np.random.normal(size=(Y.shape[0])), np.random.normal(
        size=(Y.shape[1]))

    # Fit the embedding model via alternating minimization
    rmse = np.inf
    for step in range(max_steps):
        if verbose:
            print('Step {}'.format(step))

        # Track the previous iteration to assess convergence
        prev_W, prev_V = np.copy(W), np.copy(V)
        prev_rmse = rmse

        # Fix V and fit W
        for i in range(W.shape[0]):

            def fun(x):
                logit = np.einsum('k,mk,t->mt', x[1:], V,
                                  concentrations) + x[0] + b[:, None]
                return np.nansum(
                    (Y[i] - ilogit(logit))**2) + regularizer * (x**2).mean()
                bounds = [(-10, 10) for _ in range(nembeds + 1)]

            bounds = [(-10, 10) for _ in range(nembeds + 1)]
            res = minimize(fun,
                           x0=np.concatenate([a[i:i + 1], W[i]]),
                           method='SLSQP',
                           options={
                               'ftol': 1e-8,
                               'maxiter': 1000
                           },
                           bounds=bounds)
            a[i], W[i] = res.x[0], res.x[1:]

        # Fix W and fit V
        for j in range(V.shape[0]):

            def fun(x):
                logit = np.einsum('k,nk,t->nt', x[1:], W,
                                  concentrations) + x[0] + a[:, None]
                return np.nansum((Y[:, j] - ilogit(logit))**
                                 2) + regularizer * (x**2).mean()

            bounds = [(-10, 10) for _ in range(nembeds + 1)]
            res = minimize(fun,
                           x0=np.concatenate([b[j:j + 1], V[j]]),
                           method='SLSQP',
                           options={
                               'ftol': 1e-8,
                               'maxiter': 1000
                           },
                           bounds=bounds)
            b[j], V[j] = res.x[0], res.x[1:]

        # delta = np.linalg.norm(np.concatenate([(prev_W - W).flatten(), (prev_V - V).flatten()]))
        Mu = ilogit(
            np.einsum('nk,mk,t->nmt', W, V, concentrations) +
            a[:, None, None] + b[None, :, None])
        rmse = np.sqrt(np.nansum((Y - Mu)**2))
        delta = (prev_rmse - rmse) / rmse

        if verbose:
            print('delta: {}'.format(delta))
        if delta <= tol:
            break

    Mu = ilogit(
        np.einsum('nk,mk,t->nmt', W, V, concentrations) + a[:, None, None] +
        b[None, :, None])

    return Mu, W, V, a, b
    np.random.seed(seed)

    # Sample from the prior
    model = init_model()

    # Ground truth drawn from the model
    W_true, V_true = create_wiggly_with_jumps()

    # Get the true mean values
    Mu = np.einsum('nk,mtk->nmt', W_true, V_true)
    print('Mean ranges: [{},{}]'.format(Mu.min(), Mu.max()))

    # Generate the data
    N = np.full((nrows, ncols, ndepth), nreplicates)
    Y = np.random.binomial(N, ilogit(Mu))

    # Convert to acceptable formats for sampling
    N = N.astype(float)
    Y = Y.astype(float)

    # Hold out some curves
    Y_missing = Y.copy()
    Y_missing[:3, :3] = np.nan
    N_missing = N.copy()
    N_missing[np.isnan(Y_missing)] = np.nan

    ####### Run the Gibbs sampler #######
    results = model.run_gibbs((Y_missing, N_missing),
                              nburn=nburn,
                              nthin=nthin,