কেরাসের সাথে মাস্কিং এবং প্যাডিং

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) এই ক্ষেত্রে), নমুনা যে দীর্ঘতম আইটেমটি প্রয়োজন চেয়ে খাটো কিছু স্থানধারক মান (অথবা, এক সঙ্গে padded করা ছোট নমুনাগুলি প্যাড করার আগে দীর্ঘ নমুনাগুলিও কাটতে পারে)।

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.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 প্রক্রিয়াকরণের সময় উপেক্ষা করা উচিত নয়।

কার্যকরী API এবং অনুক্রমিক API-এ মাস্ক প্রচার

প্রায়োগিক API বা অনুক্রমিক এপিআই ব্যবহার করার সময়, একটি মাস্ক একটি দ্বারা উত্পন্ন Embedding বা Masking লেয়ার কোন স্তর যে তাদের (উদাহরণস্বরূপ, RNN স্তর) ব্যবহার করে করতে সক্ষম জন্য নেটওয়ার্কের মাধ্যমে প্রচারিত করা হবে না। কেরাস স্বয়ংক্রিয়ভাবে একটি ইনপুটের সাথে সম্পর্কিত মুখোশটি আনবে এবং এটিকে কীভাবে ব্যবহার করতে জানে এমন যে কোনও স্তরে এটি প্রেরণ করবে।

উদাহরণস্বরূপ, নিম্নলিখিত অনুক্রমিক মডেল, 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 স্বাক্ষর। ইনপুটগুলির সাথে যুক্ত মুখোশটি যখনই উপলব্ধ হবে তখনই আপনার স্তরে প্রেরণ করা হবে।

এখানে একটি সাধারণ উদাহরণ নিচে দেওয়া হল: একটি স্তর যা একটি ইনপুট ক্রমের সময় মাত্রা (অক্ষ 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__ পদ্ধতি। এটি আরএনএন স্তরগুলির ক্ষেত্রে।
  • কার্যকরী API এবং অনুক্রমিক API-এ, মুখোশ তথ্য স্বয়ংক্রিয়ভাবে প্রচারিত হয়।
  • একটি স্বতন্ত্র ভাবে স্তর ব্যবহার করার সময়, আপনি পাস করতে পারেন mask ম্যানুয়ালি স্তর আর্গুমেন্ট।
  • আপনি সহজেই লেয়ার লিখতে পারেন যা বর্তমান মাস্ককে পরিবর্তন করে, যা একটি নতুন মুখোশ তৈরি করে বা ইনপুটগুলির সাথে যুক্ত মুখোশ ব্যবহার করে।