【问题标题】:How to build RNN with multimodal input to classify time series如何使用多模态输入构建 RNN 以对时间序列进行分类
【发布时间】:2023-04-06 23:14:01
【问题描述】:

我有每个时间序列 50 个样本的数据。 我想构建一个时间序列分类器。

每个样本都有三个输入 - 一个形状为 1X768 的向量、一个形状为 1X25 的向量、一个形状为 1X496 的向量。

每个输入都来自不同的模态,因此在连接所有输入之前需要经过一些特定于输入的层。

数据存储在数据框中:

df = time_series_id timestamp    input1     input2     input3     time_series_label 
           0         0          [x0..x768] [x0..x25] [x0..x496]     A  
           0         1          [x0..x768] [x0..x25] [x0..x496]     A
     ..
           0         50         [x0..x768] [x0..x25] [x0..x496]     A  
           1         0          [x0..x768] [x0..x25] [x0..x496]     B
           1         50         [x0..x768] [x0..x25] [x0..x496]     B

我是 DL 新手,我想构建一个网络,将每个 50 个时间戳长的时间序列分类为 2 个类之一,但我找不到任何教程来举例说明如何将多模态数据插入 Conv1dLSTM层。

如何构建这样的网络,最好使用 keras,并在我的数据帧上进行训练以对时间序列进行分类? (所以,当我给它一个包含 50 个时间戳的新时间序列时,我将获得整个时间序列的 A/B 预测)?

请注意,具有相同 id 的所有行的标签都是相同的。所以每次,我只需要为 RNN 提供具有相同 id 的样本。

【问题讨论】:

    标签: keras deep-learning lstm recurrent-neural-network


    【解决方案1】:

    我为你创建了一个很好的例子:

    # Define mini-dataset  similar to yours example
    df = pd.DataFrame({'A':[np.zeros((768))]*100,'B':[np.ones((25))]*100})
    # 100 rows, 2 columns (each value in column A is a list size 768, each value in column B is a list size 25)
    

    预处理数据以匹配 50 个时间戳的滚动窗口

    # Create windows of data:
    list_of_indexes=[]
    df.index.to_series().rolling(50).apply((lambda x: list_of_indexes.append(x.tolist()) or 0), raw=False)
    d_A = df.A.apply(list)
    d_B = df.B.apply(list)
    a = [[d_A[ix] for ix in x] for x in list_of_indexes]
    b = [[d_B[ix] for ix in x] for x in list_of_indexes]
    a = np.array(a)
    b = np.array(b)
    
    print(f'a shape: {a.shape}')
    print(f'b shape: {b.shape}')
    

    预处理后的数据:

    a shape: (51, 50, 768)
    b shape: (51, 50, 25)
    

    解释:

    a:51 个样本,每个样本包含 50 个时间戳,每个时间戳包含 768 个值。 (b与25个值相同。)

    创建一个有两个输入的模型,输入a和输入b,你可以分别处理它们,然后连接起来。

    # define two sets of inputs
    input_A = Input(shape=(50, 768))
    input_B = Input(shape=(50, 25))
    
    LSTM_A = Bidirectional(LSTM(32))(input_A)
    LSTM_B = Bidirectional(LSTM(32))(input_B)
                   
    combined = concatenate([
                            LSTM_A,
                            LSTM_B
                           ])
    dense1 = Dense(32, activation='relu')(combined)
    output = Dense(1, activation='sigmoid')(dense1)
    model = Model(inputs=[
                         input_A,
                         input_B
                         ], outputs=output)
    model.summary()
    

    模型总结:

    拟合模型:

    adam = Adam(lr=0.00001)
    model.compile(loss='binary_crossentropy', optimizer=adam)
    history = model.fit([a,b], y, batch_size=2, epochs=2)
    

    当然你可以在 LSTM 之前进行连接:

    # define two sets of inputs
    input_A = Input(shape=(50, 768))
    input_B = Input(shape=(50, 25))
    
    combined = concatenate([
                            input_A,
                            input_B
                           ])
    LSTM_layer = Bidirectional(LSTM(32))(combined)
    dense1 = Dense(32, activation='relu')(LSTM_layer)
    output = Dense(1, activation='sigmoid')(dense1)
    model = Model(inputs=[
                         input_A,
                         input_B
                         ], outputs=output)
    model.summary()
    

    编辑:

    df:

    形状:(100, 4)

    预处理代码:

    def split_into_inputs(group):
        x_data_inp1.append(group.input1)
        x_data_inp2.append(group.input2)
        # supposing time_series_id have the same label for all of its rows (thats what i understood from the question details)
        y_data.append(group.time_series_label.unique()[0])
    
    
    x_data_inp1 = []
    x_data_inp2 = []
    y_data = []
    
    df.groupby('time_series_id').apply(lambda group: split_into_inputs(group))
    # convert list into array with np.float dtype to match the nn.
    x_data_inp1 = np.array(x_data_inp1, dtype=np.float)
    x_data_inp2 = np.array(x_data_inp2, dtype=np.float)
    
    # Convert labels from chars into digits
    from sklearn.preprocessing import LabelEncoder
    # creating instance of labelencoder
    labelencoder = LabelEncoder()
    # Assigning numerical values. Convert 'A','B' into 0, 1
    y_data = labelencoder.fit_transform(y_data)
    
    x_data_inp1.shape, x_data_inp2.shape, y_data.shape
    

    输出:

    ((2, 50, 768), (2, 50, 25), (2,))
    

    对我们的100个样本进行预处理后,根据“time_series_id”列有2个序列,每个50个样本,有2个标签,第一个序列的标签A为0,第二个序列的标签B为1顺序。 问题:每个50个样本的序列都有不同的“time_series_id”?

    定义模式:

    # define two sets of inputs
    input_A = Input(shape=(50, 768))
    input_B = Input(shape=(50, 25))
    
    LSTM_A = Bidirectional(LSTM(32))(input_A)
    LSTM_B = Bidirectional(LSTM(32))(input_B)
    
    combined = concatenate([
                            LSTM_A,
                            LSTM_B
                           ])
    dense1 = Dense(32, activation='relu')(combined)
    output = Dense(1, activation='sigmoid')(dense1)
    model = Model(inputs=[
                         input_A,
                         input_B
                         ], outputs=output)
    model.summary()
    

    拟合模型:

    adam = Adam(lr=0.00001)
    model.compile(loss='binary_crossentropy', optimizer=adam)
    history = model.fit([x_data_inp1, x_data_inp2], y_data, batch_size=2, epochs=2)
    

    【讨论】:

    • 谢谢!你能解释一下预处理/滚动步骤吗?这背后的目的是什么?以及为什么要在它之后有 51 行(之前是 100 行?
    • 是的,我明白了,但为什么要把它转换成 (51,50,X)?为什么是51?为什么这一步是必要的?
    • 写完之前发评论,不好意思。 100 行仅用于示例。我创建了一个您提到的大小的滑动窗口(50)。滑动窗口是一种流行的技术(例如stackoverflow.com/questions/8269916/…)。
    • 上一条评论的继续:根据 (51,50,X) - 50 是每个时间序列的样本(回溯大小) | 51 是在具有 100 个时间戳的集合上创建滑动窗口后的样本数(正如我之前提到的,100 只是示例,我不知道您的真实数据的大小是多少)。例如,如果我们在这个数据上使用 3 的滑动窗口:[1,2,3,4,5,6],我们得到 [1,2,3], [2,3,4], [3, 4,5],[4,5,6]。形状:(4,3,X)
    • 改成x_data_inp1.append(list(group.input1))后终于成功了,谢谢!
    【解决方案2】:

    使用一些网络(线性、MLP 等)将它们嵌入到相同的维度,您可以使用加法、逐元素乘法、双(三)线性或任何您想要将这些组合到 RNN 的维度统一输入或CNN。 或者你可以只连接每个时间步,每个时间步一个数据,这对 CNN 来说没问题

    【讨论】:

    • 请看我的编辑 - 分类是在时间序列级别
    • 最简单的,按照时间步连接它们,然后就可以了。
    • 我想在连接之前通过一些层运行每个模式。 Input1 应该首先通过 Dense(25) 并且 input3 应该通过 Dense(30) 所以我将在连接后得到一个 1X80 的向量,它应该是 Conv1D 的输入。我不明白我如何让神经网络“理解”所有 50 个时间戳都属于同一个时间序列。您可能有代码示例吗?
    • 您可以简单地使用像 RNN 这样的顺序模型,在您的情况下,双向模型(如 Bi-LSTM、Bi-GRU)似乎更好。在 RNN 的情况下,完整的 50 个输入被顺序组合并被视为一个输入,形状类似于 [T, F],其中 T 是时间长度,F 是每个时间步的特征大小
    • 对不起,我从理论上理解它,但我只是不明白我如何实现一个可以同时满足这两个需求的网络:1)所有 50 个时间戳都是同一时间序列的一部分 2)我有 3 个输入并且每个都可以在连接之前经过不同的层。也许你有一个代码示例?
    猜你喜欢
    • 2017-11-07
    • 2019-06-11
    • 2020-02-16
    • 2020-08-14
    • 2014-11-15
    • 2018-11-11
    • 1970-01-01
    • 1970-01-01
    • 2020-09-07
    相关资源
    最近更新 更多