MindSpore两日集训——控制流的使用
题目:控制流
看到这个作业,不禁心中一喜,就想是不是分分钟的事?但事实并不是这样。
解析:
Step1: 先看参考文档:https://www.mindspore.cn/docs/programming_guide/zh-CN/r1.5/control_flow.html
得出一下几点结论:
1、 while for和平时的python用法一样,对Python基础较好的同学非常友好。
2、 条件为变量的场景:
条件表达式中存在Tensor或者元素为Tensor类型的List、Tuple、Dict,并且条件表达式的结果受Tensor的值影响。
3、 使用流程控制语句时,MindSpore会依据条件是否为变量来决策是否在网络中生成控制流算子,只有条件为变量时网络中才会生成控制流算子。如果条件表达式的结果需要在图编译时确定,则条件为常量,否则条件为变量。
4、 不符合结论3会产生报错,我们用例子来看这一点。
例1
import numpy as np
from mindspore import context
from mindspore import Tensor, nn
from mindspore import dtype as ms
class SingleIfNet(nn.Cell):
def construct(self, x, y, z):
if x < y:
out = x
else:
out = z
out = out + 1
return out
forward_net = SingleIfNet()
x = Tensor(np.array(0), dtype=ms.int32)
y = Tensor(np.array(1), dtype=ms.int32)
z = Tensor(np.array([0, 1]), dtype=ms.int32)
output = forward_net(x, y, z)
报错:ValueError: mindspore/ccsrc/pipeline/jit/static_analysis/static_analysis.cc:734 ProcessEvalResults] The return values of different branches do not match. Shape Join Failed: shape1 = (2), shape2 = ()..
看了这段话,还是不明白是什么意思。
参考官方文档:
在例1中,out在true分支被赋值为[0],在false分支被赋值为[0, 1],条件x < y为变量,因此在out = out + 1这一句无法确定输入out的数据类型,会导致图编译出现异常。
什么意思?
就是说,这个if x&amp;amp;amp;amp;lt;y
是变量表达式,但是里面的out = out + 1
含有常量,就不可以,因为在编译的时候,x,y均为确定,于是无法确定后序操作。那么怎办呢?
方法1
初始化x,y,把表达式改为常量表达式
import numpy as np
from mindspore import context
from mindspore import Tensor, nn
from mindspore import dtype as ms
class SingleIfNet(nn.Cell):
def construct(self, z):
x = 0
y = 1
if x < y + 1:
out = x
else:
out = z
out = out + 1
return out
forward_net = SingleIfNet()
z = Tensor(np.array([0, 1]), dtype=ms.int32)
output = forward_net(z)
可以看到,我们设定了if x &amp;amp;amp;amp;lt; y + 1:
,于是乎out = out + 1
就不会出错了。
方法2
全部用变量表达式
import numpy as np
from mindspore import context
from mindspore import Tensor, nn
from mindspore import dtype as ms
class SingleIfNet(nn.Cell):
def construct(self, x, y, z):
if x < y:
out = x
else:
out = z
out = out + y
return out
forward_net = SingleIfNet()
x = Tensor(np.array(0), dtype=ms.int32)
y = Tensor(np.array(1), dtype=ms.int32)
z = Tensor(np.array(5), dtype=ms.int32)
output = forward_net(x, y, z)
作业1
回到作业,用while写一个求和循环,在Python里面非常简单。
x = 1
out = 0
for i in range(0, 1001):
out += i
print(out)
或者
x = 0
out = 0
while x!=1001:
out += x
x += 1
print(out)
或者
import sys
sys.setrecursionlimit(2000)
def f(n):
if n == 1000:
return n
else:
return n + f(n + 1)
print(f(0))
注意递归超过998需要设高最大层数。此处需要设置。
我们可以看到运行时间只有86ms,还是很快的。
那么用MindSpore呢?
首先可以用for循环:https://www.mindspore.cn/docs/programming_guide/zh-CN/r1.5/control_flow.html#for
由于题目要求while,因此略过for。
同时注意while循环还有如下好处:
while
语句相比for
语句更为灵活。当while
的条件为常量时,while
对循环体的处理和for
类似,会展开循环体里的内容。当while
的条件为变量时,while
不会展开循环体例的内容,则会在执行图产生控制流算子。
因此我们注意,应该选择变量表达式。
如果是常量表达式,就需要注意前面提到的。
就是说,这个if x&amp;amp;amp;amp;lt;y
是变量表达式,但是里面的out = out + 1
含有常量,就不可以,因为在编译的时候,x,y均为确定,于是无法确定后序操作。
import sys
# sys.setrecursionlimit(20000)
import numpy as np
from mindspore import context
from mindspore import Tensor, nn
from mindspore import dtype as ms
# context.set_context(max_call_depth=20000)
class IfInForNet(nn.Cell):
def construct(self, x, y, out):
while x != 1000:
x = x + y
out = out + x
return out
forward_net = IfInForNet()
x = Tensor(np.array(0), dtype=ms.int32)
y = Tensor(np.array(1), dtype=ms.int32)
out = Tensor(np.array(0), dtype=ms.int32)
output = forward_net(x, y, out)
print(output)
结果正确。
作业2
同理,递归。
import sys
import numpy as np
from mindspore import context
from mindspore import Tensor, nn
from mindspore import dtype as ms
sys.setrecursionlimit(20000)
context.set_context(max_call_depth=20000)
class IfInForNet(nn.Cell):
def construct(self, x, y, out):
if x == k:
return out
else:
x = x + y
out = out + x
return self.construct(x, y, out)
forward_net = IfInForNet()
x = Tensor(np.array(0), dtype=ms.int32)
y = Tensor(np.array(1), dtype=ms.int32)
out = Tensor(np.array(0), dtype=ms.int32)
k = Tensor(np.array(1000), dtype=ms.int32)
output = forward_net(x, y, out)
print(output)
结果正确。
同时这个题有更简单的写法,感谢大佬提出。
import sys
sys.setrecursionlimit(20000)
import numpy as np
from mindspore import context
from mindspore import Tensor, nn
from mindspore import dtype as ms
context.set_context(max_call_depth=20000)
n = Tensor(np.array(1000), dtype=ms.int32)
one = Tensor(np.array(1), dtype=ms.int32)
class IfInForNet(nn.Cell):
def construct(self, n):
if n == one:
return one
else:
return self.construct(n - one) + n
forward_net = IfInForNet()
output = forward_net(n)
print(output)
最后这一种是通过types的控制流方法
一种很快的方法(参考文档)
import types
def f_recursive(cur_i, cur_computer_result=1):
if cur_i == 1:
yield cur_computer_result
yield f_recursive(cur_i - 1, cur_i + cur_computer_result)
def f_recursive_wapper(generator, i):
gen = generator(i)
while isinstance(gen, types.GeneratorType):
gen = gen.__next__()
return gen
print(f_recursive_wapper(f_recursive, 1000000))