# 静态图语法支持 ## 概述 在Graph模式下,Python代码并不是由Python解释器去执行,而是将代码编译成静态计算图,然后执行静态计算图。 当前支持`@jit`装饰器修饰函数,Cell及其子类、`@jit_class`修饰的类或者自定义普通类的成员方法。 对于函数,则编译函数定义;对于网络,则编译`construct`方法及其调用的其他方法或者函数。 `jit`使用规则详见[jit API文档](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/mindspore/mindspore.jit.html#mindspore.jit)。 `Cell`定义详见[Cell API文档](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/nn/mindspore.nn.Cell.html)。 由于语法解析的限制,当前在编译构图时,支持的数据类型、语法以及相关操作并没有完全与Python语法保持一致,部分使用受限。 本文主要介绍,在编译静态图时,支持的数据类型、语法以及相关操作,这些规则仅适用于Graph模式。 ## 数据类型 ### Python内置数据类型 当前支持的`Python`内置数据类型包括:`Number`、`String`、`List`、`Tuple`和`Dictionary`。 #### Number 支持`int`、`float`、`bool`,不支持complex(复数)。 支持在网络里定义`Number`,即支持语法:`y = 1`、`y = 1.2`、 `y = True`。 当数据为常量时,编译时期可以获取到数值,因此在网络中可以支持强转`Number`的语法:`y = int(x)`、`y = float(x)`、`y = bool(x)`。 #### String 支持在网络里构造`String`,即支持语法`y = "abcd"`。 可以通过str()的方式进行将常量转换成字符串,支持str.format() 对字符串进行格式化,但是不支持format内部参数为变量和kwargs输入场景。 例如: ```python import mindspore as ms @ms.jit() def test_str_format(): x = "{} is zero".format(0) return x x = test_str_format() print(x) ``` 结果如下: ```text 0 is zero ``` #### List 支持在网络里构造`List`,即支持语法`y = [1, 2, 3]`。 计算图中最终需要输出的`List`会转换为`Tuple`输出。 需要注意的是MindSpore的List取值由于将其转换成了ListGetItem算子,该算子返回的始终为原List的一个拷贝,所以有时可能会和Python的List的引用表示有差异。 比如: 原生Python: ```python >>>a = [[1,2,3],4,5] >>>b = a[0] >>>b[0] = 123123 >>>a [123123, 2, 3], 4, 5] ``` MindSpore: ```python import mindspore as ms @ms.jit def test_list(): a = [[1,2,3],4,5] b = a[0] b[0] = 123123 return a a = test_list() print('a:{}'.format(a)) ``` 结果如下: ```text x: ((1, 2, 3), 4, 5) ``` - 支持接口 `append`: 向`list`里追加元素。 示例如下: ```python import mindspore as ms @ms.jit() def test_list(): x = [1, 2, 3] x.append(4) return x x = test_list() print('x:{}'.format(x)) ``` 结果如下: ```text x: (1, 2, 3, 4) ``` `insert`: 在`list`里的指定位置插入指定的元素。 示例如下: ```python import mindspore as ms @ms.jit() def test_list_insert(): x = [1, 3, 4] x.insert(0, 2) return x x = test_list_insert() print('x:{}'.format(x)) ``` 结果如下: ```text x: (2, 1, 3, 4) ``` `pop`: 移除`list`里的指定位置的元素,默认移除最后一个。 示例如下: ```python import mindspore as ms @ms.jit() def test_list_pop(): x = [1, 3, 4] y = x.pop() return x, y x, y = test_list_pop() print('x:{}'.format(x)) print('y:', y) ``` 结果如下: ```text x: (1, 3) y: 4 ``` `clear`: 清空`list`里的元素。 示例如下: ```python import mindspore as ms @ms.jit() def test_list_clear(): x = [1, 3, 4] x.clear() return x x = test_list_clear() print('x:{}'.format(x)) ``` 结果如下: ```text x: () ``` `extend`: 在`list`末尾追加另一个序列的多个值。 示例如下: ```python import mindspore as ms @ms.jit() def test_list_extend(): x = [1, 2, 3, 4] y = [5, 6, 7] x.extend(y) return x x = test_list_extend() print('x:{}'.format(x)) ``` 结果如下: ```text x: (1, 2, 3, 4, 5, 6, 7) ``` `reverse`: 逆转`list`中的元素。 示例如下: ```python import mindspore as ms @ms.jit() def test_list_reverse(): x = [1, 2, 3, 4] x.reverse() return x x = test_list_reverse() print('x:{}'.format(x)) ``` 结果如下: ```text x: (4, 3, 2, 1) ``` `count`: 统计`list`中的某个元素出现的次数。当前count方法仅支持常量场景。 示例如下: ```python import mindspore as ms @ms.jit() def test_list_count(): x = [1, 2, 3, 4] num = x.count(2) return num num = test_list_count() print('num:', num) ``` 结果如下: ```text num: 1 ``` 如果count的使用场景中存在Tensor变量,将会抛出相关异常。 ```python import mindspore as ms @ms.jit() def test_list_count(input_x): x = [1, 2, 3, 4] num = x.count(input_x) return num input_x = ms.Tensor(2) num = test_list_count() print('num:', num) ``` 结果如下: ```text The list count not support variable scene now. The count data is Tensor type. ``` - 支持索引取值和赋值 支持单层和多层索引取值以及赋值。 索引值仅支持`int`和`slice`。 `slice`内部数据必须为编译时能够确定的常量,即不能为计算后的`Tensor`。 赋值时,所赋的值支持`Number`、`String`、`Tuple`、`List`、`Tensor`。 当前切片赋值右值为`Tensor`时,需要将`Tensor`转换为`List`,在MindSpore静态图模式下这种转化目前是通过[JIT Fallback](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/design/jit_fallback.html?highlight=Fallback)实现,所以暂时不能支持变量场景。 示例如下: ```python import mindspore as ms import numpy as np t = ms.Tensor(np.array([1, 2, 3])) @ms.jit() def test_index(): x = [[1, 2], 2, 3, 4] m = x[0][1] z = x[1::2] x[1] = t x[2] = "ok" x[3] = (1, 2, 3) x[0][1] = 88 n = x[-3] return m, z, x, n m, z, x, n = test_index() print('m:{}'.format(m)) print('z:{}'.format(z)) print('x:{}'.format(x)) print('n:{}'.format(n)) ``` 结果如下: ```text m:2 z:[2, 4] x:[[1, 88], Tensor(shape=[3], dtype=Int64, value= [1, 2, 3]), 'ok', (1, 2, 3)] n:[1 2 3] ``` #### Tuple 支持在网络里构造`Tuple`,即支持语法`y = (1, 2, 3)`。 关于Tuple取值的引用类型问题与List相同,请见List的相关介绍。 - 支持索引取值 索引值支持`int`、`slice`、`Tensor`,也支持多层索引取值,即支持语法`data = tuple_x[index0][index1]...`。 索引值为`Tensor`有如下限制: - `tuple`里存放的都是`Cell`,每个`Cell`要在tuple定义之前完成定义,每个`Cell`的入参个数、入参类型和入参`shape`要求一致,每个`Cell`的输出个数、输出类型和输出`shape`也要求一致。 - 索引`Tensor`是一个`dtype`为`int32`的标量`Tensor`,取值范围在`[-tuple_len, tuple_len)`,`Ascend`后端不支持负数索引。 - 该语法不支持`if`、`while`、`for`控制流条件为变量的运行分支,仅支持控制流条件为常量。 - 支持`GPU`和`Ascend`后端。 `int`、`slice`索引示例如下: ```python import mindspore as ms import numpy as np t = ms.Tensor(np.array([1, 2, 3])) @ms.jit() def test_index(): x = (1, (2, 3, 4), 3, 4, t) y = x[1][1] z = x[4] m = x[1:4] n = x[-4] return y, z, m, n y, z, m, n = test_index() print('y:{}'.format(y)) print('z:{}'.format(z)) print('m:{}'.format(m)) print('n:{}'.format(n)) ``` 结果如下: ```text y:3 z:[1 2 3] m:((2, 3, 4), 3, 4) n:(2, 3, 4) ``` `Tensor`索引示例如下: ```python import mindspore as ms from mindspore import nn, set_context set_context(mode=ms.GRAPH_MODE) class Net(nn.Cell): def __init__(self): super(Net, self).__init__() self.relu = nn.ReLU() self.softmax = nn.Softmax() self.layers = (self.relu, self.softmax) def construct(self, x, index): ret = self.layers[index](x) return ret x = ms.Tensor([-1.0], ms.float32) net = Net() ret = net(x, 0) print('ret:{}'.format(ret)) ``` 结果如下: ```text ret:[0.] ``` #### Dictionary 支持在网络里构造`Dictionary`,即支持语法`y = {"a": 1, "b": 2}`。 当前`key`类型支持`String`、`Number`、常量`Tensor`以及只包含这些类型对象的`Tuple`,`value`类型支持`Number`、`Tuple`、`Tensor`、`List`、`Dictionary`。需要注意的是,如果计算图的最终输出有`Dictionary`,返回的不是`Dictionary`,而是由其所有的`value`值组成的`Tuple`。 - 支持接口 `keys`:取出`dict`里所有的`key`值,组成`Tuple`返回。 `values`:取出`dict`里所有的`value`值,组成`Tuple`返回。 `items`:取出`dict`里每一对`key`和`value`组成的`Tuple`,组成`Tuple`返回。 `get`:`dict.get(key[, value])`返回指定`key`对应的`value`值,如果指定`key`不存在,返回默认值`None`或者设置的默认值`value`。 `clear`:删除`dict`里所有的元素。 `has_key`:`dict.has_key(key)`判断`dict`里是否存在指定`key`。 `update`:`dict1.update(dict2)`把`dict2`中的元素更新到`dict1`中。 `fromkeys`:`dict.fromkeys(seq([, value]))`用于创建新的`Dictionary`,以序列`seq`中的元素做`Dictionary`的`key`,`value`为所有`key`对应的初始值。 示例如下: ```python import mindspore as ms import numpy as np x = {"a": ms.Tensor(np.array([1, 2, 3])), "b": ms.Tensor(np.array([4, 5, 6])), "c": ms.Tensor(np.array([7, 8, 9]))} @ms.jit() def test_dict(): x_keys = x.keys() x_values = x.values() x_items = x.items() value_a = x.get("a") check_key = x.has_key("a") y = {"a": ms.Tensor(np.array([0, 0, 0]))} x.update(y) new_dict = x.fromkeys("abcd", 123) return x_keys, x_values, x_items, value_a, check_key, x, new_dict x_keys, x_values, x_items, value_a, check_key, new_x, new_dict = test_dict() print('x_keys:{}'.format(x_keys)) print('x_values:{}'.format(x_values)) print('x_items:{}'.format(x_items)) print('value_a:{}'.format(value_a)) print('check_key:{}'.format(check_key)) print('new_x:{}'.format(new_x)) print('new_dict:{}'.format(new_dict)) ``` 结果如下: ```text x_keys:('a', 'b', 'c') x_values:(Tensor(shape=[3], dtype=Int64, value= [1, 2, 3]), Tensor(shape=[3], dtype=Int64, value= [4, 5, 6]), Tensor(shape=[3], dtype=Int64, value= [7, 8, 9])) x_items:(('a', Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])), ('b', Tensor(shape=[3], dtype=Int64, value= [4, 5, 6])), ('c', Tensor(shape=[3], dtype=Int64, value= [7, 8, 9]))) value_a:[1 2 3] check_key: True new_x: {'a': ms.Tensor(np.array([0, 0, 0])), 'b': ms.Tensor(np.array([4, 5, 6])), 'c': ms.Tensor(np.array([7, 8, 9]))} new_dict: {'a': 123, 'b': 123, 'c': 123, 'd': 123} ``` - 支持索引取值和赋值 示例如下: ```python import mindspore as ms import numpy as np x = {"a": ms.Tensor(np.array([1, 2, 3])), "b": ms.Tensor(np.array([4, 5, 6])), "c": ms.Tensor(np.array([7, 8, 9]))} @ms.jit() def test_dict(): y = x["b"] x["a"] = (2, 3, 4) return x, y x, y = test_dict() print('x:{}'.format(x)) print('y:{}'.format(y)) ``` 结果如下: ```text x:{'a': (2, 3, 4), 'b': Tensor(shape=[3], dtype=Int64, value= [4, 5, 6]), 'c': Tensor(shape=[3], dtype=Int64, value= [7, 8, 9])} y:[4 5 6] ``` ### MindSpore自定义数据类型 当前MindSpore自定义数据类型包括:`Tensor`、`Primitive`、`Cell`和`Parameter`。 #### Tensor 目前已支持在网络里构造Tensor。 Tensor的属性与接口详见[Tensor API文档](https://mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/mindspore/mindspore.Tensor.html#mindspore-tensor)。 #### Primitive 当前支持在construct里构造`Primitive`及其子类的实例。 但在调用时,参数只能通过位置参数方式传入,不支持通过键值对方式传入。 示例如下: ```python import mindspore as ms from mindspore import nn, ops, Tensor, set_context import numpy as np set_context(mode=ms.GRAPH_MODE) class Net(nn.Cell): def __init__(self): super().__init__() def construct(self, x): reduce_sum = ops.ReduceSum(True) #支持在construct里构造`Primitive`及其子类的实例 ret = reduce_sum(x, axis=2) return ret x = Tensor(np.random.randn(3, 4, 5, 6).astype(np.float32)) net = Net() ret = net(x) print('ret.shape:{}'.format(ret.shape)) ``` 上面所定义的网络里,reduce_sum(x, axis=2)的参数不支持通过键值对方式传入,只能通过位置参数方式传入,即reduce_sum(x, 2)。 结果报错如下: ```text TypeError: Only supported positional parameter type for python primitive, but got keyword parameter type. ``` 当前不支持在网络调用`Primitive`及其子类相关属性和接口。 当前已定义的`Primitive`详见[Primitive API文档](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/ops/mindspore.ops.Primitive.html#mindspore.ops.Primitive)。 #### Cell 当前支持在网络里构造`Cell`及其子类的实例,即支持语法`cell = Cell(args...)`。 但在调用时,参数只能通过位置参数方式传入,不支持通过键值对方式传入,即不支持在语法`cell = Cell(arg_name=value)`。 当前不支持在网络调用`Cell`及其子类相关属性和接口,除非是在`Cell`自己的`construct`中通过`self`调用。 `Cell`定义详见[Cell API文档](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/nn/mindspore.nn.Cell.html)。 #### Parameter `Parameter`是变量张量,代表在训练网络时,需要被更新的参数。 `Parameter`的定义和使用详见[Parameter API文档](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/mindspore/mindspore.Parameter.html#mindspore.Parameter)。 ## 原型 原型代表编程语言中最紧密绑定的操作。 ### 属性引用 属性引用是后面带有一个句点加一个名称的原型。 在MindSpore的Cell 实例中使用属性引用作为左值需满足如下要求: - 被修改的属性属于本`cell`对象,即必须为`self.xxx`。 - 该属性在Cell的`__init__`函数中完成初始化且其为Parameter类型。 示例如下: ```python import mindspore as ms from mindspore import nn, set_context import numpy as np from mindspore.ops import constexpr set_context(mode=ms.GRAPH_MODE) class Net(nn.Cell): def __init__(self): super().__init__() self.weight = ms.Parameter(ms.Tensor(3, ms.float32), name="w") self.m = 2 def construct(self, x, y): self.weight = x # 满足条件可以修改 # self.m = 3 # self.m 非Parameter类型禁止修改 # y.weight = x # y不是self,禁止修改 return x net = Net() ret = net(1, 2) print('ret:{}'.format(ret)) ``` 结果如下: ```text ret:1 ``` ### 索引取值 对序列`Tuple`、`List`、`Dictionary`、`Tensor`的索引取值操作(Python称为抽取)。 `Tuple`的索引取值请参考本文的[Tuple](#tuple)章节。 `List`的索引取值请参考本文的[List](#list)章节。 `Dictionary`的索引取值请参考本文的[Dictionary](#dictionary)章节。 `Tensor`的索引取详见[Tensor 索引取值文档](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/note/index_support.html#索引取值)。 ### 调用 所谓调用就是附带可能为空的一系列参数来执行一个可调用对象(例如:`Cell`、`Primitive`)。 示例如下: ```python import mindspore as ms from mindspore import nn, ops, set_context import numpy as np set_context(mode=ms.GRAPH_MODE) class Net(nn.Cell): def __init__(self): super().__init__() self.matmul = ops.MatMul() def construct(self, x, y): out = self.matmul(x, y) # Primitive调用 return out x = ms.Tensor(np.ones(shape=[1, 3]), ms.float32) y = ms.Tensor(np.ones(shape=[3, 4]), ms.float32) net = Net() ret = net(x, y) print('ret:{}'.format(ret)) ``` 结果如下: ```text ret:[[3. 3. 3. 3.]] ``` ## 运算符 算术运算符和赋值运算符支持`Number`和`Tensor`运算,也支持不同`dtype`的`Tensor`运算。 规则可参考:[隐式类型转换规则](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/note/operator_list_implicit.html#转换规则)。 ### 单目算术运算符 | 单目算术运算符 | 支持类型 | | :------------- | :---------------------------------------------- | | `+` | `Number`、`Tensor`,取正值。 | | `-` | `Number`、`Tensor`、`COOTensor`、`CSRTensor`,取负值。 | | `~` | `Tensor`, 且其数据类型为`Bool`。成员逐个取反。 | 说明: - 在Python中`~`操作符对输入的整数按位取反; MindSpore对`~`的功能重新定义为对`Tensor(Bool)`的逻辑取反。 ### 二元算术运算符 | 二元算术运算符 | 支持类型 | | :------------- | :----------------------------------------------------------- | | `+` | `Number` + `Number`、`String` + `String`、`Number` + `Tensor`、`Tensor` + `Number`、`Tuple` + `Tensor`、`Tensor` + `Tuple`、`List` + `Tensor`、`Tensor`+`List`、`List`+`List`、`Tensor` + `Tensor`、`Tuple` + `Tuple`、`COOTensor` + `Tensor`、`Tensor` + `COOTensor`、`COOTensor` + `COOTensor`、`CSRTensor` + `CSRTensor`。 | | `-` | `Number` - `Number`、`Tensor` - `Tensor`、`Number` - `Tensor`、`Tensor` - `Number`、`Tuple` - `Tensor`、`Tensor` - `Tuple`、`List` - `Tensor`、`Tensor` - `List`、`COOTensor` - `Tensor`、`Tensor` - `COOTensor`、`COOTensor` - `COOTensor`、`CSRTensor` - `CSRTensor`。 | | `*` | `Number` \* `Number`、`Tensor` \* `Tensor`、`Number` \* `Tensor`、`Tensor` \* `Number`、`List` \* `Number`、`Number` \* `List`、`Tuple` \* `Number`、`Number` \* `Tuple`、`Tuple` \* `Tensor`、`Tensor` \* `Tuple`、 `List` \* `Tensor`、`Tensor` \* `List`、`COOTensor` \* `Tensor`、`Tensor` \* `COOTensor`、`CSRTensor` \* `Tensor`、`Tensor` \* `CSRTensor`。 | | `/` | `Number` / `Number`、`Tensor` / `Tensor`、`Number` / `Tensor`、`Tensor` / `Number`、`Tuple` / `Tensor`、`Tensor` / `Tuple`、`List` / `Tensor`、`Tensor` / `List`、`COOTensor` / `Tensor`、`CSRTensor` / `Tensor`。 | | `%` | `Number` % `Number`、`Tensor` % `Tensor`、`Number` % `Tensor`、`Tensor` % `Number`、`Tuple` % `Tensor`、`Tensor` % `Tuple`、`List` % `Tensor`、`Tensor` % `List`。 | | `**` | `Number` \*\* `Number`、`Tensor` \*\* `Tensor`、`Number` \*\* `Tensor`、`Tensor` \*\* `Number`、`Tuple` \*\* `Tensor`、`Tensor` \*\* `Tuple`、 `List` \*\* `Tensor`、`Tensor` \*\* `List`。 | | `//` | `Number` // `Number`、`Tensor` // `Tensor`、`Number` // `Tensor`、`Tensor` // `Number`、`Tuple` // `Tensor`、`Tensor` // `Tuple`、`List` // `Tensor`、`Tensor` // `List`。 | | `&` | `Number` & `Number`、`Tensor` & `Tensor`、`Number` & `Tensor`、`Tensor` & `Number`。 | | `∣` | `Number` | `Number`、`Tensor` | `Tensor`、`Number` | `Tensor`、`Tensor` | `Number`。 | | `^` | `Number` ^ `Number`、`Tensor` ^ `Tensor`、`Number` ^ `Tensor`、`Tensor` ^ `Number`。 | | `<<` | `Number` << `Number`。 | | `>>` | `Number` >> `Number`。 | 限制: - 当左右操作数都为`Number`类型时,`Number`的值不可为`Bool` 类型。 - 当左右操作数都为`Number`类型时,不支持`Float64` 和 `Int32`间的运算。 - 当任一操作数为`Tensor`类型时,左右操作数的值不可同时为`Bool`。 - `List/Tuple`和`Number`进行`*`运算时表示将`List/Tuple`复制`Number`份后串联起来,`List/Tuple`内的数据类型必须为`Number`、`String`、`None`或由以上类型构成的`List/Tuple`。 ### 赋值运算符 | 赋值运算符 | 支持类型 | | :--------- | :----------------------------------------------------------- | | `=` | MindSpore支持的Python内置数据类型和MindSpore自定义数据类型 | | `+=` | `Number` += `Number`、`String` += `String`、`Number` += `Tensor`、`Tensor` += `Number`、`Tuple` += `Tensor`、`Tensor` += `Tuple`、`List` += `Tensor`、`Tensor` += `List`、`List` += `List`、`Tensor` += `Tensor`、`Tuple` += `Tuple`。 | | `-=` | `Number` -= `Number`、`Tensor` -= `Tensor`、`Number` -= `Tensor`、`Tensor` -= `Number`、`Tuple` -= `Tensor`、`Tensor` -= `Tuple`、`List` -= `Tensor`、`Tensor` -= `List`。 | | `*=` | `Number` \*= `Number`、`Tensor` \*= `Tensor`、`Number` \*= `Tensor`、`Tensor` \*= `Number`、`List` \*= `Number`、`Number` \*= `List`、`Tuple` \*= `Number`、`Number` \*= `Tuple`、`Tuple` \*= `Tensor`、`Tensor` \*= `Tuple`、 `List` \*= `Tensor`、`Tensor` \*= `List`。 | | `/=` | `Number` /= `Number`、`Tensor` /= `Tensor`、`Number` /= `Tensor`、`Tensor` /= `Number`、`Tuple` /= `Tensor`、`Tensor` /= `Tuple`、`List` /= `Tensor`、`Tensor` /= `List`。 | | `%=` | `Number` %= `Number`、`Tensor` %= `Tensor`、`Number` %= `Tensor`、`Tensor` %= `Number`、`Tuple` %= `Tensor`、`Tensor` %= `Tuple`、`List` %= `Tensor`、`Tensor` %= `List`。 | | `**=` | `Number` \*\*= `Number`、`Tensor` \*\*= `Tensor`、`Number` \*\*= `Tensor`、`Tensor` \*\*= `Number`、`Tuple` \*\*= `Tensor`、`Tensor` \*\*= `Tuple`、 `List` \*\*= `Tensor`、`Tensor` \*\*= `List`。 | | `//=` | `Number` //= `Number`、`Tensor` //= `Tensor`、`Number` //= `Tensor`、`Tensor` //= `Number`、`Tuple` //= `Tensor`、`Tensor` //= `Tuple`、`List` //= `Tensor`、`Tensor` //= `List`。 | | `&=` | `Number` &= `Number`、`Tensor` &= `Tensor`、`Number` &= `Tensor`、`Tensor` &= `Number`。 | | `∣=` | `Number` |= `Number`、`Tensor` |= `Tensor`、`Number` |= `Tensor`、`Tensor` |= `Number`。 | | `^=` | `Number` ^= `Number`、`Tensor` ^= `Tensor`、`Number` ^= `Tensor`、`Tensor` ^= `Number`。 | | `<<=` | `Number` <<= `Number`。 | | `>>=` | `Number` >>= `Number`。 | 限制: - 对于 `=`来说,不支持下列场景: 在`construct`函数中仅支持创建`Cell`和`Primitive`类型对象,使用`xx = Tensor(...)`的方式创建`Tensor`会失败。 在`construct`函数中仅支持为self 的`Parameter`类型的属性赋值, 详情参考:[属性引用](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/note/static_graph_syntax_support.html#属性引用)。 - 当`AugAssign`的左右操作数都为`Number`类型时,`Number`的值不可为`Bool` 类型。 - 当`AugAssign`的左右操作数都为`Number`类型时,不支持`Float64` 和 `Int32`间的运算。 - 当`AugAssign`的任一操作数为`Tensor`类型时,左右操作数的值不可同时为`Bool`。 - `List/Tuple`和`Number`进行`*=`运算时表示将`List/Tuple`复制`Number`份后串联起来,`List/Tuple`内的数据类型必须为`Number`、`String`、`None`或由以上类型构成的`List/Tuple`。 ### 逻辑运算符 | 逻辑运算符 | 支持类型 | | :--------- | :----------------------------------------------------------- | | `and` | `String`、 `Number`、 `Tuple`、`List` 、`Dict`、`None`、标量、Tensor。 | | `or` | `String`、 `Number`、 `Tuple`、`List` 、`Dict`、`None`、标量、Tensor。 | | `not` | `Number`、`Tuple`、`List`、只有一个成员的Tensor。 | 限制: - `and`、`or`的左操作数必须要能被转换成布尔值。例如:左操作数不能为存在多个元素的Tensor。当`and`、`or`的左操作数是变量Tensor时,右操作数必须也是同类型Tensor且Tensor成员个数只能有一个。在其余情况下,右操作数无要求。 - `and`、`or`的左右操作数存在图模式无法支持的对象(例如:第三方对象以及由图模式不原生支持的语法产生的对象)时,左右操作数需要均为常量。 ### 比较运算符 | 比较运算符 | 支持类型 | | :--------- | :----------------------------------------------------------- | | `in` | `Number` in `tuple`、`String` in `tuple`、`Tensor` in `Tuple`、`Number` in `List`、`String` in `List`、`Tensor` in `List`、`String` in `Dictionary`。 | | `not in` | 与`in`相同。 | | `is` | 仅支持判断是`None`、 `True`或者`False`。 | | `is not` | 仅支持判断不是`None`、 `True`或者`False`。 | | < | `Number` < `Number`、`Number` < `Tensor`、`Tensor` < `Tensor`、`Tensor` < `Number`。 | | <= | `Number` <= `Number`、`Number` <= `Tensor`、`Tensor` <= `Tensor`、`Tensor` <= `Number`。 | | > | `Number` > `Number`、`Number` > `Tensor`、`Tensor` > `Tensor`、`Tensor` > `Number`。 | | >= | `Number` >= `Number`、`Number` >= `Tensor`、`Tensor` >= `Tensor`、`Tensor` >= `Number`。 | | != | `Number` != `Number`、`Number` != `Tensor`、`Tensor` != `Tensor`、`Tensor` != `Number`、`mstype` != `mstype`、`String` != `String`、`Tuple !` = `Tuple`、`List` != `List`。 | | == | `Number` == `Number`、`Number` == `Tensor`、`Tensor` == `Tensor`、`Tensor` == `Number`、`mstype` == `mstype`、`String` == `String`、`Tuple` == `Tuple`、`List` == `List`。 | 限制: - 对于`<`、`<=`、`>`、`>=`、`!=`来说,当左右操作数都为`Number`类型时,`Number`的值不可为`Bool` 类型。 - 对于`<`、`<=`、`>`、`>=`、`!=`、`==`来说,当左右操作数都为`Number`类型时,不支持`Float64` 和 `Int32`间的运算。 - 对于`<`、`<=`、`>`、`>=`、`!=`、`==`来说,当左右任一操作数为`Tensor`类型时,左右操作数的值不可同时为`Bool`。 - 对于`==`来说,当左右操作数都为`Number`类型时,支持左右操作数同时为`Bool`,不支持只有一个操作数为`Bool`。 - 对于`!=`、`==`来说除`mstype`外,其他取值均可和`None`进行比较来判空。 - 不支持链式比较,如: `a>b>c`。 ## 复合语句 ### 条件控制语句 #### if语句 使用方式: - `if (cond): statements...` - `x = y if (cond) else z` 参数:`cond` -- 支持`Bool`类型的变量,也支持类型为`Number`、`List`、`Tuple`、`Dict`、`String`类型的常量。 限制: - 如果`cond`不为常量,在不同分支中同一符号被赋予的变量或者常量的数据类型应一致,如果是被赋予变量或者常量数据类型是`Tensor`,则要求`Tensor`的type和shape也应一致。shape一致性约束详见[ShapeJoin规则](https://www.mindspore.cn/tutorials/experts/zh-CN/r2.0.0-alpha/network/control_flow.html#shapejoin规则)。 示例1: ```python import mindspore as ms x = ms.Tensor([1, 2], ms.int32) y = ms.Tensor([0, 3], ms.int32) m = 'xx' n = 'yy' @ms.jit() def test_cond(x, y): if (x > y).any(): return m else: return n ret = test_cond(x, y) print('ret:{}'.format(ret)) ``` `if`分支返回的`m`和`else`分支返回的`n`,二者数据类型必须一致。 结果如下: ```text ret:xx ``` 示例2: ```python import mindspore as ms x = ms.Tensor([1, 2], ms.int32) y = ms.Tensor([0, 3], ms.int32) m = 'xx' n = 'yy' @ms.jit() def test_cond(x, y): out = 'init' if (x > y).any(): out = m else: out = n return out ret = test_cond(x, y) print('ret:{}'.format(ret)) ``` `if`分支中`out`被赋值的变量或者常量`m`与`else`分支中`out`被赋值的变量或者常量`n`的数据类型必须一致。 结果如下: ```text ret:xx ``` 示例3: ```python import mindspore as ms x = ms.Tensor([1, 2], ms.int32) y = ms.Tensor([0, 3], ms.int32) m = 'xx' @ms.jit() def test_cond(x, y): out = 'init' if (x > y).any(): out = m return out ret = test_cond(x, y) print('ret:{}'.format(ret)) ``` `if`分支中`out`被赋值的变量或者常量`m`与`out`初始赋值的变量或者常量`init`的数据类型必须一致。 结果如下: ```text ret:xx ``` ### 循环语句 #### for语句 使用方式: - `for i in sequence statements...` - `for i in sequence statements... if (cond) break` - `for i in sequence statements... if (cond) continue` 参数:`sequence` -- 遍历序列(`Tuple`、`List`、`range`等) 限制: - 图的算子数量和`for`循环的迭代次数成倍数关系,`for`循环迭代次数过大可能会导致图占用内存超过使用限制。 - 不支持`for...else...`语句。 示例: ```python import mindspore as ms import numpy as np z = ms.Tensor(np.ones((2, 3))) @ms.jit() def test_cond(): x = (1, 2, 3) for i in x: z += i return z ret = test_cond() print('ret:{}'.format(ret)) ``` 结果如下: ```text ret:[[7. 7. 7.] [7. 7. 7.]] ``` #### while语句 使用方式: - `while (cond) statements...` - `while (cond) statements... if (cond1) break` - `while (cond) statements... if (cond1) continue` 参数:`cond` -- 支持`Bool`类型的变量,也支持类型为`Number`、`List`、`Tuple`、`Dict`、`String`类型的常量。 限制: - 如果`cond`不为常量,在循环体内外同一符号被赋值的变量或者常量的数据类型应一致,如果是被赋予数据类型`Tensor`,则要求`Tensor`的type和shape也应一致。shape一致性约束详见[ShapeJoin规则](https://www.mindspore.cn/tutorials/experts/zh-CN/r2.0.0-alpha/network/control_flow.html#shapejoin规则)。 - 不支持`while...else...`语句 - 如果`cond`不为常量, 循环体内部不能更新循环体外的`Number`、`List`、`Tuple`类型数据, 不能更改`Tensor`类型数据的shape。 示例1: ```python import mindspore as ms m = 1 n = 2 @ms.jit() def test_cond(x, y): while x < y: x += 1 return m return n ret = test_cond(1, 5) print('ret:{}'.format(ret)) ``` `while`循环内返回的`m`和`while`外返回的`n`数据类型必须一致。 结果如下: ```text ret:1 ``` 示例2: ```python import mindspore as ms m = 1 n = 2 def ops1(a, b): return a + b @ms.jit() def test_cond(x, y): out = m while x < y: x += 1 out = ops1(out, x) return out ret = test_cond(1, 5) print('ret:{}'.format(ret)) ``` `while`内,`out`在循环体内被赋值的变量`op1`的输出类型和初始类型`m`必须一致。 结果如下: ```text ret:15 ``` ### 函数定义语句 #### def关键字 用于定义函数。 使用方式: `def function_name(args): statements...` 示例如下: ```python import mindspore as ms def number_add(x, y): return x + y @ms.jit() def test(x, y): return number_add(x, y) ret = test(1, 5) print('ret:{}'.format(ret)) ``` 结果如下: ```text ret: 6 ``` 限制: - 函数必须有返回语句。 - 最外层网络模型的`construct`函数不支持kwargs,即不支持 `def construct(**kwargs):`。 - 不支持变参和非变参的混合使用,即不支持 `def function(x, y, *args):`和 `def function(x = 1, y = 1, **kwargs):`。 #### lambda表达式 用于生成函数。 使用方式:`lambda x, y: x + y` 示例如下: ```python import mindspore as ms @ms.jit() def test(x, y): number_add = lambda x, y: x + y return number_add(x, y) ret = test(1, 5) print('ret:{}'.format(ret)) ``` 结果如下: ```text ret: 6 ``` ### 列表生成式和生成器表达式 支持列表生成式(List Comprehension)和生成器表达式(Generator Expression)。 #### 列表生成式 用于生成列表。由于编译器会自动把List类型转换成Tuple类型,经过编译后最终输出类型为Tuple。 使用方式:参考Python语法说明。 示例如下: ```python import mindspore as ms @ms.jit() def test(x, y): l = [x * x for x in range(1, 11) if x % 2 == 0] return l ret = test(1, 5) print('ret:{}'.format(ret)) ``` 结果如下: ```text ret:(4, 16, 36, 64, 100) ``` 限制: 不支持多层嵌套迭代器的使用方式。 限制用法示例如下(使用了两层迭代器): ```python l = [y for x in ((1, 2), (3, 4), (5, 6)) for y in x] ``` 会提示错误: ```text TypeError: The `generators` supports one `comprehension` in ListComp/GeneratorExp, but got 2 comprehensions. ``` #### 生成器表达式 用于生成列表,与列表生成式动作完全一致,最终的输出类型同样是Tuple。此表达式即刻产生List值,与Python解释器中列表生成式的动作有所差异。 使用方式:同列表生成式。 示例如下: ```python import mindspore as ms @ms.jit() def test(x, y): l = (x * x for x in range(1, 11) if x % 2 == 0) return l ret = test(1, 5) print('ret:{}'.format(ret)) ``` 结果如下: ```text ret:(4, 16, 36, 64, 100) ``` 使用限制同列表生成式。 ### with语句 在图模式下,有限制地支持with语句。with语句要求对象必须有两个魔术方法:`__enter__()`和`__exit__()`。 示例如下: ```python import mindspore as ms import mindspore.nn as nn from mindspore import set_context set_context(mode=ms.GRAPH_MODE) @ms.jit_class class Sample: def __init__(self): super(Sample, self).__init__() self.num = ms.Tensor([2]) def __enter__(self): return self.num * 2 def __exit__(self, exc_type, exc_value, traceback): return self.num * 4 class TestNet(nn.Cell): def construct(self): res = 1 obj = Sample() with obj as sample: res += sample return res, obj.num test_net = TestNet() out1, out2 = test_net() print("out1:", out1) print("out2:", out2) ``` 结果如下: ```text out1: [5] out2: [2] ``` ## 函数 ### Python内置函数 当前支持的Python内置函数包括:`int`、`float`、`bool`、`str`、`list`、`tuple`、`getattr`、`hasattr`、`len`、`isinstance`、`all`、`any`、`round`、`max`、`min`、`sum`、`abs`、`partial`、`map`、`range`、`enumerate`、`super`、`pow`和`filter`。图模式下内置函数的使用方法与对应的Python内置函数类似。 #### int 功能:返回一个基于数字或字符串构造的整数对象。 调用:`int(x=0, base=10)` 入参: - `x` -- 需要被转换为整数的对象,支持类型为`int`、`float`、`bool`、`str`、常量`Tensor`以及第三方对象(例如`numpy.ndarray`)。 - `base` -- 待转换进制, 只有在`x`为`str`类型的时候, 才可以设置该输入。 返回值:转换后的整数值。 代码用例如下: ```python import mindspore as ms @ms.jit def func(): a = int(3) b = int(3.6) c = int('12', 16) d = int('0xa', 16) e = int('10', 8) return a, b, c, d, e a, b, c, d, e = func() print("a: ", a) print("b: ", b) print("c: ", c) print("d: ", d) print("e: ", e) ``` 输出结果: ```text a: 3 b: 3 c: 18 d: 10 e: 8 ``` #### float 功能:返回一个基于数字或字符串构造的浮点数对象。 调用:`float(x=0)` 入参:`x` -- 需要被转换为浮点数的对象,支持类型为`int`、`float`、`bool`、`str`、常量`Tensor`以及第三方对象(例如`numpy.ndarray`)。 返回值:转换后的浮点数值。 代码用例如下: ```python import mindspore as ms @ms.jit def func(): a = float(1) b = float(112) c = float(-123.6) d = float('123') return a, b, c, d a, b, c, d = func() print("a: ", a) print("b: ", b) print("c: ", c) print("d: ", d) ``` 输出结果: ```text a: 1.0 b: 112.0 c: -123.6 d: 123.0 ``` #### bool 功能:返回一个基于输入构造的布尔值的对象。 调用:`bool(x=false)` 入参:`x` -- 需要被转换为布尔值的对象,支持类型为`int`、`float`、`bool`、`str`、`list`、 `tuple`、 `dict`、`Tensor`以及第三方对象(例如`numpy.ndarray`)。 返回值:若输入 `x` 不是 `Tensor`,则返回转换后的布尔值。若输入 `x` 为 `Tensor`,则返回布尔类型的 `Tensor`。 代码用例如下: ```python import mindspore as ms @ms.jit def func(): a = bool() b = bool(0) c = bool("abc") d = bool([1, 2, 3, 4]) e = bool(ms.Tensor([10])) return a, b, c, d, e a, b, c, d, e = func() print("a: ", a) print("b: ", b) print("c: ", c) print("d: ", d) print("e: ", e) ``` 输出结果: ```text a: False b: False c: True d: True e: [True] # e 为布尔类型的Tensor ``` #### str 功能:返回一个基于输入构造的字符串的对象。 调用:`str(x='')` 入参:`x` -- 需要被转换为字符串的对象,支持类型为`int`、`float`、`bool`、`str`、`list`、 `tuple`、 `dict`、常量`Tensor`以及第三方对象(例如`numpy.ndarray`)。其中,`list`、 `tuple`以及`dict`中不能含有非常量值。 返回值:输入`x`转换后的字符串。 代码用例如下: ```python import numpy as np import mindspore as ms @ms.jit def func(): a = str() b = str(0) c = str([1, 2, 3, 4]) d = str(Tensor([10])) e = str(np.array([1, 2, 3, 4])) return a, b, c, d, e a, b, c, d, e = func() print("a: ", a) print("b: ", b) print("c: ", c) print("d: ", d) print("e: ", e) ``` 输出结果: ```text a: # a 为空字符串 b: 0 c: [1, 2, 3, 4] d: Tensor(shape=[1], dtype=Int64, value=[10]) e: [1 2 3 4] ``` #### tuple 功能:返回一个基于输入构造的元组。 调用:`tuple(x=())` 入参:`x` -- 需要被转换为元组的对象,支持类型为`list`、 `tuple`、 `dict`、`Tensor`以及第三方对象(例如`numpy.ndarray`)。 返回值:按照`x`的第零纬度拆分得到的元组。 代码用例如下: ```python import numpy as np import mindspore as ms @ms.jit def func(): a = tuple((1, 2, 3)) b = tuple(np.array([1, 2, 3])) c = tuple({'a': 1, 'b': 2, 'c': 3}) d = tuple(ms.Tensor([1, 2, 3])) return a, b, c ,d a, b, c ,d = func() print("a: ", a) print("b: ", b) print("c: ", c) print("d: ", d) ``` 输出结果: ```text a: (1, 2, 3) b: (1, 2, 3) c: ('a', 'b', 'c') d: (Tensor(shape=[], dtype=Int64, value= 1), Tensor(shape=[], dtype=Int64, value= 2), Tensor(shape=[], dtype=Int64, value= 3)) ``` #### list 功能:返回一个基于输入构造的列表。 调用:`list(x=())` 入参:`x` -- 需要被转换为列表的对象,支持类型为`list`、 `tuple`、 `dict`、`Tensor`以及第三方对象(例如`numpy.ndarray`)。 返回值:按照`x`的第零纬度拆分得到的列表。 代码用例如下: ```python import mindspore as ms @ms.jit def func(): a = list((1, 2, 3)) b = list(np.array([1, 2, 3])) c = list({'a':1, 'b':2, 'c':3}) d = list(ms.Tensor([1, 2, 3])) return a, b, c, d a_t, b_t, c_t, d_t = func() print("a_t: ", a_t) print("b_t: ", b_t) print("c_t: ", c_t) print("d_t: ", d_t) ``` 输出结果: ```text a_t: (1, 2, 3) b_t: (1, 2, 3) c_t: ('a', 'b', 'c') d_t: (Tensor(shape=[], dtype=Int64, value= 1), Tensor(shape=[], dtype=Int64, value= 2), Tensor(shape=[], dtype=Int64, value= 3)) ``` 在静态图模式下,若返回值内存在列表,则会被自动转换为元组。因此上述用例的`a_t`、`b_t`、`c_t`、`d_t`均为元组。但是`a`、`b`、`c`、`d`仍为列表。 #### getattr 功能:获取对象的属性。 调用:`getattr(x, attr, default)` 入参: - `x` -- 需要被获取属性的对象,可以为任意的图模式支持类型,不支持第三方库类型。 - `attr` -- 需要获取的属性, 需要为`str`。 - `default` -- 可选参数。若`x`没有`attr`, 则返回`default`, 可以为任意的图模式支持类型,不支持第三方库类型。若未输入`default`,且`x`没有属性`attr`,则会抛出AttributeError。 返回值:目标属性或者`default`。 代码用例如下: ```python import mindspore as ms @ms.jit_class class MSClass1: def __init__(self): self.num0 = 0 ms_obj = MSClass1() @ms.jit def func(): a = getattr(ms_obj, 'num0') b = getattr(ms_obj, 'num1', 2) return a, b a, b = func() print("a: ", a) print("b: ", b) ``` 输出结果: ```text a: 0 b: 2 ``` 在静态图模式下对象的属性可能会和动态图模式下有区别,建议使用`default`输入,或者在使用`getattr`前先使用`hasattr`进行校验。 #### hasattr 功能:判断对象是否具有该属性。 调用:`hasattr(x, attr)` 入参: - `x` -- 需要被判断是否具有某属性的对象,可以为任意的图模式支持类型,也可以为第三方库类型。 - `attr` -- 属性名, 需要为`str`。 返回值:布尔值, 表示是否具有该属性。 代码用例如下: ```python import mindspore as ms @ms.jit_class class MSClass1: def __init__(self): self.num0 = 0 ms_obj = MSClass1() @ms.jit def func(): a = hasattr(ms_obj, 'num0') b = hasattr(ms_obj, 'num1') return a, b a, b = func() print("a: ", a) print("b: ", b) ``` 输出结果: ```text a: True b: False ``` #### len 功能:求序列的长度。 调用:`len(sequence)` 入参:`sequence` -- `Tuple`、`List`、`Dictionary`、`Tensor`以及第三方对象(例如numpy.ndarray)。 返回值:序列的长度,类型为`int`。当入参是`Tensor`时,返回的是`Tensor`第零维的长度。 示例如下: ```python import mindspore as ms import numpy as np z = ms.Tensor(np.ones((6, 4, 5))) @ms.jit() def test(): x = (2, 3, 4) y = [2, 3, 4] d = {"a": 2, "b": 3} n = np.array([1, 2, 3, 4]) x_len = len(x) y_len = len(y) d_len = len(d) z_len = len(z) n_len = len(n) return x_len, y_len, d_len, z_len, n_len x_len, y_len, d_len, z_len, n_len = test() print('x_len:{}'.format(x_len)) print('y_len:{}'.format(y_len)) print('d_len:{}'.format(d_len)) print('z_len:{}'.format(z_len)) print('n_len:{}'.format(n_len)) ``` 结果如下: ```text x_len:3 y_len:3 d_len:2 z_len:6 z_len:4 ``` #### isinstance 功能:判断对象是否为类的实例。区别于算子Isinstance,该算子的第二个入参是MindSpore的dtype模块下定义的类型。 调用:`isinstance(obj, type)` 入参: - `obj` -- MindSpore支持类型的一个实例。 - `type` -- `bool`、`int`、`float`、`str`、`list`、`tuple`、`dict`、`Tensor`、`Parameter`,或者是一个只包含这些类型的`tuple`。 返回值:`obj`为`type`的实例,返回`True`,否则返回`False`。 示例如下: ```python import mindspore as ms import numpy as np z = ms.Tensor(np.ones((6, 4, 5))) @ms.jit() def test(): x = (2, 3, 4) y = [2, 3, 4] x_is_tuple = isinstance(x, tuple) y_is_list = isinstance(y, list) z_is_tensor = isinstance(z, Tensor) return x_is_tuple, y_is_list, z_is_tensor x_is_tuple, y_is_list, z_is_tensor = test() print('x_is_tuple:{}'.format(x_is_tuple)) print('y_is_list:{}'.format(y_is_list)) print('z_is_tensor:{}'.format(z_is_tensor)) ``` 结果如下: ```text x_is_tuple:True y_is_list:True z_is_tensor:True ``` #### all 功能:判断输入中的元素是否均为真值。 调用:`all(x)` 入参:`x` -- 可迭代对象,支持类型包括`tuple`、`list`、`dict`、`Tensor`以及第三方对象(例如`numpy.ndarray`)。 返回值:布尔值, 表示输入中的元素是否均为真值。 代码用例如下: ```python import mindspore as ms @ms.jit def func(): a = all(['a', 'b', 'c', 'd']) b = all(['a', 'b', '', 'd']) c = all([0, 1, 2, 3]) d = all(('a', 'b', 'c', 'd')) e = all(('a', 'b', '', 'd')) f = all((0, 1, 2, 3)) g = all([]) h = all(()) return a, b, c, d, e, f, g, h a, b, c, d, e, f, g, h = func() print("a: ", a) print("b: ", b) print("c: ", c) print("d: ", d) print("e: ", e) print("f: ", f) print("g: ", g) print("h: ", h) ``` 输出结果: ```text a: True b: False c: False d: True e: False f: False g: True h: True ``` #### any 功能:判断输入中的元素是存在为真值。 调用:`any(x)` 入参:`x` -- 可迭代对象,支持类型包括`tuple`、`list`、`dict`、`Tensor`以及第三方对象(例如`numpy.ndarray`)。 返回值:布尔值,表示输入中的元素是否存在真值。 代码用例如下: ```python import mindspore as ms @ms.jit def func(): a = any(['a', 'b', 'c', 'd']) b = any(['a', 'b', '', 'd']) c = any([0, '', False]) d = any(('a', 'b', 'c', 'd')) e = any(('a', 'b', '', 'd')) f = any((0, '', False)) g = any([]) h = any(()) return a, b, c, d, e, f, g, h a, b, c, d, e, f, g, h = func() print("a: ", a) print("b: ", b) print("c: ", c) print("d: ", d) print("e: ", e) print("f: ", f) print("g: ", g) print("h: ", h) ``` 输出结果: ```text a: True b: True c: False d: True e: True f: False g: False h: False ``` #### round 功能:返回输入的四舍五入。 调用:`round(x, digit=0)` 入参: - `x` -- 需要四舍五入的值,有效类型为 `int`、`float`、`bool`、`Tensor` 以及定义了魔术方法 `__round__()` 第三方对象。 - `digit` -- 表示进行四舍五入的小数点位数,默认值为0,支持 `int` 类型以及 `None`。 若 `x` 为 `Tensor` 类型, 则不支持输入 `digit`。 返回值:四舍五入后的值。 代码用例如下: ```python import mindspore as ms @ms.jit def func(): a = round(10) b = round(10.123) c = round(10.567) d = round(10, 0) e = round(10.72, -1) f = round(17.12, -1) g = round(10.17, 1) h = round(10.12, 1) return a, b, c, d, e, f, g, h a, b, c, d, e, f, g, h = func() print("a: ", a) print("b: ", b) print("c: ", c) print("d: ", d) print("e: {:.2f}".format(e)) print("f: {:.2f}".format(f)) print("g: {:.2f}".format(g)) print("h: {:.2f}".format(h)) ``` 输出结果: ```text a: 10 b: 10 c: 11 d: 10 e: 10.00 f: 20.00 g: 10.20 h: 10.10 ``` #### max 功能:返回最大值。 调用:`max(*data)` 入参: - `*data` -- 若`*data`为单输入,则会比较单个输入内的各个元素,此时`data`必须为可迭代对象。若存在多个输入,则比较每个输入。`data`有效类型为`int`、`float`、`bool`、`list`、`tuple`、`dict`、`Tensor`以及第三方对象(例如`numpy.ndarray`)。 返回值:最大值。 代码用例如下: ```python import numpy as np import mindspore as ms @ms.jit def func(): a = max([0, 1, 2, 3]) b = max((0, 1, 2, 3)) c = max({1: 10, 2: 20, 3: 3}) d = max(np.array([1, 2, 3, 4])) e = max(('a', 'b', 'c')) f = max((1, 2, 3), (1, 4)) g = max(ms.Tensor([1, 2, 3])) return a, b, c, ms.Tensor(d), e, f, g a, b, c, d, e, f, g = func() print("a: ", a) print("b: ", b) print("c: ", c) print("d: ", d) print("e: ", e) print("f: ", f) print("g: ", g) ``` 输出结果: ```text a: 3 b: 3 c: 3 d: 4 e: c f: (1, 4) g: 3 ``` #### min 功能:返回最小值。 调用:`min(*data)` 入参: - `*data` -- 若`*data`为单输入,则会比较单个输入内的各个元素,此时`data`必须为可迭代对象。若存在多个输入,则比较每个输入。`data`有效类型为`int`、`float`、`bool`、`list`、`tuple`、`dict`、`Tensor`以及第三方对象(例如`numpy.ndarray`)。 返回值:最小值。 代码用例如下: ```python import numpy as np import mindspore as ms @ms.jit def func(): a = min([0, 1, 2, 3]) b = min((0, 1, 2, 3)) c = min({1: 10, 2: 20, 3: 3}) d = min(np.array([1, 2, 3, 4])) e = min(('a', 'b', 'c')) f = min((1, 2, 3), (1, 4)) g = min(ms.Tensor([1, 2, 3])) return a, b, c, ms.Tensor(d), e, f, g a, b, c, d, e, f, g = func() print("a: ", a) print("b: ", b) print("c: ", c) print("d: ", d) print("e: ", e) print("f: ", f) print("g: ", g) ``` 输出结果: ```text a: 0 b: 0 c: 1 d: 1 e: a f: (1, 2, 3) g: 1 ``` #### sum 功能:对输入序列进行求和计算。 调用:`sum(x, n=0)` 入参: - `x` -- 表示可迭代对象,有效类型为`list`、`tuple`、`Tensor`以及第三方对象(例如`numpy.ndarray`)。 - `n` -- 表示指定相加的参数,缺省值为0。 返回值:对`x`求和后与`n`相加得到的值。 代码用例如下: ```python import numpy as np import mindspore as ms @ms.jit def func(): a = sum([0, 1, 2]) b = sum((0, 1, 2), 10) c = sum(np.array([1, 2, 3])) d = sum(ms.Tensor([1, 2, 3]), 10) e = sum(ms.Tensor([[1, 2], [3, 4]])) f = sum([1, ms.Tensor([[1, 2], [3, 4]]), ms.Tensor([[1, 2], [3, 4]])], ms.Tensor([[1, 1], [1, 1]])) return a, b, ms.Tensor(c), d, e, f a, b, c, d, e, f = func() print("a: ", a) print("b: ", b) print("c: ", c) print("d: ", d) print("e: ", e) print("f: ", f) ``` 输出结果: ```text a: 3 b: 13 c: 6 d: 16 e: [4 6] f: [[ 4 6] [ 8 10]] ``` #### abs 功能:返回绝对值,使用方法与python的`abs()`一致。 调用:`abs(x)` 入参: - `x` -- 有效类型为`int`、`float`、`bool`、`complex`、`Tensor`以及第三方对象(例如`numpy.ndarray`)。 返回值:绝对值。 代码用例如下: ```python import mindspore as ms @ms.jit def func(): a = abs(-45) b = abs(100.12) return a, b a, b = func() print("a: ", a) print("b: {:.2f}".format(b)) ``` 输出结果: ```text a: 45 b: 100.12 ``` #### partial 功能:偏函数,固定函数入参。 调用:`partial(func, arg, ...)` 入参: - `func` -- 函数。 - `arg` -- 一个或多个要固定的参数,支持位置参数和键值对传参。 返回值:返回某些入参固定了值的函数。 示例如下: ```python import mindspore as ms from mindspore import ops def add(x, y): return x + y @ms.jit() def test(): add_ = ops.partial(add, x=2) m = add_(y=3) n = add_(y=5) return m, n m, n = test() print('m:{}'.format(m)) print('n:{}'.format(n)) ``` 结果如下: ```text m:5 n:7 ``` #### map 功能:根据提供的函数对一个或者多个序列做映射,由映射的结果生成一个新的序列。 如果多个序列中的元素个数不一致,则生成的新序列与最短的那个长度相同。 调用:`map(func, sequence, ...)` 入参: - `func` -- 函数。 - `sequence` -- 一个或多个序列(`Tuple`或者`List`)。 返回值:返回一个`Tuple`。 示例如下: ```python import mindspore as ms def add(x, y): return x + y @ms.jit() def test(): elements_a = (1, 2, 3) elements_b = (4, 5, 6) ret = map(add, elements_a, elements_b) return ret ret = test() print('ret:{}'.format(ret)) ``` 结果如下: ```text ret: (5, 7, 9) ``` #### zip 功能:将多个序列中对应位置的元素打包成一个个元组,然后由这些元组组成一个新序列, 如果各个序列中的元素个数不一致,则生成的新序列与最短的那个长度相同。 调用:`zip(sequence, ...)` 入参:`sequence` -- 一个或多个序列(`Tuple`或`List`)。 返回值:返回一个`Tuple`。 示例如下: ```python import mindspore as ms @ms.jit() def test(): elements_a = (1, 2, 3) elements_b = (4, 5, 6) ret = zip(elements_a, elements_b) return ret ret = test() print('ret:{}'.format(ret)) ``` 结果如下: ```text ret:((1, 4), (2, 5), (3, 6)) ``` #### range 功能:根据起始值、结束值和步长创建一个`Tuple`。 调用: - `range(start, stop, step)` - `range(start, stop)` - `range(stop)` 入参: - `start` -- 计数起始值,类型为`int`,默认为0。 - `stop` -- 计数结束值,但不包括在内,类型为`int`。 - `step` -- 步长,类型为`int`,默认为1。 返回值:返回一个`Tuple`。 示例如下: ```python import mindspore as ms @ms.jit() def test(): x = range(0, 6, 2) y = range(0, 5) z = range(3) return x, y, z x, y, z = test() print('x:{}'.format(x)) print('y:{}'.format(y)) print('z:{}'.format(z)) ``` 结果如下: ```text x:(0, 2, 4) y:(0, 1, 2, 3, 4) z:(0, 1, 2) ``` #### enumerate 功能:生成一个序列的索引序列,索引序列包含数据和对应下标。 调用: - `enumerate(sequence, start)` - `enumerate(sequence)` 入参: - `sequence` -- 一个序列(`Tuple`、`List`、`Tensor`)。 - `start` -- 下标起始位置,类型为`int`,默认为0。 返回值:返回一个`Tuple`。 示例如下: ```python import mindspore as ms import numpy as np y = ms.Tensor(np.array([[1, 2], [3, 4], [5, 6]])) @ms.jit() def test(): x = (100, 200, 300, 400) m = enumerate(x, 3) n = enumerate(y) return m, n m, n = test() print('m:{}'.format(m)) print('n:{}'.format(n)) ``` 结果如下: ```text m:((3, 100), (4, 200), (5, 300), (6, 400)) n:((0, Tensor(shape=[2], dtype=Int64, value= [1, 2])), (1, Tensor(shape=[2], dtype=Int64, value= [3, 4])), (2, Tensor(shape=[2], dtype=Int64, value= [5, 6]))) ``` #### super 功能:用于调用父类(超类)的一个方法,一般在`super`之后调用父类的方法。 调用: - `super().xxx()` - `super(type, self).xxx()` 入参: - `type` -- 类。 - `self` -- 对象。 返回值:返回父类的方法。 示例如下: ```python import mindspore as ms from mindspore import nn, set_context set_context(mode=ms.GRAPH_MODE) class FatherNet(nn.Cell): def __init__(self, x): super(FatherNet, self).__init__(x) self.x = x def construct(self, x, y): return self.x * x def test_father(self, x): return self.x + x class SingleSubNet(FatherNet): def __init__(self, x, z): super(SingleSubNet, self).__init__(x) self.z = z def construct(self, x, y): ret_father_construct = super().construct(x, y) ret_father_test = super(SingleSubNet, self).test_father(x) return ret_father_construct, ret_father_test ``` #### pow 功能:求幂。 调用:`pow(x, y)` 入参: - `x` -- 底数, `Number`或`Tensor`。 - `y` -- 幂指数, `Number`或`Tensor`。 返回值:返回`x`的`y`次幂,`Number`或`Tensor`。 示例如下: ```python import mindspore as ms import numpy as np x = ms.Tensor(np.array([1, 2, 3])) y = ms.Tensor(np.array([1, 2, 3])) @ms.jit() def test(x, y): return pow(x, y) ret = test(x, y) print('ret:{}'.format(ret)) ``` 结果如下: ```text ret:[ 1 4 27] ``` #### print 功能:用于打印。 调用:`print(arg, ...)` 入参:`arg` -- 要打印的信息(`int` 、`float`、`bool`、`String`或`Tensor`)。 当打印的数据是`int`,`float`或者`bool`时,会将其包成一个`0-D`的tensor打印出来。 返回值:无返回值。 示例如下: ```python import mindspore as ms import numpy as np x = ms.Tensor(np.array([1, 2, 3]), ms.int32) y = ms.Tensor(3, ms.int32) @ms.jit() def test(x, y): print(x) print(y) return x, y ret = test(x, y) ``` 结果如下: ```text Tensor(shape=[3], dtype=Int32, value= [1 2 3]) 3 ``` #### filter 功能:根据提供的函数对一个序列的元素做判断,每个元素依次作为参数传入函数中,将返回结果不为0或False的元素组成新的序列。 调用:`filter(func, sequence)` 入参: - `func` -- 函数。 - `sequence` -- 序列(`Tuple`或`List`)。 返回值:返回一个`Tuple`。 示例如下: ```python import mindspore as ms def is_odd(x): if x % 2: return True return False @ms.jit() def test(): elements = (1, 2, 3, 4, 5) ret = filter(is_odd, elements) return ret ret = test() print('ret:{}'.format(ret)) ``` 结果如下: ```text ret:(1, 3, 5) ``` ### 函数参数 - 参数默认值:目前不支持默认值设为`Tensor`类型数据,支持`int`、`float`、`bool`、`None`、`str`、`tuple`、`list`、`dict`类型数据。 - 可变参数:支持带可变参数网络的推理和训练。 - 键值对参数:目前不支持带键值对参数的函数求反向。 - 可变键值对参数:目前不支持带可变键值对的函数求反向。 ## 网络定义 ### 网络入参 整网(最外层网络)入参仅支持`bool`、`int`、`float`、`Tensor`、`None`、`mstype.number(mstype.bool_、mstype.int、mstype.float、mstype.uint)`,以及只包含这些类型对象的`list`或者`tuple`,和`value`值是这些类型的`Dictionary`。 在对整网入参求梯度的时候,会忽略非`Tensor`的入参,只计算`Tensor`入参的梯度。例如整网入参`(x, y, z)`中,`x`和`z`是`Tensor`,`y`是非`Tensor`时,在对整网入参求梯度的时候,只会计算`x`和`z`的梯度,返回`(grad_x, grad_z)`。 如果网络里要使用其他类型,可在初始化网络的时候,传入该类型对象,作为网络属性保存起来,然后在`construct`里使用。 内层调用的网络入参无此限制。 示例如下: ```python import mindspore as ms from mindspore import nn, ops, set_context import numpy as np set_context(mode=ms.GRAPH_MODE) class Net(nn.Cell): def __init__(self, flag): super(Net, self).__init__() self.flag = flag def construct(self, x, y, z): if self.flag == "ok": return x + y + z return x - y - z class GradNet(nn.Cell): def __init__(self, net): super(GradNet, self).__init__() self.forward_net = net def construct(self, x, y, z): return ms.grad(self.forward_net, grad_position=(0, 1, 2))(x, y, z) flag = "ok" input_x = ms.Tensor(np.ones((2, 3)).astype(np.float32)) input_y = 2 input_z = ms.Tensor(np.ones((2, 3)).astype(np.float32) * 2) net = Net(flag) grad_net = GradNet(net) ret = grad_net(input_x, input_y, input_z) print('ret:{}'.format(ret)) ``` 结果如下: ```text ret:(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]]), 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]])) ``` 上面定义的Net网络里,在初始化时传入一个`string` flag,作为网络的属性保存起来,然后在`construct`里使用`self.flag`这个属性。 整网入参`x`和`z`是`Tensor`,`y`是`int`数,`grad_net`在对整网入参`(x, y, z)`求梯度时,会自动忽略`y`的梯度,只计算`x`和`z`的梯度,`ret = (grad_x, grad_z)`。 ### 网络实例类型 - 带[@jit](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/mindspore/mindspore.jit.html)装饰器的普通Python函数。 - 继承自[nn.Cell](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/nn/mindspore.nn.Cell.html)的Cell子类。 ### 网络构造组件 | 类别 | 内容 | | :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Cell`实例 | [mindspore/nn/*](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/mindspore.nn.html)、自定义[Cell](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/nn/mindspore.nn.Cell.html)。 | | `Cell`实例的成员函数 | Cell的construct中可以调用其他类成员函数。 | | `jit_class`实例 | 使用[@jit_class](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/mindspore/mindspore.jit_class.html)装饰的类。 | | `Primitive`算子 | [mindspore/ops/operations/*](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/mindspore.ops.primitive.html) | | `Composite`算子 | [mindspore/ops/composite/*](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/mindspore.ops.primitive.html) | | `constexpr`生成算子 | 使用[@constexpr](https://www.mindspore.cn/docs/zh-CN/r2.0.0-alpha/api_python/ops/mindspore.ops.constexpr.html)生成的值计算算子。 | | 函数 | 自定义Python函数、前文中列举的系统函数。 | ### 网络使用约束 1. 不允许修改网络的非`Parameter`类型数据成员。 示例如下: ```python import mindspore as ms from mindspore import nn, set_context import numpy as np set_context(mode=ms.GRAPH_MODE) class Net(nn.Cell): def __init__(self): super(Net, self).__init__() self.x = 2 self.par = ms.Parameter(ms.Tensor(np.ones((2, 3, 4))), name="par") def construct(self, x, y): self.par[0] = y self.x = x return x + y net = Net() net(1, 2) ``` 上面所定义的网络里,`self.x`不是一个`Parameter`,不允许被修改,而`self.par`是一个`Parameter`,可以被修改。 结果报错如下: ```Text TypeError: 'self.x' should be initialized as a 'Parameter' type in the '__init__' function ``` 2. 当`construct`函数里,使用未定义的类成员时,不会像Python解释器那样抛出`AttributeError`,而是作为`None`处理。 示例如下: ```python from mindspore import nn, set_context set_context(mode=ms.GRAPH_MODE) class Net(nn.Cell): def __init__(self): super(Net, self).__init__() def construct(self, x): return x + self.y net = Net() net(1) ``` 上面所定义的网络里,`construct`里使用了并未定义的类成员`self.y`,此时会将`self.y`作为`None`处理。 结果报错如下: ```Text RuntimeError: mindspore/ccsrc/frontend/operator/composite/multitype_funcgraph.cc:161 GenerateFromTypes] The 'add' operation does not support the type [Int64, kMetaTypeNone] ``` 3. `nn.Cell`不支持`classmethod`修饰的类方法。