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;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;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需要设高最大层数。此处需要设置。
图片.png

我们可以看到运行时间只有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;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)

image.png

结果正确。

作业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)

image.png
结果正确。
同时这个题有更简单的写法,感谢大佬提出。

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))
posted @ 2021-12-25 15:47  MS小白  阅读(15)  评论(0)    收藏  举报