541 lines
15 KiB
Python
541 lines
15 KiB
Python
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import logging
|
|
import unittest
|
|
|
|
import numpy as np
|
|
from dygraph_to_static_utils import (
|
|
Dy2StTestBase,
|
|
enable_to_static_guard,
|
|
test_ast_only,
|
|
)
|
|
|
|
import paddle
|
|
import paddle.jit.dy2static as _jst
|
|
from paddle.jit.dy2static.utils import TransformOptions, func_to_source_code
|
|
|
|
SEED = 2020
|
|
np.random.seed(SEED)
|
|
|
|
# Situation 1 : test recursive call
|
|
|
|
|
|
# Use a decorator to test exception
|
|
@paddle.jit.to_static(full_graph=True)
|
|
def dyfunc_with_if(x_v):
|
|
if paddle.mean(x_v).numpy() > 5:
|
|
x_v = x_v - 1
|
|
else:
|
|
x_v = x_v + 1
|
|
return x_v
|
|
|
|
|
|
@paddle.jit.to_static(full_graph=True)
|
|
def nested_func(x_v):
|
|
x_v = paddle.assign(x_v)
|
|
|
|
def fn1():
|
|
return x_v
|
|
|
|
res = fn1()
|
|
return res
|
|
|
|
|
|
@paddle.jit.to_static(full_graph=True)
|
|
def dyfunc_with_third_library_logging(x_v):
|
|
logging.info('test dyfunc_with_third_library_logging')
|
|
if paddle.mean(x_v).numpy() > 5:
|
|
x_v = x_v - 1
|
|
else:
|
|
x_v = x_v + 1
|
|
return x_v
|
|
|
|
|
|
class A:
|
|
@staticmethod
|
|
def add(a, b):
|
|
"""
|
|
dygraph mode, return a numpy object.
|
|
static graph mode, return a variable object.
|
|
"""
|
|
return paddle.to_tensor(a.numpy() + b.numpy())
|
|
|
|
|
|
@paddle.jit.to_static(full_graph=True)
|
|
def dyfunc_with_staticmethod(x_v):
|
|
a = A()
|
|
return a.add(x_v, x_v)
|
|
|
|
|
|
class TestRecursiveCall1(Dy2StTestBase):
|
|
def setUp(self):
|
|
self.input = np.random.random([10, 16]).astype('float32')
|
|
|
|
def init_test_func(self):
|
|
self.dyfunc = nested_func
|
|
|
|
def get_dygraph_output(self):
|
|
with enable_to_static_guard(False):
|
|
res = self.dyfunc(self.input).numpy()
|
|
return res
|
|
|
|
def get_static_output(self):
|
|
with enable_to_static_guard(True):
|
|
res = self.dyfunc(self.input).numpy()
|
|
return res
|
|
|
|
def test_transformed_static_result(self):
|
|
self.init_test_func()
|
|
static_res = self.get_static_output()
|
|
dygraph_res = self.get_dygraph_output()
|
|
np.testing.assert_allclose(
|
|
dygraph_res,
|
|
static_res,
|
|
rtol=1e-05,
|
|
err_msg=f'dygraph res is {dygraph_res}\nstatic_res is {static_res}',
|
|
)
|
|
|
|
|
|
lambda_fun = lambda x: x
|
|
|
|
|
|
class MyConvLayer(paddle.nn.Layer):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self._conv = paddle.nn.Conv2D(
|
|
in_channels=3,
|
|
out_channels=2,
|
|
kernel_size=3,
|
|
weight_attr=paddle.ParamAttr(
|
|
initializer=paddle.nn.initializer.Constant(value=0.99)
|
|
),
|
|
bias_attr=paddle.ParamAttr(
|
|
initializer=paddle.nn.initializer.Constant(value=0.5)
|
|
),
|
|
)
|
|
|
|
@paddle.jit.to_static(full_graph=True)
|
|
def forward(self, inputs):
|
|
y = dyfunc_with_if(inputs)
|
|
y = lambda_fun(y)
|
|
y = self.dymethod(y)
|
|
return y
|
|
|
|
@paddle.jit.to_static(full_graph=True)
|
|
def dymethod(self, x_v):
|
|
x_v = paddle.assign(x_v)
|
|
return x_v
|
|
|
|
|
|
class MyLayer(paddle.nn.Layer):
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
self.conv = MyConvLayer()
|
|
self.fc = paddle.nn.Linear(
|
|
in_features=5,
|
|
out_features=1,
|
|
weight_attr=paddle.ParamAttr(
|
|
initializer=paddle.nn.initializer.Constant(value=0.99)
|
|
),
|
|
bias_attr=paddle.ParamAttr(
|
|
initializer=paddle.nn.initializer.Constant(value=0.5)
|
|
),
|
|
)
|
|
self.act = paddle.nn.ReLU()
|
|
|
|
@paddle.jit.to_static(full_graph=True)
|
|
def forward(self, inputs):
|
|
h = self.conv(inputs)
|
|
out = self.fc(h)
|
|
return self.act(out)
|
|
|
|
|
|
class TestRecursiveCall2(Dy2StTestBase):
|
|
def setUp(self):
|
|
self.input = np.random.random((1, 3, 3, 5)).astype('float32')
|
|
|
|
def set_func(self):
|
|
self.dygraph_func = MyLayer()
|
|
|
|
def _run(self):
|
|
data = paddle.to_tensor(self.input)
|
|
res = self.dygraph_func(data)
|
|
|
|
return res.numpy()
|
|
|
|
def get_dygraph_output(self):
|
|
with enable_to_static_guard(False):
|
|
return self._run()
|
|
|
|
def get_static_output(self):
|
|
with enable_to_static_guard(True):
|
|
return self._run()
|
|
|
|
def test_transformed_static_result(self):
|
|
self.set_func()
|
|
dygraph_res = self.get_dygraph_output()
|
|
static_res = self.get_static_output()
|
|
np.testing.assert_allclose(dygraph_res, static_res, rtol=1e-05)
|
|
|
|
|
|
class TestThirdPartyLibrary(TestRecursiveCall2):
|
|
def set_func(self):
|
|
self.dygraph_func = dyfunc_with_third_library_logging
|
|
|
|
|
|
class TestStaticMethod(TestRecursiveCall2):
|
|
def set_func(self):
|
|
self.dygraph_func = dyfunc_with_staticmethod
|
|
|
|
|
|
# Situation 2 : test not_to_static
|
|
class NotToStaticHelper(paddle.nn.Layer):
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
def sum(self, x):
|
|
if x.shape[0] > 1:
|
|
res = x + 1
|
|
res = paddle.sum(x)
|
|
return res
|
|
|
|
def outer(self, x):
|
|
res = self.sum(x)
|
|
return res
|
|
|
|
def inner(self, x):
|
|
return self.outer(x)
|
|
|
|
|
|
class TestNotToConvert(TestRecursiveCall2):
|
|
def set_func(self):
|
|
self.net = NotToStaticHelper()
|
|
# Apply the `not_to_static` decorator to `self.net.sum`.
|
|
paddle.jit.not_to_static()(self.net.sum)
|
|
self.dygraph_func = paddle.jit.to_static(self.net.outer)
|
|
|
|
def test_transform_options(self):
|
|
self.set_func()
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
self.net.sum, TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
TransformOptions.check_fn_need_transform(
|
|
self.net.sum, TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
|
|
def test_code(self):
|
|
self.set_func()
|
|
# check 'if statement' is not converted
|
|
self.assertIn(
|
|
"if x.shape[0] > 1", func_to_source_code(_jst.Call(self.net.sum))
|
|
)
|
|
|
|
|
|
class TestNotToConvert2(TestRecursiveCall2):
|
|
def set_func(self):
|
|
self.net = NotToStaticHelper()
|
|
# for to_static(not_to_static(function)) == enable_static
|
|
paddle.jit.not_to_static(self.net.sum)
|
|
self.dygraph_func = paddle.jit.to_static(self.net.sum)
|
|
|
|
def test_transform_options(self):
|
|
self.set_func()
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
self.net.sum, TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
TransformOptions.check_fn_need_transform(
|
|
self.net.sum, TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
|
|
@test_ast_only
|
|
def test_code(self):
|
|
self.set_func()
|
|
self.dygraph_func = paddle.jit.to_static(self.net.sum)
|
|
# check 'if statement' is not converted
|
|
self.assertIn("if x.shape[0] > 1", self.dygraph_func.code)
|
|
|
|
|
|
# Situation 3 : test to_static for paddle api
|
|
@paddle.jit.not_to_static
|
|
def forward(self, x):
|
|
if x.shape[0] > 1:
|
|
x = x + 1
|
|
return x
|
|
|
|
|
|
class TestConvertPaddleAPI(Dy2StTestBase):
|
|
@test_ast_only
|
|
def test_functional_api(self):
|
|
func = paddle.nn.functional.relu
|
|
func = paddle.jit.to_static(func)
|
|
self.assertNotIn("_jst.IfElse", func.code)
|
|
self.assertIn("if in_dynamic_or_pir_mode()", func.code)
|
|
|
|
@test_ast_only
|
|
def test_class_api(self):
|
|
bn = paddle.nn.SyncBatchNorm(2)
|
|
paddle.jit.to_static(bn)
|
|
self.assertNotIn("_jst.IfElse", bn.forward.code)
|
|
self.assertIn("if in_dynamic_or_pir_mode()", bn.forward.code)
|
|
|
|
@test_ast_only
|
|
def test_class_patch_api(self):
|
|
paddle.nn.SyncBatchNorm.forward = forward
|
|
bn = paddle.nn.SyncBatchNorm(2)
|
|
paddle.jit.to_static(bn)
|
|
self.assertNotIn("_jst.IfElse", bn.forward.code)
|
|
self.assertIn("if x.shape[0] > 1", bn.forward.code)
|
|
|
|
|
|
class TestMarkerUnified(Dy2StTestBase):
|
|
def test_plain_function(self):
|
|
def fn(x):
|
|
return x
|
|
|
|
self.assertTrue(
|
|
TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
|
|
def test_decorator_skip_sot_only(self):
|
|
@paddle.jit.marker.unified(for_sot=True, for_ast=False)
|
|
def fn(x):
|
|
return x
|
|
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
|
|
def test_decorator_skip_ast_only(self):
|
|
@paddle.jit.marker.unified(for_sot=False, for_ast=True)
|
|
def fn(x):
|
|
return x
|
|
|
|
self.assertTrue(
|
|
TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
|
|
def test_decorator_skip_ast_and_sot(self):
|
|
@paddle.jit.marker.unified(for_sot=True, for_ast=True)
|
|
def fn(x):
|
|
return x
|
|
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
|
|
def test_decorator_no_arg(self):
|
|
@paddle.jit.marker.unified
|
|
def fn(x):
|
|
return x
|
|
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
|
|
def test_function_call_skip_sot_only(self):
|
|
def fn(x):
|
|
return x
|
|
|
|
paddle.jit.marker.unified(fn, for_sot=True, for_ast=False)
|
|
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
|
|
def test_function_call_skip_ast_only(self):
|
|
def fn(x):
|
|
return x
|
|
|
|
paddle.jit.marker.unified(fn, for_sot=False, for_ast=True)
|
|
|
|
self.assertTrue(
|
|
TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
|
|
def test_function_call_skip_ast_and_sot(self):
|
|
def fn(x):
|
|
return x
|
|
|
|
paddle.jit.marker.unified(fn, for_sot=True, for_ast=True)
|
|
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_transform(
|
|
fn, TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
|
|
def test_nn_layer_subclass_skip_sot_only(self):
|
|
@paddle.jit.marker.unified(for_sot=True, for_ast=False)
|
|
class MyLayer(paddle.nn.Layer):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.w = paddle.create_parameter(shape=[1], dtype='float32')
|
|
|
|
def forward(self, x):
|
|
return x * self.w
|
|
|
|
self.assertFalse(
|
|
TransformOptions.check_fn_need_transform(
|
|
MyLayer(), TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
|
|
self.assertTrue(
|
|
TransformOptions.check_fn_need_transform(
|
|
MyLayer(), TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
|
|
def test_nn_layer_subclass_skip_ast_only(self):
|
|
@paddle.jit.marker.unified(for_sot=False, for_ast=True)
|
|
class MyLayer(paddle.nn.Layer):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.w = paddle.create_parameter(shape=[1], dtype='float32')
|
|
|
|
def forward(self, x):
|
|
return x * self.w
|
|
|
|
self.assertTrue(
|
|
TransformOptions.check_fn_need_transform(
|
|
MyLayer(), TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
|
|
self.assertFalse(
|
|
TransformOptions.check_fn_need_transform(
|
|
MyLayer(), TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
|
|
def test_nn_layer_subclass_skip_ast_and_sot(self):
|
|
@paddle.jit.marker.unified()
|
|
class MyLayer(paddle.nn.Layer):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.w = paddle.create_parameter(shape=[1], dtype='float32')
|
|
|
|
def forward(self, x):
|
|
return x * self.w
|
|
|
|
self.assertFalse(
|
|
TransformOptions.check_fn_need_transform(
|
|
MyLayer(), TransformOptions.ToStaticMode.SOT
|
|
)
|
|
)
|
|
|
|
self.assertFalse(
|
|
TransformOptions.check_fn_need_transform(
|
|
MyLayer(), TransformOptions.ToStaticMode.AST
|
|
)
|
|
)
|
|
|
|
|
|
class TestCaptureControlFlow(Dy2StTestBase):
|
|
def test_decorator(self):
|
|
def fn1(x):
|
|
return x
|
|
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_capture_control_flow(fn1)
|
|
)
|
|
|
|
@paddle.jit.marker.capture_control_flow()
|
|
def fn2(x):
|
|
return x
|
|
|
|
self.assertTrue(
|
|
TransformOptions.check_fn_need_capture_control_flow(fn2)
|
|
)
|
|
|
|
def test_decorator_no_arg(self):
|
|
def fn(x):
|
|
return x
|
|
|
|
self.assertTrue(
|
|
not TransformOptions.check_fn_need_capture_control_flow(fn)
|
|
)
|
|
|
|
fn = paddle.jit.marker.capture_control_flow(fn)
|
|
|
|
self.assertTrue(TransformOptions.check_fn_need_capture_control_flow(fn))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|