এক্সটেনশনের ধরন

TensorFlow.org এ দেখুন Google Colab-এ চালান GitHub-এ উৎস দেখুননোটবুক ডাউনলোড করুন

সেটআপ

!pip install -q tf_nightly
import tensorflow as tf
import numpy as np
from typing import Tuple, List, Mapping, Union, Optional
import tempfile

এক্সটেনশন প্রকার

ব্যবহারকারী-সংজ্ঞায়িত প্রকারগুলি প্রকল্পগুলিকে আরও পাঠযোগ্য, মডুলার, রক্ষণাবেক্ষণযোগ্য করে তুলতে পারে। যাইহোক, বেশিরভাগ TensorFlow API-এর ব্যবহারকারী-সংজ্ঞায়িত পাইথন প্রকারের জন্য খুব সীমিত সমর্থন রয়েছে। এতে উচ্চ-স্তরের API (যেমন Keras , tf.function , tf.SavedModel ) এবং নিম্ন-স্তরের API (যেমন tf.while_loop এবং tf.concat ) উভয়ই অন্তর্ভুক্ত রয়েছে। TensorFlow এক্সটেনশন প্রকারগুলি ব্যবহারকারী-সংজ্ঞায়িত অবজেক্ট-ভিত্তিক প্রকারগুলি তৈরি করতে ব্যবহার করা যেতে পারে যা TensorFlow এর APIগুলির সাথে নির্বিঘ্নে কাজ করে। একটি এক্সটেনশন টাইপ তৈরি করতে, tf.experimental.ExtensionType এর বেস হিসাবে একটি পাইথন ক্লাস সংজ্ঞায়িত করুন এবং প্রতিটি ক্ষেত্রের জন্য টাইপ নির্দিষ্ট করতে টাইপ টীকা ব্যবহার করুন।

class TensorGraph(tf.experimental.ExtensionType):
  """A collection of labeled nodes connected by weighted edges."""
  edge_weights: tf.Tensor               # shape=[num_nodes, num_nodes]
  node_labels: Mapping[str, tf.Tensor]  # shape=[num_nodes]; dtype=any

class MaskedTensor(tf.experimental.ExtensionType):
  """A tensor paired with a boolean mask, indicating which values are valid."""
  values: tf.Tensor
  mask: tf.Tensor       # shape=values.shape; false for missing/invalid values.

class CSRSparseMatrix(tf.experimental.ExtensionType):
  """Compressed sparse row matrix (https://en.wikipedia.org/wiki/Sparse_matrix)."""
  values: tf.Tensor     # shape=[num_nonzero]; dtype=any
  col_index: tf.Tensor  # shape=[num_nonzero]; dtype=int64
  row_index: tf.Tensor  # shape=[num_rows+1]; dtype=int64

tf.experimental.ExtensionType বেস ক্লাসটি স্ট্যান্ডার্ড পাইথন লাইব্রেরি থেকে টাইপিং. typing.NamedTuple এবং @dataclasses.dataclass এর মতোই কাজ করে। বিশেষ করে, এটি স্বয়ংক্রিয়ভাবে একটি কনস্ট্রাক্টর এবং বিশেষ পদ্ধতি (যেমন __repr__ এবং __eq__ ) ক্ষেত্র প্রকারের টীকাগুলির উপর ভিত্তি করে যোগ করে।

সাধারণত, এক্সটেনশনের ধরন দুটি বিভাগের মধ্যে একটিতে পড়ে:

  • ডেটা স্ট্রাকচার , যা সংশ্লিষ্ট মানগুলির একটি সংগ্রহকে একত্রিত করে এবং সেই মানগুলির উপর ভিত্তি করে দরকারী ক্রিয়াকলাপ প্রদান করতে পারে। ডেটা স্ট্রাকচার মোটামুটি সাধারণ হতে পারে (যেমন উপরের TensorGraph উদাহরণ); অথবা তারা একটি নির্দিষ্ট মডেল অত্যন্ত কাস্টমাইজ করা হতে পারে.

  • টেনসর-সদৃশ প্রকারগুলি , যা "টেনসর" ধারণাটিকে বিশেষায়িত বা প্রসারিত করে। এই বিভাগের প্রকারগুলির একটি rank , একটি shape এবং সাধারণত একটি dtype ; এবং টেনসর ক্রিয়াকলাপগুলির সাথে (যেমন tf.stack , tf.add , বা tf.matmul ) ব্যবহার করা বোধগম্য। MaskedTensor এবং CSRSparseMatrix হল টেনসরের মত ধরনের উদাহরণ।

সমর্থিত API

এক্সটেনশন প্রকারগুলি নিম্নলিখিত TensorFlow API দ্বারা সমর্থিত:

  • কেরাস : কেরাস Models এবং Layers জন্য এক্সটেনশনের ধরনগুলি ইনপুট এবং আউটপুট হিসাবে ব্যবহার করা যেতে পারে।
  • tf.data.Dataset : এক্সটেনশন প্রকারগুলি ডেটাসেটে অন্তর্ভুক্ত করা যেতে পারে এবং ডেটাসেট Datasets দ্বারা ফেরত দেওয়া Iterators পারে।
  • টেনসরফ্লো হাব : এক্সটেনশন প্রকারগুলি tf.hub মডিউলগুলির জন্য ইনপুট এবং আউটপুট হিসাবে ব্যবহার করা যেতে পারে।
  • SavedModel : সংরক্ষিত SavedModel ফাংশনের জন্য এক্সটেনশনের ধরন ইনপুট এবং আউটপুট হিসাবে ব্যবহার করা যেতে পারে।
  • tf.function : @tf.function ডেকোরেটর দিয়ে মোড়ানো ফাংশনের জন্য এক্সটেনশনের ধরন আর্গুমেন্ট এবং রিটার্ন মান হিসাবে ব্যবহার করা যেতে পারে।
  • while loops : tf.while_loop এ এক্সটেনশনের ধরন লুপ ভেরিয়েবল হিসাবে ব্যবহার করা যেতে পারে এবং while-loop-এর বডির জন্য আর্গুমেন্ট এবং রিটার্ন মান হিসাবে ব্যবহার করা যেতে পারে।
  • শর্তাবলী : এক্সটেনশন প্রকারগুলি শর্তসাপেক্ষে tf.cond এবং tf.case ব্যবহার করে নির্বাচন করা যেতে পারে।
  • py_function : এক্সটেনশনের ধরনগুলি আর্গুমেন্ট হিসাবে ব্যবহার করা যেতে পারে এবং tf.py_functionfunc আর্গুমেন্টের জন্য মান ফেরত দিতে পারে।
  • টেনসর অপ্স : টেনসর ইনপুট গ্রহণ করে এমন বেশিরভাগ টেনসরফ্লো অপ্সকে সমর্থন করার জন্য এক্সটেনশনের ধরন বাড়ানো যেতে পারে (যেমন, tf.matmul , tf.gather , এবং tf.reduce_sum )। আরও তথ্যের জন্য নীচের " প্রেরণ " বিভাগটি দেখুন।
  • বন্টন কৌশল : এক্সটেনশনের ধরন প্রতি-প্রতিরূপ মান হিসাবে ব্যবহার করা যেতে পারে।

আরও বিশদ বিবরণের জন্য, নীচে "TensorFlow APIs যেটি ExtensionTypes সমর্থন করে" বিভাগটি দেখুন।

প্রয়োজনীয়তা

ক্ষেত্র প্রকার

সমস্ত ক্ষেত্র (ওরফে ইনস্ট্যান্স ভেরিয়েবল) অবশ্যই ঘোষণা করতে হবে এবং প্রতিটি ক্ষেত্রের জন্য একটি টাইপ টীকা প্রদান করতে হবে। নিম্নলিখিত ধরনের টীকা সমর্থিত:

টাইপ উদাহরণ
পাইথন পূর্ণসংখ্যা i: int
পাইথন ভাসছে f: float
পাইথন স্ট্রিং s: str
পাইথন বুলিয়ানস b: bool
পাইথন কোনটি নয় n: None
টেনসর আকার shape: tf.TensorShape
টেনসর dtypes dtype: tf.DType
টেনসর t: tf.Tensor
এক্সটেনশন প্রকার mt: MyMaskedTensor
রাগড টেনসর rt: tf.RaggedTensor
স্পারস টেনসর st: tf.SparseTensor
সূচীকৃত স্লাইস s: tf.IndexedSlices
ঐচ্ছিক টেনসর o: tf.experimental.Optional
টাইপ ইউনিয়ন int_or_float: typing.Union[int, float]
টিপলস params: typing.Tuple[int, float, tf.Tensor, int]
ভার-দৈর্ঘ্যের টিপল lengths: typing.Tuple[int, ...]
ম্যাপিং tags: typing.Mapping[str, tf.Tensor]
ঐচ্ছিক মান weight: typing.Optional[tf.Tensor]

পরিবর্তনশীলতা

এক্সটেনশনের ধরন অপরিবর্তনীয় হতে হবে। এটি নিশ্চিত করে যে টেনসরফ্লো-এর গ্রাফ-ট্রেসিং প্রক্রিয়ার দ্বারা সঠিকভাবে ট্র্যাক করা যেতে পারে। আপনি যদি নিজেকে একটি এক্সটেনশন টাইপ মান পরিবর্তন করতে চান তবে মানগুলিকে রূপান্তরিত করে এমন পদ্ধতিগুলিকে সংজ্ঞায়িত করার পরিবর্তে বিবেচনা করুন৷ উদাহরণস্বরূপ, একটি MaskedTensor পরিবর্তন করার জন্য একটি set_mask পদ্ধতি সংজ্ঞায়িত করার পরিবর্তে, আপনি একটি MaskedTensor পদ্ধতি সংজ্ঞায়িত করতে পারেন যা একটি নতুন replace_mask MaskedTensor :

class MaskedTensor(tf.experimental.ExtensionType):
  values: tf.Tensor
  mask: tf.Tensor

  def replace_mask(self, new_mask):
      self.values.shape.assert_is_compatible_with(new_mask.shape)
      return MaskedTensor(self.values, new_mask)

ExtensionType দ্বারা কার্যকারিতা যোগ করা হয়েছে

ExtensionType বেস ক্লাস নিম্নলিখিত কার্যকারিতা প্রদান করে:

  • একজন কনস্ট্রাক্টর ( __init__ )।
  • একটি মুদ্রণযোগ্য উপস্থাপনা পদ্ধতি ( __repr__ )।
  • সমতা এবং অসমতা অপারেটর ( __eq__ )।
  • একটি বৈধতা পদ্ধতি ( __validate__ )।
  • অপরিবর্তনীয়তা প্রয়োগ করা হয়েছে।
  • একটি নেস্টেড TypeSpec .
  • টেনসর API প্রেরণ সমর্থন।

এই কার্যকারিতা কাস্টমাইজ করার বিষয়ে আরও তথ্যের জন্য নীচের "কাস্টমাইজিং এক্সটেনশন টাইপস" বিভাগটি দেখুন৷

কনস্ট্রাক্টর

ExtensionType দ্বারা যোগ করা কনস্ট্রাক্টর প্রতিটি ক্ষেত্রকে একটি নামযুক্ত আর্গুমেন্ট হিসাবে নেয় (যে ক্রমে তারা ক্লাস সংজ্ঞাতে তালিকাভুক্ত ছিল)। এই কনস্ট্রাক্টর প্রতিটি প্যারামিটার টাইপ-চেক করবে এবং প্রয়োজনে সেগুলিকে রূপান্তর করবে। বিশেষ করে, Tensor ক্ষেত্রগুলি tf.convert_to_tensor ব্যবহার করে রূপান্তরিত হয়; Tuple ক্ষেত্রগুলি tuple s এ রূপান্তরিত হয়; এবং Mapping ক্ষেত্রগুলি অপরিবর্তনীয় ডিক্টে রূপান্তরিত হয়।

class MaskedTensor(tf.experimental.ExtensionType):
  values: tf.Tensor
  mask: tf.Tensor

# Constructor takes one parameter for each field.
mt = MaskedTensor(values=[[1, 2, 3], [4, 5, 6]],
                  mask=[[True, True, False], [True, False, True]])

# Fields are type-checked and converted to the declared types.
# E.g., mt.values is converted to a Tensor.
print(mt.values)
tf.Tensor(
[[1 2 3]
 [4 5 6]], shape=(2, 3), dtype=int32)

কনস্ট্রাক্টর একটি TypeError উত্থাপন করে যদি একটি ক্ষেত্রের মান তার ঘোষিত প্রকারে রূপান্তর করা না যায়:

try:
  MaskedTensor([1, 2, 3], None)
except TypeError as e:
  print(f"Got expected TypeError: {e}")
Got expected TypeError: mask: expected a Tensor, got None

একটি ক্ষেত্রের জন্য ডিফল্ট মান শ্রেণি স্তরে তার মান সেট করে নির্দিষ্ট করা যেতে পারে:

class Pencil(tf.experimental.ExtensionType):
  color: str = "black"
  has_erasor: bool = True
  length: tf.Tensor = 1.0

Pencil()
Pencil(color='black', has_erasor=True, length=<tf.Tensor: shape=(), dtype=float32, numpy=1.0>)
Pencil(length=0.5, color="blue")
Pencil(color='blue', has_erasor=True, length=<tf.Tensor: shape=(), dtype=float32, numpy=0.5>)

মুদ্রণযোগ্য উপস্থাপনা

ExtensionType একটি ডিফল্ট মুদ্রণযোগ্য উপস্থাপনা পদ্ধতি ( __repr__ ) যোগ করে যা প্রতিটি ক্ষেত্রের জন্য ক্লাসের নাম এবং মান অন্তর্ভুক্ত করে:

print(MaskedTensor(values=[1, 2, 3], mask=[True, True, False]))
MaskedTensor(values=<tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 2, 3], dtype=int32)>, mask=<tf.Tensor: shape=(3,), dtype=bool, numpy=array([ True,  True, False])>)

সমতা অপারেটর

ExtensionType ডিফল্ট সমতা অপারেটর যোগ করে ( __eq__ এবং __ne__ ) যে দুটি মান সমান বিবেচনা করে যদি তাদের একই প্রকার থাকে এবং তাদের সমস্ত ক্ষেত্র সমান হয়। টেনসর ক্ষেত্র সমান হিসাবে বিবেচিত হয় যদি তাদের আকৃতি একই থাকে এবং সমস্ত উপাদানের জন্য উপাদান অনুসারে সমান হয়।

a = MaskedTensor([1, 2], [True, False])
b = MaskedTensor([[3, 4], [5, 6]], [[False, True], [True, True]])
print(f"a == a: {a==a}")
print(f"a == b: {a==b}")
print(f"a == a.values: {a==a.values}")
a == a: True
a == b: False
a == a.values: False

বৈধতা পদ্ধতি

ExtensionType একটি __validate__ পদ্ধতি যোগ করে, যা ক্ষেত্রগুলিতে বৈধতা যাচাই করার জন্য ওভাররাইড করা যেতে পারে। কনস্ট্রাক্টরকে কল করার পরে এবং ক্ষেত্রগুলি টাইপ-চেক করার পরে এবং তাদের ঘোষিত প্রকারগুলিতে রূপান্তরিত হওয়ার পরে এটি চালানো হয়, তাই এটি ধরে নেওয়া যেতে পারে যে সমস্ত ক্ষেত্রে তাদের ঘোষিত প্রকার রয়েছে।

তিনি নিম্নোক্ত উদাহরণগুলি dtype এর ক্ষেত্রগুলির shape s এবং MaskedTensor s যাচাই করতে আপডেট করেছেন:

class MaskedTensor(tf.experimental.ExtensionType):
  """A tensor paired with a boolean mask, indicating which values are valid."""
  values: tf.Tensor
  mask: tf.Tensor
  def __validate__(self):
    self.values.shape.assert_is_compatible_with(self.mask.shape)
    assert self.mask.dtype.is_bool, 'mask.dtype must be bool'
try:
  MaskedTensor([1, 2, 3], [0, 1, 0])  # wrong dtype for mask.
except AssertionError as e:
  print(f"Got expected AssertionError: {e}")
Got expected AssertionError: mask.dtype must be bool
try:
  MaskedTensor([1, 2, 3], [True, False])  # shapes don't match.
except ValueError as e:
  print(f"Got expected ValueError: {e}")
Got expected ValueError: Shapes (3,) and (2,) are incompatible

অপরিবর্তনীয়তা প্রয়োগ করা হয়েছে

ExtensionType টাইপ মিউটেশন প্রতিরোধ করতে __setattr__ এবং __delattr__ পদ্ধতিগুলিকে ওভাররাইড করে, নিশ্চিত করে যে এক্সটেনশন প্রকারের মানগুলি অপরিবর্তনীয়।

mt = MaskedTensor([1, 2, 3], [True, False, True])
try:
  mt.mask = [True, True, True]
except AttributeError as e:
  print(f"Got expected AttributeError: {e}")
Got expected AttributeError: Cannot mutate attribute `mask` outside the custom constructor of ExtensionType.
try:
  mt.mask[0] = False
except TypeError as e:
  print(f"Got expected TypeError: {e}")
Got expected TypeError: 'tensorflow.python.framework.ops.EagerTensor' object does not support item assignment
try:
  del mt.mask
except AttributeError as e:
  print(f"Got expected AttributeError: {e}")
Got expected AttributeError: Cannot mutate attribute `mask` outside the custom constructor of ExtensionType.

নেস্টেড টাইপস্পেক

প্রতিটি ExtensionType ক্লাসে একটি সংশ্লিষ্ট TypeSpec ক্লাস থাকে, যেটি স্বয়ংক্রিয়ভাবে তৈরি হয় এবং <extension_type_name>.Spec হিসেবে সংরক্ষণ করা হয়।

এই শ্রেণী কোনো নেস্টেড টেনসরের মান ব্যতীত একটি মান থেকে সমস্ত তথ্য ক্যাপচার করে। বিশেষ করে, একটি মানের জন্য TypeSpec তৈরি করা হয় যে কোনো নেস্টেড টেনসর, এক্সটেনশন টাইপ, বা কম্পোজিট টেনসর এর TypeSpec দিয়ে প্রতিস্থাপন করে।

class Player(tf.experimental.ExtensionType):
  name: tf.Tensor
  attributes: Mapping[str, tf.Tensor]

anne = Player("Anne", {"height": 8.3, "speed": 28.1})
anne_spec = tf.type_spec_from_value(anne)
print(anne_spec.name)  # Records dtype and shape, but not the string value.
print(anne_spec.attributes)  # Records keys and TensorSpecs for values.
WARNING:tensorflow:Mapping types may not work well with tf.nest. Prefer using MutableMapping for <class 'tensorflow.python.framework.immutable_dict.ImmutableDict'>
TensorSpec(shape=(), dtype=tf.string, name=None)
ImmutableDict({'height': TensorSpec(shape=(), dtype=tf.float32, name=None), 'speed': TensorSpec(shape=(), dtype=tf.float32, name=None)})

TypeSpec মানগুলি স্পষ্টভাবে তৈরি করা যেতে পারে, অথবা এগুলি tf.type_spec_from_value ব্যবহার করে একটি ExtensionType মান থেকে তৈরি করা যেতে পারে :

spec1 = Player.Spec(name=tf.TensorSpec([], tf.float32), attributes={})
spec2 = tf.type_spec_from_value(anne)

TypeSpec ব্যবহার করে মানগুলিকে একটি স্ট্যাটিক উপাদান এবং একটি গতিশীল উপাদানে ভাগ করতে:

  • স্ট্যাটিক উপাদান (যা গ্রাফ-নির্মাণের সময় স্থির করা হয়) একটি tf.TypeSpec দিয়ে এনকোড করা হয়।
  • ডাইনামিক কম্পোনেন্ট (যা প্রতিবার গ্রাফ চালানোর সময় পরিবর্তিত হতে পারে) tf.Tensor s-এর তালিকা হিসাবে এনকোড করা হয়।

উদাহরণস্বরূপ, যখনই কোনো আর্গুমেন্টে পূর্বে অদেখা TypeSpec থাকে তখনই tf.function তার মোড়ানো ফাংশনকে রিট্রেস করে:

@tf.function
def anonymize_player(player):
  print("<<TRACING>>")
  return Player("<anonymous>", player.attributes)
# Function gets traced (first time the function has been called):
anonymize_player(Player("Anne", {"height": 8.3, "speed": 28.1}))
WARNING:tensorflow:Mapping types may not work well with tf.nest. Prefer using MutableMapping for <class 'tensorflow.python.framework.immutable_dict.ImmutableDict'>
WARNING:tensorflow:Mapping types may not work well with tf.nest. Prefer using MutableMapping for <class 'tensorflow.python.framework.immutable_dict.ImmutableDict'>
<<TRACING>>
Player(name=<tf.Tensor: shape=(), dtype=string, numpy=b'<anonymous>'>, attributes=ImmutableDict({'height': <tf.Tensor: shape=(), dtype=float32, numpy=8.3>, 'speed': <tf.Tensor: shape=(), dtype=float32, numpy=28.1>}))
# Function does NOT get traced (same TypeSpec: just tensor values changed)
anonymize_player(Player("Bart", {"height": 8.1, "speed": 25.3}))
Player(name=<tf.Tensor: shape=(), dtype=string, numpy=b'<anonymous>'>, attributes=ImmutableDict({'height': <tf.Tensor: shape=(), dtype=float32, numpy=8.1>, 'speed': <tf.Tensor: shape=(), dtype=float32, numpy=25.3>}))
# Function gets traced (new TypeSpec: keys for attributes changed):
anonymize_player(Player("Chuck", {"height": 11.0, "jump": 5.3}))
<<TRACING>>
Player(name=<tf.Tensor: shape=(), dtype=string, numpy=b'<anonymous>'>, attributes=ImmutableDict({'height': <tf.Tensor: shape=(), dtype=float32, numpy=11.0>, 'jump': <tf.Tensor: shape=(), dtype=float32, numpy=5.3>}))

আরও তথ্যের জন্য, tf.function গাইড দেখুন।

এক্সটেনশন প্রকার কাস্টমাইজ করা

কেবলমাত্র ক্ষেত্র এবং তাদের প্রকারগুলি ঘোষণা করার পাশাপাশি, এক্সটেনশন প্রকারগুলি হতে পারে:

  • ডিফল্ট মুদ্রণযোগ্য উপস্থাপনা ( __repr__ ) ওভাররাইড করুন।
  • পদ্ধতির সংজ্ঞা দাও।
  • শ্রেণিপদ্ধতি এবং স্ট্যাটিক পদ্ধতির সংজ্ঞা দাও।
  • বৈশিষ্ট্য সংজ্ঞায়িত করুন।
  • ডিফল্ট কনস্ট্রাক্টর ( __init__ ) ওভাররাইড করুন।
  • ডিফল্ট সমতা অপারেটর ( __eq__ ) ওভাররাইড করুন।
  • অপারেটর সংজ্ঞায়িত করুন (যেমন __add__ এবং __lt__ )।
  • ক্ষেত্রগুলির জন্য ডিফল্ট মান ঘোষণা করুন।
  • উপশ্রেণীর সংজ্ঞা দাও।

ডিফল্ট মুদ্রণযোগ্য উপস্থাপনা ওভাররাইড করা হচ্ছে

আপনি এক্সটেনশন প্রকারের জন্য এই ডিফল্ট স্ট্রিং রূপান্তর অপারেটর ওভাররাইড করতে পারেন। নিম্নোক্ত উদাহরণটি MaskedTensor ক্লাস আপডেট করে একটি আরও পঠনযোগ্য স্ট্রিং উপস্থাপনা তৈরি করার জন্য যখন মানগুলি ইজার মোডে মুদ্রিত হয়।

class MaskedTensor(tf.experimental.ExtensionType):
  """A tensor paired with a boolean mask, indicating which values are valid."""
  values: tf.Tensor
  mask: tf.Tensor       # shape=values.shape; false for invalid values.

  def __repr__(self):
    return masked_tensor_str(self.values, self.mask)

def masked_tensor_str(values, mask):
  if isinstance(values, tf.Tensor):
    if hasattr(values, 'numpy') and hasattr(mask, 'numpy'):
      return f'<MaskedTensor {masked_tensor_str(values.numpy(), mask.numpy())}>'
    else:
      return f'MaskedTensor(values={values}, mask={mask})'
  if len(values.shape) == 1:
    items = [repr(v) if m else '_' for (v, m) in zip(values, mask)]
  else:
    items = [masked_tensor_str(v, m) for (v, m) in zip(values, mask)]
  return '[%s]' % ', '.join(items)

mt = MaskedTensor(values=[[1, 2, 3], [4, 5, 6]],
                  mask=[[True, True, False], [True, False, True]])
print(mt)
<MaskedTensor [[1, 2, _], [4, _, 6]]>

সংজ্ঞায়িত পদ্ধতি

এক্সটেনশন প্রকারগুলি পদ্ধতিগুলি সংজ্ঞায়িত করতে পারে, ঠিক যে কোনও সাধারণ পাইথন ক্লাসের মতো। উদাহরণস্বরূপ, MaskedTensor প্রকার একটি with_default পদ্ধতি নির্ধারণ করতে পারে যা একটি প্রদত্ত default মান দ্বারা প্রতিস্থাপিত মুখোশযুক্ত মান সহ self -এর একটি অনুলিপি প্রদান করে। পদ্ধতিগুলি ঐচ্ছিকভাবে @tf.function ডেকোরেটরের সাথে টীকা করা যেতে পারে।

class MaskedTensor(tf.experimental.ExtensionType):
  values: tf.Tensor
  mask: tf.Tensor

  def with_default(self, default):
    return tf.where(self.mask, self.values, default)

MaskedTensor([1, 2, 3], [True, False, True]).with_default(0)
<tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 0, 3], dtype=int32)>

ক্লাস পদ্ধতি এবং স্ট্যাটিক পদ্ধতি সংজ্ঞায়িত করা

এক্সটেনশন প্রকারগুলি @classmethod এবং @staticmethod ডেকোরেটর ব্যবহার করে পদ্ধতিগুলিকে সংজ্ঞায়িত করতে পারে। উদাহরণস্বরূপ, MaskedTensor প্রকার একটি ফ্যাক্টরি পদ্ধতি সংজ্ঞায়িত করতে পারে যা একটি প্রদত্ত মান সহ যেকোনো উপাদানকে মাস্ক করে:

class MaskedTensor(tf.experimental.ExtensionType):
  values: tf.Tensor
  mask: tf.Tensor

  def __repr__(self):
    return masked_tensor_str(self.values, self.mask)

  @staticmethod
  def from_tensor_and_value_to_mask(values, value_to_mask):
    return MaskedTensor(values, values == value_to_mask)

x = tf.constant([[1, 0, 2], [3, 0, 0]])
MaskedTensor.from_tensor_and_value_to_mask(x, 0)
<MaskedTensor [[_, 0, _], [_, 0, 0]]>

বৈশিষ্ট্য সংজ্ঞায়িত করা

এক্সটেনশন প্রকারগুলি @property ডেকোরেটর ব্যবহার করে বৈশিষ্ট্যগুলিকে সংজ্ঞায়িত করতে পারে, ঠিক যে কোনও সাধারণ পাইথন ক্লাসের মতো। উদাহরণস্বরূপ, MaskedTensor টাইপ একটি dtype সম্পত্তি সংজ্ঞায়িত করতে পারে যা মানগুলির dtype-এর জন্য একটি সংক্ষিপ্ত বিবরণ:

class MaskedTensor(tf.experimental.ExtensionType):
  values: tf.Tensor
  mask: tf.Tensor

  @property
  def dtype(self):
    return self.values.dtype

MaskedTensor([1, 2, 3], [True, False, True]).dtype
tf.int32

ডিফল্ট কনস্ট্রাক্টরকে ওভাররাইড করা হচ্ছে

আপনি এক্সটেনশন প্রকারের জন্য ডিফল্ট কনস্ট্রাক্টর ওভাররাইড করতে পারেন। কাস্টম কনস্ট্রাক্টরদের অবশ্যই প্রতিটি ঘোষিত ক্ষেত্রের জন্য একটি মান সেট করতে হবে; এবং কাস্টম কনস্ট্রাক্টর ফিরে আসার পরে, সমস্ত ক্ষেত্র টাইপ-চেক করা হবে, এবং মান উপরে বর্ণিত হিসাবে রূপান্তরিত হবে।

class Toy(tf.experimental.ExtensionType):
  name: str
  price: tf.Tensor
  def __init__(self, name, price, discount=0):
    self.name = name
    self.price = price * (1 - discount)

print(Toy("ball", 5.0, discount=0.2))  # On sale -- 20% off!
Toy(name='ball', price=<tf.Tensor: shape=(), dtype=float32, numpy=4.0>)

বিকল্পভাবে, আপনি ডিফল্ট কনস্ট্রাক্টরটিকে যেমন আছে-এভাবে ছেড়ে দেওয়ার কথা বিবেচনা করতে পারেন, তবে এক বা একাধিক কারখানার পদ্ধতি যোগ করতে পারেন। যেমন:

class Toy(tf.experimental.ExtensionType):
  name: str
  price: tf.Tensor

  @staticmethod
  def new_toy_with_discount(name, price, discount):
    return Toy(name, price * (1 - discount))

print(Toy.new_toy_with_discount("ball", 5.0, discount=0.2))
Toy(name='ball', price=<tf.Tensor: shape=(), dtype=float32, numpy=4.0>)

ডিফল্ট সমতা অপারেটরকে ওভাররাইড করা হচ্ছে ( __eq__ )

আপনি এক্সটেনশন প্রকারের জন্য ডিফল্ট __eq__ অপারেটর ওভাররাইড করতে পারেন। সমতার জন্য তুলনা করার সময় নিম্নলিখিত উদাহরণটি মাস্কড এলিমেন্টকে উপেক্ষা করার জন্য MaskedTensor আপডেট করে।

class MaskedTensor(tf.experimental.ExtensionType):
  values: tf.Tensor
  mask: tf.Tensor

  def __repr__(self):
    return masked_tensor_str(self.values, self.mask)

  def __eq__(self, other):
    result = tf.math.equal(self.values, other.values)
    result = result | ~(self.mask & other.mask)
    return tf.reduce_all(result)

x = MaskedTensor([1, 2, 3, 4], [True, True, False, True])
y = MaskedTensor([5, 2, 0, 4], [False, True, False, True])
print(x == y)
tf.Tensor(True, shape=(), dtype=bool)

ফরোয়ার্ড রেফারেন্স ব্যবহার করে

যদি একটি ক্ষেত্রের ধরন এখনও সংজ্ঞায়িত করা না হয়, তাহলে আপনি পরিবর্তে টাইপের নাম ধারণকারী একটি স্ট্রিং ব্যবহার করতে পারেন। নিম্নলিখিত উদাহরণে, "Node" স্ট্রিংটি children ক্ষেত্রে টীকা করতে ব্যবহৃত হয় কারণ Node ধরনটি এখনও (সম্পূর্ণ) সংজ্ঞায়িত করা হয়নি।

class Node(tf.experimental.ExtensionType):
  value: tf.Tensor
  children: Tuple["Node", ...] = ()

Node(3, [Node(5), Node(2)])
Node(value=<tf.Tensor: shape=(), dtype=int32, numpy=3>, children=(Node(value=<tf.Tensor: shape=(), dtype=int32, numpy=5>, children=()), Node(value=<tf.Tensor: shape=(), dtype=int32, numpy=2>, children=())))

সাবক্লাস সংজ্ঞায়িত করা

স্ট্যান্ডার্ড পাইথন সিনট্যাক্স ব্যবহার করে এক্সটেনশনের ধরনগুলি সাবক্লাস করা যেতে পারে। এক্সটেনশন টাইপ সাবক্লাস নতুন ক্ষেত্র, পদ্ধতি, এবং বৈশিষ্ট্য যোগ করতে পারে; এবং কনস্ট্রাক্টর, মুদ্রণযোগ্য উপস্থাপনা এবং সমতা অপারেটরকে ওভাররাইড করতে পারে। নিম্নলিখিত উদাহরণটি একটি মৌলিক TensorGraph ক্লাস সংজ্ঞায়িত করে যা নোডের মধ্যে প্রান্তগুলির একটি সেট এনকোড করতে তিনটি Tensor ক্ষেত্র ব্যবহার করে। এটি তারপর একটি সাবক্লাস সংজ্ঞায়িত করে যা প্রতিটি নোডের জন্য একটি "বৈশিষ্ট্য মান" রেকর্ড করতে একটি Tensor ক্ষেত্র যোগ করে। উপশ্রেণীটি প্রান্ত বরাবর বৈশিষ্ট্যের মানগুলি প্রচার করার একটি পদ্ধতিও সংজ্ঞায়িত করে।

class TensorGraph(tf.experimental.ExtensionType):
  num_nodes: tf.Tensor
  edge_src: tf.Tensor   # edge_src[e] = index of src node for edge e.
  edge_dst: tf.Tensor   # edge_dst[e] = index of dst node for edge e.

class TensorGraphWithNodeFeature(TensorGraph):
  node_features: tf.Tensor  # node_features[n] = feature value for node n.

  def propagate_features(self, weight=1.0) -> 'TensorGraphWithNodeFeature':
    updates = tf.gather(self.node_features, self.edge_src) * weight
    new_node_features = tf.tensor_scatter_nd_add(
        self.node_features, tf.expand_dims(self.edge_dst, 1), updates)
    return TensorGraphWithNodeFeature(
        self.num_nodes, self.edge_src, self.edge_dst, new_node_features)

g = TensorGraphWithNodeFeature(  # Edges: 0->1, 4->3, 2->2, 2->1
    num_nodes=5, edge_src=[0, 4, 2, 2], edge_dst=[1, 3, 2, 1],
    node_features=[10.0, 0.0, 2.0, 5.0, -1.0, 0.0])

print("Original features:", g.node_features)
print("After propagating:", g.propagate_features().node_features)
Original features: tf.Tensor([10.  0.  2.  5. -1.  0.], shape=(6,), dtype=float32)
After propagating: tf.Tensor([10. 12.  4.  4. -1.  0.], shape=(6,), dtype=float32)

ব্যক্তিগত ক্ষেত্র সংজ্ঞায়িত করা

একটি এক্সটেনশন প্রকারের ক্ষেত্রগুলিকে একটি আন্ডারস্কোর (স্ট্যান্ডার্ড পাইথন নিয়ম অনুসরণ করে) দিয়ে প্রিফিক্স করে ব্যক্তিগত চিহ্নিত করা যেতে পারে। এটি টেনসরফ্লো যেভাবে ক্ষেত্রগুলির সাথে আচরণ করে তা প্রভাবিত করে না; কিন্তু এক্সটেনশন টাইপের যেকোন ব্যবহারকারীর কাছে সহজভাবে একটি সংকেত হিসাবে কাজ করে যে সেই ক্ষেত্রগুলি ব্যক্তিগত।

ExtensionType এর TypeSpec কাস্টমাইজ করা

প্রতিটি ExtensionType ক্লাসে একটি সংশ্লিষ্ট TypeSpec ক্লাস থাকে, যেটি স্বয়ংক্রিয়ভাবে তৈরি হয় এবং <extension_type_name>.Spec হিসেবে সংরক্ষণ করা হয়। আরও তথ্যের জন্য, উপরের "নেস্টেড টাইপস্পেক" বিভাগটি দেখুন।

TypeSpec কাস্টমাইজ করতে, শুধু Spec নামে আপনার নিজস্ব নেস্টেড ক্লাস সংজ্ঞায়িত করুন, এবং ExtensionType এটি স্বয়ংক্রিয়ভাবে নির্মিত TypeSpec এর ভিত্তি হিসাবে ব্যবহার করবে। আপনি এর দ্বারা Spec ক্লাস কাস্টমাইজ করতে পারেন:

  • ডিফল্ট মুদ্রণযোগ্য উপস্থাপনা ওভাররাইড করা হচ্ছে।
  • ডিফল্ট কনস্ট্রাক্টরকে ওভাররাইড করা হচ্ছে।
  • পদ্ধতি, শ্রেণিপদ্ধতি, স্ট্যাটিক পদ্ধতি এবং বৈশিষ্ট্য সংজ্ঞায়িত করা।

নিম্নলিখিত উদাহরণটি ব্যবহার করা সহজ করতে MaskedTensor.Spec ক্লাস কাস্টমাইজ করে:

class MaskedTensor(tf.experimental.ExtensionType):
  values: tf.Tensor
  mask: tf.Tensor

  shape = property(lambda self: self.values.shape)
  dtype = property(lambda self: self.values.dtype)

  def __repr__(self):
    return masked_tensor_str(self.values, self.mask)

  def with_values(self, new_values):
    return MaskedTensor(new_values, self.mask)

  class Spec:
    def __init__(self, shape, dtype=tf.float32):
      self.values = tf.TensorSpec(shape, dtype)
      self.mask = tf.TensorSpec(shape, tf.bool)

    def __repr__(self):
      return f"MaskedTensor.Spec(shape={self.shape}, dtype={self.dtype})"

    shape = property(lambda self: self.values.shape)
    dtype = property(lambda self: self.values.dtype)

টেনসর API প্রেরণ

এক্সটেনশনের ধরন "টেনসরের মতো" হতে পারে, এই অর্থে যে তারা tf.Tensor টাইপ দ্বারা সংজ্ঞায়িত ইন্টারফেসটিকে বিশেষায়িত বা প্রসারিত করে। টেনসর-সদৃশ এক্সটেনশন প্রকারের উদাহরণগুলির মধ্যে রয়েছে RaggedTensor , SparseTensor , এবং MaskedTensor । টেনসর-সদৃশ এক্সটেনশন প্রকারে প্রয়োগ করার সময় টেনসরফ্লো অপারেশনগুলির ডিফল্ট আচরণকে ওভাররাইড করতে ডিসপ্যাচ ডেকোরেটর ব্যবহার করা যেতে পারে। TensorFlow বর্তমানে তিনটি প্রেরণ ডেকোরেটর সংজ্ঞায়িত করে:

একটি একক API-এর জন্য প্রেরণ

tf.experimental.dispatch_for_api ডেকোরেটর একটি নির্দিষ্ট TensorFlow অপারেশনের ডিফল্ট আচরণকে ওভাররাইড করে যখন এটিকে নির্দিষ্ট স্বাক্ষর সহ কল ​​করা হয়। উদাহরণ স্বরূপ, tf.stack কিভাবে tf.stack মান প্রক্রিয়া MaskedTensor তা উল্লেখ করতে আপনি এই ডেকোরেটর ব্যবহার করতে পারেন:

@tf.experimental.dispatch_for_api(tf.stack)
def masked_stack(values: List[MaskedTensor], axis = 0):
  return MaskedTensor(tf.stack([v.values for v in values], axis),
                      tf.stack([v.mask for v in values], axis))

যখনই এটিকে MaskedTensor মানগুলির একটি তালিকা দিয়ে ডাকা হয় তখন এটি tf.stack এর জন্য ডিফল্ট বাস্তবায়নকে ওভাররাইড করে (যেহেতু values আর্গুমেন্টটি টাইপিং-এর সাথে টীকা করা হয় typing.List[MaskedTensor] ):

x = MaskedTensor([1, 2, 3], [True, True, False])
y = MaskedTensor([4, 5, 6], [False, True, True])
tf.stack([x, y])
<MaskedTensor [[1, 2, _], [_, 5, 6]]>

tf.stack কে মিশ্র MaskedTensor এবং Tensor মানগুলির তালিকা পরিচালনা করার অনুমতি দেওয়ার জন্য, আপনি values প্যারামিটারের জন্য টাইপ টীকাটি পরিমার্জন করতে পারেন এবং ফাংশনের মূল অংশটি যথাযথভাবে আপডেট করতে পারেন:

tf.experimental.unregister_dispatch_for(masked_stack)

def convert_to_masked_tensor(x):
  if isinstance(x, MaskedTensor):
    return x
  else:
    return MaskedTensor(x, tf.ones_like(x, tf.bool))

@tf.experimental.dispatch_for_api(tf.stack)
def masked_stack_v2(values: List[Union[MaskedTensor, tf.Tensor]], axis = 0):
  values = [convert_to_masked_tensor(v) for v in values]
  return MaskedTensor(tf.stack([v.values for v in values], axis),
                      tf.stack([v.mask for v in values], axis))
x = MaskedTensor([1, 2, 3], [True, True, False])
y = tf.constant([4, 5, 6])
tf.stack([x, y, x])
<MaskedTensor [[1, 2, _], [4, 5, 6], [1, 2, _]]>

ওভাররাইড করা যেতে পারে এমন APIগুলির একটি তালিকার জন্য, tf.experimental.dispatch_for_api এর API ডকুমেন্টেশন দেখুন।

সমস্ত unary elementwise API-এর জন্য প্রেরণ

tf.experimental.dispatch_for_unary_elementwise_apis ডেকোরেটর সমস্ত unary elementwise ops (যেমন tf.math.cos ) এর ডিফল্ট আচরণকে ওভাররাইড করে যখনই প্রথম আর্গুমেন্টের (সাধারণত নাম x ) টাইপ টীকা x_type এর সাথে মেলে। সজ্জিত ফাংশন দুটি আর্গুমেন্ট নিতে হবে:

  • api_func : একটি ফাংশন যা একটি একক প্যারামিটার নেয় এবং উপাদান অনুযায়ী কাজ করে (যেমন, tf.abs )।
  • x : এলিমেন্টওয়াইজ অপারেশনের প্রথম আর্গুমেন্ট।

নিম্নলিখিত উদাহরণটি MaskedTensor ধরন পরিচালনা করার জন্য সমস্ত ইউনারি এলিমেন্টওয়াইজ অপারেশন আপডেট করে:

@tf.experimental.dispatch_for_unary_elementwise_apis(MaskedTensor)
 def masked_tensor_unary_elementwise_api_handler(api_func, x):
   return MaskedTensor(api_func(x.values), x.mask)

এই ফাংশনটি এখন ব্যবহার করা হবে যখনই একটি MaskedTensor এ একটি unary elementwise অপারেশন কল করা হবে।

x = MaskedTensor([1, -2, -3], [True, False, True])
 print(tf.abs(x))
<MaskedTensor [1, _, 3]>
print(tf.ones_like(x, dtype=tf.float32))
<MaskedTensor [1.0, _, 1.0]>

বাইনারি সব elementwise API-এর জন্য প্রেরণ করুন

একইভাবে, tf.experimental.dispatch_for_binary_elementwise_apis ব্যবহার করা যেতে পারে MaskedTensor টাইপ পরিচালনা করার জন্য সমস্ত বাইনারি এলিমেন্টওয়াইজ অপারেশন আপডেট করতে:

@tf.experimental.dispatch_for_binary_elementwise_apis(MaskedTensor, MaskedTensor)
def masked_tensor_binary_elementwise_api_handler(api_func, x, y):
  return MaskedTensor(api_func(x.values, y.values), x.mask & y.mask)
x = MaskedTensor([1, -2, -3], [True, False, True])
y = MaskedTensor([[4], [5]], [[True], [False]])
tf.math.add(x, y)
<MaskedTensor [[5, _, 1], [_, _, _]]>

ওভাররাইড করা elementwise APIগুলির একটি তালিকার জন্য, tf.experimental.dispatch_for_unary_elementwise_apis এবং tf.experimental.dispatch_for_binary_elementwise_apis এর API ডকুমেন্টেশন দেখুন।

ব্যাচযোগ্য এক্সটেনশন প্রকার

একটি ExtensionType টাইপ ব্যাচযোগ্য যদি একটি একক দৃষ্টান্ত মানগুলির একটি ব্যাচকে উপস্থাপন করতে ব্যবহার করা যায়। সাধারণত, সমস্ত নেস্টেড Tensor s-এ ব্যাচের মাত্রা যোগ করে এটি সম্পন্ন করা হয়। নিম্নলিখিত টেনসরফ্লো এপিআইগুলির প্রয়োজন যে কোনও এক্সটেনশন প্রকারের ইনপুট ব্যাচযোগ্য হতে হবে:

ডিফল্টরূপে, BatchableExtensionType যেকোনো নেস্টেড Tensor , CompositeTensor টেনসর, এবং ExtensionType টাইপগুলি ব্যাচ করে ব্যাচ করা মান তৈরি করে। যদি এটি আপনার ক্লাসের জন্য উপযুক্ত না হয়, তাহলে এই ডিফল্ট আচরণকে ওভাররাইড করতে আপনাকে tf.experimental.ExtensionTypeBatchEncoder ব্যবহার করতে হবে। উদাহরণস্বরূপ, পৃথক পৃথক স্পার্স টেনসরের values , indices এবং dense_shape ক্ষেত্রগুলিকে স্ট্যাক করার মাধ্যমে tf.SparseTensor মানগুলির একটি ব্যাচ তৈরি করা উপযুক্ত হবে না -- বেশিরভাগ ক্ষেত্রে, আপনি এই টেনসরগুলিকে স্ট্যাক করতে পারবেন না, যেহেতু তাদের বেমানান আকার রয়েছে ; এবং এমনকি যদি আপনি করতে পারেন, ফলাফল একটি বৈধ SparseTensor হবে না।

ব্যাচযোগ্য এক্সটেনশন টাইপ উদাহরণ: নেটওয়ার্ক

একটি উদাহরণ হিসাবে, লোড ব্যালেন্সিংয়ের জন্য ব্যবহৃত একটি সাধারণ Network ক্লাস বিবেচনা করুন, যা প্রতিটি নোডে কতটা কাজ বাকি আছে এবং নোডের মধ্যে কাজ সরানোর জন্য কত ব্যান্ডউইথ উপলব্ধ রয়েছে তা ট্র্যাক করে:

class Network(tf.experimental.ExtensionType):  # This version is not batchable.
  work: tf.Tensor       # work[n] = work left to do at node n
  bandwidth: tf.Tensor  # bandwidth[n1, n2] = bandwidth from n1->n2

net1 = Network([5., 3, 8], [[0., 2, 0], [2, 0, 3], [0, 3, 0]])
net2 = Network([3., 4, 2], [[0., 2, 2], [2, 0, 2], [2, 2, 0]])

এই টাইপটিকে ব্যাচযোগ্য করতে, বেস টাইপটিকে BatchableExtensionType এ পরিবর্তন করুন এবং ঐচ্ছিক ব্যাচের মাত্রা অন্তর্ভুক্ত করতে প্রতিটি ক্ষেত্রের আকৃতি সামঞ্জস্য করুন। নিম্নলিখিত উদাহরণটি ব্যাচের আকারের ট্র্যাক রাখার জন্য একটি shape ক্ষেত্র যোগ করে। এই shape ক্ষেত্রটি tf.data.Dataset বা tf.map_fn দ্বারা প্রয়োজন হয় না, তবে এটি tf.Keras দ্বারা প্রয়োজন হয়

class Network(tf.experimental.BatchableExtensionType):
  shape: tf.TensorShape  # batch shape.  A single network has shape=[].
  work: tf.Tensor        # work[*shape, n] = work left to do at node n
  bandwidth: tf.Tensor   # bandwidth[*shape, n1, n2] = bandwidth from n1->n2

  def __init__(self, work, bandwidth):
    self.work = tf.convert_to_tensor(work)
    self.bandwidth = tf.convert_to_tensor(bandwidth)
    work_batch_shape = self.work.shape[:-1]
    bandwidth_batch_shape = self.bandwidth.shape[:-2]
    self.shape = work_batch_shape.merge_with(bandwidth_batch_shape)

  def __repr__(self):
    return network_repr(self)

def network_repr(network):
  work = network.work
  bandwidth = network.bandwidth
  if hasattr(work, 'numpy'):
    work = ' '.join(str(work.numpy()).split())
  if hasattr(bandwidth, 'numpy'):
    bandwidth = ' '.join(str(bandwidth.numpy()).split())
  return (f"<Network shape={network.shape} work={work} bandwidth={bandwidth}>")
net1 = Network([5., 3, 8], [[0., 2, 0], [2, 0, 3], [0, 3, 0]])
net2 = Network([3., 4, 2], [[0., 2, 2], [2, 0, 2], [2, 2, 0]])
batch_of_networks = Network(
    work=tf.stack([net1.work, net2.work]),
    bandwidth=tf.stack([net1.bandwidth, net2.bandwidth]))
print(f"net1={net1}")
print(f"net2={net2}")
print(f"batch={batch_of_networks}")
net1=<Network shape=() work=[5. 3. 8.] bandwidth=[[0. 2. 0.] [2. 0. 3.] [0. 3. 0.]]>
net2=<Network shape=() work=[3. 4. 2.] bandwidth=[[0. 2. 2.] [2. 0. 2.] [2. 2. 0.]]>
batch=<Network shape=(2,) work=[[5. 3. 8.] [3. 4. 2.]] bandwidth=[[[0. 2. 0.] [2. 0. 3.] [0. 3. 0.]] [[0. 2. 2.] [2. 0. 2.] [2. 2. 0.]]]>

তারপরে আপনি নেটওয়ার্কের ব্যাচের মাধ্যমে পুনরাবৃত্তি করতে tf.data.Dataset ব্যবহার করতে পারেন:

dataset = tf.data.Dataset.from_tensor_slices(batch_of_networks)
for i, network in enumerate(dataset):
  print(f"Batch element {i}: {network}")
Batch element 0: <Network shape=() work=[5. 3. 8.] bandwidth=[[0. 2. 0.] [2. 0. 3.] [0. 3. 0.]]>
Batch element 1: <Network shape=() work=[3. 4. 2.] bandwidth=[[0. 2. 2.] [2. 0. 2.] [2. 2. 0.]]>

এবং আপনি প্রতিটি ব্যাচ উপাদানে একটি ফাংশন প্রয়োগ করতে map_fn ব্যবহার করতে পারেন:

def balance_work_greedy(network):
  delta = (tf.expand_dims(network.work, -1) - tf.expand_dims(network.work, -2))
  delta /= 4
  delta = tf.maximum(tf.minimum(delta, network.bandwidth), -network.bandwidth)
  new_work = network.work + tf.reduce_sum(delta, -1)
  return Network(new_work, network.bandwidth)

tf.map_fn(balance_work_greedy, batch_of_networks)
<Network shape=(2,) work=[[5.5 1.25 9.25] [3. 4.75 1.25]] bandwidth=[[[0. 2. 0.] [2. 0. 3.] [0. 3. 0.]] [[0. 2. 2.] [2. 0. 2.] [2. 2. 0.]]]>

টেনসরফ্লো এপিআই যা এক্সটেনশন টাইপ সমর্থন করে

@tf.function

tf.function হল একটি ডেকোরেটর যা Python ফাংশনগুলির জন্য TensorFlow গ্রাফগুলিকে প্রি-কম্পিউট করে, যা আপনার TensorFlow কোডের কার্যকারিতা উল্লেখযোগ্যভাবে উন্নত করতে পারে। এক্সটেনশন প্রকারের মানগুলি @tf.function সজ্জিত ফাংশনগুলির সাথে স্বচ্ছভাবে ব্যবহার করা যেতে পারে।

class Pastry(tf.experimental.ExtensionType):
  sweetness: tf.Tensor  # 2d embedding that encodes sweetness
  chewiness: tf.Tensor  # 2d embedding that encodes chewiness

@tf.function
def combine_pastry_features(x: Pastry):
  return (x.sweetness + x.chewiness) / 2

cookie = Pastry(sweetness=[1.2, 0.4], chewiness=[0.8, 0.2])
combine_pastry_features(cookie)
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([1. , 0.3], dtype=float32)>

আপনি যদি input_signature এর জন্য tf.function স্পষ্টভাবে উল্লেখ করতে চান, তাহলে আপনি এক্সটেনশন প্রকারের TypeSpec ব্যবহার করে তা করতে পারেন।

pastry_spec = Pastry.Spec(tf.TensorSpec([2]), tf.TensorSpec(2))

@tf.function(input_signature=[pastry_spec])
def increase_sweetness(x: Pastry, delta=1.0):
  return Pastry(x.sweetness + delta, x.chewiness)

increase_sweetness(cookie)
Pastry(sweetness=<tf.Tensor: shape=(2,), dtype=float32, numpy=array([2.2, 1.4], dtype=float32)>, chewiness=<tf.Tensor: shape=(2,), dtype=float32, numpy=array([0.8, 0.2], dtype=float32)>)

কংক্রিট ফাংশন

কংক্রিট ফাংশন tf.function দ্বারা নির্মিত পৃথক ট্রেস করা গ্রাফগুলিকে এনক্যাপসুলেট করে। এক্সটেনশন প্রকারগুলি কংক্রিট ফাংশনগুলির সাথে স্বচ্ছভাবে ব্যবহার করা যেতে পারে।

cf = combine_pastry_features.get_concrete_function(pastry_spec)
cf(cookie)
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([1. , 0.3], dtype=float32)>

নিয়ন্ত্রণ প্রবাহ অপারেশন

এক্সটেনশন প্রকারগুলি TensorFlow-এর কন্ট্রোল-ফ্লো অপারেশন দ্বারা সমর্থিত:

# Example: using tf.cond to select between two MaskedTensors.  Note that the
# two MaskedTensors don't need to have the same shape.
a = MaskedTensor([1., 2, 3], [True, False, True])
b = MaskedTensor([22., 33, 108, 55], [True, True, True, False])
condition = tf.constant(True)
print(tf.cond(condition, lambda: a, lambda: b))
<MaskedTensor [1.0, _, 3.0]>
# Example: using tf.while_loop with MaskedTensor.
cond = lambda i, _: i < 10
def body(i, mt):
  return i + 1, mt.with_values(mt.values + 3 / 7)
print(tf.while_loop(cond, body, [0, b])[1])
<MaskedTensor [26.285717, 37.285698, 112.285736, _]>

অটোগ্রাফ নিয়ন্ত্রণ প্রবাহ

এক্সটেনশন প্রকারগুলি tf.function-এ (অটোগ্রাফ ব্যবহার করে) নিয়ন্ত্রণ প্রবাহ বিবৃতি দ্বারা সমর্থিত। নিম্নলিখিত উদাহরণে, if স্টেটমেন্ট এবং for স্টেটমেন্ট স্বয়ংক্রিয়ভাবে tf.cond এবং tf.while_loop অপারেশনে রূপান্তরিত হয়, যা এক্সটেনশন প্রকারগুলিকে সমর্থন করে।

@tf.function
def fn(x, b):
  if b:
    x = MaskedTensor(x, tf.less(x, 0))
  else:
    x = MaskedTensor(x, tf.greater(x, 0))
  for i in tf.range(5 if b else 7):
    x = x.with_values(x.values + 1 / 2)
  return x

print(fn(tf.constant([1., -2, 3]), tf.constant(True)))
print(fn(tf.constant([1., -2, 3]), tf.constant(False)))
<MaskedTensor [_, 0.5, _]>
<MaskedTensor [4.5, _, 6.5]>

কেরাস

tf.keras হল TensorFlow-এর উচ্চ-স্তরের API যা গভীর শিক্ষার মডেল তৈরি ও প্রশিক্ষণের জন্য। এক্সটেনশনের ধরনগুলি কেরাস মডেলে ইনপুট হিসাবে পাস করা যেতে পারে, কেরাস স্তরগুলির মধ্যে পাস করা যেতে পারে এবং কেরাস মডেল দ্বারা ফেরত দেওয়া হতে পারে। কেরাস বর্তমানে এক্সটেনশন প্রকারের জন্য দুটি প্রয়োজনীয়তা রাখে:

  • এগুলি অবশ্যই ব্যাচযোগ্য হতে হবে (উপরে "ব্যাচযোগ্য এক্সটেনশন টাইপস" দেখুন)।
  • shape নামে একটি ক্ষেত্র বা সম্পত্তি থাকতে হবে। shape[0] কে ব্যাচের মাত্রা বলে ধরে নেওয়া হয়।

নিচের দুটি উপবিভাগ উদাহরণ দেয় যে কিভাবে এক্সটেনশনের ধরন কেরাসের সাথে ব্যবহার করা যেতে পারে।

কেরাস উদাহরণ: Network

প্রথম উদাহরণের জন্য, উপরের "ব্যাচযোগ্য এক্সটেনশন টাইপস" বিভাগে সংজ্ঞায়িত Network ক্লাস বিবেচনা করুন, যা নোডের মধ্যে লোড ব্যালেন্সিং কাজের জন্য ব্যবহার করা যেতে পারে। এর সংজ্ঞা এখানে পুনরাবৃত্তি করা হয়েছে:

class Network(tf.experimental.BatchableExtensionType):
  shape: tf.TensorShape  # batch shape.  A single network has shape=[].
  work: tf.Tensor        # work[*shape, n] = work left to do at node n
  bandwidth: tf.Tensor   # bandwidth[*shape, n1, n2] = bandwidth from n1->n2

  def __init__(self, work, bandwidth):
    self.work = tf.convert_to_tensor(work)
    self.bandwidth = tf.convert_to_tensor(bandwidth)
    work_batch_shape = self.work.shape[:-1]
    bandwidth_batch_shape = self.bandwidth.shape[:-2]
    self.shape = work_batch_shape.merge_with(bandwidth_batch_shape)

  def __repr__(self):
    return network_repr(self)
single_network = Network(  # A single network w/ 4 nodes.
    work=[8.0, 5, 12, 2],
    bandwidth=[[0.0, 1, 2, 2], [1, 0, 0, 2], [2, 0, 0, 1], [2, 2, 1, 0]])

batch_of_networks = Network(  # Batch of 2 networks, each w/ 2 nodes.
    work=[[8.0, 5], [3, 2]],
    bandwidth=[[[0.0, 1], [1, 0]], [[0, 2], [2, 0]]])

আপনি একটি নতুন কেরাস স্তর সংজ্ঞায়িত করতে পারেন যা Network s প্রক্রিয়া করে।

class BalanceNetworkLayer(tf.keras.layers.Layer):
  """Layer that balances work between nodes in a network.

  Shifts work from more busy nodes to less busy nodes, constrained by bandwidth.
  """
  def call(self, inputs):
    # This function is defined above, in "Batchable ExtensionTypes" section.
    return balance_work_greedy(inputs)

তারপরে আপনি একটি সাধারণ মডেল তৈরি করতে এই স্তরগুলি ব্যবহার করতে পারেন। একটি মডেলে একটি ExtensionType খাওয়ানোর জন্য, আপনি একটি tf.keras.layer.Input .ইনপুট লেয়ার ব্যবহার করতে পারেন যাতে TypeSpec এক্সটেনশন টাইপের type_spec এ সেট করা হয়। যদি কেরাস মডেলটি ব্যাচ প্রক্রিয়া করতে ব্যবহার করা হয়, তাহলে type_spec ব্যাচের মাত্রা অন্তর্ভুক্ত করতে হবে।

input_spec = Network.Spec(shape=None,
                          work=tf.TensorSpec(None, tf.float32),
                          bandwidth=tf.TensorSpec(None, tf.float32))
model = tf.keras.Sequential([
    tf.keras.layers.Input(type_spec=input_spec),
    BalanceNetworkLayer(),
    ])

অবশেষে, আপনি একটি একক নেটওয়ার্কে এবং নেটওয়ার্কের একটি ব্যাচে মডেলটি প্রয়োগ করতে পারেন৷

model(single_network)
<Network shape=() work=[ 9.25 5. 14. -1.25] bandwidth=[[0. 1. 2. 2.] [1. 0. 0. 2.] [2. 0. 0. 1.] [2. 2. 1. 0.]]>
model(batch_of_networks)
<Network shape=(2,) work=[[8.75 4.25] [3.25 1.75]] bandwidth=[[[0. 1.] [1. 0.]] [[0. 2.] [2. 0.]]]>

কেরাস উদাহরণ: মাস্কডটেনসর

এই উদাহরণে, MaskedTensor Keras সমর্থন করার জন্য প্রসারিত করা হয়েছে। shape একটি সম্পত্তি হিসাবে সংজ্ঞায়িত করা হয় যা values ক্ষেত্র থেকে গণনা করা হয়। কেরাসের জন্য প্রয়োজন যে আপনি এই প্রপার্টিটি এক্সটেনশন টাইপ এবং এর TypeSpec উভয় ক্ষেত্রেই যোগ করুন। MaskedTensor একটি __name__ ভেরিয়েবলকেও সংজ্ঞায়িত করে, যা SavedModel জন্য প্রয়োজন হবে (নীচে)।

class MaskedTensor(tf.experimental.BatchableExtensionType):
  # __name__ is required for serialization in SavedModel; see below for details.
  __name__ = 'extension_type_colab.MaskedTensor'

  values: tf.Tensor
  mask: tf.Tensor

  shape = property(lambda self: self.values.shape)
  dtype = property(lambda self: self.values.dtype)

  def with_default(self, default):
    return tf.where(self.mask, self.values, default)

  def __repr__(self):
    return masked_tensor_str(self.values, self.mask)

  class Spec:
    def __init__(self, shape, dtype=tf.float32):
      self.values = tf.TensorSpec(shape, dtype)
      self.mask = tf.TensorSpec(shape, tf.bool)

    shape = property(lambda self: self.values.shape)
    dtype = property(lambda self: self.values.dtype)

    def with_shape(self):
      return MaskedTensor.Spec(tf.TensorSpec(shape, self.values.dtype),
                               tf.TensorSpec(shape, self.mask.dtype))

এরপরে, ডিসপ্যাচ ডেকোরেটরগুলি বেশ কয়েকটি TensorFlow API-এর ডিফল্ট আচরণ ওভাররাইড করতে ব্যবহৃত হয়। যেহেতু এই এপিআইগুলি স্ট্যান্ডার্ড কেরাস স্তরগুলি (যেমন Dense স্তর) দ্বারা ব্যবহৃত হয়, তাই এগুলিকে ওভাররাইড করা আমাদেরকে সেই স্তরগুলিকে MaskedTensor সাথে ব্যবহার করার অনুমতি দেবে। এই উদাহরণের উদ্দেশ্যে, মুখোশযুক্ত matmul জন্য matmul সংজ্ঞায়িত করা হয়েছে মুখোশযুক্ত মানগুলিকে শূন্য হিসাবে বিবেচনা করার জন্য (অর্থাৎ, সেগুলিকে পণ্যে অন্তর্ভুক্ত না করা)।

@tf.experimental.dispatch_for_unary_elementwise_apis(MaskedTensor)
def unary_elementwise_op_handler(op, x):
 return MaskedTensor(op(x.values), x.mask)

@tf.experimental.dispatch_for_binary_elementwise_apis(
    Union[MaskedTensor, tf.Tensor],
    Union[MaskedTensor, tf.Tensor])
def binary_elementwise_op_handler(op, x, y):
  x = convert_to_masked_tensor(x)
  y = convert_to_masked_tensor(y)
  return MaskedTensor(op(x.values, y.values), x.mask & y.mask)

@tf.experimental.dispatch_for_api(tf.matmul)
def masked_matmul(a: MaskedTensor, b,
                  transpose_a=False, transpose_b=False,
                  adjoint_a=False, adjoint_b=False,
                  a_is_sparse=False, b_is_sparse=False,
                  output_type=None):
  if isinstance(a, MaskedTensor):
    a = a.with_default(0)
  if isinstance(b, MaskedTensor):
    b = b.with_default(0)
  return tf.matmul(a, b, transpose_a, transpose_b, adjoint_a,
                   adjoint_b, a_is_sparse, b_is_sparse, output_type)

তারপরে আপনি একটি কেরাস মডেল তৈরি করতে পারেন যা MaskedTensor ইনপুট গ্রহণ করে, স্ট্যান্ডার্ড কেরাস স্তরগুলি ব্যবহার করে:

input_spec = MaskedTensor.Spec([None, 2], tf.float32)

masked_tensor_model = tf.keras.Sequential([
    tf.keras.layers.Input(type_spec=input_spec),
    tf.keras.layers.Dense(16, activation="relu"),
    tf.keras.layers.Dense(1)])
masked_tensor_model.compile(loss='binary_crossentropy', optimizer='rmsprop')
a = MaskedTensor([[1., 2], [3, 4], [5, 6]],
                  [[True, False], [False, True], [True, True]])
masked_tensor_model.fit(a, tf.constant([[1], [0], [1]]), epochs=3)
print(masked_tensor_model(a))
Epoch 1/3
1/1 [==============================] - 1s 955ms/step - loss: 10.2833
Epoch 2/3
1/1 [==============================] - 0s 5ms/step - loss: 10.2833
Epoch 3/3
1/1 [==============================] - 0s 5ms/step - loss: 10.2833
tf.Tensor(
[[-0.09944128]
 [-0.7225147 ]
 [-1.3020657 ]], shape=(3, 1), dtype=float32)

সংরক্ষিত মডেল

একটি SavedModel হল একটি ক্রমিক টেনসরফ্লো প্রোগ্রাম, যার মধ্যে ওজন এবং গণনা উভয়ই রয়েছে। এটি একটি কেরাস মডেল বা একটি কাস্টম মডেল থেকে তৈরি করা যেতে পারে। উভয় ক্ষেত্রেই, সংরক্ষিত মডেল দ্বারা সংজ্ঞায়িত ফাংশন এবং পদ্ধতিগুলির সাথে এক্সটেনশন প্রকারগুলি স্বচ্ছভাবে ব্যবহার করা যেতে পারে।

SavedModel মডেল, স্তর এবং ফাংশনগুলি সংরক্ষণ করতে পারে যা এক্সটেনশন প্রকারগুলিকে প্রক্রিয়া করে, যতক্ষণ না এক্সটেনশন প্রকারগুলির একটি __name__ ক্ষেত্র থাকে৷ এই নামটি এক্সটেনশন টাইপ নিবন্ধন করতে ব্যবহৃত হয়, তাই মডেলটি লোড হলে এটি অবস্থিত হতে পারে।

উদাহরণ: কেরাস মডেল সংরক্ষণ করা

কেরাস মডেলগুলি যেগুলি এক্সটেনশন প্রকারগুলি ব্যবহার করে SavedModel ব্যবহার করে সংরক্ষণ করা যেতে পারে৷

masked_tensor_model_path = tempfile.mkdtemp()
tf.saved_model.save(masked_tensor_model, masked_tensor_model_path)
imported_model = tf.saved_model.load(masked_tensor_model_path)
imported_model(a)
2021-11-06 01:25:14.285250: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
WARNING:absl:Function `_wrapped_model` contains input name(s) args_0 with unsupported characters which will be renamed to args_0_1 in the SavedModel.
INFO:tensorflow:Assets written to: /tmp/tmp3ceuupv9/assets
INFO:tensorflow:Assets written to: /tmp/tmp3ceuupv9/assets
<tf.Tensor: shape=(3, 1), dtype=float32, numpy=
array([[-0.09944128],
       [-0.7225147 ],
       [-1.3020657 ]], dtype=float32)>

উদাহরণ: একটি কাস্টম মডেল সংরক্ষণ

SavedModel এছাড়াও কাস্টম tf.Module সাবক্লাসগুলিকে ফাংশনগুলির সাথে সংরক্ষণ করতে ব্যবহার করা যেতে পারে যা এক্সটেনশন প্রকারগুলিকে প্রক্রিয়া করে।

class CustomModule(tf.Module):
  def __init__(self, variable_value):
    super().__init__()
    self.v = tf.Variable(variable_value)

  @tf.function
  def grow(self, x: MaskedTensor):
    """Increase values in `x` by multiplying them by `self.v`."""
    return MaskedTensor(x.values * self.v, x.mask)

module = CustomModule(100.0)

module.grow.get_concrete_function(MaskedTensor.Spec(shape=None,
                                                    dtype=tf.float32))
custom_module_path = tempfile.mkdtemp()
tf.saved_model.save(module, custom_module_path)
imported_model = tf.saved_model.load(custom_module_path)
imported_model.grow(MaskedTensor([1., 2, 3], [False, True, False]))
INFO:tensorflow:Assets written to: /tmp/tmp2x8zq5kb/assets
INFO:tensorflow:Assets written to: /tmp/tmp2x8zq5kb/assets
<MaskedTensor [_, 200.0, _]>

এক্সটেনশন টাইপ অনুপলব্ধ হলে একটি সংরক্ষিত মডেল লোড করা হচ্ছে

আপনি যদি একটি SavedModel লোড করেন যা একটি ExtensionType ব্যবহার করে, কিন্তু সেই ExtensionType উপলব্ধ না থাকে (যেমন, আমদানি করা হয়নি), তাহলে আপনি একটি সতর্কতা দেখতে পাবেন এবং TensorFlow একটি "বেনামী এক্সটেনশন টাইপ" অবজেক্ট ব্যবহার করতে ফিরে আসবে৷ এই অবজেক্টের মূল টাইপের মতো একই ক্ষেত্র থাকবে, কিন্তু টাইপের জন্য আপনার যোগ করা আর কোনো কাস্টমাইজেশনের অভাব থাকবে, যেমন কাস্টম পদ্ধতি বা বৈশিষ্ট্য।

TensorFlow পরিবেশনের সাথে ExtensionTypes ব্যবহার করা

বর্তমানে, TensorFlow পরিবেশন (এবং SavedModel "স্বাক্ষর" অভিধানের অন্যান্য ভোক্তাদের) প্রয়োজন যে সমস্ত ইনপুট এবং আউটপুট কাঁচা টেনসর হওয়া উচিত। আপনি যদি এক্সটেনশন প্রকারগুলি ব্যবহার করে এমন একটি মডেলের সাথে টেনসরফ্লো সার্ভিং ব্যবহার করতে চান, তাহলে আপনি র্যাপার পদ্ধতিগুলি যোগ করতে পারেন যা টেনসর থেকে এক্সটেনশন প্রকারের মানগুলি রচনা বা পচন করে। যেমন:

class CustomModuleWrapper(tf.Module):
  def __init__(self, variable_value):
    super().__init__()
    self.v = tf.Variable(variable_value)

  @tf.function
  def var_weighted_mean(self, x: MaskedTensor):
    """Mean value of unmasked values in x, weighted by self.v."""
    x = MaskedTensor(x.values * self.v, x.mask)
    return (tf.reduce_sum(x.with_default(0)) /
            tf.reduce_sum(tf.cast(x.mask, x.dtype)))

  @tf.function()
  def var_weighted_mean_wrapper(self, x_values, x_mask):
    """Raw tensor wrapper for var_weighted_mean."""
    return self.var_weighted_mean(MaskedTensor(x_values, x_mask))

module = CustomModuleWrapper([3., 2., 8., 5.])

module.var_weighted_mean_wrapper.get_concrete_function(
    tf.TensorSpec(None, tf.float32), tf.TensorSpec(None, tf.bool))
custom_module_path = tempfile.mkdtemp()
tf.saved_model.save(module, custom_module_path)
imported_model = tf.saved_model.load(custom_module_path)
x = MaskedTensor([1., 2., 3., 4.], [False, True, False, True])
imported_model.var_weighted_mean_wrapper(x.values, x.mask)
INFO:tensorflow:Assets written to: /tmp/tmpxhh4zh0i/assets
INFO:tensorflow:Assets written to: /tmp/tmpxhh4zh0i/assets
<tf.Tensor: shape=(), dtype=float32, numpy=12.0>

ডেটাসেট

tf.data হল একটি API যা আপনাকে সহজ, পুনরায় ব্যবহারযোগ্য টুকরো থেকে জটিল ইনপুট পাইপলাইন তৈরি করতে সক্ষম করে। এর মূল ডেটা স্ট্রাকচার হল tf.data.Dataset , যা উপাদানগুলির একটি ক্রম প্রতিনিধিত্ব করে, যেখানে প্রতিটি উপাদান এক বা একাধিক উপাদান নিয়ে গঠিত।

এক্সটেনশন প্রকারের সাথে ডেটাসেট তৈরি করা

Dataset.from_tensors , Dataset.from_tensor_slices , বা Dataset.from_generator ব্যবহার করে এক্সটেনশন প্রকারের মান থেকে ডেটাসেট তৈরি করা যেতে পারে :

ds = tf.data.Dataset.from_tensors(Pastry(5, 5))
iter(ds).next()
Pastry(sweetness=<tf.Tensor: shape=(), dtype=int32, numpy=5>, chewiness=<tf.Tensor: shape=(), dtype=int32, numpy=5>)
mt = MaskedTensor(tf.reshape(range(20), [5, 4]), tf.ones([5, 4]))
ds = tf.data.Dataset.from_tensor_slices(mt)
for value in ds:
  print(value)
<MaskedTensor [0, 1, 2, 3]>
<MaskedTensor [4, 5, 6, 7]>
<MaskedTensor [8, 9, 10, 11]>
<MaskedTensor [12, 13, 14, 15]>
<MaskedTensor [16, 17, 18, 19]>
def value_gen():
  for i in range(2, 7):
    yield MaskedTensor(range(10), [j%i != 0 for j in range(10)])

ds = tf.data.Dataset.from_generator(
    value_gen, output_signature=MaskedTensor.Spec(shape=[10], dtype=tf.int32))
for value in ds:
  print(value)
<MaskedTensor [_, 1, _, 3, _, 5, _, 7, _, 9]>
<MaskedTensor [_, 1, 2, _, 4, 5, _, 7, 8, _]>
<MaskedTensor [_, 1, 2, 3, _, 5, 6, 7, _, 9]>
<MaskedTensor [_, 1, 2, 3, 4, _, 6, 7, 8, 9]>
<MaskedTensor [_, 1, 2, 3, 4, 5, _, 7, 8, 9]>

এক্সটেনশন প্রকারের সাথে ডেটাসেট ব্যাচিং এবং আনব্যাচিং

এক্সটেনশন প্রকারের ডেটাসেটগুলি Dataset.batch এবং Dataset.batch ব্যবহার করে ব্যাচ্যান্ড এবং Dataset.unbatch করা যেতে পারে।

batched_ds = ds.batch(2)
for value in batched_ds:
  print(value)
<MaskedTensor [[_, 1, _, 3, _, 5, _, 7, _, 9], [_, 1, 2, _, 4, 5, _, 7, 8, _]]>
<MaskedTensor [[_, 1, 2, 3, _, 5, 6, 7, _, 9], [_, 1, 2, 3, 4, _, 6, 7, 8, 9]]>
<MaskedTensor [[_, 1, 2, 3, 4, 5, _, 7, 8, 9]]>
unbatched_ds = batched_ds.unbatch()
for value in unbatched_ds:
  print(value)
<MaskedTensor [_, 1, _, 3, _, 5, _, 7, _, 9]>
<MaskedTensor [_, 1, 2, _, 4, 5, _, 7, 8, _]>
<MaskedTensor [_, 1, 2, 3, _, 5, 6, 7, _, 9]>
<MaskedTensor [_, 1, 2, 3, 4, _, 6, 7, 8, 9]>
<MaskedTensor [_, 1, 2, 3, 4, 5, _, 7, 8, 9]>