Tensor与Parameter

查看源文件

Tensor

张量(Tensor)是MindSpore网络运算中的基本数据结构,其功能类似Numpy数组(ndarray),MindSpore使用Tensor来表示神经网络中传递的数据。

关于Tensor创建、Tensor运算、Tensor与NumPy转换等操作,请参考张量 Tensor

Tensor索引支持

Tensor 支持单层与多层索引取值,赋值以及增强赋值,支持动态图(PyNative)以及静态图(Graph)模式。

索引取值

索引值支持intboolNoneellipsissliceTensorListTuple

  • int索引取值

    支持单层和多层int索引取值,单层int索引取值:tensor_x[int_index],多层int索引取值:tensor_x[int_index0][int_index1]...

    int索引取值操作的是第零维,索引值小于第零维长度,在取出第零维对应位置数据后,会消除第零维。

    例如,如果对一个shape(3, 4, 5)的tensor进行单层int索引取值,取得结果的shape(4, 5)

    多层索引取值可以理解为,后一层索引取值在前一层索引取值结果上再进行int索引取值。

    示例如下:

    import mindspore as ms
    import mindspore.numpy as np
    tensor_x = ms.Tensor(np.arange(2 * 3 * 2).reshape((2, 3, 2)))
    data_single = tensor_x[0]
    data_multi = tensor_x[0][1]
    print('data_single:')
    print(data_single)
    print('data_multi:')
    print(data_multi)
    
    data_single:
    [[0 1]
     [2 3]
     [4 5]]
    data_multi:
    [2 3]
    
  • bool索引取值

    支持单层和多层bool索引取值,单层bool索引取值:tensor_x[True],多层bool索引取值:tensor_x[True][True]...

    bool索引取值操作的是第零维,在取出所有数据后,会在axis=0轴上扩展一维,对应True/False,该维长度分别为1/0。False将会在shape中引入0,因此暂只支持True

    例如,对一个shape(3, 4, 5)的tensor进行单层True索引取值,取得结果的shape(1, 3, 4, 5)

    多层索引取值可以理解为,后一层索引取值在前一层索引取值结果上再进行bool索引取值。

    示例如下:

    import mindspore as ms
    import mindspore.numpy as np
    tensor_x = ms.Tensor(np.arange(2 * 3).reshape((2, 3)))
    data_single = tensor_x[True]
    data_multi = tensor_x[True][True]
    print('data_single:')
    print(data_single)
    print('data_multi:')
    print(data_multi)
    
    data_single:
    [[[0 1 2]
      [3 4 5]]]
    data_multi:
    [[[[0 1 2]
       [3 4 5]]]]
    
  • None索引取值

    None索引取值和True索引取值一致,可参考True索引取值,这里不再赘述。

  • ellipsis索引取值

    支持单层和多层ellipsis索引取值,单层ellipsis索引取值:tensor_x[...],多层ellipsis索引取值:tensor_x[...][...]...

    ellipsis索引取值操作在所有维度上取出所有数据。一般多作为Tuple索引的组成元素,Tuple索引将于下面介绍。

    例如,对一个shape(3, 4, 5)的tensor进行ellipsis索引取值,取得结果的shape依然是(3, 4, 5)

    import mindspore as ms
    import mindspore.numpy as np
    tensor_x = ms.Tensor(np.arange(2 * 3).reshape((2, 3)))
    data_single = tensor_x[...]
    data_multi = tensor_x[...][...]
    print('data_single:')
    print(data_single)
    print('data_multi:')
    print(data_multi)
    
    data_single:
    [[0 1 2]
    [3 4 5]]
    data_multi:
    [[0 1 2]
    [3 4 5]]
    
  • slice索引取值

    支持单层和多层slice索引取值,单层slice索引取值:tensor_x[slice_index],多层slice索引取值:tensor_x[slice_index0][slice_index1]...

    slice索引取值操作的是第零维,取出第零维所切到位置的元素,slice不会降维,即使切到长度为1,区别于int索引取值。

    例如,tensor_x[0:1:1] != tensor_x[0],因为shape_former = (1,) + shape_latter

    多层索引取值可以理解为,后一层索引取值在前一层索引取值结果上再进行slice索引取值。

    slicestartstopstep组成。start默认值为0,stop默认值为该维长度,step默认值为1。

    例如,tensor_x[:] == tensor_x[0:length:1]

    示例如下:

    import mindspore as ms
    import mindspore.numpy as np
    tensor_x = ms.Tensor(np.arange(4 * 2 * 2).reshape((4, 2, 2)))
    data_single = tensor_x[1:4:2]
    data_multi = tensor_x[1:4:2][1:]
    print('data_single:')
    print(data_single)
    print('data_multi:')
    print(data_multi)
    
    data_single:
    [[[ 4  5]
     [ 6  7]]
    
    [[12 13]
     [14 15]]]
    data_multi:
    [[[12 13]
     [14 15]]]
    
  • Tensor索引取值

    支持单层和多层Tensor索引取值,单层Tensor索引取值:tensor_x[tensor_index],多层Tensor索引取值:tensor_x[tensor_index0][tensor_index1]...

    Tensor索引取值操作的是第零维,取出第零维对应位置的元素。

    索引Tensor数据类型支持int型和bool型。

    当数据类型是int型时,可以是(int8, int16, int32, int64),值必须为非负数,且小于第零维长度。

    Tensor索引取值得到结果的data_shape = tensor_index.shape + tensor_x.shape[1:]

    例如,对一个shape(6, 4, 5)的tensor通过shape(2, 3)的tensor进行索引取值,取得结果的shape(2, 3, 4, 5)

    当数据类型是bool型时,Tensor索引取值得到结果的维度是 tensor_x.ndim - tensor_index.ndim + 1

    tensor_index 中True的数量是 num_truetensor_x 的shape是 (N0, N1, ... Ni-1, Ni, Ni+1, ..., Nk), tensor_index 的shape是 (N0, N1, ... Ni-1), 则返回值的shape是 (num_true, Ni+1, Ni+2, ... , Nk)

    示例如下:

    from mindspore import dtype as mstype
    import mindspore as ms
    import mindspore.numpy as np
    tensor_x = ms.Tensor([1, 2, 3])
    tensor_index = ms.Tensor([True, False, True], dtype=mstype.bool_)
    output = tensor_x[tensor_index]
    print(output)
    
    [1 3]
    

    多层索引取值可以理解为,后一层索引取值在前一层索引取值结果上再进行Tensor索引取值。

    示例如下:

    from mindspore import dtype as mstype
    import mindspore as ms
    import mindspore.numpy as np
    tensor_x = ms.Tensor(np.arange(4 * 2 * 3).reshape((4, 2, 3)))
    tensor_index0 = ms.Tensor(np.array([[1, 2], [0, 3]]), mstype.int32)
    tensor_index1 = ms.Tensor(np.array([[0, 0]]), mstype.int32)
    data_single = tensor_x[tensor_index0]
    data_multi = tensor_x[tensor_index0][tensor_index1]
    print('data_single:')
    print(data_single)
    print('data_multi:')
    print(data_multi)
    
    data_single:
    [[[[ 6  7  8]
      [ 9 10 11]]
    
     [[12 13 14]
      [15 16 17]]]
    
    
    [[[ 0  1  2]
      [ 3  4  5]]
    
     [[18 19 20]
      [21 22 23]]]]
    data_multi:
    [[[[[ 6  7  8]
       [ 9 10 11]]
    
      [[12 13 14]
       [15 16 17]]]
    
    
     [[[ 6  7  8]
       [ 9 10 11]]
    
      [[12 13 14]
       [15 16 17]]]]]
    
  • List索引取值

    支持单层和多层List索引取值,单层List索引取值:tensor_x[list_index],多层List索引取值:tensor_x[list_index0][list_index1]...

    List索引取值操作的是第零维,取出第零维对应位置的元素。

    索引List数据类型必须是int、bool或两者混合。若数据类型为int,则取值在[-dimension_shape, dimension_shape-1]之间;若数据类型为bool, 则限制bool个数为对应维度长度,筛选对应维度上值为True的元素;若值为前两者混合,则bool类型的True/False将转为int类型的1/0

    List索引取值得到结果的data_shape = list_index.shape + tensor_x.shape[1:]

    例如,对一个shape(6, 4, 5)的tensor通过shape(3,)的tensor进行索引取值,取得结果的shape(3, 4, 5)

    多层索引取值可以理解为,后一层索引取值在前一层索引取值结果上再进行List索引取值。

    示例如下:

    import mindspore as ms
    import mindspore.numpy as np
    tensor_x = ms.Tensor(np.arange(4 * 2 * 3).reshape((4, 2, 3)))
    list_index0 = [1, 2, 0]
    list_index1 = [True, False, True]
    data_single = tensor_x[list_index0]
    data_multi = tensor_x[list_index0][list_index1]
    print('data_single:')
    print(data_single)
    print('data_multi:')
    print(data_multi)
    
    data_single:
    [[[ 6  7  8]
     [ 9 10 11]]
    
    [[12 13 14]
     [15 16 17]]
    
    [[ 0  1  2]
     [ 3  4  5]]]
    data_multi:
    [[[ 6  7  8]
     [ 9 10 11]]
    
    [[ 0  1  2]
     [ 3  4  5]]]
    
  • Tuple索引取值

    索引Tuple的数据类型可以为intboolNonesliceellipsisTensorListTuple。支持单层和多层Tuple索引取值,单层Tuple索引取值:tensor_x[tuple_index],多层Tuple索引取值:tensor_x[tuple_index0][tuple_index1]...Tuple中包含的ListTuple包含元素规则与单独的List规则相同,其他元素规则与单独元素也相同。

    索引Tuple中元素按照最终索引Broadcast规则,分为Basic IndexAdvanced Index两类。Basic Index包含sliceellipsisintNone四种类型,Advanced Index包含boolTensorListTuple等类型。索引过程中,所有的Advanced Index将会做Broadcast,若Advaned Index连续,最终broadcast shape将插入在第一个Advanced Index位置;若不连续,则broadcast shape插入在0位置。

    索引里除None扩展对应维度,bool扩展对应维度后与Advanced Index做Broadcast。除ellipsisboolNone外每个元素操作对应位置维度,即Tuple中第0个元素操作第零维,第1个元素操作第一维,以此类推。每个元素的索引规则与该元素类型索引取值规则一致。

    Tuple索引里最多只有一个ellipsisellipsis前半部分索引元素从前往后对应Tensor第零维往后,后半部分索引元素从后往前对应Tensor最后一维往前,其他未指定的维度,代表全取。

    元素里包含的Tensor数据类型必须是int型或bool型,int型可以是(int8, int16, int32, int64),值必须为非负数,且小于第零维长度。

    例如,tensor_x[0:3, 1, tensor_index] == tensor_x[(0:3, 1, tensor_index)],因为0:3, 1, tensor_index就是一个Tuple

    多层索引取值可以理解为,后一层索引取值在前一层索引取值结果上再进行Tuple索引取值。

    示例如下:

    from mindspore import dtype as mstype
    import mindspore as ms
    import mindspore.numpy as np
    tensor_x = ms.Tensor(np.arange(2 * 3 * 4).reshape((2, 3, 4)))
    tensor_index = ms.Tensor(np.array([[1, 2, 1], [0, 3, 2]]), mstype.int32)
    data = tensor_x[1, 0:1, tensor_index]
    print('data:')
    print(data)
    
    data:
    [[[13]
     [14]
     [13]]
    
    [[12]
     [15]
     [14]]]
    

索引赋值

对于形如: tensor_x[index] = valueindex的类型支持intboolellipsissliceNoneTensorListTuple

value的类型支持NumberTupleListTensor。被赋的值会首先被转换为Tensor,数据类型与原Tensor(tensor_x)相符。

valueNumber时,可以理解为将tensor_x[index]索引对应元素都更新为Number

value为数组,即只包含NumberTupleListTensor时,value.shape需要可以与tensor_x[index].shape做广播,将value广播到tensor_x[index].shape后,更新tensor_x[index]对应的值。

valueTupleList时,若value中元素包含NumberTupleListTensor等多种类型,该TupleList 目前只支持一维。

valueTupleList,且存在Tensor时,非Tensor的元素会首先被转换为Tensor,然后这些Tensoraxis=0轴上打包之后成为一个新的赋值Tensor,这时按照valueTensor的规则进行赋值。所有Tensor的数据类型必须保持一致。

索引赋值可以理解为对索引到的位置元素按照一定规则进行赋值,所有索引赋值都不会改变原Tensorshape

当索引中有多个元素指向原Tensor的同一个位置时,该值的更新受底层算子限制,可能出现随机的情况。因此暂不支持索引中重复对Tensor中一个位置的值反复更新。详情请见:TensorScatterUpdate 算子介绍

当前只支持单层索引(tensor_x[index] = value),多层索引(tensor_x[index1][index2]... = value)暂不支持。

  • int索引赋值

    支持单层int索引赋值:tensor_x[int_index] = u

    示例如下:

    import mindspore.numpy as np
    tensor_x = np.arange(2 * 3).reshape((2, 3)).astype(np.float32)
    tensor_y = np.arange(2 * 3).reshape((2, 3)).astype(np.float32)
    tensor_x[1] = 88.0
    tensor_y[1] = np.array([66, 88, 99]).astype(np.float32)
    print('tensor_x:')
    print(tensor_x)
    print('tensor_y:')
    print(tensor_y)
    
    tensor_x:
    [[ 0.  1.  2.]
    [88. 88. 88.]]
    tensor_y:
    [[ 0.  1.  2.]
    [66. 88. 99.]]
    
  • bool索引赋值

    支持单层bool索引赋值:tensor_x[bool_index] = u

    示例如下:

    import mindspore.numpy as np
    tensor_x = np.arange(2 * 3).reshape((2, 3)).astype(np.float32)
    tensor_y = np.arange(2 * 3).reshape((2, 3)).astype(np.float32)
    tensor_z = np.arange(2 * 3).reshape((2, 3)).astype(np.float32)
    tensor_x[True] = 88.0
    tensor_y[True] = np.array([66, 88, 99]).astype(np.float32)
    tensor_z[True] = (66, 88, 99)
    print('tensor_x:')
    print(tensor_x)
    print('tensor_y:')
    print(tensor_y)
    print('tensor_z:')
    print(tensor_z)
    
    tensor_x:
    [[88. 88. 88.]
    [88. 88. 88.]]
    tensor_y:
    [[66. 88. 99.]
    [66. 88. 99.]]
    tensor_z:
    [[66. 88. 99.]
    [66. 88. 99.]]
    
  • ellipsis索引赋值

    支持单层ellipsis索引赋值,单层ellipsis索引赋值:tensor_x[...] = u

    示例如下:

    import mindspore.numpy as np
    tensor_x = np.arange(2 * 3).reshape((2, 3)).astype(np.float32)
    tensor_y = np.arange(2 * 3).reshape((2, 3)).astype(np.float32)
    tensor_z = np.arange(2 * 3).reshape((2, 3)).astype(np.float32)
    tensor_x[...] = 88.0
    tensor_y[...] = np.array([[22, 44, 55], [22, 44, 55]])
    tensor_z[...] = ([11, 22, 33], [44, 55, 66])
    print('tensor_x:')
    print(tensor_x)
    print('tensor_y:')
    print(tensor_y)
    print('tensor_z:')
    print(tensor_z)
    
    tensor_x:
    [[88. 88. 88.]
    [88. 88. 88.]]
    tensor_y:
    [[22. 44. 55.]
    [22. 44. 55.]]
    tensor_z:
    [[11. 22. 33.]
    [44. 55. 66.]]
    
  • slice索引赋值

    支持单层slice索引赋值:tensor_x[slice_index] = u

    示例如下:

    import mindspore.numpy as np
    tensor_x = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_y = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_z = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_k = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_x[0:1] = 88.0
    tensor_y[0:2] = 88.0
    tensor_z[0:2] = np.array([[11, 12, 13], [11, 12, 13]]).astype(np.float32)
    tensor_k[0:2] = ([11, 12, 13], (14, 15, 16))
    print('tensor_x:')
    print(tensor_x)
    print('tensor_y:')
    print(tensor_y)
    print('tensor_z:')
    print(tensor_z)
    print('tensor_k:')
    print(tensor_k)
    
    tensor_x:
    [[88. 88. 88.]
    [ 3.  4.  5.]
    [ 6.  7.  8.]]
    tensor_y:
    [[88. 88. 88.]
    [88. 88. 88.]
    [ 6.  7.  8.]]
    tensor_z:
    [[11. 12. 13.]
    [11. 12. 13.]
    [ 6.  7.  8.]]
    tensor_k:
    [[11. 12. 13.]
    [14. 15. 16.]
    [ 6.  7.  8.]]
    
  • None索引赋值

    支持单层None索引赋值:tensor_x[none_index] = u

    示例如下:

    import mindspore.numpy as np
    tensor_x = np.arange(2 * 3).reshape((2, 3)).astype(np.float32)
    tensor_y = np.arange(2 * 3).reshape((2, 3)).astype(np.float32)
    tensor_z = np.arange(2 * 3).reshape((2, 3)).astype(np.float32)
    tensor_x[None] = 88.0
    tensor_y[None] = np.array([66, 88, 99]).astype(np.float32)
    tensor_z[None] = (66, 88, 99)
    print('tensor_x:')
    print(tensor_x)
    print('tensor_y:')
    print(tensor_y)
    print('tensor_z:')
    print(tensor_z)
    
    tensor_x:
    [[88. 88. 88.]
    [88. 88. 88.]]
    tensor_y:
    [[66. 88. 99.]
    [66. 88. 99.]]
    tensor_z:
    [[66. 88. 99.]
    [66. 88. 99.]]
    
  • Tensor索引赋值

    支持单层Tensor索引赋值,即tensor_x[tensor_index] = u

    当前支持索引Tensor为 int 型和 bool 型。

    int 型示例如下:

    import mindspore.numpy as np
    tensor_x = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_y = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_z = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_index = np.array([[2, 0, 2], [0, 2, 0], [0, 2, 0]], np.int32)
    tensor_x[tensor_index] = 88.0
    tensor_y[tensor_index] = np.array([11.0, 12.0, 13.0]).astype(np.float32)
    tensor_z[tensor_index] = [11, 12, 13]
    print('tensor_x:')
    print(tensor_x)
    print('tensor_y:')
    print(tensor_y)
    print('tensor_z:')
    print(tensor_z)
    
    tensor_x:
    [[88. 88. 88.]
    [ 3.  4.  5.]
    [88. 88. 88.]]
    tensor_y:
    [[11. 12. 13.]
    [ 3.  4.  5.]
    [11. 12. 13.]]
    tensor_z:
    [[11. 12. 13.]
    [ 3.  4.  5.]
    [11. 12. 13.]]
    

    bool 型示例如下:

    [[-1. -1. -1.] [ 3. 4. 5.] [-1. -1. -1.]]

from mindspore import dtype as mstype import mindspore as ms tensor_x = ms.Tensor([[0, 1, 2], [3, 4, 5], [6, 7, 8]], mstype.float32) tensor_index = ms.Tensor([True, False, True], mstype.bool_) tensor_x[tensor_index] = -1 print(tensor_x)

  • List索引赋值

    支持单层List索引赋值:tensor_x[list_index] = u

    List索引赋值和List索引取值对索引的支持一致。

    示例如下:

    import mindspore.numpy as np
    tensor_x = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_y = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_index = np.array([[0, 1], [1, 0]]).astype(np.int32)
    tensor_x[[0, 1]] = 88.0
    tensor_y[[True, False, False]] = np.array([11, 12, 13]).astype(np.float32)
    print('tensor_x:')
    print(tensor_x)
    print('tensor_y:')
    print(tensor_y)
    
    tensor_x:
    [[88. 88. 88.]
    [88. 88. 88.]
    [ 6.  7.  8.]]
    tensor_y:
    [[11. 12. 13.]
    [ 3.  4.  5.]
    [ 6.  7.  8.]]
    
  • Tuple索引赋值

    支持单层Tuple索引赋值:tensor_x[tuple_index] = u

    Tuple索引赋值和Tuple索引取值对索引的支持一致,但不支持Tuple中包含None

    示例如下:

    import mindspore.numpy as np
    tensor_x = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_y = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_z = np.arange(3 * 3).reshape((3, 3)).astype(np.float32)
    tensor_index = np.array([0, 1]).astype(np.int32)
    tensor_x[1, 1:3] = 88.0
    tensor_y[1:3, tensor_index] = 88.0
    tensor_z[1:3, tensor_index] = np.array([11, 12]).astype(np.float32)
    print('tensor_x:')
    print(tensor_x)
    print('tensor_y:')
    print(tensor_y)
    print('tensor_z:')
    print(tensor_z)
    
    tensor_x:
    [[ 0.  1.  2.]
    [ 3. 88. 88.]
    [ 6.  7.  8.]]
    tensor_y:
    [[ 0.  1.  2.]
    [88. 88.  5.]
    [88. 88.  8.]]
    tensor_z:
    [[ 0.  1.  2.]
    [11. 12.  5.]
    [11. 12.  8.]]
    

索引增强赋值

增强索引赋值,支持+=-=*=/=%=**=//=七种类型,indexvalue的规则约束与索引赋值相同。索引值支持intboolellipsissliceNoneTensorListTuple八种类型,赋值支持NumberTensorTupleList四种类型。

索引增强赋值可以理解为对索引到的位置元素按照一定规则进行取值,取值所得再与value进行操作符运算,最终将运算结果进行赋值,所有索引增强赋值都不会改变原Tensorshape

当索引中有多个元素指向原Tensor的同一个位置时,该值的更新受底层算子限制,可能出现随机的情况。因此暂不支持索引中重复对Tensor中一个位置的值反复更新。详情请见:TensorScatterUpdate 算子介绍

目前索引中包含 TrueFalseNone的情况暂不支持。

  • 规则与约束

    与索引赋值相比,增加了取值与运算的过程。取值过程中index的约束规则与索引取值中index相同,支持intboolTensorSliceEllipsisNoneListTuple。上述几种类型的数据中所包含int值,需在[-dim_size, dim_size-1]闭合区间内。 运算过程中value的约束规则与索引赋值中value的约束规则相同,value类型需为(NumberTensorListTuple)之一,且value类型不是Number时, value的形状需要可以广播到tensor_x[index]的形状。

    示例如下:

    import mindspore as ms
    tensor_x = ms.Tensor(np.arange(3 * 4).reshape(3, 4).astype(np.float32))
    tensor_y = ms.Tensor(np.arange(3 * 4).reshape(3, 4).astype(np.float32))
    tensor_x[[0, 1], 1:3] += 2
    tensor_y[[1], ...] -= [4, 3, 2, 1]
    print('tensor_x:')
    print(tensor_x)
    print('tensor_y:')
    print(tensor_y)
    
    tensor_x:
    [[ 0.  3.  4.  3.]
    [ 4.  7.  8.  7.]
    [ 8.  9. 10. 11.]]
    tensor_y:
    [[ 0.  1.  2.  3.]
    [ 0.  2.  4.  6.]
    [ 8.  9. 10. 11.]]
    

Tensor视图

Tensor视图(Tensor Views)是指一个Tensor经过view类算子的返回值,与该Tensor共享内存数据,避免了数据复制,从而可以进行快速且内存高效的重塑、切片和逐元素操作。

例如,要获取Tensor t的视图,可以用t.view(…)。

from mindspore import Tensor
import numpy as np
t = Tensor(np.array([[1, 2, 3], [2, 3, 4]], dtype=np.float32))
b = t.view((3, 2))
# Modifying view Tensor changes base Tensor as well.
b[0][0] = 100
print(t[0][0])
# 100

由于视图与其原来的Tensor共享底层数据,因此如果在视图中修改数据,它也会反映在原来的Tensor中。

通常,MindSpore算子操作会返回一个新的Tensor作为输出,例如add()。 但在视图操作的情况下,输出是输入Tensor的视图,以避免不必要的数据复制。创建视图时不会发生数据移动,视图Tensor只是改变它解析相同数据的方式。 使用Tensor Views可能会使内存储存连续的Tensor产生内存存储非连续的Tensor。用户应格外注意,因为连续性可能会对性能产生隐式影响。transpose()是一个常见的例子。

from mindspore import Tensor
import numpy as np
base = Tensor([[0, 1], [2, 3]])
base.is_contiguous()
# True
t = base.transpose(1, 0) # t is a view of base. No data movement happened here.
t.is_contiguous()
# False
# To get a contiguous Tensor, call `.contiguous()` to enforce
# copying data when `t` is not contiguous.
c = t.contiguous()
c.is_contiguous()
# True

view类算子

作为参考,以下是MindSpore中支持view特性算子的完整列表:

broadcast_to()

diagonal()

expand_as()

movedim()

narrow()

permute()

squeeze()

transpose()

t()

T

unsqueeze()

view()

view_as()

unbind()

split()

hsplit()

vsplit()

tensor_split()

swapaxes()

swapdims()"

Parameter

参数(Parameter)是一类特殊的Tensor,是指在模型训练过程中可以对其值进行更新的变量。MindSpore提供mindspore.Parameter类进行Parameter的构造。为了对不同用途的Parameter进行区分,下面对两种不同类别的Parameter进行定义:

  • 可训练参数。在模型训练过程中根据反向传播算法求得梯度后进行更新的Tensor,此时需要将requires_grad设置为True

  • 不可训练参数。不参与反向传播,但需要更新值的Tensor(如BatchNorm中的meanvar变量),此时需要将requires_grad设置为False

Parameter默认设置requires_grad=True

下面我们构造一个简单的全连接层:

import numpy as np
import mindspore
from mindspore import nn
from mindspore import ops
from mindspore import Tensor, Parameter

class Network(nn.Cell):
    def __init__(self):
        super().__init__()
        self.w = Parameter(Tensor(np.random.randn(5, 3), mindspore.float32), name='w') # weight
        self.b = Parameter(Tensor(np.random.randn(3,), mindspore.float32), name='b') # bias

    def construct(self, x):
        z = ops.matmul(x, self.w) + self.b
        return z

net = Network()

Cell__init__方法中,我们定义了wb两个Parameter,并配置name进行命名空间管理。在construct方法中使用self.attr直接调用参与Tensor运算。

获取Parameter

在使用Cell和Parameter构造神经网络层后,我们可以使用多种方法来获取Cell管理的Parameter。

获取单个参数

单独获取某个特定参数,直接调用Python类的成员变量即可。

print(net.b.asnumpy())
[-1.2192779  -0.36789745  0.0946381 ]

获取可训练参数

可使用Cell.trainable_params方法获取可训练参数,通常在配置优化器时需调用此接口。

print(net.trainable_params())
[Parameter (name=w, shape=(5, 3), dtype=Float32, requires_grad=True), Parameter (name=b, shape=(3,), dtype=Float32, requires_grad=True)]

获取所有参数

使用Cell.get_parameters()方法可获取所有参数,此时会返回一个Python迭代器。

print(type(net.get_parameters()))
<class 'generator'>

或者可以调用Cell.parameters_and_names返回参数名称及参数。

for name, param in net.parameters_and_names():
    print(f"{name}:\n{param.asnumpy()}")
w:
[[ 4.15680408e-02 -1.20311625e-01  5.02573885e-02]
 [ 1.22175144e-04 -1.34980649e-01  1.17642188e+00]
 [ 7.57667869e-02 -1.74758151e-01 -5.19092619e-01]
 [-1.67846107e+00  3.27240258e-01 -2.06452996e-01]
 [ 5.72323874e-02 -8.27963874e-02  5.94243526e-01]]
b:
[-1.2192779  -0.36789745  0.0946381 ]

修改Parameter

直接修改参数值

Parameter是一种特殊的Tensor,因此可以使用Tensor索引修改的方式对其值进行修改。

net.b[0] = 1.
print(net.b.asnumpy())
[ 1.         -0.36789745  0.0946381 ]

覆盖修改参数值

可调用Parameter.set_data方法,使用相同Shape的Tensor对Parameter进行覆盖。该方法常用于使用Initializer进行Cell遍历初始化

net.b.set_data(Tensor([3, 4, 5]))
print(net.b.asnumpy())
[3. 4. 5.]

运行时修改参数值

在深度学习模型训练中,参数的核心功能在于其值的迭代更新,从而优化模型性能。鉴于MindSpore使用静态图加速的编译设计,需要使用mindspore.ops.assign接口对参数进行赋值。该方法常用于自定义优化器场景。下面是一个简单的运行时修改参数值样例:

import mindspore as ms

@ms.jit
def modify_parameter():
    b_hat = ms.Tensor([7, 8, 9])
    ops.assign(net.b, b_hat)
    return True

modify_parameter()
print(net.b.asnumpy())
[7. 8. 9.]

Parameter Tuple

变量元组 ParameterTuple 用于保存多个Parameter,该数据类型继承于元组tuple,并提供了克隆功能。

如下示例提供 ParameterTuple 创建和克隆方法:

from mindspore.common.initializer import initializer
from mindspore import ParameterTuple
# 创建ParameterTuple
x = Parameter(default_input=ms.Tensor(np.arange(2 * 3).reshape((2, 3))), name="x")
y = Parameter(default_input=initializer('ones', [1, 2, 3], ms.float32), name='y')
z = Parameter(default_input=2.0, name='z')
params = ParameterTuple((x, y, z))

# 克隆ParameterTuple
params_copy = params.clone("params_copy")

print(params)
print(params_copy)
  (Parameter (name=x, shape=(2, 3), dtype=Int64, requires_grad=True), Parameter (name=y, shape=(1, 2, 3), dtype=Float32, requires_grad=True), Parameter (name=z, shape=(), dtype=Float32, requires_grad=True))
  (Parameter (name=params_copy.x, shape=(2, 3), dtype=Int64, requires_grad=True), Parameter (name=params_copy.y, shape=(1, 2, 3), dtype=Float32, requires_grad=True), Parameter (name=params_copy.z, shape=(), dtype=Float32, requires_grad=True))