![]() | ![]() | ![]() | ![]() |
Mempersiapkan
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
pengantar
Masking adalah cara untuk memberitahu urut-pengolahan lapisan yang timesteps tertentu dalam input hilang, dan dengan demikian harus dilewati saat memproses data.
Padding adalah bentuk khusus dari masking di mana langkah-langkah bertopeng berada di awal atau akhir berurutan. Padding berasal dari kebutuhan untuk mengkodekan data urutan ke dalam batch yang berdekatan: untuk membuat semua urutan dalam batch sesuai dengan panjang standar yang diberikan, perlu untuk menambahkan atau memotong beberapa urutan.
Mari kita lihat dari dekat.
Data urutan padding
Saat memproses data urutan, sangat umum untuk sampel individu memiliki panjang yang berbeda. Perhatikan contoh berikut (teks yang diberi token sebagai kata-kata):
[
["Hello", "world", "!"],
["How", "are", "you", "doing", "today"],
["The", "weather", "will", "be", "nice", "tomorrow"],
]
Setelah pencarian kosakata, data mungkin di-vektorkan sebagai bilangan bulat, misalnya:
[
[71, 1331, 4231]
[73, 8, 3215, 55, 927],
[83, 91, 1, 645, 1253, 927],
]
Data adalah daftar bersarang di mana sampel individu memiliki panjang 3, 5, dan 6, masing-masing. Karena input data untuk model pembelajaran yang mendalam harus tensor tunggal (bentuk misalnya (batch_size, 6, vocab_size)
dalam hal ini), sampel yang lebih pendek dari item kebutuhan terpanjang untuk empuk dengan beberapa nilai placeholder (alternatif, salah satu mungkin juga memotong sampel panjang sebelum mengisi sampel pendek).
Keras menyediakan fungsi utilitas untuk truncate dan pad daftar Python dengan panjang umum: tf.keras.preprocessing.sequence.pad_sequences
.
raw_inputs = [
[711, 632, 71],
[73, 8, 3215, 55, 927],
[83, 91, 1, 645, 1253, 927],
]
# By default, this will pad using 0s; it is configurable via the
# "value" parameter.
# Note that you could "pre" padding (at the beginning) or
# "post" padding (at the end).
# We recommend using "post" padding when working with RNN layers
# (in order to be able to use the
# CuDNN implementation of the layers).
padded_inputs = tf.keras.preprocessing.sequence.pad_sequences(
raw_inputs, padding="post"
)
print(padded_inputs)
[[ 711 632 71 0 0 0] [ 73 8 3215 55 927 0] [ 83 91 1 645 1253 927]]
penyamaran
Sekarang semua sampel memiliki panjang yang seragam, model harus diinformasikan bahwa beberapa bagian dari data sebenarnya adalah padding dan harus diabaikan. Mekanisme yang masking.
Ada tiga cara untuk memperkenalkan masker input dalam model Keras:
- Tambahkan
keras.layers.Masking
lapisan. - Mengkonfigurasi
keras.layers.Embedding
layer denganmask_zero=True
. - Lulus
mask
argumen secara manual saat memanggil lapisan yang mendukung argumen ini (misalnya RNN lapisan).
Lapisan topeng menghasilkan: Embedding
dan Masking
Di bawah tenda, lapisan ini akan membuat tensor masker (tensor 2D dengan bentuk (batch, sequence_length)
), dan melampirkannya pada output tensor dikembalikan oleh Masking
atau Embedding
lapisan.
embedding = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)
masked_output = embedding(padded_inputs)
print(masked_output._keras_mask)
masking_layer = layers.Masking()
# Simulate the embedding lookup by expanding the 2D input to 3D,
# with embedding dimension of 10.
unmasked_embedding = tf.cast(
tf.tile(tf.expand_dims(padded_inputs, axis=-1), [1, 1, 10]), tf.float32
)
masked_embedding = masking_layer(unmasked_embedding)
print(masked_embedding._keras_mask)
tf.Tensor( [[ True True True False False False] [ True True True True True False] [ True True True True True True]], shape=(3, 6), dtype=bool) tf.Tensor( [[ True True True False False False] [ True True True True True False] [ True True True True True True]], shape=(3, 6), dtype=bool)
Seperti yang Anda lihat dari hasil cetak, topeng adalah 2D tensor boolean dengan bentuk (batch_size, sequence_length)
, di mana setiap individu False
entri menunjukkan bahwa sesuai timestep harus diabaikan selama pemrosesan.
Propagasi mask di Functional API dan Sequential API
Bila menggunakan API Fungsional atau API Sequential, masker dihasilkan oleh Embedding
atau Masking
lapisan akan disebarkan melalui jaringan untuk setiap lapisan yang mampu menggunakan mereka (misalnya, RNN lapisan). Keras akan secara otomatis mengambil topeng yang sesuai dengan input dan meneruskannya ke lapisan mana pun yang tahu cara menggunakannya.
Misalnya, dalam model Sequential berikutnya, LSTM
lapisan akan secara otomatis menerima masker, yang berarti akan mengabaikan nilai-nilai empuk:
model = keras.Sequential(
[layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True), layers.LSTM(32),]
)
Ini juga berlaku untuk model API Fungsional berikut:
inputs = keras.Input(shape=(None,), dtype="int32")
x = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)(inputs)
outputs = layers.LSTM(32)(x)
model = keras.Model(inputs, outputs)
Melewati tensor topeng langsung ke lapisan
Lapisan yang dapat menangani masker (seperti LSTM
layer) memiliki mask
argumen dalam mereka __call__
metode.
Sementara itu, lapisan yang menghasilkan topeng (misalnya Embedding
) mengekspos compute_mask(input, previous_mask)
metode yang dapat Anda menelepon.
Dengan demikian, Anda dapat melewati output dari compute_mask()
metode dari layer mask-memproduksi dengan __call__
metode lapisan masker memakan, seperti ini:
class MyLayer(layers.Layer):
def __init__(self, **kwargs):
super(MyLayer, self).__init__(**kwargs)
self.embedding = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)
self.lstm = layers.LSTM(32)
def call(self, inputs):
x = self.embedding(inputs)
# Note that you could also prepare a `mask` tensor manually.
# It only needs to be a boolean tensor
# with the right shape, i.e. (batch_size, timesteps).
mask = self.embedding.compute_mask(inputs)
output = self.lstm(x, mask=mask) # The layer will ignore the masked values
return output
layer = MyLayer()
x = np.random.random((32, 10)) * 100
x = x.astype("int32")
layer(x)
<tf.Tensor: shape=(32, 32), dtype=float32, numpy= array([[-3.6287602e-04, 8.8942451e-03, -4.5623952e-03, ..., 3.6509466e-04, -4.3871473e-03, -1.7532009e-03], [ 2.6261162e-03, -2.5420082e-03, 7.6517118e-03, ..., 5.8210879e-03, -1.5617531e-03, -1.7562184e-03], [ 6.8687932e-03, 1.2330032e-03, -1.2028826e-02, ..., 2.0486799e-03, 5.7172528e-03, 2.6641595e-03], ..., [-3.4327951e-04, 1.3967649e-03, -1.2102776e-02, ..., 3.8406218e-03, -2.3374180e-03, -4.9669710e-03], [-2.3023323e-03, 1.8474255e-03, 2.7329330e-05, ..., 6.1798934e-03, 4.2709545e-04, 3.9026213e-03], [ 7.4090287e-03, 1.9879336e-03, -2.0261200e-03, ..., 8.2100276e-03, 8.7051848e-03, 9.9167246e-03]], dtype=float32)>
Mendukung masking di lapisan khusus Anda
Kadang-kadang, Anda mungkin perlu untuk menulis lapisan yang menghasilkan topeng (seperti Embedding
), atau lapisan yang perlu memodifikasi topeng saat ini.
Misalnya, setiap lapisan yang menghasilkan tensor dengan dimensi waktu yang berbeda dari input, seperti Concatenate
lapisan yang merangkai pada dimensi waktu, akan perlu memodifikasi topeng saat ini sehingga lapisan hilir akan dapat benar mengambil timesteps bertopeng ke Akun.
Untuk melakukan hal ini, lapisan Anda harus menerapkan layer.compute_mask()
metode, yang menghasilkan topeng baru yang diberikan input dan masker saat.
Berikut adalah contoh dari TemporalSplit
lapisan yang perlu memodifikasi topeng saat ini.
class TemporalSplit(keras.layers.Layer):
"""Split the input tensor into 2 tensors along the time dimension."""
def call(self, inputs):
# Expect the input to be 3D and mask to be 2D, split the input tensor into 2
# subtensors along the time axis (axis 1).
return tf.split(inputs, 2, axis=1)
def compute_mask(self, inputs, mask=None):
# Also split the mask into 2 if it presents.
if mask is None:
return None
return tf.split(mask, 2, axis=1)
first_half, second_half = TemporalSplit()(masked_embedding)
print(first_half._keras_mask)
print(second_half._keras_mask)
tf.Tensor( [[ True True True] [ True True True] [ True True True]], shape=(3, 3), dtype=bool) tf.Tensor( [[False False False] [ True True False] [ True True True]], shape=(3, 3), dtype=bool)
Berikut ini adalah contoh lain dari CustomEmbedding
lapisan yang mampu menghasilkan topeng dari nilai input:
class CustomEmbedding(keras.layers.Layer):
def __init__(self, input_dim, output_dim, mask_zero=False, **kwargs):
super(CustomEmbedding, self).__init__(**kwargs)
self.input_dim = input_dim
self.output_dim = output_dim
self.mask_zero = mask_zero
def build(self, input_shape):
self.embeddings = self.add_weight(
shape=(self.input_dim, self.output_dim),
initializer="random_normal",
dtype="float32",
)
def call(self, inputs):
return tf.nn.embedding_lookup(self.embeddings, inputs)
def compute_mask(self, inputs, mask=None):
if not self.mask_zero:
return None
return tf.not_equal(inputs, 0)
layer = CustomEmbedding(10, 32, mask_zero=True)
x = np.random.random((3, 10)) * 9
x = x.astype("int32")
y = layer(x)
mask = layer.compute_mask(x)
print(mask)
tf.Tensor( [[ True True True True True True True True True True] [ True True True True True False True True True True] [ True True True True True True True True False True]], shape=(3, 10), dtype=bool)
Memilih untuk menutupi propagasi pada lapisan yang kompatibel
Sebagian besar lapisan tidak mengubah dimensi waktu, jadi tidak perlu mengubah topeng saat ini. Namun, mereka mungkin masih ingin dapat menyebarkan masker saat ini, tidak berubah, ke lapisan berikutnya. Ini adalah perilaku opt-in. Secara default, lapisan kustom akan menghancurkan topeng saat ini (karena kerangka kerja tidak memiliki cara untuk mengetahui apakah menyebarkan topeng aman untuk dilakukan).
Jika Anda memiliki lapisan khusus yang tidak mengubah dimensi waktu, dan jika Anda ingin untuk dapat menyebarkan masukan topeng saat ini, Anda harus menetapkan self.supports_masking = True
di konstruktor lapisan. Dalam hal ini, perilaku default compute_mask()
adalah untuk hanya lulus topeng saat melalui.
Berikut adalah contoh lapisan yang masuk daftar putih untuk propagasi topeng:
class MyActivation(keras.layers.Layer):
def __init__(self, **kwargs):
super(MyActivation, self).__init__(**kwargs)
# Signal that the layer is safe for mask propagation
self.supports_masking = True
def call(self, inputs):
return tf.nn.relu(inputs)
Anda sekarang dapat menggunakan ini lapisan kustom di-antara layer mask menghasilkan (seperti Embedding
) dan layer mask memakan (seperti LSTM
), dan itu akan berlalu masker bersama sehingga mencapai layer mask memakan.
inputs = keras.Input(shape=(None,), dtype="int32")
x = layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True)(inputs)
x = MyActivation()(x) # Will pass the mask along
print("Mask found:", x._keras_mask)
outputs = layers.LSTM(32)(x) # Will receive the mask
model = keras.Model(inputs, outputs)
Mask found: KerasTensor(type_spec=TensorSpec(shape=(None, None), dtype=tf.bool, name=None), name='Placeholder_1:0')
Menulis lapisan yang membutuhkan informasi topeng
Beberapa lapisan adalah konsumen mask: mereka menerima mask
argumen dalam call
dan menggunakannya untuk menentukan apakah akan melewatkan langkah waktu tertentu.
Untuk menulis sebuah layer tersebut, Anda hanya dapat menambahkan mask=None
argumen di Anda call
tanda tangan. Topeng yang terkait dengan input akan diteruskan ke lapisan Anda kapan pun tersedia.
Berikut adalah contoh sederhana di bawah ini: lapisan yang menghitung softmax selama dimensi waktu (sumbu 1) dari urutan input, sambil membuang langkah waktu bertopeng.
class TemporalSoftmax(keras.layers.Layer):
def call(self, inputs, mask=None):
broadcast_float_mask = tf.expand_dims(tf.cast(mask, "float32"), -1)
inputs_exp = tf.exp(inputs) * broadcast_float_mask
inputs_sum = tf.reduce_sum(
inputs_exp * broadcast_float_mask, axis=-1, keepdims=True
)
return inputs_exp / inputs_sum
inputs = keras.Input(shape=(None,), dtype="int32")
x = layers.Embedding(input_dim=10, output_dim=32, mask_zero=True)(inputs)
x = layers.Dense(1)(x)
outputs = TemporalSoftmax()(x)
model = keras.Model(inputs, outputs)
y = model(np.random.randint(0, 10, size=(32, 100)), np.random.random((32, 100, 1)))
Ringkasan
Itu saja yang perlu Anda ketahui tentang padding & masking di Keras. Untuk rekap:
- "Masking" adalah bagaimana lapisan dapat mengetahui kapan harus melewati / mengabaikan langkah waktu tertentu dalam input urutan.
- Beberapa lapisan yang mask-generator:
Embedding
dapat menghasilkan topeng dari nilai input (jikamask_zero=True
), dan sehingga dapat denganMasking
lapisan. - Beberapa lapisan yang mask-konsumen: mereka mengekspos
mask
argumen dalam mereka__call__
metode. Ini adalah kasus untuk lapisan RNN. - Di Functional API dan Sequential API, informasi mask disebarkan secara otomatis.
- Bila menggunakan lapisan dalam cara yang mandiri, Anda dapat melewati
mask
argumen untuk lapisan secara manual. - Anda dapat dengan mudah menulis lapisan yang memodifikasi topeng saat ini, yang menghasilkan topeng baru, atau yang menggunakan topeng yang terkait dengan input.