Ejemplo n.º 1
0
    def forward(self, features, trip_counts):
        pyro.module("model", self)
        total_hours = len(features)
        observed_hours, num_origins, num_destins = trip_counts.shape
        assert observed_hours <= total_hours
        assert num_origins == self.num_stations
        assert num_destins == self.num_stations
        time_plate = pyro.plate("time", observed_hours, dim=-3)
        origins_plate = pyro.plate("origins", num_origins, dim=-2)
        destins_plate = pyro.plate("destins", num_destins, dim=-1)

        # The first half of the model performs exact inference over
        # the observed portion of the time series.
        hmm = dist.GaussianHMM(*self._dynamics(features[:observed_hours]))
        gate_rate = pyro.sample("gate_rate", hmm)
        gate, rate = self._unpack_gate_rate(gate_rate, event_dim=2)
        with time_plate, origins_plate, destins_plate:
            pyro.sample("trip_count",
                        dist.ZeroInflatedPoisson(gate, rate),
                        obs=trip_counts)

        # The second half of the model forecasts forward.
        forecast = []
        forecast_hours = total_hours - observed_hours
        if forecast_hours > 0:
            _, trans_matrix, trans_dist, obs_matrix, obs_dist = \
                self._dynamics(features[observed_hours:])
        state = None
        for t in range(forecast_hours):
            if state is None:  # on first step
                state_dist = hmm.filter(gate_rate)
            else:
                loc = vm(state, trans_matrix) + trans_dist.loc
                scale_tril = trans_dist.scale_tril
                state_dist = dist.MultivariateNormal(loc,
                                                     scale_tril=scale_tril)
            state = pyro.sample("state_{}".format(t), state_dist)

            loc = vm(state, obs_matrix) + obs_dist.base_dist.loc[..., t, :]
            scale = obs_dist.base_dist.scale[..., t, :]
            gate_rate = pyro.sample("gate_rate_{}".format(t),
                                    dist.Normal(loc, scale).to_event(1))
            gate, rate = self._unpack_gate_rate(gate_rate, event_dim=1)

            with origins_plate, destins_plate:
                forecast.append(
                    pyro.sample("trip_count_{}".format(t),
                                dist.ZeroInflatedPoisson(gate, rate)))
        return forecast
Ejemplo n.º 2
0
    def _forward_pyro_forecast(self,
                               features,
                               trip_counts,
                               origins_plate,
                               destins_plate,
                               state=None,
                               state_dist=None):
        total_hours = len(features)
        observed_hours, num_origins, num_destins = trip_counts.shape
        forecast = []
        forecast_hours = total_hours - observed_hours
        if forecast_hours > 0:
            _, trans_matrix, trans_dist, obs_matrix, obs_dist = \
                self._dynamics(features[observed_hours:])
        for t in range(forecast_hours):
            if state is not None:
                loc = vm(state, trans_matrix) + trans_dist.loc
                scale_tril = trans_dist.scale_tril
                state_dist = dist.MultivariateNormal(loc,
                                                     scale_tril=scale_tril)
            state = pyro.sample("state_{}".format(t), state_dist)

            loc = vm(state, obs_matrix) + obs_dist.base_dist.loc[..., t, :]
            scale = obs_dist.base_dist.scale[..., t, :]
            gate_rate = pyro.sample("gate_rate_{}".format(t),
                                    dist.Normal(loc, scale).to_event(1))
            gate, rate = self._unpack_gate_rate(gate_rate, event_dim=1)

            with origins_plate, destins_plate:
                forecast.append(
                    pyro.sample("trip_count_{}".format(t),
                                dist.ZeroInflatedPoisson(gate, rate)))
        return forecast
Ejemplo n.º 3
0
    def _forward_pyro(self, features, trip_counts):
        total_hours = len(features)
        observed_hours, num_origins, num_destins = trip_counts.shape
        assert observed_hours <= total_hours
        assert num_origins == self.num_stations
        assert num_destins == self.num_stations
        time_plate = pyro.plate("time", observed_hours, dim=-3)
        origins_plate = pyro.plate("origins", num_origins, dim=-2)
        destins_plate = pyro.plate("destins", num_destins, dim=-1)

        # The first half of the model performs exact inference over
        # the observed portion of the time series.
        hmm = dist.GaussianHMM(*self._dynamics(features[:observed_hours]))
        gate_rate = pyro.sample("gate_rate", hmm)
        gate, rate = self._unpack_gate_rate(gate_rate, event_dim=2)
        with time_plate, origins_plate, destins_plate:
            pyro.sample("trip_count",
                        dist.ZeroInflatedPoisson(gate, rate),
                        obs=trip_counts)

        # The second half of the model forecasts forward.
        if total_hours > observed_hours:
            state_dist = hmm.filter(gate_rate)
            return self._forward_pyro_forecast(features,
                                               trip_counts,
                                               origins_plate,
                                               destins_plate,
                                               state_dist=state_dist)
Ejemplo n.º 4
0
def test_zip_shape():
    gate = torch.ones(3, 2) / 2
    rate = torch.ones(3, 2) / 2
    d = dist.ZeroInflatedPoisson(rate, gate=gate)
    assert d.batch_shape == (3, 2)
    assert d.event_shape == ()
    assert d.shape() == (3, 2)
    assert d.sample().size() == d.shape()
Ejemplo n.º 5
0
def post_model(
    p_data,
    t_data,
    s_data,
    r_data,
    y,
    p_types,
    p_stories,
    p_subreddits,
    zero_inflated,
):
    coef_scale_prior = 0.1

    num_posts, num_p_indeps = p_data.shape

    # shared prior
    gamma_loc = torch.zeros((num_p_indeps, 1), dtype=torch.float64)

    if zero_inflated:
        gamma_gate_loc = torch.zeros((num_p_indeps, 1), dtype=torch.float64)

    with pyro.plate("p_indep", num_p_indeps, dim=-2):
        gamma = pyro.sample("gamma", dist.Normal(gamma_loc, coef_scale_prior))

        if zero_inflated:
            gamma_gate = pyro.sample(
                "gamma_gate", dist.Normal(gamma_gate_loc, coef_scale_prior)
            )

    # for each post,
    # use the correct set of coefficients to run our post-level regression
    with pyro.plate("post", num_posts, dim=-1) as p:

        # indep vars for this post
        indeps = p_data[p, :]

        mu = torch.matmul(
            indeps, gamma
        ).flatten()  # ( num_posts, num_p_indeps) x (num_p_indeps, 1)

        # defining response dist
        if zero_inflated:
            gate = torch.nn.Sigmoid()(
                torch.matmul(indeps, gamma_gate).flatten()
            )  # ( num_posts, num_p_indeps) x (num_p_indeps, 1)

            response_dist = dist.ZeroInflatedPoisson(
                rate=torch.exp(mu), gate=gate
            )
        else:
            response_dist = dist.Poisson(rate=torch.exp(mu))

        # sample
        if y is None:
            pyro.sample("obs", response_dist, obs=y)
        else:
            pyro.sample("obs", response_dist, obs=y[p])
Ejemplo n.º 6
0
    def _forward_pyro_mean_field(self, features, trip_counts):
        total_hours = len(features)
        observed_hours, num_origins, num_destins = trip_counts.shape
        assert observed_hours <= total_hours
        assert num_origins == self.num_stations
        assert num_destins == self.num_stations
        time_plate = pyro.plate("time", observed_hours, dim=-3)
        origins_plate = pyro.plate("origins", num_origins, dim=-2)
        destins_plate = pyro.plate("destins", num_destins, dim=-1)
        init_dist, trans_matrix, trans_dist, obs_matrix, obs_dist = \
            self._dynamics(features[:observed_hours])

        # This is a parallelizable crf representation of the HMM.
        # We first pull random variables from the guide, masking all factors.
        with poutine.mask(mask=False):
            shape = (1 + observed_hours, self.args.state_dim)  # includes init
            state = pyro.sample("state",
                                dist.Normal(0, 1).expand(shape).to_event(2))

            shape = (observed_hours, 2 * num_origins * num_destins)
            gate_rate = pyro.sample(
                "gate_rate",
                dist.Normal(0, 1).expand(shape).to_event(2))

        # We then declare CRF factors.
        pyro.sample("init", init_dist, obs=state[0])
        pyro.sample("trans",
                    trans_dist.expand((observed_hours, )).to_event(1),
                    obs=state[..., 1:, :] - state[..., :-1, :] @ trans_matrix)
        pyro.sample("obs",
                    obs_dist.expand((observed_hours, )).to_event(1),
                    obs=gate_rate - state[..., 1:, :] @ obs_matrix)
        gate, rate = self._unpack_gate_rate(gate_rate, event_dim=2)
        with time_plate, origins_plate, destins_plate:
            pyro.sample("trip_count",
                        dist.ZeroInflatedPoisson(gate, rate),
                        obs=trip_counts)

        # The second half of the model forecasts forward.
        if total_hours > observed_hours:
            return self._forward_pyro_forecast(features,
                                               trip_counts,
                                               origins_plate,
                                               destins_plate,
                                               state=state[..., -1, :])
Ejemplo n.º 7
0
    def model(self, data, demand):
        coef = {}

        for s in self.features['station']['names']:
            coef[s] = pyro.sample(s, dist.Normal(0, 1))
            s += '_gate'
            coef[s] = pyro.sample(s, dist.Normal(0, 1))

        for h in self.features['hour']['names']:
            for d in self.features['daytype']['names']:
                name = h + '_' + d
                coef[name] = pyro.sample(name, dist.Normal(0, 1))
                name += '_gate'
                coef[name] = pyro.sample(name, dist.Normal(0, 1))

        log_lmbda = 0
        gate_mean = 0
        for i in range(len(self.features['station']['names'])):
            name = self.features['station']['names'][i]
            index = self.features['station']['index'][i]
            log_lmbda += coef[name] * data[:, index]
            gate_mean += coef[name + '_gate'] * data[:, index]

        for h in range(len(self.features['hour']['names'])):
            for d in range(len(self.features['daytype']['names'])):
                h_name = self.features['hour']['names'][h]
                h_index = self.features['hour']['index'][h]
                d_name = self.features['daytype']['names'][d]
                d_index = self.features['daytype']['index'][d]
                log_lmbda += coef[h_name + '_' + d_name] * \
                    data[:, h_index] * data[:, d_index]

                gate_mean += coef[h_name + '_' + d_name + '_gate'] * \
                    data[:, h_index] * data[:, d_index]

        lmbda = log_lmbda.exp()
        gate = sigmoid(gate_mean)

        with pyro.plate("data", len(data)):
            pyro.sample("obs",
                        dist.ZeroInflatedPoisson(gate, lmbda),
                        obs=demand)

            return gate, lmbda
Ejemplo n.º 8
0
    def model(self, data, demand):
        coef = {}

        for s in self.features['station']['names']:
            coef[s] = pyro.sample(s, dist.Normal(0, 1))

        for h in self.features['hour']['names']:
            for d in self.features['daytype']['names']:
                name = h + '_' + d
                coef[name] = pyro.sample(name, dist.Normal(0, 1))

        log_lmbda = 0
        for i in range(len(self.features['station']['names'])):
            name = self.features['station']['names'][i]
            index = self.features['station']['index'][i]
            log_lmbda += coef[name] * data[:, index]

        for h in range(len(self.features['hour']['names'])):
            for d in range(len(self.features['daytype']['names'])):
                h_name = self.features['hour']['names'][h]
                h_index = self.features['hour']['index'][h]
                d_name = self.features['daytype']['names'][d]
                d_index = self.features['daytype']['index'][d]
                log_lmbda += coef[h_name + '_' + d_name] * \
                    data[:, h_index] * data[:, d_index]

        lmbda = log_lmbda.exp()

        gate_alpha = pyro.sample('gate_alpha', dist.Gamma(2, 2))
        gate_beta = pyro.sample('gate_beta', dist.Gamma(3, 2))
        gate = pyro.sample('gate', dist.Beta(gate_alpha, gate_beta))

        with pyro.plate("data", len(data)):
            pyro.sample(
                "obs", dist.ZeroInflatedPoisson(
                    gate, lmbda), obs=demand)

            # should we be returning lmbda?
            return gate, lmbda
Ejemplo n.º 9
0
def type_model(
    p_data,
    t_data,
    s_data,
    r_data,
    y,
    p_types,
    p_stories,
    p_subreddits,
    zero_inflated,
):
    coef_scale_prior = 0.1

    num_posts, num_p_indeps = p_data.shape
    num_types, num_t_indeps = t_data.shape

    # type priors
    alpha_loc = torch.zeros((num_p_indeps, num_t_indeps), dtype=torch.float64)
    alpha_scale = coef_scale_prior * torch.ones(
        (num_p_indeps, num_t_indeps), dtype=torch.float64)

    if zero_inflated:
        eta_gate_loc = torch.zeros((num_p_indeps, num_t_indeps),
                                   dtype=torch.float64)
        eta_gate_scale = coef_scale_prior * torch.ones(
            (num_p_indeps, num_t_indeps), dtype=torch.float64)

    with pyro.plate("p_indep", num_p_indeps, dim=-2):

        # Type Level
        with pyro.plate("t_indep", num_t_indeps, dim=-1):
            eta = pyro.sample("eta", dist.Normal(alpha_loc, alpha_scale))
            if zero_inflated:
                eta_gate = pyro.sample(
                    "eta_gate", dist.Normal(eta_gate_loc, eta_gate_scale))

        with pyro.plate("type", num_types, dim=-1) as t:
            phi_loc = torch.matmul(
                eta, t_data[t, :].T
            )  # (num_p_indeps, num_t_indeps) x (num_t_indeps, num_types)

            phi = pyro.sample("phi", dist.Normal(phi_loc, coef_scale_prior))

            if zero_inflated:
                phi_gate_loc = torch.matmul(
                    eta_gate, t_data[t, :].T
                )  # (num_p_indeps, num_t_indeps) x (num_t_indeps, num_types)

                phi_gate = pyro.sample(
                    "phi_gate", dist.Normal(phi_gate_loc, coef_scale_prior))

    # for each post,
    # use the correct set of coefficients to run our post-level regression
    with pyro.plate("post", num_posts, dim=-1) as p:
        t = p_types[p]

        # indep vars for this post
        indeps = p_data[p, :]

        t_coefs = phi[:, t]  # (num_p_indeps,num_posts)

        type_level_products = torch.mul(
            t_coefs,
            indeps.T)  # (num_p_indeps, num_posts) .* (num_p_indeps, num_posts)

        # calculate the mean: desired shape (num_posts, 1)
        mu = (type_level_products).sum(
            dim=0)  # (num_p_indeps, num_posts).sum(over indeps)

        # defining response dist
        if zero_inflated:
            t_coefs_gate = phi_gate[:, t]  # (num_p_indeps,num_posts)

            type_level_products_gate = torch.mul(
                t_coefs_gate, indeps.T
            )  # (num_p_indeps, num_posts) .* (num_p_indeps, num_posts)

            # calculate the mean: desired shape (num_posts, 1)
            gate = torch.nn.Sigmoid()((type_level_products_gate).sum(
                dim=0))  # (num_p_indeps, num_posts).sum(over indeps)

            response_dist = dist.ZeroInflatedPoisson(rate=torch.exp(mu),
                                                     gate=gate)
        else:
            response_dist = dist.Poisson(rate=torch.exp(mu))

        # sample
        if y is None:
            pyro.sample("obs", response_dist, obs=y)
        else:
            pyro.sample("obs", response_dist, obs=y[p])
def complete_model(
    p_data,
    t_data,
    s_data,
    r_data,
    y,
    p_types,
    p_stories,
    p_subreddits,
    zero_inflated,
):
    coef_scale_prior = 0.1

    num_posts, num_p_indeps = p_data.shape
    num_types, num_t_indeps = t_data.shape
    num_stories, num_s_indeps = s_data.shape
    num_subreddits, num_r_indeps = r_data.shape

    # type priors
    alpha_loc = torch.zeros((num_p_indeps, num_t_indeps), dtype=torch.float64)
    alpha_scale = coef_scale_prior * torch.ones(
        (num_p_indeps, num_t_indeps), dtype=torch.float64)

    # story priors
    beta_loc = torch.zeros((num_p_indeps, num_s_indeps), dtype=torch.float64)
    beta_scale = coef_scale_prior * torch.ones(
        (num_p_indeps, num_s_indeps), dtype=torch.float64)

    # subreddit priors
    tau_loc = torch.zeros((num_p_indeps, num_r_indeps), dtype=torch.float64)
    tau_scale = coef_scale_prior * torch.ones(
        (num_p_indeps, num_r_indeps), dtype=torch.float64)

    if zero_inflated:
        # type priors
        eta_gate_loc = torch.zeros((num_p_indeps, num_t_indeps),
                                   dtype=torch.float64)
        eta_gate_scale = coef_scale_prior * torch.ones(
            (num_p_indeps, num_t_indeps), dtype=torch.float64)

        # story priors
        beta_gate_loc = torch.zeros((num_p_indeps, num_s_indeps),
                                    dtype=torch.float64)
        beta_gate_scale = coef_scale_prior * torch.ones(
            (num_p_indeps, num_s_indeps), dtype=torch.float64)

        # subreddit priors
        tau_gate_loc = torch.zeros((num_p_indeps, num_r_indeps),
                                   dtype=torch.float64)
        tau_gate_scale = coef_scale_prior * torch.ones(
            (num_p_indeps, num_r_indeps), dtype=torch.float64)

    with pyro.plate("p_indep", num_p_indeps, dim=-2):

        # Type Level
        with pyro.plate("t_indep", num_t_indeps, dim=-1):
            eta = pyro.sample("eta", dist.Normal(alpha_loc, alpha_scale))
            if zero_inflated:
                eta_gate = pyro.sample(
                    "eta_gate", dist.Normal(eta_gate_loc, eta_gate_scale))

        with pyro.plate("type", num_types, dim=-1) as t:
            phi_loc = torch.matmul(eta, t_data[t, :].T)
            # (num_p_indeps, num_t_indeps) x (num_t_indeps, num_types)

            phi = pyro.sample("phi", dist.Normal(phi_loc, coef_scale_prior))

            if zero_inflated:
                phi_gate_loc = torch.matmul(
                    eta_gate, t_data[t, :].T
                )  # (num_p_indeps, num_t_indeps) x (num_t_indeps, num_types)

                phi_gate = pyro.sample(
                    "phi_gate", dist.Normal(phi_gate_loc, coef_scale_prior))

        # Story Level
        with pyro.plate("s_indep", num_s_indeps, dim=-1):
            beta = pyro.sample("beta", dist.Normal(beta_loc, beta_scale))
            if zero_inflated:
                beta_gate = pyro.sample(
                    "beta_gate", dist.Normal(beta_gate_loc, beta_gate_scale))

        with pyro.plate("story", num_stories, dim=-1) as s:
            theta_loc = torch.matmul(
                beta, s_data[s, :].T
            )  # (num_p_indeps, num_s_indeps) x (num_s_indeps, num_stories)

            theta = pyro.sample("theta",
                                dist.Normal(theta_loc, coef_scale_prior))

            if zero_inflated:
                theta_gate_loc = torch.matmul(
                    beta_gate, s_data[s, :].T
                )  # (num_p_indeps, num_t_indeps) x (num_t_indeps, num_types)

                theta_gate = pyro.sample(
                    "theta_gate", dist.Normal(theta_gate_loc,
                                              coef_scale_prior))

        # Subreddit Level
        with pyro.plate("r_indep", num_r_indeps, dim=-1):
            tau = pyro.sample("tau", dist.Normal(tau_loc, tau_scale))
            if zero_inflated:
                tau_gate = pyro.sample(
                    "tau_gate", dist.Normal(tau_gate_loc, tau_gate_scale))

        with pyro.plate("subreddit", num_subreddits, dim=-1) as r:
            rho_loc = torch.matmul(
                tau, r_data[r, :].T
            )  # (num_p_indeps, num_r_indeps) x (num_r_indeps, num_subreddits)

            rho = pyro.sample("rho", dist.Normal(rho_loc, coef_scale_prior))
            if zero_inflated:
                rho_gate_loc = torch.matmul(
                    tau_gate, r_data[r, :].T
                )  # (num_p_indeps, num_t_indeps) x (num_t_indeps, num_types)

                rho_gate = pyro.sample(
                    "rho_gate", dist.Normal(rho_gate_loc, coef_scale_prior))

    # for each post,
    # use the correct set of coefficients to run our post-level regression
    with pyro.plate("post", num_posts, dim=-1) as p:
        t = p_types[p]
        s = p_stories[p]
        r = p_subreddits[p]

        # indep vars for this post
        indeps = p_data[p, :]

        t_coefs = phi[:, t]  # (num_p_indeps,num_posts)
        s_coefs = theta[:, s]  # (num_p_indeps,num_posts)
        r_coefs = rho[:, r]  # (num_p_indeps,num_posts)

        type_level_products = torch.mul(
            t_coefs,
            indeps.T)  # (num_p_indeps, num_posts) .* (num_p_indeps, num_posts)
        story_level_products = torch.mul(
            s_coefs,
            indeps.T)  # (num_p_indeps, num_posts) .* (num_p_indeps, num_posts)
        subreddit_level_products = torch.mul(
            r_coefs,
            indeps.T)  # (num_p_indeps, num_posts) .* (num_p_indeps, num_posts)

        # calculate the mean: desired shape (num_posts, 1)
        mu = (subreddit_level_products + type_level_products +
              story_level_products).sum(
                  dim=0)  # (num_p_indeps, num_posts).sum(over indeps)

        # defining response dist
        if zero_inflated:
            t_coefs_gate = phi_gate[:, t]  # (num_p_indeps,num_posts)
            s_coefs_gate = theta_gate[:, s]  # (num_p_indeps,num_posts)
            r_coefs_gate = rho_gate[:, r]  # (num_p_indeps,num_posts)

            type_level_products_gate = torch.mul(
                t_coefs_gate, indeps.T
            )  # (num_p_indeps, num_posts) .* (num_p_indeps, num_posts)
            story_level_products_gate = torch.mul(
                s_coefs_gate, indeps.T
            )  # (num_p_indeps, num_posts) .* (num_p_indeps, num_posts)
            subreddit_level_products_gate = torch.mul(
                r_coefs_gate, indeps.T
            )  # (num_p_indeps, num_posts) .* (num_p_indeps, num_posts)

            # calculate the mean: desired shape (num_posts, 1)
            gate = torch.nn.Sigmoid()(
                (type_level_products_gate + story_level_products_gate +
                 subreddit_level_products_gate).sum(
                     dim=0))  # (num_p_indeps, num_posts).sum(over indeps)

            response_dist = dist.ZeroInflatedPoisson(rate=torch.exp(mu),
                                                     gate=gate)
        else:
            response_dist = dist.Poisson(rate=torch.exp(mu))

        # sample
        if y is None:
            pyro.sample("obs", response_dist, obs=y)
        else:
            pyro.sample("obs", response_dist, obs=y[p])