کمک به حفاظت از دیواره بزرگ مرجانی با TensorFlow در Kaggle اضافه کردن چالش

ماسک زدن و پر کردن با Keras

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

برپایی

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

معرفی

پوشش راه به لایه های دنباله پردازش که timesteps خاص را به یک ورودی از دست رفته، و در نتیجه باید نادیده گرفته شود در هنگام پردازش اطلاعات است.

پارچه کشی، نوع خاصی از پوشش که در آن مراحل پوشانده در شروع یا پایان یک دنباله است. Padding از نیاز به رمزگذاری داده‌های توالی به دسته‌های پیوسته ناشی می‌شود: برای اینکه همه دنباله‌ها در یک دسته متناسب با طول استاندارد معین شوند، لازم است برخی از دنباله‌ها را pad یا کوتاه کنیم.

بیایید نگاهی دقیق بیندازیم.

داده‌های توالی پد

هنگام پردازش داده های توالی، بسیار متداول است که نمونه های جداگانه دارای طول های مختلف باشند. مثال زیر را در نظر بگیرید (متن نشانه گذاری شده به صورت کلمات):

[
  ["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]]

نقاب زدن

اکنون که تمام نمونه ها دارای طول یکنواخت هستند، باید به مدل اطلاع داده شود که بخشی از داده ها در واقع padding هستند و باید نادیده گرفته شوند. که ساز و پوشش است.

سه راه برای معرفی ماسک ورودی در مدل های 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 ورود نشان می دهد که timestep مربوطه باید در طول پردازش کنه.

انتشار ماسک در Functional API و Sequential API

هنگامی که با استفاده از 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 است.
  • در Functional API و Sequential API، اطلاعات ماسک به طور خودکار منتشر می شود.
  • هنگام استفاده از لایه ها در یک راه مستقل نیست، شما می توانید عبور mask استدلال به لایه های دستی.
  • می‌توانید به راحتی لایه‌هایی بنویسید که ماسک فعلی را تغییر می‌دهند، ماسک جدیدی ایجاد می‌کنند، یا ماسک مرتبط با ورودی‌ها را مصرف می‌کنند.