下载
EN
注册

TIK数据定义

基本数据类型

  • 指用于数据定义和运算的每一个数的数值类型,包括:int8, uint8, int16, uint16, int32, uint32, float16, float32,uint1(bool)等。
  • 不同的TIK API支持不同的数据类型。
  • TIK为强类型语言,即不同的数据类型之间无法进行计算。

TIK对象的标准数据类型

TIK对象的标准数据类型,指在内存中的对象的数据类型,表示用户能以什么格式创建和读写数据,包括两种:Scalar标量数据、Tensor张量数据。

定义一个Scalar标量数据

TIK提供了Scalar接口用于定义Scalar数据,接口原型为:

# 接口原型(其中,name和init_value参数可以省略)
Scalar(dtype="int64", name="reg_buf", init_value=None)
表1 参数说明

参数名称

输入/输出

含义

dtype

输入

指定Scalar对象的数据类型,取值:

int8,uint8,int16, uint16,float16,int32,uint32,float32, int64, uint64

默认值:int64

name

输入

Scalar名字,支持string类型。名字支持数字0-9,A-Z,a-z及下划线组成的字符串,不允许以数字开头

默认值:reg_buf$(COUNT), COUNT从零开始计数。

init_value

输入

初始化值: 可以是立即数(支持int, float类型)、

Scalar变量(支持int, float类型)、

Tensor的某个值、

Expr:包括Scalar变量、立即数组成的Expr。

须知:

如果是Expr,立即数不能是float。

# 示例1:使用全部3个参数
# 定义一个初值为10.2的立即数(float32)的Scalar数据 data_A
# 结果:data_A : 10.2
data_A = tik_instance.Scalar(dtype = "float32", name = "data_A", init_value = 10.2)

# 示例2:省略name参数
# 定义一个初值为4的立即数(int16)的Scalar数据 data_B
# 结果:data_B : 4
data_B = tik_instance.Scalar(dtype = "int16", init_value = 4)

# 示例3:省略name和init_value参数
# 定义一个不设置初始值的int32的Scalar数据 data_C
# 结果:相当于声明了一个类型为int32的data_C
data_C = tik_instance.Scalar(dtype = "int32")
# 注意:如果init_value是Expr,Expr中的立即数不能是float,只能是int。
# 正确初始化:Expr(data_a + 1)由Scalar和int组成:
data_a = tik_instance.Scalar(dtype = "int16", init_value = 1)
data_b = tik_instance.Scalar(dtype = "int16", init_value = data_a + 1)
# 错误初始化:Expr(data_a + 1.2)由Scalar和float组成:
data_a = tik_instance.Scalar(dtype = "int16", init_value = 1)
data_b = tik_instance.Scalar(dtype = "int16", init_value = data_a + 1.2)

定义一个Tensor张量数据

Tensor数据对应于存储Buffer中的数据,TIK提供了Tensor接口用于定义Tensor数据,用户一般只需关注张量定义的数据类型(dtype)、形状(shape)与数据存储空间(scope)即可,函数原型为:

# 接口原型1(仅使用基本参数)
data = tik_instance.Tensor(dtype, shape, name = "", scope = tik.xxx)
# 接口原型2(使用拓展参数)
Tensor(dtype, shape, scope, name, enable_buffer_reuse=False, is_workspace=False, is_atomic_add=False, max_mem_size=None, init_value=None)
表2 参数说明

参数名称

输入/输出

含义

dtype

输入

指定Tensor对象的数据类型,取值:

uint8、int8、uint16、int16、float16、uint32、int32、float32、uint64、int64

shape

输入

指定Tensor对象的形状,支持List、Tuple类型,List、Tuple中的元素可以为立即数(int、float)、Scalar(int、uint)或Expr(int、uint)。不建议使用float类型的立即数。

shape的大小需要注意以下约束:
  • shape的大小不能超过8维。
  • shape大小请根据实际需要设置,但不能超过scope指定的存储单元的大小,存储单元的大小可通过get_soc_spec查询。

scope

输入

Tensor 内存类型范围,指定Tensor对象的所在buffer空间,取值:

  • scope_cbuf:L1 Buffer
  • scope_cbuf_out:L1OUT Buffer
  • scope_ubuf : Unified Buffer
  • scope_gm: Global Memory

其中scope_gm代表外部存储,其他为内部存储,只有将外部存储中的数据搬运到内部存储中,才能完成相应的计算;当scope为gm时,不能在for_range内部定义Tensor。

name

输入

Tensor名字,string类型。名字支持数字0-9,A-Z,a-z及下划线组成的字符串,不支持以数字开头。

tensor 的名字需要保持唯一。

说明:

scope为scope_gm时,不支持为"__fake_tensor"。

enable_buffer_reuse

输入

保留参数,不建议使用。

is_workspace

输入

bool值,默认值为False。

当该值为True时,代表当前Tensor是用于中间存储数据,而不用于输入输出。当取值为True时,需要保证scope为scope_gm,同时,不包含在输入输出中,即输入输出的Tensor的name中不含workspace中的Tensor。

is_atomic_add

输入

是否进行初始化GM空间,默认值为False。

  • 当设置为True时,默认将GM空间初始化为0。
  • 当设置为False时,GM空间不进行初始化。

当该值为True时,需要保证scope为scope_gm。

说明:

仅算子在网络中执行的场景下,此参数才生效。网络执行时,Graph Engine会根据此参数判断是否进行GM的空间初始化。

max_mem_size

输入

Tensor所占据的空间大小。

  • 当shape中不含变量时,用户可以不指定该值,或者指定max_mem_size为shape中各元素乘积再乘上dtype_size。
  • 当shape中包含变量时,用户既可以指定一个常数max_mem_size,且max_mem_size必须大于等于shape中各元素乘积再乘上dtype_size,并且为dtype_size的整数倍数;也可以不指定max_mem_size,程序运行时拿到shape的具体数值再分配内存(动态shape Tensor)。但当tensor的is_workspace字段为True时,必须指定max_mem_size。

init_value

输入

Tensor的初始值,为单个元素或list/tuple类型的值。

注意:该参数为预留参数,当前版本暂不支持。

# 定义一个数值类型为float16,存储空间在global_memory,size为128的Tensor data_B
data_B = tik_instance.Tensor("float16", (128,), name="data_B", scope=tik.scope_gm)

TIK会自动为每个申请的Tensor对象分配空间,并且避免各个数据块之间的地址冲突。且TIK会自动检查数据之间的数据依赖性,实现数据的同步。

注意:

用户定义的Tensor在内存分配时会对起始地址进行对齐,不同scope的对齐要求请参见通用约束。用户需特别注意因地址对齐导致的超出对应内存类型的总容量时,会引起编译报错。

Scalar数据赋值及修改值(set_as)

TIK提供了set_as接口,用于设置或改变Scalar的值。

from tbe import tik
tik_instance = tik.Tik()

# 示例1:将Scalar的值设置为一个立即数(整数)
s1 = tik_instance.Scalar(dtype = "int32")      # 声明一个int32类型的scalar变量 s1
s1.set_as(10)                                  # 将s1的值设为10

# 示例2:将Scalar的值设置为一个立即数(float)
s2 = tik_instance.Scalar(dtype = "float16")    # 声明一个float16类型的scalar变量 s2
s2.set_as(10.2)                                # 将s2的值设为10.2

# 示例3:将Scalar的值设置为另一个Scalar变量的值
s3 = tik_instance.Scalar(dtype = "float16", init_value = 2.4)  # 定义一个初值为2.4的标量s3
s4 = tik_instance.Scalar(dtype = "float16")    # 声明一个float16类型的scalar变量 s4
s4.set_as(s3)                                  # 将s3的值赋给s4

# 示例4:将Scalar的值设置为由Scalar和立即数组成的Expr的值
s5 = tik_instance.Scalar(dtype = "int32", init_value = 1)     # 定义一个初值为1的标量s5
s6 = tik_instance.Scalar(dtype = "int32")     # 声明一个int32类型的scalar变量 s6
s6.set_as(s5+20)                              # 将Expr(s5+20)的值赋给s6,结果:s6 = 21

# 示例5:将Scalar的值设置为Tensor的某个值
s7 = tik_instance.Scalar(dtype = "int16")
tensor_a = tik_instance.Tensor("int16", (4,), name="tensor_a", scope=tik.scope_ubuf)
s7.set_as(tensor_a[0]) 
'''
例如:
tensor_a = [1,2,3,4]
则经过赋值后:
s7 = 1
'''

为Tensor赋值

TIK提供了两种方式为Tensor赋值,设置或修改Tensor的内容。

  1. 方法一:通过set_as设置或改变Tensor的值。
    # 示例:
    from tbe import tik
    tik_instance = tik.Tik()
    data_A = tik_instance.Tensor("int16", (4,), name="data_A", scope=tik.scope_ubuf)
    data_B = tik_instance.Tensor("int16", (4,), name="data_B", scope=tik.scope_ubuf)
    data_B[1].set_as(4)             # 将data_B的第二个元素的值设为4                   (1)
    data_A[0].set_as(data_B[1])     # 将data_B的第二个元素的值赋给data_A的第一个元素   (2)
    '''
    例如:
    data_A = [1, 1, 1, 1]
    data_B = [0, 0, 0, 0]
    经过赋值语句(1)后:
    data_B = [0, 4, 0, 0]
    经过赋值语句(2)后:
    data_A = [4, 1, 1, 1]
    '''
  2. 方法二:通过Tensor数组下标,改变Tensor内容,相关接口:改变Tensor内容
    # 示例:
    '''
    data_A初始值为:[1.2, 2.4, 3.6, 4.8]
    '''
    from tbe import tik
    tik_instance = tik.Tik()
    data_A = tik_instance.Tensor("float16", (4,), name="data_A", scope=tik.scope_ubuf)
    scalar_B = tik_instance.Scalar(dtype="float16", name="scalar_B", init_value=2.0)
    scalar_idx = tik_instance.Scalar(dtype="int16", name="scalar_idx", init_value=1)
    
    # index为0, value为1.0, 下面这条语句将一个立即数赋给data_A的第一个元素
    data_A[0] = 1.0
    '''
    经过下标修改值后:
    data_A = [1.0, 2.4, 3.6, 4.8]
    '''
    # index为scalar_idx, value为scalar_B, 下面这条语句将一个scalar赋给data_A的第二个元素
    data_A[scalar_idx] = scalar_B  
    '''
    经过下标修改值后:
    data_A = [1.0, 2.0, 3.6, 4.8]
    '''
    # index为由scalar(int16)和立即数int组成的Expr, value为由scalar(float16)和立即数float组成的Expr, 下面这条语句将一个Expr赋给data_A的第三个元素
    data_A[scalar_idx+1] = scalar_B + 2.2
    '''
    经过下标修改值后:
    data_A = [1.0, 2.0, 4.2, 4.8]
    '''

除了以上两种修改Tensor值的方法,还有其余所有的Tensor运算API都可以对Tensor的值进行修改,例如:使用vec_add接口可以进行加法运算,将相加的结果赋给目的Tensor。

获取Tensor形状

TIK提供了shape接口用于获取Tensor shape。

# 示例:
from tbe import tik 
tik_instance = tik.Tik() 
data_A = tik_instance.Tensor("float16", (128,), name="data_A", scope=tik.scope_ubuf) 
data_A.shape   # 输出data_A的shape,为:[128]

改变Tensor形状

TIK提供了reshape接口用于改变Tensor shape。

# 示例:
from tbe import tik
tik_instance = tik.Tik()
data_A = tik_instance.Tensor("int16", (4,), name="data_A", scope=tik.scope_gm)
# 将data_A的形状由原来的4*1改变为2*2
data_A.reshape((2,2))
'''
初始:data_A = [1, 2, 3, 4]
经过reshape后:
data_A = [[1, 2], [3, 4]]
'''

通过下标切片获取Tensor部分数据

TIK提供了相关接口,可以通过Tensor数组下标,获得Tensor内部分数据,形成新的Tensor,相关接口:获取Tensor部分数据

# 示例:
from tbe import tik
tik_instance = tik.Tik()
data_A = tik_instance.Tensor("int16", (8,), name="data_A", scope=tik.scope_gm)
data_B = tik_instance.Tensor("int16", (4,), name="data_B", scope=tik.scope_gm)
data_B = data_A[4:]   # 对data_A进行切片,data_B包含data_A第5个数及其之后的所有数据
'''
例如:
data_A = [1,2,3,4,5,6,7,8]
经过切片取值后:
data_B = [5,6,7,8]
'''

以指定数据类型读取数据

TIK提供了reinterpret_cast_to接口,用于以用户指定的数据类型,读取相同内存数据,并对内存中的字节重新解释。

例如:128个float16,则可以使用reinterpret_cast_to指定以float32的方式读取该段数据,会获得64个float32,因此reinterpret_cast_to并非真正将每一个float16数据转化成对应float32数据,这一点与数据精度转换接口vec_conv不同。

# 示例:
from tbe import tik
tik_instance = tik.Tik()
data_A = tik_instance.Tensor("float16", (16,), name="data_A", scope=tik.scope_gm)
data_B = data_A.reinterpret_cast_to("uint32")
data_C = data_B.reinterpret_cast_to("float16")

"""调用结果如下:
输入数据:       # data_A 原本是float16的数据
data_A:
[ 4.812e+00  1.870e-04 -5.692e-02  2.528e-02 -9.225e+02 -1.431e+02
 -1.541e+01 -2.018e-03  1.653e-03 -4.090e+00  2.016e+01 -5.846e+04
 -8.072e-03  2.627e+00 -3.174e-02 -3.088e-01]
输出数据:
data_B:         # data_B 是以uint32的形式读取的
[ 169952464  645507913 3631866677 2552417204 3289847493 4213394698
 1094819874 3035736080]
data_C:         # data_C 是以float16的形式读取的
[ 4.812e+00  1.870e-04 -5.692e-02  2.528e-02 -9.225e+02 -1.431e+02
 -1.541e+01 -2.018e-03  1.653e-03 -4.090e+00  2.016e+01 -5.846e+04
 -8.072e-03  2.627e+00 -3.174e-02 -3.088e-01]
"""