![]() | ![]() | ![]() | ![]() |
يثبت
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
مقدمة
اخفاء هو وسيلة لمعرفة طبقات تجهيز تسلسل أن بعض timesteps في مدخلا مفقودة، وبالتالي يجب أن يتم تخطي عند معالجة البيانات.
الحشو هو شكل خاص من اخفاء حيث هي الخطوات ملثمين في بداية أو نهاية التسلسل. يأتي الحشو من الحاجة إلى تشفير بيانات التسلسل إلى دفعات متجاورة: من أجل جعل جميع التسلسلات في الدفعة تتناسب مع طول قياسي معين ، من الضروري حشو بعض التسلسلات أو اقتطاعها.
دعونا نلقي نظرة فاحصة.
بيانات تسلسل الحشو
عند معالجة بيانات التسلسل ، من الشائع جدًا أن يكون للعينات الفردية أطوال مختلفة. خذ بعين الاعتبار المثال التالي (النص الذي تم ترميزه على هيئة كلمات):
[
["Hello", "world", "!"],
["How", "are", "you", "doing", "today"],
["The", "weather", "will", "be", "nice", "tomorrow"],
]
بعد البحث عن المفردات ، قد يتم توجيه البيانات كأعداد صحيحة ، على سبيل المثال:
[
[71, 1331, 4231]
[73, 8, 3215, 55, 927],
[83, 91, 1, 645, 1253, 927],
]
البيانات عبارة عن قائمة متداخلة حيث يبلغ طول العينات الفردية 3 و 5 و 6 على التوالي. منذ إدخال البيانات لنموذج التعلم العميق يجب أن يكون موتر واحد (من الشكل مثل (batch_size, 6, vocab_size)
في هذه الحالة)، والعينات التي هي أقصر من أطول الحاجة العنصر المراد مبطن مع بعض القيمة نائبا (بدلا من ذلك، واحدة قد تقوم أيضًا باقتطاع عينات طويلة قبل حشو عينات قصيرة).
يوفر Keras وظيفة الأداة المساعدة لاقتطاع وسادة قوائم بيثون على طول مشترك: 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]]
قناع
الآن بعد أن أصبحت جميع العينات ذات طول موحد ، يجب إعلام النموذج بأن جزءًا من البيانات عبارة عن حشو فعليًا ويجب تجاهله. هذه الآلية تخفى.
هناك ثلاث طرق لتقديم أقنعة الإدخال في نماذج Keras:
- إضافة
keras.layers.Masking
طبقة. - تكوين
keras.layers.Embedding
طبقة معmask_zero=True
. - تمرير
mask
حجة يدويا عند الاتصال طبقات التي تدعم هذه الحجة (مثل طبقات RNN).
طبقات توليد قناع: Embedding
و Masking
تحت غطاء محرك السيارة، وهذه الطبقات إنشاء موتر قناع (2D موتر مع شكل (batch, sequence_length)
)، وإرفاقه إلى الإخراج موتر إرجاعها بواسطة Masking
أو Embedding
طبقة.
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)
وكما ترون من نتيجة المطبوعة، القناع هو 2D موتر منطقية مع شكل (batch_size, sequence_length)
، حيث كل فرد False
يشير دخول ان خطوة زمنية المقابلة يجب أن يتم تجاهل أثناء المعالجة.
انتشار القناع في واجهة برمجة التطبيقات الوظيفية وواجهة برمجة التطبيقات المتسلسلة
عند استخدام API الوظيفي أو API متسلسل، قناع التي تم إنشاؤها بواسطة Embedding
أو Masking
سيتم نشر طبقة من خلال الشبكة لأي طبقة قادرة على استخدامها (على سبيل المثال، والطبقات RNN). سيقوم Keras تلقائيًا بجلب القناع المقابل لإدخال ما وتمريره إلى أي طبقة تعرف كيفية استخدامه.
على سبيل المثال، في النموذج التتابعي التالي، LSTM
سوف تحصل تلقائيا طبقة قناع، مما يعني أنه سيتم تجاهل القيم مبطن:
model = keras.Sequential(
[layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True), layers.LSTM(32),]
)
هذا هو الحال أيضًا بالنسبة لنموذج API الوظيفي التالي:
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)
تمرير موترات القناع مباشرة إلى الطبقات
الطبقات التي يمكن التعامل مع الأقنعة (مثل LSTM
طبقة) لديها mask
حجة في حياتهم __call__
الأسلوب.
وفي الوقت نفسه، والطبقات التي تنتج قناع (على سبيل المثال Embedding
) بالكشف عن compute_mask(input, previous_mask)
الطريقة التي يمكنك الاتصال بها.
وهكذا، يمكنك تمرير إخراج compute_mask()
طريقة طبقة المنتجة للقناع إلى __call__
طريقة طبقة تستغرق قناع، مثل هذا:
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)>
دعم التقنيع في طبقاتك المخصصة
في بعض الأحيان، قد تحتاج إلى طبقات الكتابة التي تولد قناع (مثل Embedding
)، أو الطبقات التي تحتاج إلى تعديل قناع الحالي.
على سبيل المثال، أي الطبقة التي تنتج موتر مع البعد الزمني يختلف عن مدخلاته، مثل Concatenate
طبقة يسلسل على البعد الزمني، سوف تحتاج إلى تعديل القناع الحالي بحيث طبقات المصب سوف يكون قادرا على اتخاذ صحيح timesteps ملثمين في الحساب.
للقيام بذلك، يجب أن طبقة الخاصة بك تنفيذ layer.compute_mask()
طريقة التي تنتج قناع جديد نظرا للمدخلات وقناع الحالي.
هنا هو مثال على TemporalSplit
طبقة التي تحتاج إلى تعديل قناع الحالي.
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)
هنا هو مثال آخر على CustomEmbedding
الطبقة التي هي قادرة على توليد قناع من قيم الإدخال:
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)
الاشتراك لإخفاء الانتشار على الطبقات المتوافقة
لا تقوم معظم الطبقات بتعديل البعد الزمني ، لذلك لا تحتاج إلى تعديل القناع الحالي. ومع ذلك، فإنها قد لا تزال تريد أن تكون قادرة على نشر القناع الحالي، دون تغيير، إلى الطبقة التالية. هذا هو سلوك التمكين. بشكل افتراضي ، ستدمر الطبقة المخصصة القناع الحالي (نظرًا لأن إطار العمل ليس لديه طريقة لمعرفة ما إذا كان نشر القناع آمنًا أم لا).
إذا كان لديك طبقة المخصصة التي لم تقم بتعديل البعد الزمني، وإذا كنت تريد أن تكون قادرة على نشر قناع الإدخال الحالي، يجب تعيين self.supports_masking = True
في منشئ طبقة. في هذه الحالة، السلوك الافتراضي compute_mask()
هو مجرد تمرير قناع الحالي من خلال.
فيما يلي مثال على طبقة تمت إضافتها إلى القائمة البيضاء لنشر القناع:
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)
يمكنك الآن استخدام هذه الطبقة المخصصة في الفترات الفاصلة بين طبقة مدرة للقناع (مثل Embedding
) وطبقة تستغرق قناع (مثل LSTM
)، وسوف تمر القناع على طول بحيث تصل إلى طبقة تستغرق وقناع.
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')
طبقات الكتابة التي تحتاج إلى معلومات القناع
بعض طبقات المستهلكين قناع: فهي قبول mask
حجة في call
واستخدامها لتحديد ما إذا كان لتخطي بعض الخطوات الوقت.
لكتابة طبقة من هذه، يمكنك ببساطة إضافة mask=None
حجة في حياتك call
التوقيع. سيتم تمرير القناع المرتبط بالمدخلات إلى طبقتك متى كان ذلك متاحًا.
فيما يلي مثال بسيط أدناه: طبقة تحسب softmax عبر البعد الزمني (المحور 1) لتسلسل الإدخال ، بينما تتجاهل الخطوات الزمنية المقنعة.
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)))
ملخص
هذا كل ما تحتاج لمعرفته حول الحشو والإخفاء في Keras. إلى خلاصة:
- "الإخفاء" هو كيفية تمكن الطبقات من معرفة وقت تخطي / تجاهل خطوات زمنية معينة في مدخلات التسلسل.
- بعض الطبقات قناع مولدات:
Embedding
يمكن أن تولد قناع من قيم الإدخال (إذاmask_zero=True
)، وهكذا يمكن للMasking
طبقة. - بعض الطبقات قناع المستهلكين: أنها فضح
mask
حجة في حياتهم__call__
الأسلوب. هذا هو الحال بالنسبة لطبقات RNN. - في API الوظيفية و API المتسلسل ، يتم نشر معلومات القناع تلقائيًا.
- عند استخدام طبقات بطريقة مستقل، يمكنك تمرير
mask
الحجج إلى طبقات يدويا. - يمكنك بسهولة كتابة الطبقات التي تعدل القناع الحالي ، والتي تنشئ قناعًا جديدًا ، أو التي تستهلك القناع المرتبط بالمدخلات.