使用 funsors 的命名张量表示法(第 1 部分)¶
引言¶
在 Named Tensor Notation (Chiang, Rush, Barak 2021) 中引入的带有命名轴的数学表示法提高了涉及多维数组的数学公式的可读性。这包括张量操作,例如逐元素操作、归约、收缩、重命名、索引和广播。在本教程中,我们将 Named Tensor Notation 中的示例翻译成 funsors,以演示这些操作在 funsor 库中的实现,并让读者熟悉 funsor 语法。第 1 部分涵盖了来自 2 非正式概述、3.4.2 高级索引和 5 正式定义的示例。
首先,让我们导入一些依赖。
[ ]:
!pip install funsor[torch]@git+https://github.com/pyro-ppl/funsor
[1]:
from torch import tensor
import funsor
import funsor.ops as ops
from funsor import Number, Tensor, Variable
from funsor.domains import Bint
funsor.set_backend("torch")
命名张量¶
每个张量轴都有一个名称
[2]:
A = Tensor(tensor([[3, 1, 4], [1, 5, 9], [2, 6, 5]]))["height", "width"]
使用命名索引访问 \(A\) 的元素
[3]:
# A(height=0, width=2) =
A(width=2, height=0)
[3]:
Tensor(tensor(4))
部分索引
[4]:
A(height=0)
[4]:
Tensor(tensor([3, 1, 4]), {'width': Bint[3]})
[5]:
A(width=2)
[5]:
Tensor(tensor([4, 9, 5]), {'height': Bint[3]})
命名张量操作¶
逐元素操作和广播¶
逐元素操作
[6]:
# A.sigmoid() =
# ops.sigmoid(A) =
# 1 / (1 + ops.exp(-A)) =
1 / (1 + (-A).exp())
[6]:
Tensor(tensor([[0.9526, 0.7311, 0.9820],
[0.7311, 0.9933, 0.9999],
[0.8808, 0.9975, 0.9933]]), {'height': Bint[3], 'width': Bint[3]})
不同形状的张量在应用操作之前会自动相互广播。令
[7]:
x = Tensor(tensor([2, 7, 1]))["height"]
y = Tensor(tensor([1, 4, 1]))["width"]
二元加法操作
[8]:
# ops.add(A, x) =
A + x
[8]:
Tensor(tensor([[ 5, 3, 6],
[ 8, 12, 16],
[ 3, 7, 6]]), {'height': Bint[3], 'width': Bint[3]})
[9]:
# ops.add(A, y) =
A + y
[9]:
Tensor(tensor([[ 4, 5, 5],
[ 2, 9, 10],
[ 3, 10, 6]]), {'height': Bint[3], 'width': Bint[3]})
二元乘法操作
[10]:
# ops.mul(A, x) =
A * x
[10]:
Tensor(tensor([[ 6, 2, 8],
[ 7, 35, 63],
[ 2, 6, 5]]), {'height': Bint[3], 'width': Bint[3]})
二元最大值操作
[11]:
ops.max(A, y)
[11]:
Tensor(tensor([[3, 4, 4],
[1, 5, 9],
[2, 6, 5]]), {'height': Bint[3], 'width': Bint[3]})
归约¶
可以通过调用 .reduce
方法并指定归约算子和归约轴的名称来对命名轴进行归约。注意,归约仅定义于满足结合律和交换律的算子。
[12]:
A.reduce(ops.add, "height")
[12]:
Tensor(tensor([ 6, 12, 18]), {'width': Bint[3]})
[13]:
A.reduce(ops.add, "width")
[13]:
Tensor(tensor([ 8, 15, 13]), {'height': Bint[3]})
跨多个轴的归约
[14]:
A.reduce(ops.add, {"height", "width"})
[14]:
Tensor(tensor(36))
乘法归约
[15]:
A.reduce(ops.mul, "height")
[15]:
Tensor(tensor([ 6, 30, 180]), {'width': Bint[3]})
最大值归约
[16]:
A.reduce(ops.max, "height")
[16]:
Tensor(tensor([3, 6, 9]), {'width': Bint[3]})
收缩¶
收缩操作可以写成逐元素乘法,然后对一个轴求和
[17]:
(A * y).reduce(ops.add, "width")
[17]:
Tensor(tensor([11, 30, 31]), {'height': Bint[3]})
线性代数中的一些其他操作
[18]:
(x * x).reduce(ops.add, "height")
[18]:
Tensor(tensor(54))
[19]:
x * y
[19]:
Tensor(tensor([[ 2, 8, 2],
[ 7, 28, 7],
[ 1, 4, 1]]), {'height': Bint[3], 'width': Bint[3]})
[20]:
(A * y).reduce(ops.add, "width")
[20]:
Tensor(tensor([11, 30, 31]), {'height': Bint[3]})
[21]:
(x * A).reduce(ops.add, "height")
[21]:
Tensor(tensor([15, 43, 76]), {'width': Bint[3]})
[22]:
B = Tensor(
tensor([[3, 2, 5], [5, 4, 0], [8, 3, 6]]),
)["width", "width2"]
(A * B).reduce(ops.add, "width")
[22]:
Tensor(tensor([[ 46, 22, 39],
[100, 49, 59],
[ 76, 43, 40]]), {'height': Bint[3], 'width2': Bint[3]})
收缩可以推广到其他二元操作和归约操作
[23]:
(A + y).reduce(ops.max, "width")
[23]:
Tensor(tensor([ 5, 10, 10]), {'height': Bint[3]})
重命名和重塑¶
重命名 funsors 很简单
[24]:
# A(height=Variable("height2", Bint[3]))
A(height="height2")
[24]:
Tensor(tensor([[3, 1, 4],
[1, 5, 9],
[2, 6, 5]]), {'height2': Bint[3], 'width': Bint[3]})
[25]:
layer = Variable("layer", Bint[9])
A_layer = A(height=layer // Number(3, 4), width=layer % Number(3, 4))
A_layer
[25]:
Tensor(tensor([3, 1, 4, 1, 5, 9, 2, 6, 5]), {'layer': Bint[9]})
[26]:
height = Variable("height", Bint[3])
width = Variable("width", Bint[3])
A_layer(layer=height * Number(3, 4) + width % Number(3, 4))
[26]:
Tensor(tensor([[3, 1, 4],
[1, 5, 9],
[2, 6, 5]]), {'height': Bint[3], 'width': Bint[3]})
高级索引¶
所有高级索引都可以通过 funsors 中的名称替换来实现。
部分索引 \(\mathop{\underset{\substack{\mathsf{\vphantom{fg}vocab}}}{\vphantom{fg}\mathrm{index}}}(E,i)\)
[27]:
E = Tensor(
tensor([[2, 1, 5], [3, 4, 2], [1, 3, 7], [1, 4, 3], [5, 9, 2]]),
)["vocab", "emb"]
E(vocab=2)
[27]:
Tensor(tensor([1, 3, 7]), {'emb': Bint[3]})
整数数组索引 \(\mathop{\underset{\substack{\mathsf{\vphantom{fg}vocab}}}{\vphantom{fg}\mathrm{index}}}(E,I)\)
[28]:
I = Tensor(tensor([3, 2, 4, 0]), dtype=5)["seq"]
E(vocab=I)
[28]:
Tensor(tensor([[1, 4, 3],
[1, 3, 7],
[5, 9, 2],
[2, 1, 5]]), {'seq': Bint[4], 'emb': Bint[3]})
Gather 操作 \(\mathop{\underset{\substack{\mathsf{\vphantom{fg}vocab}}}{\vphantom{fg}\mathrm{index}}}(P,I)\)
[29]:
P = Tensor(
tensor([[6, 2, 4, 2], [8, 2, 1, 3], [5, 5, 7, 0], [1, 3, 8, 2], [5, 9, 2, 3]]),
)["vocab", "seq"]
P(vocab=I)
[29]:
Tensor(tensor([1, 5, 2, 2]), {'seq': Bint[4]})
使用两个整数数组进行索引
[30]:
I1 = Tensor(tensor([1, 2, 0]), dtype=4)["subseq"]
I2 = Tensor(tensor([3, 0, 4]), dtype=5)["subseq"]
P(seq=I1, vocab=I2)
[30]:
Tensor(tensor([3, 4, 5]), {'subseq': Bint[3]})