张量 Tensor

张量(Tensor)是仓颉 TensorBoost 网络运算中的基本数据结构,张量支持的数据类型有整型、浮点型和布尔类型,目前整型支持 Int32 和 Int64,浮点型支持 Float16、Float32 和 Float64(注: 目前 Float64 的 Tensor 仅支持部分算子)。使用仓颉 TensorBoost Tensor 结构需要导入 ops 包和 common 包:

from CangjieTB import ops.*
from CangjieTB import common.*

初始化张量

张量的初始化方式有多种,构造张量时,支持传入TensorFloat16Float32Float64Int32Int64Bool类型。

根据数据直接生成

可以根据数据创建张量。

var x = Tensor(Float32(0.1))
print(x)
x = Tensor(Array<Float32>([1.0, 2.0, 3.0, 1.0, 2.0, 3.0]), shape: Array<Int64>([2, 3]))
print(x)
x = Tensor(Float32(0.1), dtype:FLOAT64)
print(x)
x = Tensor(Array<Float32>([1.0, 2.0, 3.0, 1.0, 2.0, 3.0]), shape: Array<Int64>([2, 3]), dtype:FLOAT64)
print(x)

输出为:

Tensor(shape=[], dtype=Float32, value= 1.00000001e-01)
Tensor(shape=[2, 3], dtype=Float32, value=
[[ 1.00000000e+00  2.00000000e+00  3.00000000e+00]
 [ 1.00000000e+00  2.00000000e+00  3.00000000e+00]])
Tensor(shape=[], dtype=Float64, value= 1.00000001e-01)
Tensor(shape=[2, 3], dtype=Float64, value=
[[ 1.00000000e+00  2.00000000e+00  3.00000000e+00]
 [ 1.00000000e+00  2.00000000e+00  3.00000000e+00]])

【说明】:仓颉 TensorBoost 重载了 print 函数,可用于查看张量的数值。

继承另一个张量的属性,形成新的张量

let xZeros = zerosLike(x)
print(xZeros)

输出为:

Tensor(shape=[2, 3], dtype=Float64, value=
[[0.00000000e+00  0.00000000e+00  0.00000000e+00]
 [0.00000000e+00  0.00000000e+00  0.00000000e+00]])

输出指定大小的恒定值或随机张量

var x = zerosTensor(Array<Int64>([2, 3]))
print("zeros x", x)
x = onesTensor(Array<Int64>([2, 3]))
print("ones x", x)
x = fillTensor(Array<Int64>([6]), 2.0)
print("fill x", x)
x = randomNormalTensor(Array<Int64>([2, 3]))
print("random x", x)

输出为:

zeros x
Tensor(shape=[2, 3], dtype=Float32, value=
[[0.00000000e+00 0.00000000e+00 0.00000000e+00]
 [0.00000000e+00 0.00000000e+00 0.00000000e+00]])
ones x
Tensor(shape=[2, 3], dtype=Float32, value=
[[1.00000000e+00 1.00000000e+00 1.00000000e+00]
 [1.00000000e+00 1.00000000e+00 1.00000000e+00]])
fill x
Tensor(shape=[6], dtype=Float32, value=
[2.00000000e+00 2.00000000e+00 2.00000000e+00 2.00000000e+00 2.00000000e+00 2.00000000e+00])
random x
Tensor(shape=[2, 3], dtype=Float32, value=
[[5.24295261e-03 -8.60292558e-03 -6.73894212e-03]
 [-1.03433325e-03 -6.31256960e-03 2.36030575e-02]])

张量的属性

张量的属性包括形状(shape)和数据类型(dtype)。

  • 形状:Tensor 的 shape,是一个 Int64 的数组。
  • 数据类型:Tensor 的 dtype,是一个字符串。

获取属性的代码如下所示:

let t = onesTensor(Array<Int64>([2, 3]), dtype: INT32)
let shape: Array<Int64> = t.getShape()
let dtype: String = t.getStrDtype()
print("Shape of t: ${shape}\n")
print("DType of t: ${dtype}\n")

输出为:

Shape of t: [2, 3]
DType of t: INT32

张量运算

张量支持多种运算,包括算术、线性代数、矩阵处理(转置、索引、切片)等,张量运算的结果依然是张量。下面介绍其中几种操作:

取行/列

张量可以使用 gather 方法,根据轴或索引获取 Tensor 中的某些值。

let tensor: Tensor = randomNormalTensor(Array<Int64>([4, 4]))
print("tensor: ", tensor)
let firstRow = gather(tensor, Tensor(Int32(0), dtype: INT32), 0)
print("First row: ", firstRow)
let firstColumn = gather(tensor, Tensor(Int32(0), dtype: INT32), 1)
print("First column: ", firstColumn)
let otherColumn = gather(tensor, Tensor(Array<Int32>([1, 2]), shape: Array<Int64>([2])), 1)
print("column 2-3: ", otherColumn)

输出为:

tensor:
Tensor(shape=[4, 4], dtype=Float32, value=
[[1.57868862e-02 -4.30074194e-03 6.91929235e-05 1.57265477e-02]
 [5.19612944e-03 -6.04318548e-03 -5.03292587e-03 4.90910932e-03]
 [-1.76758785e-02 -8.70072190e-03 -5.10977907e-03 -7.41219288e-03]
 [-3.73127405e-04 -5.04293945e-03 -7.56428868e-04 -2.82030297e-03]])
First row:
Tensor(shape=[1, 4], dtype=Float32, value=
[[1.57868862e-02 -4.30074194e-03 6.91929235e-05 1.57265477e-02]])
First column:
Tensor(shape=[4, 1], dtype=Float32, value=
[[1.57868862e-02]
 [5.19612944e-03]
 [-1.76758785e-02]
 [-3.73127405e-04]])
column 2-3:
Tensor(shape=[4, 2], dtype=Float32, value=
[[-4.30074194e-03 6.91929235e-05]
 [-6.04318548e-03 -5.03292587e-03]
 [-8.70072190e-03 -5.10977907e-03]
 [-5.04293945e-03 -7.56428868e-04]])

张量连接

张量可以用 concat 或 stack 方法进行连接。二者的区别是,concat 对输入的 Tensor 在指定轴上进行合并,stack 对输入的 Tensor 在指定轴上进行堆叠。

let t1: Tensor = concat([tensor, tensor, tensor], axis: 1)
print(t1)

let t2: Tensor = stack([tensor, tensor, tensor], axis: 1)
print(t2)

输出为:

Tensor(shape=[4, 12], dtype=Float32, value=
[[1.57868862e-02 -4.30074194e-03 6.91929235e-05 ... -4.30074194e-03 6.91929235e-05 1.57265477e-02]
 [5.19612944e-03 -6.04318548e-03 -5.03292587e-03 ... -6.04318548e-03 -5.03292587e-03 4.90910932e-03]
 [-1.76758785e-02 -8.70072190e-03 -5.10977907e-03 ... -8.70072190e-03 -5.10977907e-03 -7.41219288e-03]
 [-3.73127405e-04 -5.04293945e-03 -7.56428868e-04 ... -5.04293945e-03 -7.56428868e-04 -2.82030297e-03]])
Tensor(shape=[4, 3, 4], dtype=Float32, value=
[[[1.57868862e-02 -4.30074194e-03 6.91929235e-05 1.57265477e-02]
  [1.57868862e-02 -4.30074194e-03 6.91929235e-05 1.57265477e-02]
  [1.57868862e-02 -4.30074194e-03 6.91929235e-05 1.57265477e-02]]
 [[5.19612944e-03 -6.04318548e-03 -5.03292587e-03 4.90910932e-03]
  [5.19612944e-03 -6.04318548e-03 -5.03292587e-03 4.90910932e-03]
  [5.19612944e-03 -6.04318548e-03 -5.03292587e-03 4.90910932e-03]]
 [[-1.76758785e-02 -8.70072190e-03 -5.10977907e-03 -7.41219288e-03]
  [-1.76758785e-02 -8.70072190e-03 -5.10977907e-03 -7.41219288e-03]
  [-1.76758785e-02 -8.70072190e-03 -5.10977907e-03 -7.41219288e-03]]
 [[-3.73127405e-04 -5.04293945e-03 -7.56428868e-04 -2.82030297e-03]
  [-3.73127405e-04 -5.04293945e-03 -7.56428868e-04 -2.82030297e-03]
  [-3.73127405e-04 -5.04293945e-03 -7.56428868e-04 -2.82030297e-03]]])

数学运算

let mat = onesTensor(Array<Int64>([4, 4]), dtype: FLOAT32)
// matmul 可以进行矩阵乘法
let z1 = matmul(mat, mat)

print(z1)

// 下面两个操作等价,都是 element-wise 的乘法,得到的 z2 和 z3 结果相同
let z2 = mul(mat, mat)
let z3 = mat * mat

print(z2)
print(z3)

输出为:

Tensor(shape=[4, 4], dtype=Float32, value=
[[4.00000000e+00 4.00000000e+00 4.00000000e+00 4.00000000e+00]
 [4.00000000e+00 4.00000000e+00 4.00000000e+00 4.00000000e+00]
 [4.00000000e+00 4.00000000e+00 4.00000000e+00 4.00000000e+00]
 [4.00000000e+00 4.00000000e+00 4.00000000e+00 4.00000000e+00]])
Tensor(shape=[4, 4], dtype=Float32, value=
[[1.00000000e+00 1.00000000e+00 1.00000000e+00 1.00000000e+00]
 [1.00000000e+00 1.00000000e+00 1.00000000e+00 1.00000000e+00]
 [1.00000000e+00 1.00000000e+00 1.00000000e+00 1.00000000e+00]
 [1.00000000e+00 1.00000000e+00 1.00000000e+00 1.00000000e+00]])
Tensor(shape=[4, 4], dtype=Float32, value=
[[1.00000000e+00 1.00000000e+00 1.00000000e+00 1.00000000e+00]
 [1.00000000e+00 1.00000000e+00 1.00000000e+00 1.00000000e+00]
 [1.00000000e+00 1.00000000e+00 1.00000000e+00 1.00000000e+00]
 [1.00000000e+00 1.00000000e+00 1.00000000e+00 1.00000000e+00]])

数组与张量的转换

数组转张量

根据数组构造 Tensor。

let xDataArray: Array<Float32> = Array<Float32>([1.0, 0.0, 1.0, 0.0])
let xDataTensor = Tensor(xDataArray, shape: Array<Int64>([2, 2]))
print("xDataTensor is: ", xDataTensor)

输出为:

xDataTensor is:
Tensor(shape=[2, 2], dtype=Float32, value=
[[1.00000000e+00 0.00000000e+00]
 [1.00000000e+00 0.00000000e+00]])

张量转数组

使用 toArrayFloat32() 方法,将 Float32 类型的 Tensor 转为数组。

let zerosTensor: Tensor = zerosTensor(Array<Int64>([3, 2]), dtype: FLOAT32)
print("zerosTensor is: ", zerosTensor)
let zerosArray: Array<Float32> = zerosTensor.toArrayFloat32()
print("zerosArray is: ${zerosArray.toString()}\n")

输出为:

zerosTensor is:
Tensor(shape=[3, 2], dtype=Float32, value=
[[0.00000000e+00 0.00000000e+00]
 [0.00000000e+00 0.00000000e+00]
 [0.00000000e+00 0.00000000e+00]])
zerosArray is: [0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000]

【说明】:

  • 如果 Tensor 的 dtype 为 Int32 类型,可调用toArrayInt32(),如果为 Bool 类型,可调用toArrayBool()
  • toArrayFloat32()/toArrayInt32()/toArrayBool() 转数组的方法只能用于已经有值的 Tensor 上,无值的 Tensor 不能调用。如果不确定某个 Tensor 是否有值,可以调用 isConcrete 方法进行判断。

张量释放

export TENSORSIZE=100MB

考虑到在网络执行过程中会不断创建 Tensor,占用较大的内存。如遇到内存资源不足的情况,可通过配置环境变量TENSORSIZE,及时释放设备内存,等号右侧为用户希望 Tensor 占用最大内存(必须为正整数),单位支持 KB、MB 和 GB。当 Tensor 占用的内存总量超过该值时会触发自动 GC,尝试释放出一些内存。默认情况会由仓颉 GC 自动判断,对无用 Tensor 对象进行内存回收。