LSTM用于反转整数序列

时间:2017-09-20 14:14:54

标签: python deep-learning keras lstm

我只是想训练LSTM来反转整数序列。我的方法是this教程的修改版本,其中他只是回显输入序列。它是这样的:

  1. 生成长度为R(可能值范围为0到99)的随机序列
  2. 在长度L(移动窗口)的子序列中打破上面的句子
  3. 每个子序列都有相反的真值标签
  4. 因此,这将生成(R - L + 1)个子序列,它是形状(R - L + 1)x L的输入矩阵。例如,使用:

    S = 1 2 3 4 5 ... 25 (1 to 25)
    R = 25
    L = 5 
    

    我们用21句话结束:

    s1 = 1 2 3 4 5, y1 = 5 4 3 2 1
    s2 = 2 3 4 5 6, y2 = 6 5 4 3 2
    ...
    s21 = 21 22 23 24 25, y21 = 25 24 23 22 21
    

    然后对该输入矩阵进行单热编码并馈送到keras。然后我重复进行另一个序列的过程。问题是它不会收敛,准确度很低。我做错了什么?

    在下面的代码中,我使用R = 500和L = 5,它提供496个子序列,batch_size = 16(因此我们每个'训练会话'有31个更新):

    以下是代码:

    from keras.models import Sequential
    from keras.layers import Dense
    from keras.layers import TimeDistributed
    from keras.layers import LSTM
    from random import randint
    from keras.utils.np_utils import to_categorical
    import numpy as np
    
    def one_hot_encode(sequence, n_unique=100):
        encoding = list()
        for value in sequence:
            vector = [0 for _ in range(n_unique)]
            vector[value] = 1
            encoding.append(vector)
        return np.array(encoding)
    
    def one_hot_decode(encoded_seq):
        return [np.argmax(vector) for vector in encoded_seq]
    
    def get_data(rows = 500, length = 5, n_unique=100):
        s = [randint(0, n_unique-1) for i in range(rows)]
        x = []
        y = []
    
        for i in range(0, rows-length + 1, 1):
            x.append(one_hot_encode(s[i:i+length], n_unique))
            y.append(one_hot_encode(list(reversed(s[i:i+length])), n_unique))
    
        return np.array(x), np.array(y)
    
    N = 50000
    LEN = 5
    #ROWS = LEN*LEN - LEN + 1
    TIMESTEPS = LEN
    ROWS = 10000
    FEATS = 10 #randint
    BATCH_SIZE = 588
    
    # fit model
    model = Sequential()
    model.add(LSTM(100, batch_input_shape=(BATCH_SIZE, TIMESTEPS, FEATS), return_sequences=True, stateful=True))
    model.add(TimeDistributed(Dense(FEATS, activation='softmax')))
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['acc'])
    
    print(model.summary())
    
    # train LSTM
    for epoch in range(N):
        # generate new random sequence
        X,y = get_data(500, LEN, FEATS)
        # fit model for one epoch on this sequence
        model.fit(X, y, epochs=1, batch_size=BATCH_SIZE, verbose=2, shuffle=False)
        model.reset_states()
    
    # evaluate LSTM 
    X,y = get_data(500, LEN, FEATS)
    yhat = model.predict(X, batch_size=BATCH_SIZE, verbose=0)
    
    # decode all pairs
    for i in range(len(X)):
        print('Expected:', one_hot_decode(y[i]), 'Predicted', one_hot_decode(yhat[i]))
    

    谢谢!

    修改:似乎正在拾取序列的最后一个数字:

    Expected: [7, 3, 7, 7, 6] Predicted [3, 9, 7, 7, 6]
    Expected: [6, 7, 3, 7, 7] Predicted [4, 6, 3, 7, 7]
    Expected: [6, 6, 7, 3, 7] Predicted [4, 3, 7, 3, 7]
    Expected: [1, 6, 6, 7, 3] Predicted [3, 3, 6, 7, 3]
    Expected: [8, 1, 6, 6, 7] Predicted [4, 3, 6, 6, 7]
    Expected: [8, 8, 1, 6, 6] Predicted [3, 3, 1, 6, 6]
    Expected: [9, 8, 8, 1, 6] Predicted [3, 9, 8, 1, 6]
    Expected: [5, 9, 8, 8, 1] Predicted [3, 3, 8, 8, 1]
    Expected: [9, 5, 9, 8, 8] Predicted [7, 7, 9, 8, 8]
    Expected: [0, 9, 5, 9, 8] Predicted [7, 9, 5, 9, 8]
    Expected: [7, 0, 9, 5, 9] Predicted [5, 7, 9, 5, 9]
    Expected: [1, 7, 0, 9, 5] Predicted [7, 9, 0, 9, 5]
    Expected: [9, 1, 7, 0, 9] Predicted [5, 9, 7, 0, 9]
    Expected: [4, 9, 1, 7, 0] Predicted [6, 3, 1, 7, 0]
    Expected: [4, 4, 9, 1, 7] Predicted [4, 3, 9, 1, 7]
    Expected: [0, 4, 4, 9, 1] Predicted [3, 9, 4, 9, 1]
    Expected: [1, 0, 4, 4, 9] Predicted [5, 5, 4, 4, 9]
    Expected: [3, 1, 0, 4, 4] Predicted [3, 3, 0, 4, 4]
    Expected: [0, 3, 1, 0, 4] Predicted [3, 3, 1, 0, 4]
    Expected: [2, 0, 3, 1, 0] Predicted [6, 3, 3, 1, 0]
    

1 个答案:

答案 0 :(得分:2)

可能导致模型出现问题的第一件事是使用stateful=True

只有当您想要在其中的许多部分中划分一个序列时才需要此选项,例如第二批中的序列是第一批中的序列的延续。当序列足够长以引起内存问题然后将其除去时,这很有用。

它会要求你擦除记忆" (在传递最后一批序列后,手动调用"重置状态")。

现在,LSTM图层不适合该任务,因为它们以下列方式工作:

  • 从明确的状态开始" (可以粗略地解释为清晰的记忆)。它是一个全新的序列;
  • 获取序列中的第一步/元素,计算第一个结果并更新内存;
  • 按顺序进行第二步,计算第二个结果(现在借助自己的记忆和之前的结果)并更新记忆;
  • 等到最后一步。

您可以看到更好的解释here。这个解释集中了更详细的细节,但它有很好的图片,例如:

LSTM steps illustration

想象一下有3个元素的序列。在这张图片中,X(t-1)是第一个元素。 H(t-1)是第一个结果。 X(t)和H(t)是第二个输入和输出。 X(t + 1)和H(t + 1)是最后的输入和输出。他们按顺序处理。

因此,该层的内存/状态在第一步中根本不存在。当它收到第一个数字时,它无法了解最后一个数字是什么,因为它从未见过最后一个数字。 (也许如果序列在某种程度上是可以理解的,如果数字之间存在关系,那么它将有更好的机会输出逻辑结果,但这些序列只是随机的)。

现在,当您接近最后一个数字时,该层已经构建了序列的内存,并且它有可能知道该做什么(因为它已经看到了第一个数字)。

这解释了你的结果:

  • 直到序列的前半部分,它试图输出它从未见过的数字(并且它们之间没有任何逻辑关系)。
  • 从中心数字到结尾,可以看到所有以前的数字并且可以预测。

Bidirectional图层包装器

如果第一步受到最后一步的影响很重要,那么您可能需要Bidirectional图层包装器。它使您的LSTM以两种方式处理,并复制输出功能的数量。如果传递100个单元格,则会输出(Batch, Steps, 200)。几乎有两个LSTM层,其中一个向后读取输入。

model.add(Bidirectional(LSTM(100, return_sequences=True), input_shape=(TIMESTEPS, FEATS)))