Код: Выделить всё
class Tensor:
__slots__ = (
"_backend",
"_data",
"_requires_grad",
"_node",
"_grad"
)
def __init__(
self,
data: BackendArray,
backend: Backend | None = None,
requires_grad: bool = False,
) -> None:
# Data
self._backend = backend if backend else get_backend()
self._data = self._backend.as_array(data)
# Data for autograd
self._requires_grad = requires_grad
self._node: Node | None = None # For function (autograd graph) nodes
self._grad: "Tensor | None" = None # Leaf gradient accumulator
def backward(self):
self._grad = backward(self)
def _apply(self, f: Type[Function], *args, **kwargs):
return _apply(self, f, *args, **kwargs)
Позже в том же файле я предоставляю реализации для back и _apply как функций, а не методов класса
Код: Выделить всё
def _apply(t: Tensor, f: Type[Function], *args, **kwargs):
"""
Apply a Function to a tensor.
Build the computation graph eagerly.
"""
ctx = Context(t._backend)
# We have to unwrap the args to get the raw data, else we create an infinite recursion
# due to the way that functions are currently implemented
raw_args = [
a._data if isinstance(a, Tensor) else a for a in args
]
out_data: BackendArray = f.forward(ctx, *raw_args, **kwargs)
# determine requires_grad: True if any tensor arg requires grad
requires_grad = any(isinstance(a, Tensor) and a._requires_grad for a in args)
out = Tensor(out_data, requires_grad=requires_grad)
# If requires_grad, add to the graph for backprop
if requires_grad:
parents: list[Edge] = []
for idx, a in enumerate(args):
if isinstance(a, Tensor) and a._requires_grad:
if a._requires_grad:
parents.append(Edge(
a,
idx
))
out._node = Node(
grad_buffer = None,
op = f,
ctx = ctx,
parents = parents,
# Placeholder - will be computed just before doing backward as some nodes may not participate
in_degree = None
)
return out
Код: Выделить всё
def backward(t: Tensor) -> Tensor:
if t._node is None:
raise RuntimeError("Tensor not attached to graph.")
# Initial gradient
root_grad = F.ones(t.shape, requires_grad=True, backend=t._backend).data
t._node.grad_buffer = root_grad
_backward(t._node)
# return a Tensor wrapping the original root grad
return Tensor(root_grad, backend=t._backend, requires_grad=False)
Я хочу переместить фактическую логику движка autograd в другой файл и не хранить все это в одном классе, но эта естественная циклическая зависимость мешает мне это сделать это.
Причина, по которой я хочу, чтобы класс Tensor имел их в качестве методов, заключается в том, что я хочу включить естественный синтаксис, например вызов x.backward(), если x является тензором, а не Backward(x). Действительно, если бы я хотел отказаться от этого синтаксиса, у меня не было бы этой проблемы циклических зависимостей, потому что Tensor больше не нуждался бы в дескрипторах функций, содержащих логику автоградации.
Итак, каков питонический способ решения этой проблемы? Моя конечная цель — отделить тензор (тонкий класс-оболочку вокруг некоторых данных) от основной логики автограда, но при этом иметь возможность иметь синтаксис типа x.backward(), если x, например, является тензором.
Подробнее здесь: https://stackoverflow.com/questions/798 ... te-a-simpl
Мобильная версия