随着GPU 和 TPU 等异构芯片被广泛使用, 俺也逐渐不满足与只使用 CPU 进行计算. 为了刚上时代发展的浪潮, 俺决定学习一下如何写出可以在 GPU 上运行的程序. 最基础的是, GPU 的特性是多核并行计算, 所以需要使用特殊的数据结构, 例如多维数据结构(multidimensional data structures), 又被称为 tensor, 也有翻译为"张量".

恰好最近有一个 elixir 的 tensor 计算库发布, 名为 "Nx". 这里就使用它来学习一下 tensor 的基本计算.

新建一个 tensor

使用如下函数我们就新建了一个 2x2 的多维数组, 其中元素的类型是 64 位有符号整数(s64).

iex> t = Nx.tensor([[1, 2], [3, 4]])
#Nx.Tensor<
  s64[2][2]
  [
    [1, 2],
    [3, 4]
  ]
>

形状(shape),维度(rank)和体积(size)

每个 tensor 都有自己的形状, 可以用元组来表示, 例如一个 2 行 2 列的 tensor 的形状就是 {2, 2}, 维度是 2. 这样我们可以表示更多维度的 tensor, 例如一个形状为 {1, 2, 3, 4} 的 tensor, 其维度(rank)是 4.

标量的 rank 是 0, 形状是 {}.

和二维和三维集合一样, 体积是指一个 tensor 里元素的数量, 等于 shape 里所有边的乘积.

加减乘除运算

tensor 的加减乘除运算, 是每个相同位置上的元素(Element-wise)进行加减乘除. 例如 t + t 会得到:

#Nx.Tensor<
  s64[2][2]
  [
    [2, 4],
    [6, 8]
  ]
>

形状不同的两个 tensor, 能不能进行加减乘除呢? 答案是可能可以, 即把两个 tensor 的形状变为相同的, 再加减乘除.

iex(1)> t1 = Nx.tensor([1])
#Nx.Tensor<
  s64[1]
  [1]
>
iex(2)> t2 = Nx.tensor([[1], [2]])
#Nx.Tensor<
  s64[2][1]
  [
    [1],
    [2]
  ]
>
iex(3)> Nx.add(t1, t2)
#Nx.Tensor<
  s64[2][1]
  [
    [2],
    [3]
  ]
>

这里 t1 的形状是 1x1 , 它可以扩展(broadcast)为 2x1, 即 t2 的形状. 像一个卷轴, 卷起来的时候是一维的, 展开后就变成二维的.

iex> Nx.broadcast(t1, {2, 1})
#Nx.Tensor<
  s64[2][1]
  [
    [1],
    [1]
  ]
>

类似的以 Element-wise 的形式进行的运算还有 power(乘方) 和 remainder(求余).


Ljzn
399 声望102 粉丝

网络安全;函数式编程;数字货币;人工智能