tvm icon indicating copy to clipboard operation
tvm copied to clipboard

[TOPI][OP]change float multiplication of resize op to integer division

Open huanmei9 opened this issue 3 years ago • 1 comments

This PR is aim to change floating point multiplication of resize operator to integer division when method is nearest_neighbor and coordinate_transformation_mode is asymmetric.

Why this change?

When using create_prim_func to convert topi.upsampling into primfunc, tir analyzer cannot parse complex floating point operations in tir block body. In the following case, the dimensions of read buffer are inferred as 0 : 128. We expect to be tir.reads([x[i0_1, i1_1, i2_1/scale_h, i3_1/scale_w]])in line 18,so it can perform compute_at schedule on this prim_func.

scale_h = 2
scale_w = 2
x = te.placeholder([1, 128, 128, 128], "int8", "x")
y = topi.nn.upsampling(x, scale_h , scale_w)
func = te.create_prim_func([x, y])
print(func)
primfn(var_x: handle, var_resize: handle) -> ()
  attr = {"global_symbol": "main", "tir.noalias": True}
  buffers = {x: Buffer(x_1: Pointer(global int8), int8, [1, 128, 128, 128], []),
             resize: Buffer(resize_1: Pointer(global int8), int8, [1, 128, 256, 256], [])}
  buffer_map = {var_x: x, var_resize: resize} {
  block([], "root") {
    tir.reads([])
    tir.writes([])
    for (i0: int32, 0, 1) {
      for (i1: int32, 0, 128) {
        for (i2: int32, 0, 256) {
          for (i3: int32, 0, 256) {
            block([1, 128, 256, 256], "resize") as [i0_1, i1_1, i2_1, i3_1] {
              bind(i0_1, i0)
              bind(i1_1, i1)
              bind(i2_1, i2)
              bind(i3_1, i3)
              tir.reads([x[i0_1, i1_1, 0:128, 0:128]])
              tir.writes([resize[i0_1, i1_1, i2_1, i3_1]])
              resize[i0_1, i1_1, i2_1, i3_1] = cast(int8, cast(float32, x[i0_1, i1_1, max(min(cast(int32, @tir.floor(((0.5f32*cast(float32, i2_1)) + 1e-05f32), dtype=float32)), 127), 0), max(min(cast(int32, @tir.floor(((0.5f32*cast(float32, i3_1)) + 1e-05f32), dtype=float32)), 127), 0)]))
          }
        }
      }
    }
}

Why can it be changed?

This PR only modify float multiplication to integer division when method is nearest_neighbor and coordinate_transformation_mode is asymmetric(it's default setting for topi.upsampling op).

Go further, we expect to convert $\lfloor s * x + \epsilon_1 \rfloor$ to $x//a$, $s$ is scale, a float number, $x$ is input, $\epsilon_1$ is 1e-5 in resize op, $a$ is the reciprocal of $s$, an integer.

So, we can represent output as this: $y = \lfloor s * x + \epsilon_1 \rfloor = \lfloor ( \frac{1}{a} + \epsilon_0 ) * x + \epsilon_1 \rfloor = \lfloor \frac{x}{a} + \epsilon_0 * x + \epsilon_1 \rfloor$, let $x = a * m + n$, where $m = x // a, n = x % a$ , then we can get $y = m + \lfloor \frac{n}{a} + \epsilon_0 * x + \epsilon_1 \rfloor$. In the case of upsampling op, $x$ and $a$ are always integers, so inequality $\frac{n}{a} \le \frac{a-1}{a}$ always holds. If $\epsilon_0 * x + \epsilon_1 < \frac{1}{a}$, we can get $0 < \frac{n}{a} + \epsilon_0 * x + \epsilon_1 < 1$,then $y = m + \lfloor \frac{n}{a} + \epsilon_0 * x + \epsilon_1 \rfloor = m = x // a$.

To sum up, when $\epsilon_0 * x + \epsilon_1 < \frac{1}{a}$, we can get $y = \lfloor s*x + \epsilon_1 \rfloor = x // a$.

In this PR, we assume $\epsilon_0 = \epsilon_1 = 1e-5$ and can_convert_multiply_to_intdivto check $\epsilon_0 * x + \epsilon_1 < \frac{1}{a}$.

huanmei9 avatar Aug 05 '22 08:08 huanmei9

cc @masahi

Hzfengsy avatar Aug 09 '22 11:08 Hzfengsy