使用CRF

时间:2018-12-31 12:45:18

标签: keras classification crf sequence-to-sequence crfsuite

此问题是this one的扩展,其重点是LSTM而不是CRF。不幸的是,我没有使用CRF的经验,这就是为什么我要问这些问题。

问题:

我想预测多个非独立组的二进制信号序列。我的数据集很小(每组约1000条记录),所以我想在这里尝试CRF模型。

可用数据:

我有一个包含以下变量的数据集:

  1. 时间戳
  2. 代表活动的二进制信号

使用此数据集,我希望预测group_a_activitygroup_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部分。我找到了一些相关的示例(这里是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)用于多任务方式,我不确定它是否有效。

我还有一些其他问题:

  1. CRF是否适合该问题?
  2. 这两种方法(一种基于pycrfuite和一种基于keras_contrib)有何不同?它们的优缺点是什么?
  3. 从更一般的意义上讲,将CRF和LSTM模型组合为一个模型(如讨论过的here)有什么好处

非常感谢!

0 个答案:

没有答案