| # 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. |
| |
| import itertools |
| |
| import numpy as np |
| import scipy.sparse as sp |
| |
| |
| import tvm |
| from tvm.ir import IRModule |
| from tvm import relay |
| from tvm.topi.sparse.utils import random_bsr_matrix |
| from tvm.relay.build_module import bind_params_by_name |
| |
| |
| def run_func(func, params, x): |
| with tvm.transform.PassContext(opt_level=3): |
| graph, lib, new_params = relay.build(func, "llvm", params=params) |
| |
| from tvm.contrib import graph_executor |
| |
| dev = tvm.cpu(0) |
| dtype = "float32" |
| m = graph_executor.create(graph, lib, dev) |
| # set inputs |
| m.set_input("data", tvm.nd.array(x.astype(dtype))) |
| m.set_input(**new_params) |
| # execute |
| m.run() |
| # get outputs |
| tvm_output = m.get_output(0) |
| return tvm_output.numpy() |
| |
| |
| def test_bsr_sparse_conv2d_nchw(): |
| data = relay.var("data", shape=(1, 64, 32, 32), dtype="float32") |
| x = relay.nn.relu(data) |
| w = relay.var("weight", shape=(128, 64, 1, 1), dtype="float32") |
| y = relay.nn.conv2d(x, w, channels=128, kernel_size=1, data_layout="NCHW", kernel_layout="OIHW") |
| z = relay.nn.relu(y) |
| func = relay.Function(relay.analysis.free_vars(z), z) |
| |
| params = { |
| "weight": tvm.nd.array( |
| np.array(random_bsr_matrix(128, 64, 8, 1, 0.1, "float32").todense()).reshape( |
| 128, 64, 1, 1 |
| ) |
| ) |
| } |
| |
| x_np = np.random.randn(1, 64, 32, 32).astype("float32") |
| # dense output |
| dense_output = run_func(func, params, x_np) |
| # sparse |
| sparse_func, params = relay.data_dep_optimization.bsr_conv2d.convert( |
| func, params, (8, 1), 0.2, "NCHW" |
| ) |
| sparse_output = run_func(sparse_func, params, x_np) |
| np.testing.assert_allclose(sparse_output, dense_output, atol=1e-5, rtol=1e-5) |
| |
| |
| def test_bsr_sparse_conv2d_nhwc(): |
| data = relay.var("data", shape=(1, 32, 32, 64), dtype="float32") |
| x = relay.nn.relu(data) |
| w = relay.var("weight", shape=(1, 1, 64, 128), dtype="float32") |
| y = relay.nn.conv2d(x, w, channels=128, kernel_size=1, data_layout="NHWC", kernel_layout="HWIO") |
| z = relay.nn.relu(y) |
| func = relay.Function(relay.analysis.free_vars(z), z) |
| |
| params = { |
| "weight": tvm.nd.array( |
| np.array(random_bsr_matrix(128, 64, 8, 1, 0.1, "float32").todense()).T.reshape( |
| 1, 1, 64, 128 |
| ) |
| ) |
| } |
| |
| x_np = np.random.randn(1, 32, 32, 64).astype("float32") |
| # dense output |
| dense_output = run_func(func, params, x_np) |
| # sparse |
| sparse_func, params = relay.data_dep_optimization.bsr_conv2d.convert( |
| func, params, (8, 1), 0.2, "NHWC" |
| ) |
| sparse_output = run_func(sparse_func, params, x_np) |
| np.testing.assert_allclose(sparse_output, dense_output, atol=1e-5, rtol=1e-5) |
| |
| |
| def test_bsr_sparse_conv2d_3x3_nchw(): |
| data = relay.var("data", shape=(1, 64, 32, 32), dtype="float32") |
| x = relay.nn.relu(data) |
| w = relay.var("weight", shape=(128, 64, 3, 3), dtype="float32") |
| y = relay.nn.conv2d( |
| x, w, channels=128, kernel_size=3, padding=1, data_layout="NCHW", kernel_layout="OIHW" |
| ) |
| z = relay.nn.relu(y) |
| func = relay.Function(relay.analysis.free_vars(z), z) |
| |
| params = { |
| "weight": tvm.nd.array( |
| np.array(random_bsr_matrix(128, 64 * 9, 16, 1, 0.1, "float32").todense()).reshape( |
| 128, 64, 3, 3 |
| ) |
| ) |
| } |
| |
| x_np = np.random.randn(1, 64, 32, 32).astype("float32") |
| # dense output |
| dense_output = run_func(func, params, x_np) |
| # sparse |
| func = bind_params_by_name(func, params) |
| sparse_func, params = relay.data_dep_optimization.bsr_conv2d.convert2( |
| func, {}, (16, 1), 0.2, "NCHW", 3 |
| ) |
| sparse_output = run_func(sparse_func, params, x_np) |
| np.testing.assert_allclose(sparse_output, dense_output, atol=1e-5, rtol=1e-5) |
| |
| |
| def test_bsr_sparse_conv2d_3x3_nhwc(): |
| data = relay.var("data", shape=(1, 32, 32, 64), dtype="float32") |
| x = relay.nn.relu(data) |
| w = relay.var("weight", shape=(3, 3, 64, 128), dtype="float32") |
| y = relay.nn.conv2d( |
| x, w, channels=128, kernel_size=3, padding=1, data_layout="NHWC", kernel_layout="HWIO" |
| ) |
| z = relay.nn.relu(y) |
| func = relay.Function(relay.analysis.free_vars(z), z) |
| |
| params = { |
| "weight": tvm.nd.array( |
| np.array(random_bsr_matrix(128, 64 * 9, 16, 1, 0.1, "float32").todense()).T.reshape( |
| 3, 3, 64, 128 |
| ) |
| ) |
| } |
| |
| x_np = np.random.randn(1, 32, 32, 64).astype("float32") |
| # dense output |
| dense_output = run_func(func, params, x_np) |
| # sparse |
| func = bind_params_by_name(func, params) |
| sparse_func, params = relay.data_dep_optimization.bsr_conv2d.convert2( |
| func, {}, (16, 1), 0.2, "NHWC", 3 |
| ) |
| sparse_output = run_func(sparse_func, params, x_np) |
| np.testing.assert_allclose(sparse_output, dense_output, atol=1e-5, rtol=1e-5) |
| |
| |
| if __name__ == "__main__": |
| test_bsr_sparse_conv2d_nhwc() |
| test_bsr_sparse_conv2d_nchw() |
| test_bsr_sparse_conv2d_3x3_nhwc() |
| test_bsr_sparse_conv2d_3x3_nchw() |