
Типы расширения TensorFlow, объясненные примерами
28 июля 2025 г.Обзор контента
- Настраивать
- Типы расширения
- Поддерживается API
- Требования
- Типы поля
- Изменчивость
- Функциональность добавлена по расширению
- Конструктор
- Печатное представление
- Операторы равенства
- Метод проверки
- Принудительная неизменность
- Вложенные типы
- Настройка extensionTypes
- Переопределение печатного представления по умолчанию
- Определение методов
- Определение класса -матодов и статичныхметодов
- Определение свойств
- Переоценивать конструктор по умолчанию
- Переопределение оператора равенства по умолчанию (уравнение)
- Используя прямые ссылки
- Определение подклассов
- Определение частных полей
- Настройка TypeSpec от extensionType
Настраивать
!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 высокого уровня (напримерКерасВtf.functionВtf.SavedModel
) и API нижнего уровня (напримерtf.while_loop
иtf.concat
) TensorflowТипы расширенияМожет использоваться для создания пользовательских объектно-ориентированных типов, которые плавно работают с API-интерфейсом Tensorflow. To create an extension type, simply define a Python class withtf.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
Из стандартной библиотеки Python. В частности, он автоматически добавляет конструктор и специальные методы (например, как__repr__
и__eq__
) на основе аннотаций типа поля.
Как правило, типы расширения, как правило, попадают в одну из двух категорий:
- Структуры данных, который объединяет набор связанных значений и может обеспечить полезные операции на основе этих значений. Структуры данных могут быть довольно общими (например
TensorGraph
пример выше); Или они могут быть высоко настроены на конкретную модель. - Тенсорные типы, которые специализируют или расширяют концепцию «тензора». Типы в этой категории имеют
rank
, аshape
и обычноdtype
; и имеет смысл использовать их с тензорными операциями (например, какtf.stack
Вtf.add
, илиtf.matmul
)MaskedTensor
иCSRSparseMatrix
примеры тензоров, подобных типам.
Поддерживается API
Типы расширения подтверждаются следующими API -интерфейсом tensorflow:
- Керас: Типы расширения могут использоваться в качестве входов и выходов для керов
Models
иLayers
Полем tf.data.Dataset
: Типы расширения могут быть включены вDatasets
и возвращается набором данныхIterators
Полем- Tensorflow Hub: Типы расширения могут использоваться в качестве входов и выходов для
tf.hub
модули. - Сохраняйте модель: Типы расширения могут использоваться в качестве входов и выходов для
SavedModel
функции. tf.function
: Типы расширения могут использоваться в качестве аргументов и возвратных значений для функций, завернутых@tf.function
декоратор.- Пока петли: Типы расширения могут использоваться в качестве переменных цикла в
tf.while_loop
, и может использоваться в качестве аргументов и возвращающих значений для тела That-Ploop. - Условия: Типы расширения могут быть условно выбраны с помощью
tf.cond
иtf.case
Полем tf.py_function
: Типы расширения могут использоваться в качестве аргументов и возвратных значений дляfunc
аргументtf.py_function
Полем- Tensor Ops: Типы расширения могут быть расширены для поддержки большинства Tensorflow Ops, которые принимают тензорные входы (например, как
tf.matmul
Вtf.gather
, иtf.reduce_sum
) Пойти в "Отправлять"Раздел ниже для получения дополнительной информации. - Стратегия распределения: Типы удлинения могут использоваться в качестве значений для за повторения.
Более подробную информацию см. В разделе «API -интерфейсы Tensorflow, которые поддерживают extensionTypes» ниже.
Требования
Типы поля
Все поля - переменные, которые должны быть объявлены, и аннотация типа должна быть предусмотрена для каждого поля. Поддерживаются следующие аннотации типа:
Тип | Пример |
---|---|
Python Itgers |
|
Питон плавает |
|
Струны Python |
|
Python Booleans |
|
Питон |
|
Тенсорные формы |
|
Тензор |
|
Тензоры |
|
Типы расширения |
|
Рваные тензоры |
|
Разреженные тензоры |
|
Индексированные ломтики |
|
Дополнительные тензоры |
|
Типа профсоюзов |
|
Кортежи |
|
Клетки длиной длины |
|
Отображения |
|
Необязательные значения |
|
Изменчивость
Типы расширения должны быть неизменными. Это гарантирует, что они могут быть должным образом отслеживаны механизмами графика 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
S "Раздел ниже для получения дополнительной информации о настройке этой функции.
Конструктор
Конструктор, добавленный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.
# For example, `mt.values` is converted to a Tensor.
print(mt.values)
Конструктор поднимаетTypeError
Если значение поля не может быть преобразовано в его объявленный тип:
try:
MaskedTensor([1, 2, 3], None)
except TypeError as e:
print(f"Got expected TypeError: {e}")
Значение по умолчанию для поля может быть указано, установив его значение на уровне класса:
class Pencil(tf.experimental.ExtensionType):
color: str = "black"
has_erasor: bool = True
length: tf.Tensor = 1.0
Pencil()
Pencil(length=0.5, color="blue")
Печатное представление
ExtensionType
Добавляет метод представления по умолчанию по умолчанию (__repr__
) Это включает имя класса и значение для каждого поля:
print(MaskedTensor(values=[1, 2, 3], mask=[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}")
Примечание:Если какое -либо поле содержитTensor
, затем__eq__
может вернуть скалярное логическоеTensor
(а не логическое значение Python).
Метод проверки
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}")
try:
MaskedTensor([1, 2, 3], [True, False]) # shapes don't match.
except ValueError as e:
print(f"Got expected ValueError: {e}")
Принудительная неизменность
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}")
try:
mt.mask[0] = False
except TypeError as e:
print(f"Got expected TypeError: {e}")
try:
del mt.mask
except AttributeError as e:
print(f"Got expected AttributeError: {e}")
Вложенные типы
Каждый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.
TypeSpec
значения могут быть построены явно, или они могут быть построены изExtensionType
значение с использованиемtf.type_spec_from_value
:
spec1 = Player.Spec(name=tf.TensorSpec([], tf.float32), attributes={})
spec2 = tf.type_spec_from_value(anne)
TypeSpec
s используются TensorFlow для разделения значений настатический компонентидинамический компонент:
- Астатический компонент(который фиксируется во время строительства графика) кодируется с
tf.TypeSpec
Полем - Адинамический компонент(который может варьироваться каждый раз, когда запускается график) кодируется как список
tf.Tensor
с
Например,tf.function
Поискает свою обернутую функцию всякий раз, когда у аргумента ранее невидимый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}))
# Function does NOT get traced (same TypeSpec: just tensor values changed)
anonymize_player(Player("Bart", {"height": 8.1, "speed": 25.3}))
# Function gets traced (new TypeSpec: keys for attributes changed):
anonymize_player(Player("Chuck", {"height": 11.0, "jump": 5.3}))
Для получения дополнительной информации см.TF.Function GuideПолем
НастройкаExtensionType
с
В дополнение к простому объявлению полей и их типам, типы расширения могут:
- Переопределить представление о печати по умолчанию (
__repr__
) - Определить методы.
- Определять
classmethod
песокstaticmethod
с - Определить свойства.
- Переопределить конструктор по умолчанию (
__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)
Определение методов
Типы расширения могут определять методы, как и любой обычный класс Python. Например,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)
Определениеclassmethod
песокstaticmethod
с
Типы расширения могут определять методы, используя@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)
Определение свойств
Типы расширения могут определять свойства, используя@property
Декоратор, как и любой обычный класс Python. Например,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
Переоценивать конструктор по умолчанию
Вы можете переопределить конструктор по умолчанию для типов расширения. Пользовательские конструкторы должны установить значение для каждого объявленного поля; и после возврата пользовательского конструктора все поля будут проверены типом, а значения будут преобразованы, как описано выше.
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!
В качестве альтернативы вы можете подумать о том, чтобы оставить конструктор по умолчанию как есть, но добавление одного или нескольких заводских методов. Например:
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))
Переопределение оператора равенства по умолчанию (__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)
Примечание:Как правило, вам не нужно переопределять__ne__
, поскольку его реализация по умолчанию просто вызывает__eq__
и отрицает результат.
Используя прямые ссылки
Если тип для поля еще не определен, вы можете использовать строку, содержащую имя типа. В следующем примере строка"Node"
используется для аннотированияchildren
поле, потому чтоNode
Тип еще не был определен (полностью).
class Node(tf.experimental.ExtensionType):
value: tf.Tensor
children: Tuple["Node", ...] = ()
Node(3, [Node(5), Node(2)])
Определение подклассов
Типы расширения могут быть подклассы с использованием стандартного синтаксиса Python. Подклассы типа расширения могут добавлять новые поля, методы и свойства; и может переопределить конструктор, печатное представление и оператор равенства. В следующем примере определяется базовый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)
Определение частных полей
Поля типа расширения могут быть отмечены частными путем префикса их подчеркиванием (после стандартных конвенций на Python). Это не влияет на то, как Tensorflow каким -либо образом относится к полям; Но просто служит сигналом для любого пользователям расширения, что эти поля являются частными.
НастройкаExtensionType
S.TypeSpec
КаждыйExtensionType
класс имеет соответствующийTypeSpec
класс, который создается автоматически и хранится как<extension_type_name>.Spec
Полем Для получения дополнительной информации см. Раздел «Вложенные типы» выше.
Чтобы настроитьTypeSpec
, просто определите свой собственный вложенный класс по имениSpec
, иExtensionType
будет использовать это в качестве основы для автоматически построенногоTypeSpec
Полем Вы можете настроитьSpec
класс по:
- Переоценивая представление по умолчанию.
- Переопределение конструктора по умолчанию.
- Определение методов,
classmethod
с,staticmethod
S и свойства.
В следующем примере настраивает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)
Примечание:ОбычайSpec
Класс может не использовать какие -либо переменные экземпляра, которые не были объявлены в оригиналеExtensionType
Полем
Первоначально опубликовано на
Оригинал