TensorFlow on Raspberry Pi 4 with Ubuntu 18.04

Hassan Shehawy
8 min readMay 30, 2021
TF 2.4 on RPi 4 and Ubuntu 18.04

How to install TF on RPi 4 with Ubuntu 18.04? I managed to get Ubuntu 18.04 up and running on my Raspberry Pi (RPi) 4 and the next 2 things I needed were ROS and TensorFlow (TF) (I have a tutorial for installing Ubuntu 18.04 on RPi 4 here) . It was quite easy and straightforward to install ROS (Melodic) and have it working with no problem (in fact a few issues because of some shared libraries that were compiled for x86, but it is not a big deal for now). For TensorFlow, it was not that straightforward (I thought it was) with my setup. If you have Raspberry Pi OS, then it’s actually supported. But with Ubuntu, it’s a different story and the only thing that worked with me easily is building from source.

Table of Contents

  1. The problems/issues with the existing tutorials
  2. Building from source
  3. Prerequisites
  4. The virtual environment
  5. Downloading TF and configuring
  6. Install Bazel
  7. Build and compile
  8. Testing

The main problem here is that when you ask for help, people usually have some assumptions about your setup if you ask for help saying something like “I want to install TF on RPi”. These assumptions include having Raspberry Pi OS, your OS is 32-bit or that you actually want TF lite. I even came across a tutorial that shared a wheel file with the note that “it’s valid for Ubuntu 18 and 20” which actually installed successfully. However, when I tried to import it, I got an error complaining about the GLIBC version which I couldn’t figure out the reason for it before I noticed the difference in GLIBC versions in 18 and 20!

With the many shared tutorials and endless problems and issues I was trying to solve, it was loud and clear: “build from source”! So, I did it this way and here I’m sharing the steps to make it work and I promise it’s very easy. I’m following the instructions on the official documentation here: https://www.tensorflow.org/install/source which is great. However, not everything is mentioned there. For example, it tells you that you need to install bazelisk, how? check that link! So, I complied all steps here. I also added some (expected) troubles and how I overcame them.

The only thing you need to consider is how long it’s going to take to compile; it was 22 hours for me. I can also share the wheel file if you want, but please make sure of the system setup (RPi 4 + aarch64 + Ubuntu 18.04 + Python 3.7) and that you want TF 2.4.

Alright, first things first. You need to have a few packages, tools, libraries … You might already have them, but just in case you don’t.

ubuntu@ubuntu:~$ sudo apt-get install build-essential pkg-confi gi2c-tools avahi-utils joystick libopenjp2–7-dev libtiff5-dev gfortran libatlas-base-dev libopenblas-dev libhdf5-serial-dev git ntp

If you’re happy with the current version of Python you have or if you don’t want a virtual environment, you can skip this part (although using virtual environment is always a good idea :-). I have Python 3.6 installed and I want 3.7 and so I installed it as below:

ubuntu@ubuntu:~$ sudo apt-get install python3.7 python3.7-dev python3.7-venv python3.7-distutils python3.7-lib2to3 python3.7-gdbm python3.7-tk python3-pip

Okay, now it’s time to create a virtual environment and activate it. Then install all needed modules (cython, numpy, h5py …). At the time of writing, installing the below modules resulted in smooth compiling and building.

ubuntu@ubuntu:~$ python3.7 -m venv py37env
ubuntu@ubuntu:~$ source py37env/bin/activate
(py37env) ubuntu@ubuntu:~$ python3.7 -m pip install — upgrade pip
(py37env) ubuntu@ubuntu:~$ pip install cython
(py37env) ubuntu@ubuntu:~$ pip install wheel
(py37env) ubuntu@ubuntu:~$ pip install numpy
(py37env) ubuntu@ubuntu:~$ pip install h5py
(py37env) ubuntu@ubuntu:~$ pip install pybind11
(py37env) ubuntu@ubuntu:~$ pip install -U keras_preprocessing — no-deps

Okay now we are ready! The first thing to do is to prepare the source code and you do this in 3 steps:
1. Download TF from GitHub
2. Select the version you want by selecting the branch inside the repository. Below I’m selecting 2.4
3. Run the configuration. This is optional, but you must run the configuration if you are using virtual environment because the default values won’t give you what you want.

Note that this is a big repository, so it might take some time depending on your internet connection.

(py37env) ubuntu@ubuntu:~$ git clone https://github.com/tensorflow/tensorflow.git(py37env) ubuntu@ubuntu:~$ cd tensorflow/
(py37env) ubuntu@ubuntu:~/tensorflow$ git status
On branch master
Your branch is up to date with ‘origin/master’.
nothing to commit, working tree clean
(py37env) ubuntu@ubuntu:~/tensorflow$ git checkout r2.4
Checking out files: 100% (10625/10625), done.
Branch ‘r2.4’ set up to track remote branch ‘r2.4’ from ‘origin’.
Switched to a new branch ‘r2.4’
(py37env) ubuntu@ubuntu:~/tensorflow$ python3.7 configure.py
You have bazel 3.4.0 installed.
Please specify the location of python. [Default is /home/ubuntu/py37env/bin/python3.7]:
Found possible Python library paths:
/home/ubuntu/py37env/lib/python3.7/site-packages
Please input the desired Python library path to use. Default is [/home/ubuntu/py37env/lib/python3.7/site-packages]
Do you wish to build TensorFlow with ROCm support? [y/N]: n
No ROCm support will be enabled for TensorFlow.
Do you wish to build TensorFlow with CUDA support? [y/N]: n
No CUDA support will be enabled for TensorFlow.
Do you wish to download a fresh release of clang? (Experimental) [y/N]: n
Clang will not be downloaded.
Please specify optimization flags to use during compilation when bazel option “ — config=opt” is specified [Default is -Wno-sign-compare]:
Would you like to interactively configure ./WORKSPACE for Android builds? [y/N]: n
Not configuring the WORKSPACE for Android builds.
Preconfigured Bazel build configs. You can use any of the below by adding “ — config=<>” to your build command. See .bazelrc for more details.
— config=mkl # Build with MKL support.
— config=mkl_aarch64 # Build with oneDNN support for Aarch64.
— config=monolithic # Config for mostly static monolithic build.
— config=ngraph # Build with Intel nGraph support.
— config=numa # Build with NUMA support.
— config=dynamic_kernels # (Experimental) Build kernels into separate shared objects.
— config=v2 # Build TensorFlow 2.x instead of 1.x.
Preconfigured Bazel build configs to DISABLE default on features:
— config=noaws # Disable AWS S3 filesystem support.
— config=nogcp # Disable GCP support.
— config=nohdfs # Disable HDFS support.
— config=nonccl # Disable NVIDIA NCCL support.

To actually compile, we need to use bazel which can be installed in different ways. In my opinion, using bazelisk is the easiest way and so I’m sharing how I installed it. I used npm and if you don’t have it you can install it as below:

(py37env) ubuntu@ubuntu:~$ sudo apt install nodejs npm
(py37env) ubuntu@ubuntu:~$ sudo npm install -g @bazel/bazelisk

Now it’s time to share an important detail concerning bazel which was a reason for a lot of errors. If you navigate to inside the TF source code folder, you will find a file named .bazelversion. If you selected the r2.4 branch, this file will have 3.1.0 inside it which is the version of bazel that the bazelisk will try to get. However, this version (3.1.0) does NOT exist for ARM and will give an error. Note that if there’s no such a file in the folder where you run the bazel command, it will grab the latest version. I’m showing below a sample interaction using the bazel version command to make it clearer:

(py37env) ubuntu@ubuntu:~$ bazel version
2021/05/30 14:11:18 Downloading https://releases.bazel.build/4.1.0/release/bazel-4.1.0-linux-arm64...
Bazelisk version: v1.9.0
Extracting Bazel installation…
Build label: 4.1.0
Build target: bazel-out/aarch64-opt/bin/src/main/java/com/google/devtools/build/lib/bazel/BazelServer_deploy.jar
Build timestamp: 1621595460
Build timestamp as int: 1621595460
(py37env) ubuntu@ubuntu:~$ cd tensorflow(py37env) ubuntu@ubuntu:~/tensorflow$ bazel version
2021/05/30 14:14:51 Downloading https://releases.bazel.build/3.1.0/release/bazel-3.1.0-linux-arm64...
2021/05/30 14:14:51 could not download Bazel: HTTP GET https://releases.bazel.build/3.1.0/release/bazel-3.1.0-linux-arm64 failed with error 404

If you edit the .bazelversion file and change its content to e.g., 3.4, the error is gone and it works out

(py37env) ubuntu@ubuntu:~/tensorflow$ nano .bazelversion
(py37env) ubuntu@ubuntu:~/tensorflow$ bazel version
2021/05/30 14:15:12 Downloading https://releases.bazel.build/3.4.0/release/bazel-3.4.0-linux-arm64...
Bazelisk version: v1.9.0
Extracting Bazel installation…
Starting local Bazel server and connecting to it…
Build label: 3.4.0
Build target: bazel-out/aarch64-opt/bin/src/main/java/com/google/devtools/build/lib/bazel/BazelServer_deploy.jar
Build time: Mon Jul 13 17:02:00 2020 (1594659720)
Build timestamp: 1594659720
Build timestamp as int: 1594659720

Alright, now we can compile! It is one line, but perhaps will be overnight to be done (it took 22 hours with me)

(py37env) ubuntu@ubuntu:~/tensorflow$ bazel build //tensorflow/tools/pip_package:build_pip_package

After finishing successfully, now we are ready to build the wheel (it will be saved in /tmp/tensorflow_pkg as indicated below

(py37env) ubuntu@ubuntu:~/tensorflow$ ./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg

Finally we can install it using pip with this line:

(py37env) ubuntu@ubuntu:~/tensorflow$ pip install /tmp/tensorflow_pkg/tensorflow-2.4.1-cp37-cp37m-linux_aarch64.whl

Time to test and play around! I got 2 problems that were easily fixed by reading the errors and acting accordingly:

(py37env) ubuntu@ubuntu:~/tensorflow$ python
Python 3.7.5
[GCC 8.4.0] on linux
Type “help”, “copyright”, “credits” or “license” for more information.
>>> import tensorflow
Traceback (most recent call last):
File “/home/ubuntu/tensorflow/tensorflow/python/platform/self_check.py”, line 26, in <module>
from tensorflow.python.platform import build_info
ImportError: cannot import name ‘build_info’ from ‘tensorflow.python.platform’ (/home/ubuntu/tensorflow/tensorflow/python/platform/__init__.py)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File “<stdin>”, line 1, in <module>
File “/home/ubuntu/tensorflow/tensorflow/__init__.py”, line 24, in <module>
from tensorflow.python import pywrap_tensorflow # pylint: disable=unused-import
File “/home/ubuntu/tensorflow/tensorflow/python/__init__.py”, line 39, in <module>
from tensorflow.python import pywrap_tensorflow as _pywrap_tensorflow
File “/home/ubuntu/tensorflow/tensorflow/python/pywrap_tensorflow.py”, line 25, in <module>
from tensorflow.python.platform import self_check
File “/home/ubuntu/tensorflow/tensorflow/python/platform/self_check.py”, line 28, in <module>
raise ImportError(“Could not import tensorflow. Do not import tensorflow “
ImportError: Could not import tensorflow. Do not import tensorflow from its source directory; change directory to outside the TensorFlow source tree, and relaunch your Python interpreter from there.
>>> exit()

Quite embarrassing, ha? I shouldn’t be where the TF source is, So:

(py37env) ubuntu@ubuntu:~/tensorflow$ cd ..(py37env) ubuntu@ubuntu:~$ python
Python 3.7.5
[GCC 8.4.0] on linux
Type “help”, “copyright”, “credits” or “license” for more information.
>>> import tensorflow
RuntimeError: module compiled against API version 0xe but this version of numpy is 0xd
RuntimeError: module compiled against API version 0xe but this version of numpy is 0xd
ImportError: numpy.core._multiarray_umath failed to import
ImportError: numpy.core.umath failed to import
F tensorflow/python/lib/core/bfloat16.cc:714] Check failed: PyBfloat16_Type.tp_base != nullptr
Aborted (core dumped)

Okay this one was not that embarrassing. However, the easy fix was like this:

(py37env) ubuntu@ubuntu:~$ pip3 install --upgrade numpy(py37env) ubuntu@ubuntu:~$ python
Python 3.7.5
[GCC 8.4.0] on linux
Type “help”, “copyright”, “credits” or “license” for more information.
>>> import tensorflow
>>> print(tensorflow.version.VERSION)
2.4.1
>>> mnist = tensorflow.keras.datasets.mnist
>>> (x_train, y_train), (x_test, y_test) = mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] — 5s 0us/step
>>> x_train, x_test = x_train / 255.0, x_test / 255.0
>>> model = tensorflow.keras.models.Sequential([
… tensorflow.keras.layers.Flatten(input_shape=(28, 28)),
… tensorflow.keras.layers.Dense(128, activation=’relu’),
… tensorflow.keras.layers.Dropout(0.2),
… tensorflow.keras.layers.Dense(10)
… ])
2021–05–29 21:19:12.411124: I tensorflow/compiler/jit/xla_cpu_device.cc:41] Not creating XLA devices, tf_xla_enable_xla_devices not set
>>> predictions = model(x_train[:1]).numpy()
>>> predictions
>>>
array([[-0.3281718 , -0.08708003, 0.18185253, 0.3719411 , -0.3117321 ,
0.00851816, -0.55848855, 0.7960658 , 0.23823892, -0.1364091 ]],
dtype=float32)
>>> tensorflow.nn.softmax(predictions).numpy()
array([[0.06572766, 0.0836475 , 0.10945836, 0.1323742 , 0.06681713,
0.09203876, 0.05220629, 0.20230114, 0.11580764, 0.07962137]],
dtype=float32)
>>> loss_fn = tensorflow.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
>>>
>>> loss_fn(y_train[:1], predictions).numpy()
2.3855455

Voila! It works

--

--