マスクされた入力値を処理する単純なモデルを構築するのが困難です。私のトレーニングデータは、GPSトレースの可変長リスト、つまり各要素に緯度と経度が含まれているリストで構成されています。
70のトレーニング例があります
それらは可変長であるため、ゼロを埋め込んでいます。これは、これらのゼロ値を無視するようにKerasに指示するためです。
train_data = keras.preprocessing.sequence.pad_sequences(train_data, maxlen=max_sequence_len, dtype='float32',
padding='pre', truncating='pre', value=0)
次に、このような非常に基本的なモデルを作成します
model = Sequential()
model.add(Dense(16, activation='relu',input_shape=(max_sequence_len, 2)))
model.add(Flatten())
model.add(Dense(2, activation='sigmoid'))
以前の試行錯誤の後、Flatten
レイヤーが必要であるか、モデルをフィッティングするとエラーがスローされることに気付きました
ValueError: Error when checking target: expected dense_87 to have 3 dimensions, but got array with shape (70, 2)
ただし、このFlatten
レイヤーを含めると、Masking
レイヤーを使用できなくなり(埋め込みゼロを無視するため)、Kerasがこのエラーをスローします
TypeError: Layer flatten_31 does not support masking, but was passed an input_mask: Tensor("masking_9/Any_1:0", shape=(?, 48278), dtype=bool)
GitHubの問題とたくさんのQ/Aを読んで、広範囲にわたって検索しましたが、わかりません。
マスキングはバグがあるようです。ただし、心配しないでください。0はモデルを悪化させることはありません。せいぜい効率が悪い。
純粋な密集や多分RNNではなく、たたみ込みアプローチを使用することをお勧めします。これは、GPSデータに対して非常にうまく機能すると思います。
次のコードを試してください:
from keras.preprocessing.sequence import pad_sequences
from keras import Sequential
from keras.layers import Dense, Flatten, Masking, LSTM, GRU, Conv1D, Dropout, MaxPooling1D
import numpy as np
import random
max_sequence_len = 70
n_samples = 100
num_coordinates = 2 # lat/long
data = [[[random.random() for _ in range(num_coordinates)]
for y in range(min(x, max_sequence_len))]
for x in range(n_samples)]
train_y = np.random.random((n_samples, 2))
train_data = pad_sequences(data, maxlen=max_sequence_len, dtype='float32',
padding='pre', truncating='pre', value=0)
model = Sequential()
model.add(Conv1D(32, (5, ), input_shape=(max_sequence_len, num_coordinates)))
model.add(Dropout(0.5))
model.add(MaxPooling1D())
model.add(Flatten())
model.add(Dense(2, activation='relu'))
model.compile(loss='mean_squared_error', optimizer="adam")
model.fit(train_data, train_y)
Flatten
レイヤーを使用する代わりに、Global Poolingレイヤーを使用できます。
これらは、可変長を使用する機能を失うことなく、長さ/時間ディメンションを縮小するのに適しています。
したがって、Flatten()
の代わりに、GlobalAveragePooling1D
またはGlobalMaxPooling1D
を試すことができます。
コードでsupports_masking
を使用していないため、注意して使用する必要があります。
平均的なものは、最大値よりも多くの入力を考慮します(したがって、マスクする必要がある値)。
最大値は長さから1つだけです。運が良ければ、すべての有用な値がマスクされた位置の値よりも高い場合、間接的にマスクが保持されます。おそらく、他のニューロンよりもさらに多くの入力ニューロンが必要になります。
そうは言っても、はい、Conv1D
またはRNN(LSTM
)のアプローチを試してみてください。
独自のプーリングレイヤーを作成することもできます(モデルの入力とプールするテンソルの両方を渡す機能的なAPIモデルが必要です)。
以下は、入力に基づいてマスクを適用する平均プーリングの実際の例です。
def customPooling(maskVal):
def innerFunc(x):
inputs = x[0]
target = x[1]
#getting the mask by observing the model's inputs
mask = K.equal(inputs, maskVal)
mask = K.all(mask, axis=-1, keepdims=True)
#inverting the mask for getting the valid steps for each sample
mask = 1 - K.cast(mask, K.floatx())
#summing the valid steps for each sample
stepsPerSample = K.sum(mask, axis=1, keepdims=False)
#applying the mask to the target (to make sure you are summing zeros below)
target = target * mask
#calculating the mean of the steps (using our sum of valid steps as averager)
means = K.sum(target, axis=1, keepdims=False) / stepsPerSample
return means
return innerFunc
x = np.ones((2,5,3))
x[0,3:] = 0.
x[1,1:] = 0.
print(x)
inputs = Input((5,3))
out = Lambda(lambda x: x*4)(inputs)
out = Lambda(customPooling(0))([inputs,out])
model = Model(inputs,out)
model.predict(x)