Декодирующие последовательности в GaussianHMM

Я играю со Скрытыми марковскими моделями для проблемы прогноза фондового рынка. Моя матрица данных содержит различные функции для конкретной безопасности:

01-01-2001, .025, .012, .01
01-02-2001, -.005, -.023, .02

Я подхожу к простому гауссовскому HMM:

from hmmlearn import GaussianHMM
mdl = GaussianHMM(n_components=3,covariance_type='diag',n_iter=1000)
mdl.fit(train[:,1:])

При модели (λ) я могу декодировать вектор наблюдения, чтобы найти наиболее вероятную скрытую последовательность состояний, соответствующую вектору наблюдения:

print mdl.decode(test[0:5,1:])
(72.75, array([2, 1, 2, 0, 0]))

Выше, я декодировал скрытую последовательность состояний вектора наблюдения O t= (O 1, O 2,..., O d), который содержит первые пять экземпляров в тестовом наборе. Я хотел бы оценить скрытое состояние шестого экземпляра в тестовом наборе. Идея состоит в том, чтобы перебирать дискретный набор возможных значений признаков для шестого экземпляра и выбирать последовательность наблюдения O t + 1 с наивысшим правдоподобием argmax = P (O 1, O 2,..., O d + 1 | λ). Как только мы увидим истинные значения функции O d + 1, мы можем сдвинуть последовательность (длины 5) на единицу и сделать это снова и снова:

    l = 5
    for i in xrange(len(test)-l):
        values = []
        for a in arange(-0.05,0.05,.01):
            for b in arange(-0.05,0.05,.01):
                for c in arange(-0.05,0.05,.01):
                    values.append(mdl.decode(vstack((test[i:i+l,1:],array([a,b,c])))))
     print max(enumerate(values),key=lambda x: x[1])

Проблема заключается в том, что когда я декодирую вектор наблюдения O t + 1, предсказание с наивысшим правдоподобием почти всегда одно и то же (например, оценка с наивысшим правдоподобием всегда имеет значения признаков для O d + 1, что равно [0,04 0,04 0,04] ​​и является скрытым состоянием [0]):

(555, (74.71248518927949, array([2, 1, 2, 0, 0, 0]))) [ 0.04  0.04  0.04]
(555, (69.41963358191555, array([2, 2, 0, 0, 0, 0]))) [ 0.04  0.04  0.04]
(555, (77.11516871816922, array([2, 0, 0, 0, 0, 0]))) [ 0.04  0.04  0.04]

Совершенно возможно, что я неправильно понимаю цель mdl.decode и, следовательно, неправильно ее использовал. Если это так, как лучше всего я могу выполнить повторение возможных значений O d + 1, а затем максимизировать P (O 1, O 2,..., O d + 1 | λ)?

Ответы

Ответ 1

Являются ли ваши фактические значения ограниченными [-0.05, 0.05)?

Когда я изначально построил образец данных, чтобы посмотреть на вашу проблему, я нарисовал случайные поплавки в [0,1]. Когда я это сделал, у меня также были те же результаты, которые вы наблюдаете - всегда максимальное значение (a,b,c) для шестой записи, для каждой последовательности и всегда одного и того же прогнозируемого класса. Но учитывая, что распределение моих данных (равномерно распределенных между 0 и 1) имело большую центральную тенденцию, чем значения поиска в сетке для шестой записи (между -.05 и .05), имело смысл, что HMM всегда выбирал наивысшее значение с тремя кортежами (.04,.04,.04), так как он был ближе всего к основной плотности распределений, на которые он был обучен.

Когда я перестроил набор данных с помощью ничьих из равномерного распределения в том же диапазоне возможных значений, что и для шестой записи ([-0.05,0.05)), результат был намного более разнообразным: как выбор O_t+1, так и предсказание класса разумная дифференциация по последовательностям. Из ваших данных примера, похоже, что у вас есть как положительные, так и отрицательные значения, но вы можете попытаться рассказать о распределении для каждой функции и посмотреть, действительно ли ваш диапазон возможных значений шестой записи правдоподобен.

Вот несколько примеров данных и кода оценки. Он будет распечатывать сообщение каждый раз, когда появляется новая оптимальная последовательность (a,b,c) или когда изменяется предсказание для 6-го наблюдения (просто чтобы показать, что они не все одинаковые). Наивысшая вероятность для каждой 6-элементной последовательности, а также предсказания и оптимальная 6-я точка данных сохраняются в best_per_span.

Сначала создайте образец данных:

import numpy as np
import pandas as pd

dates = pd.date_range(start="01-01-2001", end="31-12-2001", freq='D')
n_obs = len(dates)
n_feat = 3
values = np.random.uniform(-.05, .05, size=n_obs*n_feat).reshape((n_obs,n_feat))
df = pd.DataFrame(values, index=dates)

df.head()
                   0         1         2
2001-01-01  0.020891 -0.048750 -0.027131
2001-01-02  0.013571 -0.011283  0.041322
2001-01-03 -0.008102  0.034088 -0.029202
2001-01-04 -0.019666 -0.005705 -0.003531
2001-01-05 -0.000238 -0.039251  0.029307

Теперь разделите на обучающие и тестовые наборы:

train_pct = 0.7
train_size = round(train_pct*n_obs)
train_ix = np.random.choice(range(n_obs), size=train_size, replace=False)
train_dates = df.index[train_ix]

train = df.loc[train_dates]
test = df.loc[~df.index.isin(train_dates)]

train.shape # (255, 3)
test.shape # (110, 3)

Установите трехмерное HMM на данные тренировки:

# hmm throws a lot of deprecation warnings, we'll suppress them.
import warnings
with warnings.catch_warnings():
    warnings.filterwarnings("ignore",category=DeprecationWarning)
    # in the most recent hmmlearn we can't import GaussianHMM directly anymore.
    from hmmlearn import hmm

mdl = hmm.GaussianHMM(n_components=3, covariance_type='diag', n_iter=1000)
mdl.fit(train)

Теперь сетка ищет оптимальное 6-е (t+1) наблюдение:

# length of O_t
span = 5

# final store of optimal configurations per O_t+1 sequence
best_per_span = []

# update these to demonstrate heterogenous outcomes
current_abc = None
current_pred = None

for start in range(len(test)-span):
    flag = False
    end = start + span
    first_five = test.iloc[start:end].values
    output = []
    for a in np.arange(-0.05,0.05,.01):
        for b in np.arange(-0.05,0.05,.01):
            for c in np.arange(-0.05,0.05,.01):
                sixth = np.array([a, b, c])[:, np.newaxis].T
                all_six = np.append(first_five, sixth, axis=0)
                output.append((mdl.decode(all_six), (a,b,c)))

    best = max(output, key=lambda x: x[0][0])

    best_dict = {"start":start,
                 "end":end,
                 "sixth":best[1],
                 "preds":best[0][1],
                 "lik":best[0][0]}  
    best_per_span.append(best_dict)

    # below here is all reporting
    if best_dict["sixth"] != current_abc:
        current_abc = best_dict["sixth"]
        flag = True
        print("New abc for range {}:{} = {}".format(start, end, current_abc))

    if best_dict["preds"][-1] != current_pred:
        current_pred = best_dict["preds"][-1]
        flag = True
        print("New pred for 6th position: {}".format(current_pred))

    if flag:
        print("Test sequence StartIx: {}, EndIx: {}".format(start, end))
        print("Best 6th value: {}".format(best_dict["sixth"]))
        print("Predicted hidden state sequence: {}".format(best_dict["preds"]))
        print("Likelihood: {}\n".format(best_dict["nLL"]))

Пример вывода отчета при запуске цикла:

New abc for range 3:8 = [-0.01, 0.01, 0.0]
New pred for 6th position: 1
Test sequence StartIx: 3, EndIx: 8
Best 6th value: [-0.01, 0.01, 0.0]
Predicted hidden state sequence: [0 2 2 1 0 1]
Likelihood: 35.30144407374163

New abc for range 18:23 = [-0.01, -0.01, -0.01]
New pred for 6th position: 2
Test sequence StartIx: 18, EndIx: 23
Best 6th value: [-0.01, -0.01, -0.01]
Predicted hidden state sequence: [0 0 0 1 2 2]
Likelihood: 34.31813078939214

Пример вывода из best_per_span:

[{'end': 5,
  'lik': 33.791537281734904,
  'preds': array([0, 2, 0, 1, 2, 2]),
  'sixth': [-0.01, -0.01, -0.01],
  'start': 0},
 {'end': 6,
  'lik': 33.28967307589143,
  'preds': array([0, 0, 1, 2, 2, 2]),
  'sixth': [-0.01, -0.01, -0.01],
  'start': 1},
 {'end': 7,
  'lik': 34.446813870838156,
  'preds': array([0, 1, 2, 2, 2, 2]),
  'sixth': [-0.01, -0.01, -0.01],
  'start': 2}]

Помимо элемента отчетности, это не является существенным изменением для вашего первоначального подхода, но, похоже, он работает, как ожидалось, без максимизации каждый раз.