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

انواع افزونه

مشاهده در 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

انواع پسوند

انواع تعریف شده توسط کاربر می توانند پروژه ها را خواناتر، ماژولارتر و قابل نگهداری تر کنند. با این حال، اکثر API های TensorFlow پشتیبانی بسیار محدودی از انواع Python تعریف شده توسط کاربر دارند. این شامل هر دو API های سطح بالا (مانند Keras ، tf.function ، tf.SavedModel ) و کاهش سطح رابط های برنامه کاربردی (مانند tf.while_loop و tf.concat ). انواع پسوند TensorFlow می توان مورد استفاده برای ایجاد انواع شی گرا تعریف شده توسط کاربر که کار یکپارچه با رابط های برنامه کاربردی TensorFlow است. برای ایجاد یک نوع فرمت، به سادگی تعریف یک کلاس پایتون با 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 های پشتیبانی شده

انواع برنامه های افزودنی توسط API های TensorFlow زیر پشتیبانی می شوند:

  • Keras: انواع فرمت می تواند به عنوان ورودی و خروجی برای Keras استفاده Models و Layers .
  • tf.data.Dataset: انواع فرمت می توانند در Datasets ، و با مجموعه داده بازگشت Iterators .
  • مرکز Tensorflow: انواع فرمت می تواند به عنوان ورودی و خروجی برای استفاده tf.hub ماژول.
  • SavedModel: انواع فرمت می تواند به عنوان ورودی و خروجی برای استفاده SavedModel توابع.
  • tf.function: انواع فرمت می تواند به عنوان استدلال و مقادیر بازگشتی برای توابع پیچیده با استفاده @tf.function دکوراتور.
  • در حالی که حلقه: انواع فرمت می تواند به عنوان متغیرهای حلقه در استفاده tf.while_loop ، و می تواند به عنوان آرگومان و مقادیر بازگشتی برای بدن در حالی که حلقه استفاده می شود.
  • شرطی: انواع فرمت مشروط می تواند با استفاده از انتخاب tf.cond و tf.case .
  • py_function: انواع فرمت می تواند به عنوان استدلال و مقادیر بازگشتی برای استفاده func آرگومان به tf.py_function .
  • تانسور عملیات: انواع فرمت می توان توسعه یافته برای حمایت از ترین عملیات TensorFlow که ورودی تانسور (به عنوان مثال، قبول tf.matmul ، tf.gather و tf.reduce_sum ). بخش "اعزام" زیر را برای اطلاعات بیشتر ببینید.
  • استراتژی توزیع: انواع فرمت می تواند به عنوان ارزش هر ماکت استفاده می شود.

برای جزئیات بیشتر، بخش "API های TensorFlow که از 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]

تغییرپذیری

انواع پسوندها باید تغییرناپذیر باشند. این تضمین می کند که می توان آنها را به درستی توسط مکانیسم های ردیابی گراف TensorFlow ردیابی کرد. اگر متوجه شدید که می خواهید یک مقدار نوع پسوند را تغییر دهید، به جای آن روش هایی را که مقادیر را تغییر می دهند تعریف کنید. به عنوان مثال، به جای تعریف یک 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 .
  • پشتیبانی از ارسال Tensor API.

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

سازنده

سازنده اضافه شده توسط ExtensionType طول می کشد هر رشته به عنوان یک استدلال به نام (در آنها در تعریف کلاس ذکر شده بود). این سازنده هر پارامتر را تایپ می کند و در صورت لزوم آنها را تبدیل می کند. به طور خاص، Tensor زمینه با استفاده از تبدیل tf.convert_to_tensor ؛ Tuple زمینه ها تبدیل tuple S؛ و Mapping زمینه به dicts تغییرناپذیر تبدیل شده است.

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__ روش، که می تواند لغو برای انجام چک اعتبار سنجی در زمینه های. پس از فراخوانی سازنده، و پس از بررسی تایپ فیلدها و تبدیل آنها به انواع اعلام شده اجرا می شود، بنابراین می توان فرض کرد که همه فیلدها دارای انواع اعلام شده خود هستند.

او مثال زیر به روز رسانی MaskedTensor به اعتبار shape و dtype بازدید کنندگان از زمینه های آن:

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.

تو در تو TypeSpec

هر ExtensionType کلاس دارای یک متناظر TypeSpec کلاس، که به طور خودکار ایجاد شده و به عنوان <extension_type_name>.Spec .

این کلاس قطاری از تمام اطلاعات از یک مقدار به جز برای ارزش های هر تانسورها تو در تو. به طور خاص، TypeSpec برای یک مقدار است با جایگزین کردن هر تو در تو تانسور، ExtensionType یا CompositeTensor با آن ایجاد 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 ارزش را می توان به صراحت ساخته، یا آنها می توان از یک ساخته شده است ExtensionType ارزش با استفاده از tf.type_spec_from_value :

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

TypeSpec بازدید کنندگان توسط TensorFlow به ارزش تقسیم به یک جزء ثابت و یک جزء پویا استفاده می شود:

  • جزء استاتیک (که در زمان نمودار ساخت و ساز ثابت) با یک کد گذاری tf.TypeSpec .
  • جزء پویا (که می تواند هر بار که نمودار اجرا است متغیر باشد) به عنوان یک لیست از کد گذاری tf.Tensor است.

به عنوان مثال، tf.function retraces عملکرد پیچیده آن هر زمان که یک استدلال است قبلا دیده نشده TypeSpec :

@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 راهنمای .

سفارشی کردن ExtensionTypes

علاوه بر اعلام ساده فیلدها و انواع آنها، انواع پسوند ممکن است:

  • این پیشفرض را نمایندگی قابل چاپ ( __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 روش که بازده یک کپی از self را با ارزش پوشانده جای داده default مقدار است. مواد و روش ها ممکن است به صورت اختیاری با مشروح شود @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)

تعریف فیلدهای خصوصی

فیلدهای یک نوع پسوند ممکن است با پیشوند کردن آنها با یک زیرخط (طبق قراردادهای استاندارد پایتون) خصوصی مشخص شوند. این روشی که TensorFlow با فیلدها برخورد می کند به هیچ وجه تأثیری ندارد. اما به سادگی به عنوان یک سیگنال برای هر کاربر از نوع پسوند عمل می کند که آن فیلدها خصوصی هستند.

سفارشی کردن ExtensionType است TypeSpec

هر ExtensionType کلاس دارای یک متناظر TypeSpec کلاس، که به طور خودکار ایجاد شده و به عنوان <extension_type_name>.Spec . برای اطلاعات بیشتر، به بخش "Nested TypeSpec" در بالا مراجعه کنید.

برای سفارشی کردن 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)

ارسال Tensor API

انواع فرمت را می توان "تانسور مانند"، به این معنا که آنها تخصص و یا گسترش رابط های تعریف شده توسط tf.Tensor نوع. نمونه هایی از انواع پسوند تانسور مانند شامل RaggedTensor ، SparseTensor و MaskedTensor . تزئین اعزام می تواند مورد استفاده قرار گیرد به نادیده گرفتن رفتار پیشفرض عملیات TensorFlow زمانی که به تانسور مانند انواع پسوند استفاده می شود. TensorFlow در حال حاضر سه دکوراتور اعزامی را تعریف می کند:

ارسال برای یک API واحد

tf.experimental.dispatch_for_api دکوراتور رفتار پیش فرض از یک عملیات TensorFlow مشخص زمانی که آن را با امضای مشخص به نام لغو. به عنوان مثال، شما می توانید این دکوراتور مشخص کنید که چگونه با استفاده از 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))

این لغو پیاده سازی پیش فرض برای tf.stack هر زمان که آن را با یک لیست از نام MaskedTensor ارزش (از 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های عنصری واحد

tf.experimental.dispatch_for_unary_elementwise_apis دکوراتور لغو رفتار پیشفرض تمام عملیات elementwise یگانی (مانند tf.math.cos ) هر زمان که ارزش برای اولین آرگومان (معمولا به نام x ) منطبق بر نوع حاشیه نویسی x_type . تابع تزئین شده باید دو آرگومان داشته باشد:

  • api_func : یک تابع که طول می کشد تنها یک پارامتر و انجام عملیات elementwise (به عنوان مثال، tf.abs ).
  • x : اولین آرگومان به عملیات elementwise است.

در مثال زیر، به روز رسانی تمام عملیات elementwise یگانی که مسئولیت رسیدگی به 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)

این تابع در حال حاضر مورد استفاده قرار خواهد هر زمان که یک عملیات elementwise یگانی است که در یک نام MaskedTensor .

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

ارسال برای همه APIهای باینری عنصر

به طور مشابه، tf.experimental.dispatch_for_binary_elementwise_apis می توان مورد استفاده برای به روز رسانی تمام عملیات elementwise باینری که مسئولیت رسیدگی به 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 .

انواع پسوند قابل دسته بندی

ExtensionType batchable است اگر به عنوان مثال تنها می توان برای نشان دادن یک دسته ای از ارزش ها. به طور معمول، این است که با اضافه کردن ابعاد دسته ای به تمام تو در تو انجام Tensor است. API های TensorFlow زیر مستلزم این هستند که هر ورودی نوع افزونه قابل دسته بندی باشد:

به طور پیش فرض، BatchableExtensionType ارزش بسته بندی های کوچک توسط جداجدا سفارش داده شده هیچ تو در تو ایجاد Tensor ها، CompositeTensor ، و ExtensionType است. اگر این است که برای کلاس خود را مناسب نیست، پس شما نیاز به استفاده از tf.experimental.ExtensionTypeBatchEncoder به نادیده گرفتن این رفتار پیش فرض. به عنوان مثال، مناسب نخواهد بود برای ایجاد یک دسته ای از tf.SparseTensor ارزش به سادگی با انباشته تانسورها پراکنده فردی " values ، indices ، و dense_shape زمینه - در اغلب موارد، شما می توانید این تانسورها پشته نه، چرا که آنها اشکال ناسازگار ; و حتی اگر شما می توانید، در نتیجه می تواند یک معتبر نیست SparseTensor .

نمونه BatchableExtensionType: شبکه

به عنوان مثال، در نظر گرفتن ساده 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]])

برای اینکه این نوع batchable، تغییر نوع پایه به BatchableExtensionType و تنظیم شکل هر زمینه به ابعاد شامل دسته ای اختیاری است. در مثال زیر، همچنین می افزاید: shape به مسیر keept شکل دسته ای این زمینه است. این 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.]]]>

API های TensorFlow که از ExtensionTypes پشتیبانی می کنند

@tf.function

tf.function یک دکوراتور است که precomputes TensorFlow نمودار برای توابع پایتون، که می تواند قابل ملاحظه بهبود عملکرد کد TensorFlow خود را. ارزش نوع فرمت را می توان شفاف با استفاده @tf.function توابع -decorated.

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 -API سطح بالا TensorFlow برای ساخت و آموزش مدل های یادگیری عمیق است. انواع پسوند ممکن است به عنوان ورودی به یک مدل Keras ارسال شوند، بین لایه‌های Keras ارسال شوند و توسط مدل‌های Keras برگردانده شوند. Keras در حال حاضر دو الزام را در مورد انواع پسوند اعمال می کند:

  • آنها باید قابل دسته بندی باشند (به "انواع پسوند قابل دسته بندی" در بالا مراجعه کنید).
  • باید یک میدان و یا اموال به نام باید shape . shape[0] فرض می شود بعد دسته ای.

دو بخش فرعی زیر مثال‌هایی ارائه می‌دهند که نشان می‌دهد چگونه می‌توان از انواع پسوند با Keras استفاده کرد.

Keras به عنوان مثال: Network

برای مثال اول، در نظر گرفتن Network کلاس تعریف شده در بخش "Batchable ExtensionTypes" در بالا، که می تواند برای موازنه بار کار بین گره استفاده می شود. تعریف آن در اینجا تکرار شده است:

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]]])

شما می توانید یک لایه جدید Keras که پردازش تعریف Network است.

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 لایه با type_spec مجموعه ای به نوع افزونه TypeSpec . اگر مدل Keras خواهد شد به دسته های فرایند استفاده می شود، پس از آن 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.]]]>

مثال Keras: MaskedTensor

در این مثال، MaskedTensor گسترش است برای حمایت از Keras . shape به عنوان یک ویژگی است که از محاسبه تعریف values این زمینه است. Keras نیاز thatyou اضافه کردن این اموال به هر دو نوع فرمت و آن 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))

سپس، دکوراتورهای اعزام برای نادیده گرفتن رفتار پیش‌فرض چندین API TensorFlow استفاده می‌شوند. از آنجا که این API ها توسط لایه های Keras استاندارد (مانند استفاده Dense لایه)، فارغ از این اجازه خواهد داد که ما را به استفاده از این لایه با MaskedTensor . برای این منظور از این مثال، 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)

پس از آن شما می توانید یک مدل Keras است که می پذیرد MaskedTensor ورودی، استفاده از لایه های استاندارد Keras:

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 یک برنامه TensorFlow سریال، از جمله هر دو وزن و محاسبه است. می توان آن را از یک مدل Keras یا از یک مدل سفارشی ساخت. در هر صورت، انواع پسوند را می توان با توابع و روش های تعریف شده توسط SavedModel به صورت شفاف استفاده کرد.

SavedModel می توانید مدل های، لایه ها، و توابع ذخیره کنید که انواع فرمت روند، تا زمانی که انواع فرمت یک __name__ زمینه است. این نام برای ثبت نوع پسوند استفاده می‌شود، بنابراین می‌توان آن را در هنگام بارگذاری مدل پیدا کرد.

مثال: ذخیره یک مدل Keras

مدل Keras که با استفاده از انواع پسوند می تواند با استفاده از ذخیره 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 در دسترس نیست

اگر شما یک بار SavedModel که با استفاده از ExtensionType ، اما این ExtensionType در دسترس نیست (یعنی وارد نشده است)، و سپس شما یک هشدار را مشاهده کنید و TensorFlow سقوط خواهد کرد به استفاده از یک "نوع فرمت ناشناس" شی. این شیء همان فیلدهای نوع اصلی را خواهد داشت، اما فاقد هرگونه سفارشی سازی دیگری است که برای نوع اضافه کرده اید، مانند روش ها یا ویژگی های سفارشی.

استفاده از ExtensionTypes با سرویس TensorFlow

در حال حاضر، TensorFlow خدمت (و سایر مصرف کنندگان از SavedModel "امضا" فرهنگ لغت) نیاز است که تمام ورودی ها و خروجی می شود تانسورها خام. اگر می‌خواهید از سرویس TensorFlow با مدلی استفاده کنید که از انواع پسوند استفاده می‌کند، می‌توانید روش‌های wrapper را اضافه کنید که مقادیر نوع پسوند را از تانسورها ترکیب یا تجزیه می‌کنند. به عنوان مثال:

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

دسته بندی و جداسازی مجموعه داده ها با انواع پسوند

مجموعه داده با انواع فرمت را می توان batchand و unbatched با استفاده از Dataset.batch ADN 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]>