大佬教程收集整理的这篇文章主要介绍了OneFlow 是如何获取前端的计算表达式的,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
这篇文章是 OneFlow 源码的学习笔记,主要涉及如下几个内容:
之前我没有学过、用过 Python 装饰器,在 OneFlow 中看到了之后,就顺便学一下。看源码是学语言的最好方式hhh。
这个问题是在阅读 [1] 的时候产生的,OneFlow 是如何获取前端的计算表达式的,什么时候进行编译?
import oneflow as flow
from oneflow.python.framework.typing import Numpy
import oneflow.typing as tp
import numpy as np
@flow.global_function()
def zero_pad(x: tp.Numpy.Placeholder((2, 1, 3, 3))) -> tp.Numpy:
with flow.scope.placement('cpu', '0:0'):
unused = flow.reflection_pad2d(x, padding=1)
out = flow.zero_pad2d(x, padding=2)
return out
@flow.global_function()
def reflection_pad(x: tp.Numpy.Placeholder((2, 1, 3, 3))) -> tp.Numpy:
with flow.scope.placement('cpu', '0:0'):
loss = flow.reflection_pad2d(x, padding=1)
return loss
x = np.arange(18).reshape((2, 1, 3, 3)).astype(np.float)
y = zero_pad(X)
y1 = zero_pad(X)
y2 = reflection_pad(X)
print('in:n{} n out:n{}n out1:n{} nout2:n{}'.format(x, y, y1, y2))
print('{} {} {} {}'.format(type(X), type(y), type(y1), type(y2)))
OneFlow 可以大致分为三个部分,前端、编译期、运行时。前端部分定义的计算是如何获取到的,以一种什么样的中间格式传给底层,更具体一点 OneFlow 底层如何获取到 reflection_pad2d 这个算子的呢?
形如 @flow.global_function()
这样的东西,放在函数定义上面,这东西在 Java 中叫注解,在 Python 中,它叫做装饰器。链接 [2] 是关于装饰器的,扫了一眼,没细看,感觉还行吧。
def preprocess(funC):
def Decorator(*args):
print('preprocess')
func(*args)
return Decorator
@preprocess
def Hello(a, b):
print('{} says Hello to {}'.format(a, b))
Hello('Jack', 'Rose')
输出: good preprocess Jack says Hello to Rose postprocess
preprocess
Jack says Hello to Rose
分析:被装饰的 Hello 函数,被传入到 preprocess 这个函数里面,经过 Decorator 封装后返回,将结果赋值给一个叫做 Hello 的变量。
def process(*names):
def Decorator(fun):
print(*names)
def new_func(*args, **kwds):
print('preprocess')
fun(*args, **kwds)
print('postprocess')
return new_func
return Decorator
@process('good')
def Hello(a, b):
print('{} says Hello to {}'.format(a, b))
Hello('Jack', 'Rose')
输出:
good
preprocess
Jack says Hello to Rose
postprocess
分析:这个例子和上面那个其实没有本质区别。这次的装饰器可以接收参数,并且返回一个函数闭包,接收的这些参数可以在返回的函数中访问(所谓的闭包)。因为返回了一个函数,所以会将函数 Hello 传入到那个函数里面,进行装饰,后面就类似上面简单的例子了。
我认为装饰器的本质就是函数,接受的参数是函数,并且会将函数返回的内容赋值给定义的变量,上面定义的变量就是 Hello。如果返回值是函数,那么 Hello 就是函数。当然你也可以返回一个常量,后面将 Hello 打印出来,会看到 Hello 的值。
跟着上面的代码,单步调试。下面记录几个重要的函数调用和过程。
Q: 编译触发的时机
第一次调用自定义的 Job 函数的时候。最开始的代码里面,第一次调用 zero_pad 的时候,触发编译,启动。
Q: OneFlow 是如何获取前端的计算表达式的
执行用户定义的 Job 函数,里面调用了 user_op_builder.py 的方法 InferAndTryRun,通过 CurJobAddop 这个函数,将算子的配置,输入输出等信息发送到底层。Op 还有其他方法,比如设置输入 Input 和输出 Output。
Q: 以一种什么样的中间格式传给底层
user_op_builder.py:173,传一个 op_conf 给底层,op_conf 是一个 ProtoBuf message OperatorConf。
实际上,当调用自己定义的 job 函数的时候,真正执行的是 _RunLazyJob 的内容。
# v0.3.5 function_util.py:148
@enable_if.condition(
hob.in_normal_mode & ~hob.eager_execution_enabled & ~hob.session_initialized
)
def lazy_oneflow_function(function_config=FunctionConfig()):
assert isinstance(function_config, FunctionConfig)
def Decorator(job_funC):
if not hasattr(job_func, "__oneflow_function_signature__"):
job_func.__oneflow_function_signature__ = inspect.signature(job_funC)
oft_util.checkGlobalFunctionAnnotation(job_func.__oneflow_function_signature__)
sess = session_ctx.GetDefaultSession()
@functools.wraps(job_funC)
def Func(*args, **kwargs):
return _RunLazyJob(sess, job_func, *args, **kwargs)
sess.AddJob(_CloneFunctionDesc(function_config.function_desc, job_funC))
for x in dir(job_funC):
if x.startswith("__oneflow_"):
setattr(Func, x, getattr(job_func, X))
return Func
return Decorator
[1] https://zhuanlan.zhihu.com/p/344531540 [2] https://zhuanlan.zhihu.com/p/78500405
以上是大佬教程为你收集整理的OneFlow 是如何获取前端的计算表达式的全部内容,希望文章能够帮你解决OneFlow 是如何获取前端的计算表达式的所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。