Làm thế nào để cung cấp LSTM với các kích cỡ mảng đầu vào khác nhau?


18

Nếu tôi muốn viết một LSTMmạng và cung cấp cho nó bởi các kích thước mảng đầu vào khác nhau, làm thế nào có thể?

Ví dụ, tôi muốn nhận tin nhắn thoại hoặc tin nhắn văn bản bằng ngôn ngữ khác và dịch chúng. Vì vậy, đầu vào đầu tiên có thể là "xin chào" nhưng thứ hai là "bạn thế nào". Làm thế nào tôi có thể thiết kế một LSTMcó thể xử lý các kích thước mảng đầu vào khác nhau?

Tôi đang sử dụng Kerasthực hiện LSTM.

Câu trả lời:


24

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:

  1. Đệm và mặt nạ (có thể được sử dụng cho (3)),
  2. Kích thước hàng loạt = 1 và
  3. 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à -10là 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 Maskinglớ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_lenLSTM 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_sizecá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 Maskinglớ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_shapenăm Maskinglà 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

  1. 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ạ.

Cảm ơn bạn rất nhiều Esmailian cho ví dụ đầy đủ của bạn. Chỉ cần một câu hỏi: sự khác biệt giữa việc sử dụng padding + masking và chỉ sử dụng padding (giống như những gì câu trả lời khác gợi ý)? Chúng ta sẽ thấy một ảnh hưởng đáng kể đến kết quả cuối cùng?
user145959

@ user145959 niềm vui của tôi! Tôi đã thêm một ghi chú ở cuối.
Esmailian

Wow một câu trả lời tuyệt vời! Nó được gọi là xô, phải không?
Aditya

1
@Aditya Cảm ơn Aditya! Tôi nghĩ xô là phân vùng của một chuỗi lớn thành các khối nhỏ hơn, nhưng các chuỗi trong mỗi lô không nhất thiết phải là các khối của cùng một chuỗi (lớn hơn), chúng có thể là các điểm dữ liệu độc lập.
Esmailian

1
@ Flow2k Không thành vấn đề, miếng đệm hoàn toàn bị bỏ qua. Hãy xem câu hỏi này .
Esmailian

3

Chúng tôi sử dụng các lớp LSTM với nhiều kích cỡ đầu vào. Nhưng, bạn cần xử lý chúng trước khi chúng được đưa vào LSTM.

Đệm các trình tự:

Bạn cần phần đệm các chuỗi có độ dài khác nhau đến một độ dài cố định. Đối với quá trình tiền xử lý này, bạn cần xác định độ dài tối đa của chuỗi trong tập dữ liệu của mình.

Các giá trị được đệm chủ yếu bằng giá trị 0. Bạn có thể thực hiện việc này trong Keras với:

y = keras.preprocessing.sequence.pad_sequences( x , maxlen=10 )
  • Nếu chuỗi ngắn hơn độ dài tối đa, thì các số 0 sẽ được nối cho đến khi nó có độ dài bằng với độ dài tối đa.

  • Nếu chuỗi dài hơn độ dài tối đa thì chuỗi đó sẽ được cắt theo chiều dài tối đa.


3
Đệm tất cả mọi thứ đến một chiều dài cố định là lãng phí không gian.
Aditya

Tôi đồng ý với @Aditya và nó cũng chịu chi phí tính toán. Nhưng nó không phải là trường hợp mà đệm đơn giản vẫn được sử dụng rộng rãi? Keras thậm chí có một chức năng chỉ cho việc này. Có lẽ điều này là do các giải pháp khác, hiệu quả hơn và đầy thách thức không cung cấp hiệu suất mô hình đáng kể? Nếu bất cứ ai có kinh nghiệm hoặc đã thực hiện so sánh, xin vui lòng cân nhắc.
Flow2k
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.