TVM === We use `TVM `_ to compile our neural networks to run on the GPU of the robot. The networks are compiled to Vulkan shaders which run on many different GPUs including non Nvidia GPUs that don't support CUDA. This results in a significant speedup compared to running the networks on the CPU. Installation on the robot -------------------------- - Follow the install/build `guide `_ - Install Vulkan SDK with this `script `_ in `docker/install` in the TVM repo - Change `build/config.cmake` to enable profiling, llvm and vulkan during TVM build - Install helpful Vulkan tools .. code-block:: bash sudo apt install vulkan-tools - Add user to group ``render`` and ``video`` .. code-block:: bash sudo usermod -a -G render,video $USER - Install TVM python directory with .. code-block:: bash pip install ./python - Install additional deps .. code-block:: bash pip install onnx onnxsim xgboost Compile a YOEO model directly ----------------------------- If you use YOEO you can export the model to onnx with the ``yoeo-to-onnx`` command. See ``yoeo-to-onnx --help`` for more information. You can then use the onnx model to compile it with TVM using the ``yoeo-onnx-to-tvm`` command. See ``yoeo-onnx-to-tvm --help`` for more information. By doing so you can skip most of the steps above that use the ``tvmc`` cli. How to optimize a model ------------------------ - Run the following commands if you are annoyed by deprecation warnings .. code-block:: bash export PYTHONWARNINGS="ignore" - Use tvmc (located in ``~/.local/bin``, therefore check if it is in the ``PATH``!) for all simple TVM related things. If it is not in the ``PATH`` you can add it with .. code-block:: bash export PATH=$PATH:~/.local/bin You can add this to your ``.bashrc`` (if you use bash) or ``.zshrc`` (if you use zsh) to make it permanent. - Simplify your onnx model using .. code-block:: bash onnxsim This step is needed as onnx can be quite convoluted and TVM does not support all operations. - Tune your simplified onnx model using .. code-block:: bash tvmc tune --target "vulkan" --target-vulkan-from_device 0 --enable-autoscheduler --output .json --enable-autoscheduler --repeat 5 --number 50 - Only do this on the "NUCs" because the model will be optimized for the specific hardware. - If you only optimize for CPU select target "llvm" and possibly narrow it down further by setting llvm related settings, but we focus Vulkan (GPU) optimization for the rest of this guide. - Remember to replace the placeholders in the command. - Check with ``radeontop`` if the GPU is utilized. - The optimization might take hours or even days. - Compile the model using the optimizations in the json file from the previous tuning. To do this run .. code-block:: bash tvmc compile --target "vulkan" --target-vulkan-from_device 0 --output .tar --tuning-records .json - You also want to do this on the "NUC" because the model will be optimized for the specific hardware. - This should **not** take hours. - Remember to replace all placeholders in the command - You will hopefully end up with a ``.tar`` file containing the following items - ``mod.so`` - ``mod.params`` - ``mod.json`` Run your compiled model ----------------------- - To test the model run the following command .. code-block:: bash tvmc run .tar --profile --print-time --device "vulkan" --repeat 100 The command shows you the profiling of each layer. Check if they all run on ``vulkan0``. At the bottom, a timing benchmark is printed. Run the model using the Python API ---------------------------------- Extract the tar, using ``tar -xf ``. The following code snippet shows how to run a `YOEO `_ model using the Python API. Normally the ``bitbots_vision`` is used to run the compiled model. .. code-block:: python import numpy as np import tvm from tvm.contrib import graph_executor # Load model binary_lib = tvm.runtime.load_module("mod.so") loaded_params = bytearray(open("mod.params", "rb").read()) loaded_json = open("mod.json").read() # Create model module module = graph_executor.create(loaded_json, binary_lib, tvm.vulkan(0)) # Replace with tvm.cpu(0) for CPU models module.load_params(loaded_params) # Create dummy data input_data = np.random.uniform(size=(1,3, 416, 416)).astype("float32") # Run the network module.set_input(InputLayer=input_data) module.run() yolo_detections, segmentation = module.get_output(0).numpy(), module.get_output(1).numpy() print(yolo_detections.shape, segmentation.shape)