If you are interested in machine learning, you are probably interested in tensorflow
, the open-source library for distributed machine learning computation (pre-equipped with many useful tools for building Deep Neural Networks of many kinds).
If you are interested in machine learning and you like datasets that won’t fit in your memory at once, you want to use generators and iterators. That is, you want what python3
makes the default, so you want to build it in python3
. Also, print
is happier as a function.
If you wanted to get tensorflow
working with python3
as of my installing it on February 9, 2016, you needed to build it from source. As of February 13, 2016, you can use
pip3 install --upgrade https://storage.googleapis.com/tensorflow/mac/tensorflow-0.6.0-py3-none-any.whl
or see the git repo. But if you want to install from source, or even to just understand the steps needed to install it from source, this post should still be useful.
But installing from source requires jumping through some hoops. These hoops aren’t too difficult, but they are spread across a lot of places. If you stopped in the middle of the process and forgot where you were, this might prove to be a problem. Also, many of the individual steps are more interesting to understand than they are difficult to follow.
I tried to bring these steps together here. More importantly, I attempted to explain them so that, rather than trying to blindly follow my directions (which you are welcome to attempt as well, though I don’t ever recommend it), you can comprehend what each step is doing.
First, I’ll describe the steps you’ll go through. Then I’ll describe why you’re performing these steps, but I’ll do that in reverse. That is, I’ll explain the steps by unpacking the dependencies starting with the testing tensorflow
after the final pip3
call and working back to the beginning with Xcode Developer Tools. So, if you read it straight through, when you get to the end of the list of steps, that’s when the real fun begins.
Assumptions
What I’m going to assume is that you are familiar with your terminal (possibly even iTerm2
) and that you have python3.3+
(ideally python3.5
) and homebrew (brew
) installed already. If you do not, or don’t know what I’m talking about, I would recommend reading the introduction to an earlier post which covers this material in its preliminaries section. Because machine learning tends to require a bit of a programming background I’m going to assume a bit more than I did in that post. If it’s at all confusing, feel free to leave a comment, and I’ll do my best to help.
General Procedure:
- Install OSX Developer tools(
xcode
) from the developer website or from the Mac App Store (explanation)- Check that you have installed it by running
gcc --version
: (explanation)
- Check that you have installed it by running
Install Java SE Development Kit 8 (JDK8) from the Oracle website (explanation)
Go to your terminal (explanation)
- Install Bazel — ideally using
brew install bazel
(explanation)- If it says that it can’t find
bazel
, runbrew update
- If you already have it installed, use
brew upgrade bazel
- If it says that it can’t find
- Install
swig
— ideally usingbrew install swig
(explanation)- If you already have it installed, use
brew upgrade swig
- If you already have it installed, use
Install/upgrade
six
,numpy
,wheel
,ipython3
usingpip3
(explanation):
pip3 install -U six pip3 install -U numpy pip3 install -U wheel pip3 install -U ipython3
- Clone the github repo for
tensorflow
using the--recurse-submodules
flag (explanation):- If you don’t use the
--recurse-submodules
flag, we can still recover later by using:git submodule update --init
. But save yourself the unnecessary pain.
git clone --recurse-submodules https://github.com/tensorflow/tensorflow
- If you don’t use the
Go to the root of your github directory (
cd tensorflow
if you have not changed directory since the previous step) (explanation)Run
which python3
to get yourpython3
path, copy it (explanation)Run
./configure
, which will ask you for the path to yourpython3
instance, paste in the path from the previous step (explanation)Build the package (Part 1) with
bazel
(explanation)
bazel build -c opt //tensorflow/tools/pip_package:build_pip_package
Finish building your
pip
-able package as a wheel (explanation):
bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg
- Install your just built package with
pip3
(explanation):- The version of
tensorflow
that you should install here may change if you have a more recent version. Change the‽.‽.‽
tensorflow-‽.‽.‽-py3
to match the version oftensorflow
you have.
pip3 install /tmp/tensorflow_pkg/tensorflow-0.6.0-py3-none-any.whl
- The version of
Test tensorflow to see that it works.
Open ipython3
on the command line, type in
import tensorflow as tf
fun = tf.constant("I built tensorflow from source, I am unstoppable!")
sess = tf.session()
print(sess.run(fun))
Explaining the stack, LIFO[1]
Testing tensorflow
By the time that you’ve gotten to this point, tensorflow
should be functioning for you. If it isn’t you have some problem and you’ll need to debug what has gone wrong. The error printed may or may not be helpful. However, copying and pasting that error into google will help.
pip
your wheel
This step takes your pip
compatible package and installs it for you. You need to make sure that you pass in the correct path, but if you’ve done the previous steps this should work.
The reason you begin this install file with tmp/tensorflow.pkg
is because that’s where you told the previous step to build your pip
-able package.
It ends with .whl
because it installs it as a wheel
. What is a wheel
you ask? That is a great question — one I will leave for another day [2]. For now all you need to know is that it is a format for distributing file packages that is more modern than the older egg
format.
Completing your pip
-able package
This is going to create a pip
-able package in the argument’s path.
The steps it takes to do this are straightforward enough, but the key is that the directory you pass it is stored, a temporary directory is made, the wheel is built into the temp directory and then copied to the directory you passed it, and the temporary directory is passed back out.[3]
This will work if you ran this command from the root of the git repo, if you did not, it probably will fail quickly with a helpful error message reminding you of this (and if not, it will possibly fail spectacularly). I mention this because it’s worth appreciating nice error messages when you come across them, even if they weren’t actually useful for you.
Compiling the package with bazel
This is the command that was least familiar to me (having not used bazel
before). But it’s actually pretty easily comprehensible once you understand a couple of bazel
ideas.
bazel
’s build command
bazel
is Google’s build tool. The primary thing that we need to know about it is that it is invoked with the bazel build
command to build packages. In its simplest form, the command bazel build
takes a label (e.g., //my_package:my_target
) as its argument and builds the appropriate package and target (see below for explanation of packages, labels and targets).
The -c
flag indicates that it will be executed in “compilation mode” and the argument that follows indicates details about the compilation that affect how C
or C++
code is compiled. In this case, it is passed with the opt
argument meaning that the package will be built with optimization enabled, debugging info disabled and assert()
calls disabled.
Packages, Targets and Labels in bazel
A package is a collection of files along with a specification of their dependencies; it is designated by the existence of a file named BUILD
. Packages contain all the files in it and its subdirectories, unless that subdirectory is itself a package (called a subpackage, indicated by its also having a BUILD
file). The elements of a package are targets.
A target is an element of a package which include the “files” that will be used in the build procedure, “rules” that (ultimately) define how those files are used in the procedure, and “package_groups” that limit the accessibility of certain rules.
A label is the name for a target. In its canonical form, it leads with a //
, followed by a package name (my_package
), followed by a colon (:
), and a target name (my_target
). So, //my_package:my_target
is a label, but only my_package
is a package.
./configure
and which python3
This is a bash script that (for our purposes) merely asks us to input a path to the correct version of our python3
executable. This path is what is returned by which python3
. It can do other things if you are running it on Linux, but we aren’t so we don’t need to worry about that for now.
cd tensorflow
This command moves us into the tensorflow directory that we had just cloned. This is where we need to start for other commands to work properly.
git clone --recurse-submodules
Because the build system relies on protobuf
repo, which implements Google’s protocol buffer data interchange format. By including --recurse-submodules
it will include the protobuf
repo in your cloned repo. None of the other steps will work if you don’t do this. I learned this the hard way because I first forked the Google repo on github, cloned my own, and then added a remote to the Google upstream repo.
One of the side effects of my having not done this properly was that I learned that another way to effect this state of including sub-repos retroactively is to run the command git submodule update --init
. Thank you to this helpful comment.
Install python packages
To be honest, I expect that you already have these packages installed if you are attempting to do any kind of numerical computation on python3
. six
is a package for providing python2
and python3
compatibility. numpy
is a package for efficient in-memory numerical computation, particularly over n-dimensional arrays(ndarray
s). wheel
is a package for distributing wheels the distribution format I alluded to above. ipython3
is the interactive python interpreter for python3
; you should be using it instead of python3
if you are going interactively running code in your terminal (that said, if you want interactive computation you should really investigate Jupyter and the Jupyter notebook).
swig
swig
is a tool for connecting programs written in C
and C++
to high-level programming languages.
Why bazel
?
Above I describe bazel
in greater detail. But I thought it was worth noting that the reason we are installing bazel
is because that is the build tool that we’re going to be using to compile and build the libraries and packages needed to get tensorflow
running.
Terminal
On some later date, I want to go into the history of this program/device/interface. But for now, let it suffice to say that the terminal is the primary way that you can interact with the command line interface (often using bash
, the “Bourne again shell” or some other shell) on Mac OSX.
Java SDK
Oracle’s Java standard edition software development kit 8 (JDK8) is needed to install bazel
, which in turn is needed to install tensorflow
. You will need to agree to their licensing agreement to download it.
xcode
: OSX’s developer tools
We have finally reached the top of the stack, where we find the basic tools needed by a developer on OSX. You can install these either from the Mac App store or from their developer website. You probably want these if you’re going to be developing on OSX, so you should go and download them and install them immediately, even if you follow no other steps in this tutorial. They will be needed at some point.
For our purposes, you need the developer tools in order to install bazel
.
Why gcc --version
?
By default gcc
or the gnu C
compiler, is not available on OSX. The easiest way to ensure that you have it is to install xtools
, and thus by attempting to invoke it, this is a good way to either check that you have xcode
installed or to initiate the licensing agreement once you do have it installed.
By calling this command with the flag --version
we can invoke the command and trigger this process without needing to point to any particular file.
Conclusion
So now you should be able to run the example I gave. Hopefully (and this is the real purpose of this blog post), you should know a lot more about the entire process needed to get from having none of this to this final step. Now, go play and make some machines learn; you’ve made the machine of your mind work hard enough to get this far. Congrats, good luck and cheers.[4]
-
LIFO → Last In, First Out. ↩
-
If you want to know more you can check the wheel documentation. ↩
-
If you want more detail. First it saves the first(and only) argument to a variable (
DEST=$1
). Then it proceeds by making a temporary directory (TMPDIR=$(mktemp -d -t tmp.XXXXXXXXXX)
), copying the relevant files from
to the temporary directory along with a few other things, adding the temporary directory to the directory stack usingbazel-bin/tensorflow/tools/pip_package/build_pip_package.runfiles
pushd
(making the temporary directory the effective current directory), runningpython3 setup.py bdist-wheel >/dev/null
to build the wheel usingsetuptools
, copying the relevant distribution files from the temporary directory into your argument’s indicated directory (cp dist/* ${DEST}
), popping the directory stack (popd
) to return to the original directory at the root of the repo, and removes the temporary directory. The only further complication is that this is accomplished first by defining themain
function and then executing it at the end of the file, passing it the arguments as usual with the linemain "$@"
. ↩ -
Yes, the Oxford comma was purposefully omitted for the sake of the nice ambiguity. Well done for noticing. You get an extra ★. ↩