用于反转整数序列的 LSTM
LSTM for reversing integer sequence
我只是想训练 LSTM 来反转整数序列。我的方法是 this 教程的修改版本,其中他只是回应输入序列。它是这样的:
- 生成一个长度为R的随机序列S(取值范围为0到99)
- 把上面的句子分成长度为L的子序列(移动window)
- 每个子序列都有它的反向作为真值标签
因此,这将生成 (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(所以我们每个 'training session' 有 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]
可能导致您的模型出现问题的第一件事是使用 stateful=True
。
这个选项只有当你想把一个序列分成很多部分时才需要这个选项,比如第二批序列是第一批序列的延续。当您的序列足够长以致于导致内存问题时,这很有用,然后您将其分开。
并且在你通过最后一批序列后,它会要求你手动"erase memory"(称为"reset states")。
现在,LSTM 层不适合该任务,因为它们的工作方式如下:
- 从清晰的开始"state"(大致可以理解为清晰的记忆)。这是一个全新的序列;
- 取序列中第一个step/element,计算第一个结果并更新内存;
- 走序列的第二步,计算第二个结果(现在借助自己的记忆和之前的结果)并更新记忆;
- 以此类推,直到最后一步。
你可以看到更好的解释here。此解释侧重于更准确的细节,但它有漂亮的图片,例如:
想象一个包含 3 个元素的序列。在这张图中,X(t-1) 是第一个元素。 H(t-1) 是第一个结果。 X(t) 和 H(t) 是第二个输入和输出。 X(t+1) 和 H(t+1) 是最后的输入和输出。它们是按顺序处理的。
因此,图层的 memory/state 在第一步根本不存在。当它收到第一个数字时,它根本不知道最后一个数字是什么,因为它从未见过最后一个数字。 (也许如果序列在某种程度上是可以理解的,如果数字之间有关系,那么它就有更好的机会输出逻辑结果,但这些序列只是随机的)。
现在,当您接近最后一个数字时,该层已经建立了它对序列的记忆,并且它有可能知道该做什么(因为它已经看到了第一个数字)。
这解释了你的结果:
- 直到序列的前半部分,它试图输出它从未见过的数字(并且它们之间没有任何逻辑关系)。
- 从中间的数字开始到最后,前面的数字都可以看到并且可以预测。
Bidirectional
层包装器:
如果第一步受最后一步的影响很重要,您可能需要 Bidirectional 层包装器。它使您的 LSTM 以两种方式进行处理,并复制输出特征的数量。如果你通过 100 个单元格,它会输出 (Batch, Steps, 200)
。几乎就像有两个 LSTM 层,其中一个向后读取输入。
model.add(Bidirectional(LSTM(100, return_sequences=True), input_shape=(TIMESTEPS, FEATS)))
我只是想训练 LSTM 来反转整数序列。我的方法是 this 教程的修改版本,其中他只是回应输入序列。它是这样的:
- 生成一个长度为R的随机序列S(取值范围为0到99)
- 把上面的句子分成长度为L的子序列(移动window)
- 每个子序列都有它的反向作为真值标签
因此,这将生成 (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(所以我们每个 'training session' 有 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]
可能导致您的模型出现问题的第一件事是使用 stateful=True
。
这个选项只有当你想把一个序列分成很多部分时才需要这个选项,比如第二批序列是第一批序列的延续。当您的序列足够长以致于导致内存问题时,这很有用,然后您将其分开。
并且在你通过最后一批序列后,它会要求你手动"erase memory"(称为"reset states")。
现在,LSTM 层不适合该任务,因为它们的工作方式如下:
- 从清晰的开始"state"(大致可以理解为清晰的记忆)。这是一个全新的序列;
- 取序列中第一个step/element,计算第一个结果并更新内存;
- 走序列的第二步,计算第二个结果(现在借助自己的记忆和之前的结果)并更新记忆;
- 以此类推,直到最后一步。
你可以看到更好的解释here。此解释侧重于更准确的细节,但它有漂亮的图片,例如:
想象一个包含 3 个元素的序列。在这张图中,X(t-1) 是第一个元素。 H(t-1) 是第一个结果。 X(t) 和 H(t) 是第二个输入和输出。 X(t+1) 和 H(t+1) 是最后的输入和输出。它们是按顺序处理的。
因此,图层的 memory/state 在第一步根本不存在。当它收到第一个数字时,它根本不知道最后一个数字是什么,因为它从未见过最后一个数字。 (也许如果序列在某种程度上是可以理解的,如果数字之间有关系,那么它就有更好的机会输出逻辑结果,但这些序列只是随机的)。
现在,当您接近最后一个数字时,该层已经建立了它对序列的记忆,并且它有可能知道该做什么(因为它已经看到了第一个数字)。
这解释了你的结果:
- 直到序列的前半部分,它试图输出它从未见过的数字(并且它们之间没有任何逻辑关系)。
- 从中间的数字开始到最后,前面的数字都可以看到并且可以预测。
Bidirectional
层包装器:
如果第一步受最后一步的影响很重要,您可能需要 Bidirectional 层包装器。它使您的 LSTM 以两种方式进行处理,并复制输出特征的数量。如果你通过 100 个单元格,它会输出 (Batch, Steps, 200)
。几乎就像有两个 LSTM 层,其中一个向后读取输入。
model.add(Bidirectional(LSTM(100, return_sequences=True), input_shape=(TIMESTEPS, FEATS)))