Прогнозирование многомерной бинарной последовательности с помощью CRF

этот вопрос является расширением этого, который фокусируется на LSTM, а не на CRF . К сожалению, у меня нет опыта работы с CRF, поэтому я задаю эти вопросы.

Проблема:

Я хотел бы предсказать последовательность двоичного сигнала для нескольких независимых групп. Мой набор данных умеренно мал (~ 1000 записей на группу), поэтому я хотел бы попробовать здесь модель CRF.

Доступные данные:

У меня есть набор данных со следующими переменными:

  1. Временные метки
  2. Группа
  3. Двоичный сигнал, представляющий активность

Используя этот набор данных, я хотел бы спрогнозировать group_a_activity и group_b_activity, которые равны 0 или 1.

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

Что у меня есть на данный момент:

Вот настройка данных, которую вы можете воспроизвести на своей машине.

# libraries
import re
import numpy as np
import pandas as pd

data_length = 18  # how long our data series will be
shift_length = 3  # how long of a sequence do we want

df = (pd.DataFrame  # create a sample dataframe
    .from_records(np.random.randint(2, size=[data_length, 3]))
    .rename(columns={0:'a', 1:'b', 2:'extra'}))
df.head()  # check it out

# shift (assuming data is sorted already)
colrange = df.columns
shift_range = [_ for _ in range(-shift_length, shift_length+1) if _ != 0]
for c in colrange:
    for s in shift_range:
        if not (c == 'extra' and s > 0):
            charge = 'next' if s > 0 else 'last'  # 'next' variables is what we want to predict
            formatted_s = '{0:02d}'.format(abs(s))
            new_var = '{var}_{charge}_{n}'.format(var=c, charge=charge, n=formatted_s)
            df[new_var] = df[c].shift(s)

# drop unnecessary variables and trim missings generated by the shift operation
df.dropna(axis=0, inplace=True)
df.drop(colrange, axis=1, inplace=True)
df = df.astype(int)
df.head()  # check it out

#   a_last_03  a_last_02      ...        extra_last_02  extra_last_01
# 3          0          1      ...                    0              1
# 4          1          0      ...                    0              0
# 5          0          1      ...                    1              0
# 6          0          0      ...                    0              1
# 7          0          0      ...                    1              0
[5 rows x 15 columns]

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

Теперь часть CRF. Я нашел подходящий пример (вот один), но все они склонны предсказывать одно значение класса на основе предшествующей последовательности.

Вот моя попытка использовать CRF здесь:

import pycrfsuite

crf_features = []  # a container for features
crf_labels = []  # a container for response
# lets focus on group A only for this one
current_response = [c for c in df.columns if c.startswith('a_next')]
# predictors are going to have to be nested otherwise I'll run into problems with dimensions
current_predictors = [c for c in df.columns if not 'next' in c]
current_predictors = set([re.sub('_\d+$','',v) for v in current_predictors])
for index, row in df.iterrows():
    # not sure if its an effective way to iterate over a DF...
    iter_features = []
    for p in current_predictors:
        pred_feature = []
        # note that 0/1 values have to be converted into booleans
        for k in range(shift_length):
            iter_pred_feature = p + '_{0:02d}'.format(k+1)
            pred_feature.append(p + "=" + str(bool(row[iter_pred_feature])))
        iter_features.append(pred_feature)
    iter_response = [row[current_response].apply(lambda z: str(bool(z))).tolist()]
    crf_labels.extend(iter_response)
    crf_features.append(iter_features)

trainer = pycrfsuite.Trainer(verbose=True)
for xseq, yseq in zip(crf_features, crf_labels):
    trainer.append(xseq, yseq)

trainer.set_params({
    'c1': 0.0,   # coefficient for L1 penalty
    'c2': 0.0,  # coefficient for L2 penalty
    'max_iterations': 10,  # stop earlier
    # include transitions that are possible, but not observed
    'feature.possible_transitions': True
})

trainer.train('testcrf.crfsuite')
tagger = pycrfsuite.Tagger()
tagger.open('testcrf.crfsuite')
tagger.tag(xseq)
# ['False', 'True', 'False']

Кажется, мне удалось заставить его работать, но я не уверен, правильно ли я подошел к нему. Я сформулирую свои вопросы в разделе «Вопросы», но сначала вот альтернативный подход с использованием пакета keras_contrib:

from keras import Sequential
from keras_contrib.layers import CRF
from keras_contrib.losses import crf_loss

# we are gonna have to revisit data prep stage again
# separate predictors and response
response_df_dict = {}
for g in ['a','b']:
    response_df_dict[g] = df[[c for c in df.columns if 'next' in c and g in c]]

# reformat for LSTM
# the response for every row is a matrix with depth of 2 (the number of groups) and width = shift_length
# the predictors are of the same dimensions except the depth is not 2 but the number of predictors that we have

response_array_list = []
col_prefix = set([re.sub('_\d+$','',c) for c in df.columns if 'next' not in c])
for c in col_prefix:
    current_array = df[[z for z in df.columns if z.startswith(c)]].values
    response_array_list.append(current_array)

# reshape into samples (1), time stamps (2) and channels/variables (0)
response_array = np.array([response_df_dict['a'].values,response_df_dict['b'].values])
response_array = np.reshape(response_array, (response_array.shape[1], response_array.shape[2], response_array.shape[0]))
predictor_array = np.array(response_array_list)
predictor_array = np.reshape(predictor_array, (predictor_array.shape[1], predictor_array.shape[2], predictor_array.shape[0]))

model = Sequential()
model.add(CRF(2, input_shape=(predictor_array.shape[1],predictor_array.shape[2])))
model.summary()
model.compile(loss=crf_loss, optimizer='adam', metrics=['accuracy'])
model.fit(predictor_array, response_array, epochs=10, batch_size=1)
model_preds = model.predict(predictor_array)  # not gonna worry about train/test split here

Вопросы:

Мой главный вопрос заключается в том, правильно ли я построил обе свои модели CRF. Что меня беспокоит, так это то, что (1) по моделям CRF не так много документации, (2) CRF в основном используются для прогнозирования одной метки с учетом последовательности, (3) входные функции являются вложенными и (4) когда используется в многозадачном режиме, я не уверен, что это действительно.

У меня тоже есть несколько дополнительных вопросов:

  1. Подходит ли CRF для решения этой проблемы?
  2. Чем отличаются два подхода (один на основе pycrfuite и один на основе keras_contrib) и каковы их преимущества/недостатки?
  3. В более общем смысле, в чем преимущество объединения моделей CRF и LSTM в одну (например, обсуждавшуюся здесь)

Большое спасибо!


person IVR    schedule 31.12.2018    source источник