LSTM
ネットワークを作成し、さまざまな入力配列サイズでフィードしたい場合、どうすれば可能ですか?
たとえば、音声メッセージやテキストメッセージを別の言語で取得して翻訳したいと考えています。したがって、最初の入力は「こんにちは」かもしれませんが、2番目の入力は「元気ですか」です。LSTM
さまざまな入力配列サイズを処理できるをどのように設計できますか?
のKeras
実装を使用していますLSTM
。
LSTM
ネットワークを作成し、さまざまな入力配列サイズでフィードしたい場合、どうすれば可能ですか?
たとえば、音声メッセージやテキストメッセージを別の言語で取得して翻訳したいと考えています。したがって、最初の入力は「こんにちは」かもしれませんが、2番目の入力は「元気ですか」です。LSTM
さまざまな入力配列サイズを処理できるをどのように設計できますか?
のKeras
実装を使用していますLSTM
。
回答:
最も簡単な方法は、パディングとマスキングを使用することです。
可変長シーケンスを処理するには、3つの一般的な方法があります。
パディングとマスキング
このアプローチでは、短いマスクを特別な値で埋め、後でマスク(スキップ)します。たとえば、各タイムスタンプに次元2 -10
があり、特別な値であるとすると、
X = [
[[1, 1.1],
[0.9, 0.95]], # sequence 1 (2 timestamps)
[[2, 2.2],
[1.9, 1.95],
[1.8, 1.85]], # sequence 2 (3 timestamps)
]
に変換されます
X2 = [
[[1, 1.1],
[0.9, 0.95],
[-10, -10]], # padded sequence 1 (3 timestamps)
[[2, 2.2],
[1.9, 1.95],
[1.8, 1.85]], # sequence 2 (3 timestamps)
]
このように、すべてのシーケンスは同じ長さになります。次に、Masking
これらの特別なタイムスタンプが存在しないようにスキップするレイヤーを使用します。最後に完全な例を示します。
(2)と(3)の場合seq_len
、LSTMのをに設定する必要がありますNone
。例:
model.add(LSTM(units, input_shape=(None, dimension)))
このように、LSTMは異なる長さのバッチを受け入れます。ただし、各バッチ内のサンプルは同じ長さでなければなりません。次に、カスタムバッチジェネレータをmodel.fit_generator
(の代わりにmodel.fit
)にフィードする必要があります。
最後に、単純なケース(2)(バッチサイズ= 1)の完全な例を示しました。この例とリンクに基づいて、ケース(3)のジェネレーターを作成できるはずです(バッチサイズ> 1)。具体的には、(a)batch_size
同じ長さのシーケンスを返すか、(b)ほぼ同じ長さのシーケンスを選択し、ケース(1)と同じように短い方をMasking
パディングし、LSTMレイヤーの前のレイヤーを使用してパディングを無視しますタイムスタンプ、例えば
model.add(Masking(mask_value=special_value, input_shape=(None, dimension)))
model.add(LSTM(lstm_units))
ここでも、input_shape
inの最初の次元は、異なる長さのバッチを許可するためのものです。Masking
None
ケース(1)と(2)のコードは次のとおりです。
from keras import Sequential
from keras.utils import Sequence
from keras.layers import LSTM, Dense, Masking
import numpy as np
class MyBatchGenerator(Sequence):
'Generates data for Keras'
def __init__(self, X, y, batch_size=1, shuffle=True):
'Initialization'
self.X = X
self.y = y
self.batch_size = batch_size
self.shuffle = shuffle
self.on_epoch_end()
def __len__(self):
'Denotes the number of batches per epoch'
return int(np.floor(len(self.y)/self.batch_size))
def __getitem__(self, index):
return self.__data_generation(index)
def on_epoch_end(self):
'Shuffles indexes after each epoch'
self.indexes = np.arange(len(self.y))
if self.shuffle == True:
np.random.shuffle(self.indexes)
def __data_generation(self, index):
Xb = np.empty((self.batch_size, *X[index].shape))
yb = np.empty((self.batch_size, *y[index].shape))
# naively use the same sample over and over again
for s in range(0, self.batch_size):
Xb[s] = X[index]
yb[s] = y[index]
return Xb, yb
# Parameters
N = 1000
halfN = int(N/2)
dimension = 2
lstm_units = 3
# Data
np.random.seed(123) # to generate the same numbers
# create sequence lengths between 1 to 10
seq_lens = np.random.randint(1, 10, halfN)
X_zero = np.array([np.random.normal(0, 1, size=(seq_len, dimension)) for seq_len in seq_lens])
y_zero = np.zeros((halfN, 1))
X_one = np.array([np.random.normal(1, 1, size=(seq_len, dimension)) for seq_len in seq_lens])
y_one = np.ones((halfN, 1))
p = np.random.permutation(N) # to shuffle zero and one classes
X = np.concatenate((X_zero, X_one))[p]
y = np.concatenate((y_zero, y_one))[p]
# Batch = 1
model = Sequential()
model.add(LSTM(lstm_units, input_shape=(None, dimension)))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
print(model.summary())
model.fit_generator(MyBatchGenerator(X, y, batch_size=1), epochs=2)
# Padding and Masking
special_value = -10.0
max_seq_len = max(seq_lens)
Xpad = np.full((N, max_seq_len, dimension), fill_value=special_value)
for s, x in enumerate(X):
seq_len = x.shape[0]
Xpad[s, 0:seq_len, :] = x
model2 = Sequential()
model2.add(Masking(mask_value=special_value, input_shape=(max_seq_len, dimension)))
model2.add(LSTM(lstm_units))
model2.add(Dense(1, activation='sigmoid'))
model2.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
print(model2.summary())
model2.fit(Xpad, y, epochs=50, batch_size=32)
追記
[20, 21, 22, -10, -10]
は、最後に2つのノイズのある(間違った)測定値を持つセンサーレポートと同じです。モデルはこのノイズを完全にまたは少なくとも部分的に無視することを学習する可能性がありますが、最初にデータをクリーンアップする、つまりマスクを使用するのが妥当です。複数の入力サイズのLSTMレイヤーを使用します。ただし、それらをLSTMに送る前に処理する必要があります。
シーケンスのパディング:
さまざまな長さのシーケンスを固定長に埋め込む必要があります。この前処理では、データセット内のシーケンスの最大長を決定する必要があります。
値は主に値0で埋め込まれます。これはKerasで次のように実行できます:
y = keras.preprocessing.sequence.pad_sequences( x , maxlen=10 )
シーケンスが最大長より短い場合は、最大長と同じ長さになるまでゼロが追加されます。
シーケンスが最大長よりも長い場合、シーケンスは最大長にトリミングされます。