Декодирование Витерби с TensorFlow

от автора

Привет, Хабр!

Алгоритм был предложен Эндрю Витерби в 1967 году для декодирования сигналов с кодировкой, используемой в системах связи.

Алгоритм Витерби предназначен для поиска наиболее вероятной последовательности скрытых состояний в моделях с наблюдаемыми переменными, таких как скрытые марковские модели. Основное применение заключается в декодировании, где нужно определить скрытую последовательность состояний, вызвавших наблюдаемую последовательность событий.

Основные концепции алгоритма:

  • Начальные вероятности: определяют, с какой вероятностью процесс начинается в каждом возможном состоянии. В контексте скрытых марковских моделей, начальные вероятности (или начальные распределения) дают нам понимание того, каковы шансы нахождения системы в каждом из возможных начальных состояний. Например, если есть три возможных состояния S1, S2 и S3, начальные вероятности могут быть представлены как , P(S2) и P(S3), где сумма всех вероятностей равна 1.

  • Матрица переходов: матрица, в которой каждая ячейка a_{ij} представляет собой вероятность перехода из состояния S_i в состояние S_j. Матрица переходов отображает динамику системы, показывая, как вероятности изменения состояний зависят от текущего состояния. В скрытых марковских моделей если система находится в состоянии S_i в момент времени t , то a_{ij} показывает вероятность перехода в состояние S_j в момент времени t+1.

  • Матрица эмиссий: матрица определяет вероятность наблюдения каждого возможного события в каждом состоянии. Эмиссионные вероятности описывают, как наблюдаемые данные зависят от скрытых состояний. Например, если O_k представляет наблюдаемое событие, то вероятность того, что это событие произойдет, когда система находится в состоянии S_i, обозначается как b_i(O_k). В каждой строке этой матрицы указаны вероятности наблюдения различных событий для конкретного состояния, и сумма всех вероятностей в строке равна 1.

Допустим, мы наблюдаем последовательность погодных условий, и есть скрытые состояния солнечно и дождливо. Начальные вероятности могут быть такими: P(\text{солнечно}) = 0.6 ) и P(\text{дождливо}) = 0.4. Матрица переходов может показывать, что если сегодня солнечно, то завтра также будет солнечно с вероятностью 0.7, а дождливо с вероятностью 0.3, и наоборот. Матрица эмиссий может указывать, что если сегодня солнечно, то с вероятностью 0.9 мы наблюдаем солнце, а с вероятностью 0.1 — дождь, и наоборот.

Реализация функции декодирования Витерби в TF

Установим сам TensorFlow:

pip install tensorflow

Также понадобится numpy.

На старте создадим кэш, который будет использоваться для хранения промежуточных значений вероятностей:

import tensorflow as tf  class HMM:     def __init__(self, initial_prob, trans_prob, obs_prob):         self.initial_prob = tf.constant(initial_prob, dtype=tf.float64)         self.trans_prob = tf.constant(trans_prob, dtype=tf.float64)         self.obs_prob = tf.constant(obs_prob, dtype=tf.float64)         self.viterbi = tf.Variable(initial_value=tf.zeros([self.initial_prob.shape[0], None], dtype=tf.float64), trainable=False)      def get_emission(self, obs_idx):         return tf.gather(self.obs_prob, obs_idx)

Далее, объявим оператор для обновления кэша Витерби на каждом временном шаге:

class HMM:     # предыдущий код инициализации      def decode_op(self, obs_idx):         emissions = self.get_emission(obs_idx)         transitions = tf.matmul(self.viterbi, tf.transpose(emissions))         weighted_transitions = transitions * self.trans_prob         viterbi_update = tf.reduce_max(weighted_transitions, axis=1)         return tf.reshape(viterbi_update, tf.shape(self.viterbi))

Обратные указатели необходимы для отслеживания наиболее вероятных путей переходов между состояниями:

class HMM:     # предыдущий код инициализации и decode_op      def backpt_op(self):         back_transitions = tf.matmul(self.viterbi, tf.ones([self.viterbi.shape[1], self.trans_prob.shape[0]], dtype=tf.float64))         weighted_back_transitions = back_transitions * self.trans_prob         return tf.argmax(weighted_back_transitions, axis=1)

Объединяем все части для реализации полной функции:

import numpy as np  # пример данных HMM initial_prob = np.array([0.6, 0.4]) trans_prob = np.array([[0.7, 0.3], [0.4, 0.6]]) obs_prob = np.array([[0.5, 0.5], [0.1, 0.9]])  # пример наблюдений observations = np.array([0, 1, 1])  # инициализация модели hmm = HMM(initial_prob, trans_prob, obs_prob)  # создание сессии TensorFlow with tf.Session() as sess:     sess.run(tf.global_variables_initializer())          # инициализация кэша Витерби начальными вероятностями     viterbi_init = sess.run(hmm.initial_prob)          backpts = np.ones((hmm.trans_prob.shape[0], len(observations)), dtype='int32') * -1          for t in range(1, len(observations)):         feed_dict = {hmm.viterbi: viterbi_init}         viterbi, backpt = sess.run([hmm.decode_op(observations[t]), hmm.backpt_op()], feed_dict=feed_dict)         backpts[:, t] = backpt          # вычисление наиболее вероятной последовательности состояний     tokens = [np.argmax(viterbi)]     for i in range(len(observations) - 1, 0, -1):         tokens.append(backpts[tokens[-1], i])     tokens.reverse()      print('Most likely hidden states are', tokens)

Алгоритм Витерби не ограничивается только теоретическими задачам и применяется в телекоммуникациях, биоинформатики, обработки естественного языка и т.д.

Все актуальные методы и инструменты DS и ML можно освоить на онлайн-курсах OTUS: в каталоге можно посмотреть список всех программ, а в календаре — записаться на открытые уроки.


ссылка на оригинал статьи https://habr.com/ru/articles/824162/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *