How to get HMM working with real-valued data in Tensorflow

Viewed 465

I'm working with a dataset that contains data from IoT devices and I have found that Hidden Markov Models work pretty well for my use case. As such, I'm trying to alter some code from a Tensorflow tutorial I've found here. The dataset contains real-values for the observed variable compared to the count data shown in the tutorial.

In particular, I believe the following needs to be changed so that the HMM has Normally distributed emissions. Unfortunately, I can't find any code on how to alter the model to have a different emission other than Poisson.

How should I change the code to emit normally distributed values?

# Define variable to represent the unknown log rates.
trainable_log_rates = tf.Variable(
  np.log(np.mean(observed_counts)) + tf.random.normal([num_states]),
  name='log_rates')

hmm = tfd.HiddenMarkovModel(
  initial_distribution=tfd.Categorical(
      logits=initial_state_logits),
  transition_distribution=tfd.Categorical(probs=transition_probs),
  observation_distribution=tfd.Poisson(log_rate=trainable_log_rates),
  num_steps=len(observed_counts))

rate_prior = tfd.LogNormal(5, 5)

def log_prob():
 return (tf.reduce_sum(rate_prior.log_prob(tf.math.exp(trainable_log_rates))) +
         hmm.log_prob(observed_counts))

optimizer = tf.keras.optimizers.Adam(learning_rate=0.1)

@tf.function(autograph=False)
def train_op():
  with tf.GradientTape() as tape:
    neg_log_prob = -log_prob()
  grads = tape.gradient(neg_log_prob, [trainable_log_rates])[0]
  optimizer.apply_gradients([(grads, trainable_log_rates)])
  return neg_log_prob, tf.math.exp(trainable_log_rates)
1 Answers

The example model assumes that emissions x are Poisson distributed with one of four rates determined by the latent variable z. Therefore it defines trainable rates (or log rates), defines the HMM with uniform initial distributions on z, transition probabilities, and observations from the Poisson distribution with log rates given by the trainable ones.

In order to change to a normal distribution, you are saying x should be Normally distributed with trainable mean and standard deviation determined by the latent variable z. Thus, you need to replace trainable_log_rates with a trainable_loc and trainable_scale and change the

observation_distribution=tfd.Poisson(log_rate=trainable_log_rates)

to

observation_distribution=tfd.Normal(loc=trainable_loc, scale=trainable_scale)

You then need to replace your rate_prior with a loc_prior and scale_prior of your choosing and use them to calculate your new log_prob function.

Related