этот вопрос является расширением этого , который фокусируется на LSTM, а не на CRF.К сожалению, у меня нет опыта работы с CRF, поэтому я задаю эти вопросы.
Проблема:
Я хотел бы предсказать последовательность двоичных файловсигнал для нескольких, независимых групп.Мой набор данных умеренно мал (~ 1000 записей на группу), поэтому я хотел бы попробовать модель CRF здесь.
Доступные данные:
У меня есть набор данных сследующие переменные:
- метки времени
- группа
- двоичный сигнал, представляющий активность
Используя этот набор данных, я хотел бы прогнозировать 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 с помощью одной модели), и поэтому мне придетсяпредсказать каждый из них индивидуальноly.
Теперь часть CRF.Я нашел соответствующий пример (здесь one ), но все они имеют тенденцию предсказывать одно значение класса на основе предыдущей последовательности.
Вот моя попытка использовать 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) когдаиспользуется в многозадачном режиме, я не уверен, что это действительно.
У меня есть несколько дополнительных вопросов:
- Подходит ли CRF для этой проблемы?
- Чем отличаются два подхода (один на основе
pycrfuite
и один на keras_contrib
) и каковы их преимущества / недостатки? - В более общем смысле, что такоеПреимущество объединения моделей CRF и LSTM в одну (как обсуждалось здесь )
Большое спасибо!