blob: 2564913d79aeaba25d3d980f5e299e9e86f94982 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
from typing import Union
import tvm
import tvm.script
import tvm.testing
from tvm import IRModule, relax
from tvm.relax import Function
from tvm.script import relax as R, tir as T, ir as I
def test_batch_norm_inference():
@I.ir_module
class Before:
@R.function
def main(
x: R.Tensor((1, 64, 112, 112), "float32"),
gamma: R.Tensor((64,), "float32"),
beta: R.Tensor((64,), "float32"),
moving_mean: R.Tensor((64,), "float32"),
moving_var: R.Tensor((64,), "float32"),
):
with R.dataflow():
bn = R.nn.batch_norm(
x,
gamma,
beta,
moving_mean,
moving_var,
axis=1,
epsilon=1e-5,
center=True,
scale=True,
)
gv = bn[0]
R.output(gv)
return gv
@I.ir_module
class Expected:
@R.function
def main(
x: R.Tensor((1, 64, 112, 112), dtype="float32"),
gamma: R.Tensor((64,), dtype="float32"),
beta: R.Tensor((64,), dtype="float32"),
moving_mean: R.Tensor((64,), dtype="float32"),
moving_var: R.Tensor((64,), dtype="float32"),
) -> R.Tensor((1, 64, 112, 112), dtype="float32"):
with R.dataflow():
lv: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(
moving_mean, axis=[0, 2, 3]
)
lv1: R.Tensor((1, 64, 112, 112), dtype="float32") = R.subtract(x, lv)
lv2: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(
moving_var, axis=[0, 2, 3]
)
lv3: R.Tensor((1, 64, 1, 1), dtype="float32") = R.add(
lv2, R.const(9.9999997473787516e-06, "float32")
)
lv4: R.Tensor((1, 64, 1, 1), dtype="float32") = R.sqrt(lv3)
lv5: R.Tensor((1, 64, 112, 112), dtype="float32") = R.divide(lv1, lv4)
lv6: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(gamma, axis=[0, 2, 3])
lv7: R.Tensor((1, 64, 112, 112), dtype="float32") = R.multiply(lv5, lv6)
lv8: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(beta, axis=[0, 2, 3])
lv9: R.Tensor((1, 64, 112, 112), dtype="float32") = R.add(lv7, lv8)
bn: R.Tuple(
R.Tensor((1, 64, 112, 112), dtype="float32"),
R.Tensor((64,), dtype="float32"),
R.Tensor((64,), dtype="float32"),
) = (lv9, moving_mean, moving_var)
gv: R.Tensor((1, 64, 112, 112), dtype="float32") = bn[0]
R.output(gv)
return gv
After = relax.transform.DecomposeOpsForInference("main")(Before)
tvm.ir.assert_structural_equal(Expected, After)
def test_batch_norm_training():
@I.ir_module
class Before:
@R.function
def main(
x: R.Tensor((1, 64, 112, 112), "float32"),
gamma: R.Tensor((64,), "float32"),
beta: R.Tensor((64,), "float32"),
moving_mean: R.Tensor((64,), "float32"),
moving_var: R.Tensor((64,), "float32"),
):
with R.dataflow():
bn = R.nn.batch_norm(
x,
gamma,
beta,
moving_mean,
moving_var,
axis=1,
epsilon=1e-5,
center=True,
scale=True,
momentum=0.1,
)
gv0 = bn[0]
gv1 = bn[1]
gv2 = bn[2]
R.output(gv0, gv1, gv2)
return gv0, gv1, gv2
@I.ir_module
class Expected:
@R.function
def main(
x: R.Tensor((1, 64, 112, 112), dtype="float32"),
gamma: R.Tensor((64,), dtype="float32"),
beta: R.Tensor((64,), dtype="float32"),
moving_mean: R.Tensor((64,), dtype="float32"),
moving_var: R.Tensor((64,), dtype="float32"),
) -> R.Tuple(
R.Tensor((1, 64, 112, 112), dtype="float32"),
R.Tensor((64,), dtype="float32"),
R.Tensor((64,), dtype="float32"),
):
with R.dataflow():
# This portion is training-specific, computing the
# mean/variance of the dataset.
lv = R.mean(x, axis=[0, 2, 3], keepdims=False)
lv3 = R.variance(x, axis=[0, 2, 3], keepdims=False)
# This portion is identical to the batch_norm run during inference
lv1 = R.expand_dims(lv, axis=[0, 2, 3])
lv2 = R.subtract(x, lv1)
lv4 = R.expand_dims(lv3, axis=[0, 2, 3])
lv5 = R.add(lv4, R.const(9.9999997473787516e-06, "float32"))
lv6 = R.sqrt(lv5)
lv7 = R.divide(lv2, lv6)
lv8 = R.expand_dims(gamma, axis=[0, 2, 3])
lv9 = R.multiply(lv7, lv8)
lv10 = R.expand_dims(beta, axis=[0, 2, 3])
lv11 = R.add(lv9, lv10)
inner_tuple = (lv11, lv, lv3)
# This is the result that would be returned from a
# batch_norm at inference.
# However, at training we need to update the moving
# mean/variance, and to return those updated values.
inner_res = inner_tuple[0]
lv12 = R.multiply(R.const(0.89999997615814209, "float32"), moving_mean)
lv13 = R.multiply(R.const(0.10000000149011612, "float32"), lv)
lv14 = R.add(lv12, lv13)
lv15 = R.multiply(R.const(0.89999997615814209, "float32"), moving_var)
lv16 = R.multiply(R.const(0.10000000149011612, "float32"), lv3)
lv17 = R.add(lv15, lv16)
bn = (inner_res, lv14, lv17)
gv0 = bn[0]
gv1 = bn[1]
gv2 = bn[2]
R.output(gv0, gv1, gv2)
return (gv0, gv1, gv2)
After = relax.transform.DecomposeOpsForTraining("main")(Before)
tvm.ir.assert_structural_equal(Expected, After)
def test_batch_norm_multiple_functions():
@I.ir_module
class Before:
@R.function
def main(
x: R.Tensor((1, 64, 112, 112), "float32"),
gamma: R.Tensor((64,), "float32"),
beta: R.Tensor((64,), "float32"),
moving_mean: R.Tensor((64,), "float32"),
moving_var: R.Tensor((64,), "float32"),
):
with R.dataflow():
bn = R.nn.batch_norm(
x,
gamma,
beta,
moving_mean,
moving_var,
axis=1,
epsilon=1e-5,
center=True,
scale=True,
)
gv = bn[0]
R.output(gv)
return gv
@R.function
def main1(
x: R.Tensor((1, 64, 112, 112), "float32"),
gamma: R.Tensor((64,), "float32"),
beta: R.Tensor((64,), "float32"),
moving_mean: R.Tensor((64,), "float32"),
moving_var: R.Tensor((64,), "float32"),
):
with R.dataflow():
bn = R.nn.batch_norm(
x,
gamma,
beta,
moving_mean,
moving_var,
axis=1,
epsilon=1e-5,
center=True,
scale=True,
)
gv = bn[0]
R.output(gv)
return gv
@I.ir_module
class Expected:
@R.function
def main1(
x: R.Tensor((1, 64, 112, 112), dtype="float32"),
gamma: R.Tensor((64,), dtype="float32"),
beta: R.Tensor((64,), dtype="float32"),
moving_mean: R.Tensor((64,), dtype="float32"),
moving_var: R.Tensor((64,), dtype="float32"),
) -> R.Tensor((1, 64, 112, 112), dtype="float32"):
with R.dataflow():
lv10: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(
moving_mean, axis=[0, 2, 3]
)
lv11: R.Tensor((1, 64, 112, 112), dtype="float32") = R.subtract(x, lv10)
lv12: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(
moving_var, axis=[0, 2, 3]
)
lv13: R.Tensor((1, 64, 1, 1), dtype="float32") = R.add(
lv12, R.const(9.9999997473787516e-06, "float32")
)
lv14: R.Tensor((1, 64, 1, 1), dtype="float32") = R.sqrt(lv13)
lv15: R.Tensor((1, 64, 112, 112), dtype="float32") = R.divide(lv11, lv14)
lv16: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(
gamma, axis=[0, 2, 3]
)
lv17: R.Tensor((1, 64, 112, 112), dtype="float32") = R.multiply(lv15, lv16)
lv18: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(beta, axis=[0, 2, 3])
lv19: R.Tensor((1, 64, 112, 112), dtype="float32") = R.add(lv17, lv18)
bn: R.Tuple(
R.Tensor((1, 64, 112, 112), dtype="float32"),
R.Tensor((64,), dtype="float32"),
R.Tensor((64,), dtype="float32"),
) = (lv19, moving_mean, moving_var)
gv: R.Tensor((1, 64, 112, 112), dtype="float32") = bn[0]
R.output(gv)
return gv
@R.function
def main(
x: R.Tensor((1, 64, 112, 112), dtype="float32"),
gamma: R.Tensor((64,), dtype="float32"),
beta: R.Tensor((64,), dtype="float32"),
moving_mean: R.Tensor((64,), dtype="float32"),
moving_var: R.Tensor((64,), dtype="float32"),
) -> R.Tensor((1, 64, 112, 112), dtype="float32"):
with R.dataflow():
lv: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(
moving_mean, axis=[0, 2, 3]
)
lv1: R.Tensor((1, 64, 112, 112), dtype="float32") = R.subtract(x, lv)
lv2: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(
moving_var, axis=[0, 2, 3]
)
lv3: R.Tensor((1, 64, 1, 1), dtype="float32") = R.add(
lv2, R.const(9.9999997473787516e-06, "float32")
)
lv4: R.Tensor((1, 64, 1, 1), dtype="float32") = R.sqrt(lv3)
lv5: R.Tensor((1, 64, 112, 112), dtype="float32") = R.divide(lv1, lv4)
lv6: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(gamma, axis=[0, 2, 3])
lv7: R.Tensor((1, 64, 112, 112), dtype="float32") = R.multiply(lv5, lv6)
lv8: R.Tensor((1, 64, 1, 1), dtype="float32") = R.expand_dims(beta, axis=[0, 2, 3])
lv9: R.Tensor((1, 64, 112, 112), dtype="float32") = R.add(lv7, lv8)
bn: R.Tuple(
R.Tensor((1, 64, 112, 112), dtype="float32"),
R.Tensor((64,), dtype="float32"),
R.Tensor((64,), dtype="float32"),
) = (lv9, moving_mean, moving_var)
gv: R.Tensor((1, 64, 112, 112), dtype="float32") = bn[0]
R.output(gv)
return gv
After = relax.transform.DecomposeOpsForInference()(Before)
tvm.ir.assert_structural_equal(Expected, After)
def test_layer_norm():
@I.ir_module
class Before:
@R.function
def main(
x: R.Tensor((4, 64, 112, 112), "float32"),
gamma: R.Tensor((112, 112), "float32"),
beta: R.Tensor((112, 112), "float32"),
):
with R.dataflow():
ln = R.nn.layer_norm(
x,
gamma,
beta,
axes=[-2, -1],
epsilon=1e-5,
center=True,
scale=True,
)
R.output(ln)
return ln
@I.ir_module
class Expected:
@R.function
def main(
x: R.Tensor((4, 64, 112, 112), dtype="float32"),
gamma: R.Tensor((112, 112), dtype="float32"),
beta: R.Tensor((112, 112), dtype="float32"),
) -> R.Tensor((4, 64, 112, 112), dtype="float32"):
with R.dataflow():
lv: R.Tensor((4, 64, 1, 1), dtype="float32") = R.mean(
x, axis=[-2, -1], keepdims=True
)
lv1: R.Tensor((4, 64, 112, 112), dtype="float32") = R.subtract(x, lv)
lv2: R.Tensor((4, 64, 1, 1), dtype="float32") = R.variance(
x, axis=[-2, -1], keepdims=True
)
lv3: R.Tensor((4, 64, 1, 1), dtype="float32") = R.add(
lv2, R.const(9.9999997473787516e-06, "float32")
)
lv4: R.Tensor((4, 64, 1, 1), dtype="float32") = R.sqrt(lv3)
lv5: R.Tensor((4, 64, 112, 112), dtype="float32") = R.divide(lv1, lv4)
lv6: R.Tensor((4, 64, 112, 112), dtype="float32") = R.multiply(lv5, gamma)
ln: R.Tensor((4, 64, 112, 112), dtype="float32") = R.add(lv6, beta)
R.output(ln)
return ln
After = relax.transform.DecomposeOpsForTraining()(Before)
tvm.ir.assert_structural_equal(Expected, After)
def test_op_tensor_to_shape():
@I.ir_module
class Before:
@R.function
def main(t: R.Tensor([3], dtype="int64")):
gv: R.Shape(ndim=3) = R.tensor_to_shape(t)
return gv
@I.ir_module
class Expected:
@R.function
def main(t: R.Tensor([3], dtype="int64")) -> R.Shape(ndim=3):
x = T.int64()
x_1 = T.int64()
x_2 = T.int64()
gv: R.Shape(ndim=3) = R.call_pure_packed(
"vm.builtin.tensor_to_shape", t, sinfo_args=(R.Shape(ndim=3),)
)
y: R.Shape([x, x_1, x_2]) = R.match_cast(gv, R.Shape([x, x_1, x_2]))
gv_1: R.Shape([x, x_1, x_2]) = R.shape([x, x_1, x_2])
return gv_1
After = relax.transform.DecomposeOpsForInference()(Before)
tvm.ir.assert_structural_equal(Expected, After)
if __name__ == "__main__":
tvm.testing.main()