Cách dễ nhất là sử dụng Padding và Masking .
Có ba cách chung để xử lý các chuỗi có độ dài thay đổi:
- Đệm và mặt nạ (có thể được sử dụng cho (3)),
- Kích thước hàng loạt = 1 và
- Kích thước hàng loạt> 1, với các mẫu có độ dài bằng nhau trong mỗi lô.
Đệm và mặt nạ
Trong phương pháp này, chúng tôi đệm các chuỗi ngắn hơn với giá trị đặc biệt sẽ được che (bỏ qua) sau. Ví dụ: giả sử mỗi dấu thời gian có thứ nguyên 2 và -10
là giá trị đặc biệt, thì
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)
]
sẽ được chuyển đổi thành
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)
]
Bằng cách này, tất cả các chuỗi sẽ có cùng độ dài. Sau đó, chúng tôi sử dụng một Masking
lớp bỏ qua các dấu thời gian đặc biệt như chúng không tồn tại. Một ví dụ hoàn chỉnh được đưa ra ở cuối.
Đối với trường hợp (2) và (3), bạn cần đặt seq_len
LSTM thành None
, vd
model.add(LSTM(units, input_shape=(None, dimension)))
cách này LSTM chấp nhận các lô với độ dài khác nhau; mặc dù các mẫu bên trong mỗi lô phải có cùng chiều dài. Sau đó, bạn cần cung cấp một trình tạo hàng loạt tùy chỉnh cho model.fit_generator
(thay vì model.fit
).
Tôi đã cung cấp một ví dụ hoàn chỉnh cho trường hợp đơn giản (2) (kích thước lô = 1) ở cuối. Dựa trên ví dụ này và liên kết, bạn sẽ có thể xây dựng một trình tạo cho trường hợp (3) (kích thước lô> 1). Cụ thể, chúng tôi hoặc (a) trả về batch_size
các chuỗi có cùng độ dài hoặc (b) chọn các chuỗi có độ dài gần như nhau và đệm các chuỗi ngắn hơn giống như trường hợp (1) và sử dụng một Masking
lớp trước lớp LSTM để bỏ qua phần đệm dấu thời gian, vd
model.add(Masking(mask_value=special_value, input_shape=(None, dimension)))
model.add(LSTM(lstm_units))
nơi chiều kích đầu tiên của input_shape
năm Masking
là một lần nữa None
để cho phép các lô với độ dài khác nhau.
Đây là mã cho các trường hợp (1) và (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)
Ghi chú thêm
- Lưu ý rằng nếu chúng ta đệm mà không che, giá trị đệm sẽ được coi là giá trị thực, do đó, nó sẽ trở thành nhiễu trong dữ liệu. Ví dụ, một chuỗi nhiệt độ đệm
[20, 21, 22, -10, -10]
sẽ giống như một báo cáo cảm biến với hai phép đo nhiễu (sai) ở cuối. Mô hình có thể học cách bỏ qua tiếng ồn này hoàn toàn hoặc ít nhất một phần, nhưng điều hợp lý là làm sạch dữ liệu trước, tức là sử dụng mặt nạ.