एमएल समुदाय दिवस 9 नवंबर है! TensorFlow, JAX से नई जानकारी के लिए हमसे जुड़ें, और अधिक जानें

केरास के साथ मास्किंग और पैडिंग

TensorFlow.org पर देखें Google Colab में चलाएं GitHub पर स्रोत देखें नोटबुक डाउनलोड करें

सेट अप

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 एक आम लंबाई में truncate और पैड अजगर सूचियों में एक उपयोगिता समारोह प्रदान करता है: 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.layers.Masking परत।
  • एक कॉन्फ़िगर keras.layers.Embedding के साथ परत mask_zero=True
  • एक दर्रा mask तर्क मैन्युअल जब परतों कि इस तर्क (जैसे RNN परतों) का समर्थन करार दिया।

मास्क पैदा परतों: Embedding और Masking

हुड के अंतर्गत, इन परतों एक मुखौटा टेन्सर (आकार के साथ 2 डी टेन्सर पैदा करेगा (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 प्रसंस्करण के दौरान अनदेखा किया जाना चाहिए।

कार्यात्मक एपीआई और अनुक्रमिक एपीआई में मास्क का प्रसार

कार्यात्मक एपीआई या अनुक्रमिक एपीआई का उपयोग करते हैं, तो एक मुखौटा एक द्वारा उत्पन्न Embedding या Masking परत उस परत को उन्हें (उदाहरण के लिए, RNN परतों) का उपयोग करने में सक्षम है के लिए नेटवर्क के माध्यम से प्रचारित किया जाएगा। केरस स्वचालित रूप से एक इनपुट के अनुरूप मुखौटा लाएगा और इसे किसी भी परत को पास कर देगा जो जानता है कि इसका उपयोग कैसे करना है।

उदाहरण के लिए, निम्नलिखित अनुक्रमिक मॉडल में, LSTM परत स्वचालित रूप से एक मुखौटा है, जो साधन यह गद्देदार मूल्यों पर ध्यान नहीं देगा प्राप्त होगा:

model = keras.Sequential(
    [layers.Embedding(input_dim=5000, output_dim=16, mask_zero=True), layers.LSTM(32),]
)

निम्नलिखित कार्यात्मक एपीआई मॉडल के लिए भी यही स्थिति है:

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 हस्ताक्षर। जब भी यह उपलब्ध होगा, इनपुट से जुड़े मास्क को आपकी परत पर भेज दिया जाएगा।

यहां नीचे एक सरल उदाहरण दिया गया है: एक परत जो एक इनपुट अनुक्रम के समय आयाम (अक्ष 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)))

सारांश

केरस में पैडिंग और मास्किंग के बारे में आपको बस इतना ही पता होना चाहिए। संक्षेप में दुहराना:

  • "मास्किंग" यह है कि कैसे परतें यह जानने में सक्षम हैं कि अनुक्रम इनपुट में कुछ निश्चित समय-चरणों को कब छोड़ना / अनदेखा करना है।
  • कुछ परतों मुखौटा जनरेटर हैं: Embedding इनपुट मानों से एक मुखौटा उत्पन्न कर सकते हैं (यदि mask_zero=True ), और इसलिए कर सकते हैं Masking परत।
  • कुछ परतों मुखौटा-उपभोक्ता हैं: वे एक बेनकाब mask उनके में तर्क __call__ विधि। आरएनएन परतों के लिए यह मामला है।
  • कार्यात्मक एपीआई और अनुक्रमिक एपीआई में, मुखौटा जानकारी स्वचालित रूप से प्रचारित होती है।
  • एक स्टैंडअलोन तरह से परतों का उपयोग करते हैं, तो आप पास कर सकते हैं mask मैन्युअल परतों को तर्क।
  • आप आसानी से ऐसी परतें लिख सकते हैं जो वर्तमान मास्क को संशोधित करती हैं, जो एक नया मास्क उत्पन्न करती हैं, या जो इनपुट से जुड़े मास्क का उपभोग करती हैं।