附录 B 张量 API

创建张量

使用构造函数创建

仓颉 TensorBoost 为张量(Tensor)类型提供了多个构造函数,函数原型定义如下。其中 value 是传入的数组或者单个的值,入参 shape 是所创建 Tensor 的形状大小,dtype 用于指定 Tensor 的类型。

public struct Tensor {
    public init()
    public init(value: Float16, dtype!: Int32 = FLOAT16)
    public init(value: Float32, dtype!: Int32 = FLOAT32)
    public init(value: Float64, dtype!: Int32 = FLOAT64)
    public init(value: Int32, dtype!: Int32 = INT32)
    public init(value: Int64, dtype!: Int32 = INT64)
    public init(value: Bool, dtype!: Int32 = BOOL)
    public init(value: Array<Float16>, shape!: Array<Int64>, dtype!: Int32 = FLOAT16)
    public init(value: Array<Float32>, shape!: Array<Int64>, dtype!: Int32 = FLOAT32)
    public init(value: Array<Float64>, shape!: Array<Int64>, dtype!: Int32 = FLOAT64)
    public init(value: Array<Int32>, shape!: Array<Int64>, dtype!: Int32 = INT32)
    public init(value: Array<Int64>, shape!: Array<Int64>, dtype!: Int32 = INT64)
    public init(value: Array<UInt32>, shape!: Array<Int64>, dtype!: Int32 = UINT32)
    public init(value: Array<Bool>, shape!: Array<Int64>, dtype!: Int32 = BOOL)
}

当在 Tensor 构造函数中指定了 dtype 时,Tensor 类型以指定的 dtype 为准; 否则 Tensor 类型同传入的值或者 Array 类型相同。对于不同的值或者 Array 类型,可指定的 dtype 目前也做了限制,参考下表。

值或者 Array 类型dtype 可选类型
Int32, Int64, UInt32INT32, INT64, UINT32
Float16, Float32, Float64FLOAT16, FLOAT32, FLOAT64
BoolBOOL

构造 Tensor 时,如果输入不符合要求,会抛出异常,有以下几种类型:

  1. 初始化数据类型不支持
  2. 初始化参数值不符合要求
  3. 初始化 Tensor 的形状不符合要求

使用仓颉数组(数据类型为 Float32)创建大小为 Array<Int64>([2, 2]) 的张量 tensor。

let tensor = Tensor(Array<Float32>([1.0, 1.0, 1.0, 1.0]), shape: Array<Int64>([2, 2]))
print(tensor)

输出为:

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

创建符合正态分布的张量

函数 randomNormalTensor 提供正态分布随机初始化 Tensor 的能力,函数原型定义如下,其中 dtype 是传入的 Tensor 的数据类型入参, shape 是 Tensor 的 shape 大小,sigma 是标准差,seed 是全局种子,seed2 是 op 种子,两者一起作为随机数的种子, 函数返回一个 Tensor 类型。

public func randomNormalTensor(shape: Array<Int64>, sigma!: Float32 = 0.01, dtype!: Int32 = FLOAT32, seed!: Int64 = 0, seed2!: Int64 = 0): Tensor

创建数据类型为 Float32,大小为 Array<Int64>([2, 3]) ,数值按正态分布随机初始化的张量 randTensor。

let randTensor = randomNormalTensor(Array<Int64>([2, 3]))
print(randTensor)

输出为:

Tensor(shape=[2, 3], dtype=Float32, value=
[[2.41499580e-02 -6.09102054e-03 1.38444267e-02]
 [9.14979633e-03 -2.58914679e-02 -1.21405339e-02]])

【注意】: 全局种子和 op 种子均为 0 的情况下,随机数会使用时钟作为种子,生成每次都不相同的随机数。

创建符合均匀分布的张量

函数 uniformTensor 提供均匀分布随机初始化 Tensor 的能力,函数原型定义如下,其中 dtype 是传入的 Tensor 的数据类型,入参 shape 是 Tensor 的 shape 大小,以 0 为均值, boundary 为均匀分布的取值上限 (-boundary 为均匀分布的取值下限),函数返回一个 Tensor 类型。

public func uniformTensor(shape: Array<Int64>, boundary!: Float32 = 0.01, dtype!: Int32 = FLOAT32): Tensor

创建数据类型为 Float32,大小为 Array<Int64>([2, 3]) ,数值按均匀分布随机初始化的张量 randTensor。

let randTensor = uniformTensor(Array<Int64>([2, 3]))
print(randTensor)

输出为:

Tensor(shape=[2, 3], dtype=Float32, value=
[[6.29447401e-03 8.11583921e-03 -7.46026356e-03]
 [8.26751627e-03 2.64718570e-03 -8.04919191e-03]])

创建数值全 1 的张量

函数 onesTensor 提供全 1 初始化 Tensor 的能力,函数原型定义如下,其中 dtype 是传入的 Tensor 的数据类型,支持 Float16\Float32\Float64\Int32\Int64\UInt32\Bool 类型;shape 是 Tensor 的 shape 大小,函数返回一个 Tensor 类型。

public func onesTensor(shape: Array<Int64>, dtype!: Int32 = FLOAT32): Tensor

创建数据类型为 Float32,大小为 Array<Int64>([2, 3]),数值全为 1 的张量 onesTensor。

let onesTensor = onesTensor(Array<Int64>([2, 3]))
print(onesTensor)

输出为:

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]])

创建数值全 0 的张量

函数 zerosTensor 提供全 0 初始化 Tensor 的能力,函数原型定义如下,其中 dtype 是传入的 Tensor 的数据类型,支持 Float16\Float32\Float64\Int32\Int64\UInt32\Bool 类型;shape 是 Tensor 的 shape 大小,函数返回一个 Tensor 类型。

public func zerosTensor(shape: Array<Int64>, dtype!: Int32 = FLOAT32): Tensor

创建数据类型为 Float32,大小为 Array<Int64>([2, 3]),数值全为 0 的张量 zerosTensor。

let zerosTensor = zerosTensor(Array<Int64>([2, 3]))
print(zerosTensor)

输出为:

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]])

创建指定数值和大小的张量

函数 fillTensor 提供指定数值 data 和大小的 Tensor 的能力,data 可以是 Float16\Float32\Float64\Int32\Bool 的数据类型,根据填充的数据类型不同,提供了五个接口,函数原型定义如下,其中 shape 是 Tensor 的 shape 大小,data 是填充数据,函数返回一个 Tensor 类型。

public func fillTensor(shape: Array<Int64>, data: Float16): Tensor
public func fillTensor(shape: Array<Int64>, data: Float32): Tensor
public func fillTensor(shape: Array<Int64>, data: Float64): Tensor
public func fillTensor(shape: Array<Int64>, data: Int32): Tensor
public func fillTensor(shape: Array<Int64>, data: Bool): Tensor

创建数值全是 2.0,大小为 Array<Int64>([6]) 的张量 newTensor。

let newTensor = fillTensor(Array<Int64>([6]), Float32(2.0))
print(newTensor)

输出为:

Tensor(shape=[6], dtype=Float32, value=
[2.00000000e+00 2.00000000e+00 2.00000000e+00 2.00000000e+00 2.00000000e+00 2.00000000e+00])

创建对角张量

函数 eyeTensor 提供一个对角线是 1,其他位置是 0 的 Tensor,支持的类型有 FLOAT16\FLOAT32\FLOAT64\INT32\INT64\UINT32\BOOL,函数原型定义如下:

public func eyeTensor(n: Int64, dtype!: Int32 = FLOAT32): Tensor
public func eyeTensor(n: Int64, m: Int64, dtype!: Int32 = FLOAT32): Tensor

其中,n、m 分别对应输出矩阵的 row 和 column,没有传入 m,则 m = n ,dtype 是传入的数据类型,默认值为 FLOAT32。

创建数据类型为 Float32,大小为 Array<Int64>([2, 3]),对角线 Tensor,使用以下代码:

let eyeTensor = eyeTensor(2, 3)
print(eyeTensor)

输出为:

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

创建内容为空的张量

函数 noneTensor 提供空 Tensor 初始化的能力,该函数返回一个内置的 Tensor 类型的全局变量 NONETENSOR,表示 Tensor 对象中没有任何值。NONETENSOR 的 dtype 和 shape 无意义,无法调用 getDtype()getShape()isInt32() 等方法获取属性,也无法调用 evaluate() 方法进行求值。

public func noneTensor(): Tensor

创建内容为空的张量。可以调用 Tensor 的成员函数 isNone() 来判断 Tensor 对象是否为 NONETENSOR。

let none = noneTensor()

这种初始化方式可用于函数入参类型为 Tensor,但某些情况下可能为空的场景。

使用 initialize 方法创建张量

仓颉 TensorBoost 为以上提到的各种初始化方式提供了统一的调用方法:initialize 函数,这种初始化方式常用于网络中权重的初始化,如 Dense 或 Conv2d 等,参见[附录 D](###Dense 层)。定义如下:

public func initialize(shape: Array<Int64>, initializer: BaseInitializer, dtype!: Int32 = FLOAT32): Tensor
public func initialize(shape: Array<Int64>, initType!: InitType = InitType.NORMAL, dtype!: Int32 = FLOAT32): Tensor

其中:

  • BaseInitializer 是初始化器的基类,仓颉 TensorBoost 目前提供了 4 个子类:

    • RandomNormalInitializer 可以使生成的数据满足正态分布,原型定义如下,入参 sigma 代表生成数据的标准差。

      public class RandomNormalInitializer <: BaseInitializer {
          public init(sigma!: Float32 = 0.01)
      }
      
    • TruncatedNormalInitializer 生成截断正态分布,数据要求限制在[left, right]范围内。原型定义如下,入参 sigma 代表标准差;left、right 分别代表左右数据边界。

      public class TruncatedNormalInitializer <: BaseInitializer {
          public init()
          public init(sigma: Float32, left!: Float32 = -2.0, right!: Float32 = 2.0)
      }
      
    • UniformInitializer 生成在[-range, range] 之间的均匀随机分布数据。原型定义如下,入参 range 代表生成数据的边界范围。

      public class UniformInitializer <: BaseInitializer {
          public init(range!: Float32 = 0.07)
      }
      
    • ReadFileInitializer 从文件生成 Tensor。

      public class ReadFileInitializer <: BaseInitializer {
          public init(fromFile!: String = "")
      }
      
    • XavierUniformInitializer 按照 xavier uniform 算法生成在 [-boundary, boundary] 之间的均匀分布的数据。原型定义如下,入参 gain 代表缩放因子。

      public class XavierUniformInitializer <: BaseInitializer {
          public init(gain!: Float32 = 1.0)
      }
      

XavierUniformInitializer 按照 xavier uniform 算法生成在 [-boundary, boundary] 之间的均匀分布的数据,其中 boundary 的计算公式如下:

$$ boundary = gain * \sqrt{\frac{6}{n_{in} + n_{out}}} $$

上述公式中,$n_{in}$ 为权重的输入单元数,$n_{out}$ 为权重的输出单元数。

InitType 是枚举类型,定义如下:

public enum InitType { ZERO // "全 0" 初始化
              | ONE // "全 1" 初始化
              | NORMAL // "sigma 为 0.01 的正态分布" 初始化
              | TRUNCNORM  // "sigma 为0.01, left 为-2.0, right 为2.0的截断正态分布" 初始化
              | UNIFORM   // "range为0.07的均匀分布" 初始化
              | XAVIERUNIFORM // "gain 为 1 的 XAVIER 均匀分布" 初始化
}

通过加载 npy 文件来初始化 Tensor

npy 是 Python 的 Numpy 模块保存 ndarray 的标准格式。仓颉 TensorBoost 提供了 NpyFile 类与其对应,支持从 npy 文件初始化 Tensor ,也支持将 Tensor 保存成 npy 文件。 首先需要创建 NpyFile 类型,NpyFile 的第一个参数为需要读写的文件名(或路径),如路径不存在会自动创建;第二个参数指定读写模式。NpyFile 类定义如下:

public class NpyFile {
    public init(name: String, mode: String)
    public func close()
}

可以通过 Tensor 的静态 load 方法读取 npy 文件进行初始化,仅支持读模式"rb",读取的顺序需要同保存的顺序保持一致。具体用法如下:

let npy = NpyFile("test.npy", "rb")
let a = Tensor.load(npy)
let b = Tensor.load(npy)
npy.close()

另外也可以通过 Tensor 的静态 save 方法将其保存成 npy 文件,仅支持写模式"wb"。具体用法如下:

let npy = NpyFile("test.npy", "wb")
let input0 = Tensor(Array<Float32>([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]), shape: Array<Int64>([2, 3]))
let input1 = Tensor([true, false, true], shape: Array<Int64>([3]))
Tensor.save(npy, input0)
Tensor.save(npy, input1)
npy.close()

注意: close() 方法可以提前释放文件句柄。用户如果选择不调用,仓颉的 GC 也会自动进行释放,不会造成资源泄漏。

张量属性与方法

获取张量数据类型

函数 getDtype()getStrDtype() 用于获取张量的数据类型。

public func getDtype(): Int32
public func getStrDtype(): String

应用示例

let input = randomNormalTensor(Array<Int64>([32, 1, 32, 32]))
let dtype = input.getDtype()
let strDtype = input.getStrDtype()
print("dtype is ${dtype}\n")
print("strDtype is ${strDtype}\n")

输出为:

dtype is 0
strDtype is FLOAT32

获取张量形状大小

函数 getShape() 用于获取张量的形状大小。

public func getShape(): Array<Int64>

应用示例

let input = randomNormalTensor(Array<Int64>([32, 1, 32, 32]))
let shape= input.getShape()
print("shape is ${shape.toString()}\n")

输出为:

shape is [32, 1, 32, 32]

获取张量维度大小

函数 getShapeDims() 用于获取张量的维度大小。

public func getShapeDims(): Int64

应用示例

let input = randomNormalTensor(Array<Int64>([32, 1, 32, 32]))
let shapeDims= input.getShapeDims()
print("shapeDims is ${shapeDims.toString()}\n")

输出为:

shapeDims is 4

判断是否是 zerosTensor 接口初始化的 Tensor

函数 isZero() 判断是否是 zerosTensor 接口初始化的 Tensor,常用于性能优化。

public func isZero(): Bool

应用示例

let zerosTensor: Tensor = zerosTensor(Array<Int64>([3, 2]), dtype: FLOAT32)
if (zerosTensor.isZero()) {
    print("Zero\n")
} else {
    print("not Zero\n")
}

输出为:

Zero

判断张量是否为空

函数 isNone() 用于判断张量是否为空。

public func isNone(): Bool

应用示例

let none = noneTensor()
if (none.isNone()) {
    print("None\n")
} else {
    print("not None\n")
}

输出为:

None

获取张量的名称

属性 name 用于获取张量的 name,默认值为空。

public prop name: String

应用示例

let input1 = parameter(onesTensor(Array<Int64>([2, 2])), "input1")
let name1 = input1.name
print("name1 is ${name1}\n")

let input2 = onesTensor(Array<Int64>([2, 2]))
let name2 = input2.name
print("name2 is ${name2}\n")

输出为:

name1 is input1
name2 is

获取对应的 Mindspore Handle 值

函数 getMsHandle() 获取对应的 Mindspore Handle 值,常用于仓颉与 Mindspore 的数据同步操作。

public func getMsHandle()

判断是否需要从主机同步数据到设备

函数 enableSyncHostToDevice() 判断是否需要从主机同步数据到设备,常用于数据集,用户无需主动调用

public func enableSyncHostToDevice()

判断张量是否需要更新梯度

属性 requiresGrad 用于控制网络参数是否需要更新梯度,默认是 false。

mut prop requiresGrad: Bool

应用示例

var input = onesTensor(Array<Int64>([2, 2]))
print("input's requiresGrad is ${input.requiresGrad}\n")
var param = parameter(input, "weight")
print("param's requiresGrad is ${param.requiresGrad}\n")

输出为:

input's requiresGrad is false
param's requiresGrad is true

判断张量数据类型

例如函数 isInt32()isFloat32()isFloat64()isBool() 分别用于判断张量是否是 Int32、Float32、Float64 或者 Bool 数据类型。

当前提供的判断张量数据类型的接口如下:

public func isInt32(): Bool
public func isInt64(): Bool
public func isFloat16(): Bool
public func isFloat32(): Bool
public func isFloat64(): Bool
public func isBool(): Bool
public func isUInt32(): Bool

对某一个确定的 Tensor 而言,调用以上三个方法只有一个为 true,即某个确定的 Tensor 只能为一种类型。

应用示例

let a = onesTensor(Array<Int64>([1]), dtype: INT32)

print("a isInt32? : ${a.isInt32()}\n")
print("a isFloat32? : ${a.isFloat32()}\n")
print("a isBool? : ${a.isBool()}\n")

输出为:

a isInt32? : true
a isFloat32? : false
a isBool? : false

张量转为数组

例如函数 toArrayInt32()toArrayFloat32()toArrayFloat64()toArrayBool() 分别用于将 Int32、Float32、Float64 或者 Bool 类型张量转换成对应数据类型的数组。

提供toScalar<T>(),可以将 Tensor 转化为标量,例如当一个 dtype 为 Int32 类型的 Tensor 为标量时(shape 为空数组),应调用toScalar<Int32>()方法。

当前提供的张量转数组或者标量的方法如下:

public func toArrayInt32(): Array<Int32>
public func toArrayInt64(): Array<Int64>
public func toArrayFloat16(): Array<Float16>
public func toArrayFloat32(): Array<Float32>
public func toArrayFloat64(): Array<Float64>
public func toArrayBool(): Array<Bool>
public func toArrayUInt32(): Array<UInt32>
public func toScalar<T>(): T

支持使用 toScalar<T>() 的类型受到接口 Number 约束,当前 Int32 Int64 Float16 Float32 Float64 Bool Uint32 都实现了 Number 接口提供的方法

public interface Number<T> {
    static func size(): Int64
    static func zero(): T
    static func dtype(): Int32
    static func dtypeName(): String
    static func initArrayTensorByValue(
        value: Array<T>,
        shapePtr: CPointer<Int64>,
        shapeDims: Int64,
        dtype: Int32
    ): MSTensorHandle
    static func copyTensorDataToCangjie(dest: Array<T>, tensorHandle: MSTensorHandle): Unit
}

应用示例:

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 而言,只能将其转为与数据类型相对应的数组。如果某 Tensor 对象定义为 Int32 类型,则不允许调用 toArrayFloat32()toArrayBool() 方法。

evaluate 方法

以静态图模式运行时,evaluate 之前计算得出的 Tensor 是无值的。需要调用 Tensor 的 evaluate() 方法触发计算图的执行之后,才能将无值的 Tensor 变为有值的 Tensor。

public func evaluate(): Tensor

如下代码以静态图模式运行时,用仓颉数组创建的t1t2是有值的 Tensor,t5为计算得出的 Tensor 且被 evaluate 过,是有值的。t4为计算得出的 Tensor 且未被 evaluate 过,是无值的。如果不确定某个 Tensor 是否有值,可以调用 isConcrete 方法进行判断。

let t1 = Tensor(Array<Float32>([1.0, 1.0, 1.0]), shape: Array<Int64>([3]))
let t2 = Tensor(Array<Float32>([2.0, 2.0, 2.0]), shape: Array<Int64>([3]))

let t3 = t1 + t2
let t4 = t3 * t3

let t5: Tensor = t4.evaluate()
let resArr: Array<Float32> = t5.toArrayFloat32()
print("resArr: ${resArr.toString()}\n")

上述代码中,t3/t4未被 evaluate 过,不允许对t3/t4直接调用 toArrayFloat32()

判断张量是否为 Parameter

public func isParameter(): Bool
let t1 = initialize(Array<Int64>([2]), initType:InitType.NORMAL, dtype:FLOAT32)
print("t1 is Parameter? --- ${t1.isParameter()}\n")
let t2 = parameter(initialize(Array<Int64>([2]), initType:InitType.NORMAL, dtype:FLOAT32), "param")
print("t2 is Parameter? --- ${t2.isParameter()}\n")

输出为:

t1 is Parameter? --- false
t2 is Parameter? --- true

判断张量是否有值

以静态图模式运行时,evaluate 之前计算得出的 Tensor 是无值的。可以调用 Tensor 的 isConcrete() 方法来判断 Tensor 是否需要 evaluate 操作。

public func isConcrete(): Bool

如下代码以静态图模式运行时,t3 是无值的 Tensor,t4 是有值的 Tensor:

let t1 = Tensor(Array<Float32>([1.0, 1.0, 1.0]), shape: Array<Int64>([3]))
let t2 = Tensor(Array<Float32>([2.0, 2.0, 2.0]), shape: Array<Int64>([3]))

let t3 = t1 + t2
let t4 = t3.evaluate()
print("t3 is concrete?---${t3.isConcrete()}\n")
print("t4 is concrete?---${t4.isConcrete()}\n")

输出为:

t3 is concrete?---false
t4 is concrete?---true

张量自动微分

张量支持微分,提供微分初值接口和微分累加接口供自动微分系统使用

extend Tensor <: Differentiable<Tensor> {
    @Differentiable
    public func TangentZero(): Tensor
    @Differentiable
    public func TangentAdd(y: Tensor): Tensor
}

查看张量数值

仓颉 TensorBoost 提供了多种 print 重载函数,用于查看张量数值。

查看单个张量数值

public func print(tensor: Tensor): Unit

应用示例

let input = Tensor(Array<Int32>([1, 2, 3, 4]), shape: Array<Int64>([1, 4]))
print(input)

输出为:

Tensor(shape=[1, 4], dtype=Int32, value=
[[1 2 3 4]])

查看描述和单个张量数值

public func print(str: String, tensor: Tensor): Unit

应用示例

let input = Tensor(Array<Int32>([1, 2, 3, 4]), shape: Array<Int64>([1, 4]))
print("input:", input)

输出为:

input:
Tensor(shape=[1, 4], dtype=Int32, value=
[[1 2 3 4]])

查看张量数组

public func print(tensors: Array<Tensor>): Unit

应用示例

let input1 = Tensor(Array<Int32>([1, 2, 3, 4]), shape: Array<Int64>([1, 4]))
let input2 = Tensor(Array<Int32>([5, 6, 7, 8]), shape: Array<Int64>([1, 4]))
print([input1, input2])

输出为:

Tensor(shape=[1, 4], dtype=Int32, value=
[[1 2 3 4]])
Tensor(shape=[1, 4], dtype=Int32, value=
[[5 6 7 8]])

查看描述和张量数组

public func print(str: String, tensors: Array<Tensor>): Unit

应用示例

let input1 = Tensor(Array<Int32>([1, 2, 3, 4]), shape: Array<Int64>([1, 4]))
let input2 = Tensor(Array<Int32>([5, 6, 7, 8]), shape: Array<Int64>([1, 4]))
print("Print input1 and input2:", [input1, input2])

输出为:

Print input1 and input2:
Tensor(shape=[1, 4], dtype=Int32, value=
[[1 2 3 4]])
Tensor(shape=[1, 4], dtype=Int32, value=
[[5 6 7 8]])

查看(描述, Tensor) 数组

public func print(tensors: Array<(String, Tensor)>): Unit

应用示例

let input1 = Tensor(Array<Int32>([1, 2, 3, 4]), shape: Array<Int64>([1, 4]))
let input2 = Tensor(Array<Int32>([5, 6, 7, 8]), shape: Array<Int64>([1, 4]))
print([("input1:", input1), ("input2:", input2)])

输出为:

input1:
Tensor(shape=[1, 4], dtype=Int32, value=
[[1 2 3 4]])
input2:
Tensor(shape=[1, 4], dtype=Int32, value=
[[5 6 7 8]])

查看并存储张量信息

函数 logTensor() 把 Tensor 打印到文件中。

public func logTensor(tensor: Tensor, fileName: String, appendMode!: Bool = true): Unit

动静态图下张量输出的对比

在动态图模式下,打印总是按照代码执行的顺序进行输出;在静态图模式下,当 tensor 不需要通过 op 运算获得时,直接输出结果;否则要在 evaluate()后再进行输出操作。以下代码在动态图模式下的行为与静态图模式下不同:

func TestPrint1(): Unit
{
    print("STTest: Print Begin\n")
    var input1 = Tensor(Array<Int32>([1, 2, 3, 4]), shape: Array<Int64>([1, 4]))
    var input2 = Tensor(Array<Int32>([5, 6, 7, 8]), shape: Array<Int64>([1, 4]))
    var output1 = input1 + input2
    print("input1", input1) // 第 1 次打印
    print("Sum of inpu1 and input2:", output1) // 第 2 次打印
    print([("input1:", input1), ("input2:", input2), ("output1:", output1)]) // 第 3 次打印
    print("Print input1 and input2:", [input1, input2]) // 第 4 次打印
    var res = output1.evaluate()
    print("STTest: Print End\n")
}

动态图下的运行结果为:

ST-Test
STTest: Print Begin
input1
Tensor(shape=[1, 4], dtype=Int32, value=
[[1 2 3 4]])
Sum of inpu1 and input2:
Tensor(shape=[1, 4], dtype=Int32, value=
[[6 8 10 12]])
input1:
Tensor(shape=[1, 4], dtype=Int32, value=
[[1 2 3 4]])
input2:
Tensor(shape=[1, 4], dtype=Int32, value=
[[5 6 7 8]])
output1:
Tensor(shape=[1, 4], dtype=Int32, value=
[[6 8 10 12]])
Print input1 and input2:
Tensor(shape=[1, 4], dtype=Int32, value=
[[1 2 3 4]])
Tensor(shape=[1, 4], dtype=Int32, value=
[[5 6 7 8]])
STTest: Print End

静态图下的运行结果为:

ST-Test
STTest: Print Begin
input1
Tensor(shape=[1, 4], dtype=Int32, value=
[[1 2 3 4]])
Print input1 and input2:
Tensor(shape=[1, 4], dtype=Int32, value=
[[1 2 3 4]])
Tensor(shape=[1, 4], dtype=Int32, value=
[[5 6 7 8]])
Sum of inpu1 and input2:
Tensor(shape=[1, 4], dtype=Int32, value=
[[ 6  8 10 12]])
input1:
Tensor(shape=[1, 4], dtype=Int32, value=
[[1 2 3 4]])
input2:
Tensor(shape=[1, 4], dtype=Int32, value=
[[5 6 7 8]])
output1:
Tensor(shape=[1, 4], dtype=Int32, value=
[[ 6  8 10 12]])
STTest: Print End

可以看到在动态图下是按照 1、2、3、4 的顺序进行打印;而在静态图下最终打印结果的顺序为 1、4、2、3。 这是因为:第 1 次打印中 input1 是有值的,所以结果立刻进行了输出;而第 2 次打印中 output1 需要通过运算获得,所以结果在 evaluate 之后输出;第 3 次打印中包含了 1 个需要运算获得的 output1,所以也要在 evaluate 之后输出,第 4 次打印中的 2 个 Tensor 都是有值的,可以立刻输出。

Tensor 运算符重载

在仓颉 TensorBoost 中,支持对 Tensor 进行加减乘除等操作,分别对应了算子函数,以下是 Tensor 支持的运算符重载: 当前 Scalar 作为左操作数,运算符重载支持的类型有:Float16、Float32、Float64、Int32。

操作符操作符含义替换的仓颉 TensorBoost 算子
-Negative: unaryneg(input1)
*Multiply: binarymul(input1, input2)
/Divide: binaryrealDiv(input1, input2)
+Add: binaryadd(input1, input2)
-Subtract: binarysub(input1, input2)
<Less than: binaryless(input1, input2)
<=Less than or equal to: binarylessEqual(input1, input2)
>Greater than: binarygreater(input1, input2)
>=Greater than or equal to: binarygreaterEqual(input1, input2)
==Equal: binaryequal(input1, input2)
!=Not equal: binarynotEqual(input1, input2)
[]Get Item: binarygetTupleItem(input: Tensor, index: Int64)
**Pow: binary (right: 2.0)square(input1)
**Pow: binary (right: 0.5)sqrt(input1)
**Pow: binary (right: Except for 2.0 and 0.5)pow(input1, input2)