Difference between revisions of "Tensorflow JNI development"

From ElphelWiki
Jump to: navigation, search
(Java maven project in Eclipse)
(Note)
 
(73 intermediate revisions by the same user not shown)
Line 1: Line 1:
==About==
+
==<font color='blue'>Why</font>==
Based on [https://medium.com/@maxime.durand.54/build-tennsorflow-2-0-for-java-on-windows-2ab51b9cac45 this].
+
Why modify TF JNI?
 +
* Add TF features that are still missing in TF for Java like '''feeding directly from GPU memory''' thus saving time on back and forth CPU-GPU transfers if you, say, run data through a custom CUDA kernel first.
  
And this one - [https://github.com/tensorflow/tensorflow/blob/master/tensorflow/java/README.md tensorflow/java/README.md].
+
==<font color='blue'>About</font>==
  
How to:
+
Notes on how to build TF JNI, where to modify JNI if needed, install to local maven and setup a project that will use modified native functions.
* Install things
 
* Build libtensorflow.jar, libtensorflow_jni.so
 
* Install pom.xml and libtensorflow.jar to local maven repo in ~/.m2
 
* Modify Tensorflow JNI functions
 
* Create a maven project and use modified JNI
 
  
==Install==
+
Based on [https://medium.com/@maxime.durand.54/build-tennsorflow-2-0-for-java-on-windows-2ab51b9cac45 Build TensorFlow 2.0 for Java on Windows] article.
* In Kubuntu
+
Also this one - [https://github.com/tensorflow/tensorflow/blob/master/tensorflow/java/README.md tensorflow/java/README.md].
* Get TF 1.15.0 - git or archive
 
* Install bazel 0.25.2 - see *.deb in releases
 
  
==Build==
+
These instructions are for '''Linux''' and old '''TensorFlow 1.15.0'''.
  cd ~/git/tensorflow-1.15.0
+
 
  ./configure
+
How to:
  bazel build -c opt //tensorflow/java:tensorflow //tensorflow/java:libtensorflow_jni
+
* '''Build TF JNI''' - libtensorflow.jar, libtensorflow_jni.so and pom.xml
bazel build -c opt //tensorflow/java:pom
+
* '''Add TF JAR to local Maven''' which will override the [https://mvnrepository.com/artifact/org.tensorflow/tensorflow Central Maven Repository]
 +
* '''Modify TF JNI functions'''
 +
* '''Create Elipse project'''
 +
 
 +
There's [https://github.com/bytedeco/javacpp-presets JavaCPP Presets] project. <s>Seems useless</s>. Seems useful.
 +
 
 +
==<font color='blue'>Install</font>==
 +
In Kubuntu:
 +
* Get [https://github.com/tensorflow/tensorflow/archive/v1.15.0.tar.gz TensorFlow-1.15.0]
 +
* Install [https://github.com/bazelbuild/bazel/releases/download/0.25.2/bazel_0.25.2-linux-x86_64.deb bazel 0.25.2]
 +
 
 +
Based on [[Feeding_Tensorflow_from_GPU|Feeding Tensorflow from GPU]].
 +
 
 +
==<font color='blue'>Build</font>==
 +
===Note===
 +
* <font color='red'>While running bazel ate all RAM (have 16GB) a few times and PC "hanged". To limit bazel's appetites try:</font>
 +
  <font size=2>~$ bazel build --jobs 4 --local_ram_resources=4096 ...
 +
~$ bazel test --jobs 4 --local_ram_resources=4096 ...
 +
# I think that '''local_ram_resources''' is MBs per thread (have 8):
 +
~$ bazel build --local_ram_resources=2048 ...
 +
~$ bazel test --local_ram_resources=2048 ...</font>
 +
 
 +
===Quick===
 +
~/git/tensorflow-1.15.0/mvn_build.sh:
 +
<font size=2># Step 1 (Java):
 +
   
 +
  bazel build -c opt //tensorflow/java:tensorflow //tensorflow/java:libtensorflow_jni //tensorflow/java:pom
 
  mvn install:install-file -Dfile=bazel-bin/tensorflow/java/libtensorflow.jar -DpomFile=bazel-bin/tensorflow/java/pom.xml
 
  mvn install:install-file -Dfile=bazel-bin/tensorflow/java/libtensorflow.jar -DpomFile=bazel-bin/tensorflow/java/pom.xml
 +
 +
# Step 2 (JNI):
 +
 +
bazel build -c opt //tensorflow/tools/lib_package:libtensorflow_jni.tar.gz
 +
 +
rm -rf bazel-bin/tensorflow/tools/lib_package/maven
 +
mkdir -p bazel-bin/tensorflow/tools/lib_package/maven/org/tensorflow/native/linux-x86_64
 +
 +
POM="
 +
<project>
 +
  <modelVersion>4.0.0</modelVersion>
 +
  <description>Platform-dependent native code for the TensorFlow Java library. CUDA support depends on the local build.</description>
 +
  <groupId>org.tensorflow</groupId>
 +
  <artifactId>libtensorflow_jni_gpu</artifactId>
 +
  <version>1.15.0</version>
 +
  <packaging>jar</packaging>
 +
  <build>
 +
    <resources>
 +
      <resource>
 +
        <directory>.</directory>
 +
          <excludes>
 +
            <exclude>target/**</exclude>
 +
          </excludes>
 +
      </resource>
 +
    </resources>
 +
  </build>
 +
</project>
 +
"
 +
 +
echo $POM > bazel-bin/tensorflow/tools/lib_package/maven/pom.xml
 +
 +
tar -zxvf bazel-bin/tensorflow/tools/lib_package/libtensorflow_jni.tar.gz -C bazel-bin/tensorflow/tools/lib_package/maven/org/tensorflow/native/linux-x86_64
 +
cd bazel-bin/tensorflow/tools/lib_package/maven
 +
mvn package
 +
mvn install
 +
cd ../../../../..
 +
 +
===Detailed===
 +
 +
<font size=2>cd ~/git/tensorflow-1.15.0
 +
./configure # do not forget CUDA
 +
bazel build -c opt //tensorflow/java:tensorflow //tensorflow/java:libtensorflow_jni //tensorflow/java:pom</font>
 +
 +
With TF, bazel tends to rebuild everything from scratch - takes a ton of time.
 +
Is it because it gets restarted after idle timeout or something else? A somewhat solution might be
 +
<font size=2>At launch bazel starts its server which, to prevent it, add to ~/.bazelrc:
 +
startup --max_idle_secs=0</font>
 +
 +
Artifacts of interest are in '''bazel-bin/tensorflow/java/''':
 +
<font size=2>'''libtensorflow_jni.so'''
 +
'''libtensorflow.jar'''
 +
'''pom.xml'''</font>
 +
 +
* '''xml''' and '''jar''' will be taken care of by '''mvn''' command.
 +
* '''so''' will have to be in the library path (alternatively see [[Tensorflow_JNI_development#Build_so_package|'''Build so package''']] a little below and skip this linking). Link or copy to '''/usr/lib/''' or go with "java -Djava.library.path=...".
 +
 +
===[option 1] Link so library===
 +
<font size=2># /usr/lib is in the default java.library.path
 +
sudo ln -sf ~/GIT/tensorflow-1.15.0/bazel-bin/tensorflow/java/libtensorflow_jni.so /usr/lib/</font>
 +
 +
=== preferred [option 2] Build so package===
 +
<font size=2>bazel build -c opt //tensorflow/tools/lib_package:libtensorflow_jni.tar.gz</font>
 +
It puts all libs into a single archive.
 +
Now to create a JAR to replace [https://mvnrepository.com/artifact/org.tensorflow/libtensorflow_jni_gpu libtensorflow_jni_gpu], do this:
 +
<font size=2>mkdir -p bazel-bin/tensorflow/tools/lib_package/maven/org/tensorflow/native/linux-x86_64
 +
tar -zxvf bazel-bin/tensorflow/tools/lib_package/libtensorflow_jni.tar.gz -C bazel-bin/tensorflow/tools/lib_package/maven/org/tensorflow/native/linux-x86_64</font>
 +
Next create a pom.xml in ''bazel-bin/tensorflow/tools/lib_package/maven'':
 +
<font size=2><project>
 +
  <modelVersion>4.0.0</modelVersion>
 +
  <description>Platform-dependent native code for the TensorFlow Java library. CUDA support depends on the local build.</description>
 +
  <groupId>org.tensorflow</groupId>
 +
  <artifactId>'''libtensorflow_jni_gpu'''</artifactId>
 +
  <version>1.15.0</version>
 +
  <packaging>jar</packaging>
 +
  <build>
 +
    <resources>
 +
      <resource>
 +
        <directory>.</directory>
 +
          <excludes>
 +
            <exclude>target/**</exclude>
 +
          </excludes>
 +
      </resource>
 +
    </resources>
 +
  </build>
 +
</project></font>
 +
Note: '''libtensorflow_jni_gpu''' - name can be any - just make sure you use it in your project's pom.xml.
 +
Next:
 +
<font size=2>cd bazel-bin/tensorflow/tools/lib_package/maven
 +
mvn package
 +
mvn install
 +
cd ../../../../..
 +
</font>
 +
 +
==<font color='blue'>Install JAR to local Maven Repository</font>==
 +
<font size=2>~/GIT/tensorflow-1.15.0$ <b>mvn install:install-file -Dfile=bazel-bin/tensorflow/java/libtensorflow.jar -DpomFile=bazel-bin/tensorflow/java/pom.xml</b></font>
  
Artifacts of interest in bazel-bin/tensorflow/java/:
+
How to uninstall maven local repo - and switch back to official versions from Maven Central - [https://stackoverflow.com/questions/15358851/how-to-remove-jar-file-from-local-maven-repository-which-was-added-with-install this link]. Or remove unneeded stuff from '''~/.m2/repository/org/tensorflow'''
libtensorflow_jni.so
 
libtensorflow.jar
 
pom.xml
 
  
* '''xml''' and '''jar''' are taken care of.
+
<font color=green size='4'>After '''*_jni.so''' is linked (or '''jar''''d) and '''jar''' installed one can resume normal development. See below what to add to your project's pom.xml</font>
* '''so''' will have to be in the library path - set LD_LIBRARY_PATH or PATH or go "java -Djava.library.path"
 
  
==Modify TF JNI==
+
==<font color='blue'>Modify TF JNI functions</font>==
 
For example, one wants to create a new function in org.tensorflow.TensorFlow package.
 
For example, one wants to create a new function in org.tensorflow.TensorFlow package.
Then, see:
+
Then see inside:
  '''tensorflow/java/src/main/java/org/tensorflow/'''
+
  <font size=2>'''tensorflow/java/src/main/java/org/tensorflow/'''
  '''tensorflow/java/src/main/native/'''
+
  '''tensorflow/java/src/main/native/'''</font>
 
+
Three places:
 
* add native method to tensorflow/java/src/main/java/org/tensorflow/TensorFlow.java
 
* add native method to tensorflow/java/src/main/java/org/tensorflow/TensorFlow.java
 
* add to header file tensorflow/java/src/main/native/tensorflow_jni.h
 
* add to header file tensorflow/java/src/main/native/tensorflow_jni.h
 
* add to c file tensorflow/java/src/main/native/tensorflow_jni.cc
 
* add to c file tensorflow/java/src/main/native/tensorflow_jni.cc
  
* rebuild and reinstall to mvn
+
Rebuild and Reinstall.
  
==Java maven project in Eclipse==
+
The native header files seem to be regenerated but I haven't tested if they are actually used (need to test).
 +
In function naming - avoid underscores, e.g.:
 +
<font size=2>Java_org_tensorflow_TensorFlow_&lt;Name&gt;</font>
 +
 
 +
==<font color='blue'>Java Maven project in Eclipse</font>==
 +
Nothing special.
 
* Create a new maven project
 
* Create a new maven project
 
* Edit pom.xml:
 
* Edit pom.xml:
  <project>
+
  <font size=2><project>
 
   ...
 
   ...
 
   <dependencies>
 
   <dependencies>
 
     ...
 
     ...
     <dependency>
+
     <b><dependency>
 
       <groupId>org.tensorflow</groupId>
 
       <groupId>org.tensorflow</groupId>
 
       <artifactId>libtensorflow</artifactId>
 
       <artifactId>libtensorflow</artifactId>
 
       <version>1.15.0</version>
 
       <version>1.15.0</version>
     </dependency>
+
     </dependency></b>
 
     ...
 
     ...
 
   </dependencies>
 
   </dependencies>
 
   ...
 
   ...
  </project>
+
  </project></font>
 +
* Write code as usual
 +
 
 +
===Basic example code===
 +
tfhello.java:
 +
<font size=2>import org.tensorflow.TensorFlow;
 +
 +
public class tfhello{
 +
public static void main(String[] args){
 +
System.out.println(TensorFlow.version());
 +
}
 +
}</font>
 +
 
 +
==<font color='blue'>A few words on TF in Maven Central repository</font>==
 +
 
 +
===libtensorflow===
 +
Record in pom.xml:
 +
<font size=2><dependency>
 +
  <groupId>org.tensorflow</groupId>
 +
  <artifactId>libtensorflow</artifactId>
 +
  <version>1.15.0</version>
 +
</dependency></font>
 +
 
 +
Archive contains Java classes.
 +
 
 +
===libtensorflow_jni_gpu===
 +
Record in pom.xml:
 +
<font size=2><dependency>
 +
  <groupId>org.tensorflow</groupId>
 +
  <artifactId>libtensorflow_jni_gpu</artifactId>
 +
  <version>1.15.0</version>
 +
</dependency></font>
 +
 
 +
Archive contains native library:
 +
<font size=2>├── META-INF
 +
│   ├── MANIFEST.MF
 +
│   └── maven
 +
│      └── org.tensorflow
 +
│          └── libtensorflow_jni_gpu
 +
│              ├── pom.properties
 +
│              └── pom.xml
 +
└── org
 +
    └── tensorflow
 +
        └── native
 +
            ├── linux-x86_64
 +
            │   ├── '''libtensorflow_framework.so.1'''
 +
            │   ├── '''libtensorflow_jni.so'''
 +
            │   ├── LICENSE
 +
            │   └── THIRD_PARTY_TF_JNI_LICENSES
 +
            └── windows-x86_64
 +
                ├── LICENSE
 +
                └── tensorflow_jni.dll</font>

Latest revision as of 09:48, 1 April 2020

Why

Why modify TF JNI?

  • Add TF features that are still missing in TF for Java like feeding directly from GPU memory thus saving time on back and forth CPU-GPU transfers if you, say, run data through a custom CUDA kernel first.

About

Notes on how to build TF JNI, where to modify JNI if needed, install to local maven and setup a project that will use modified native functions.

Based on Build TensorFlow 2.0 for Java on Windows article. Also this one - tensorflow/java/README.md.

These instructions are for Linux and old TensorFlow 1.15.0.

How to:

  • Build TF JNI - libtensorflow.jar, libtensorflow_jni.so and pom.xml
  • Add TF JAR to local Maven which will override the Central Maven Repository
  • Modify TF JNI functions
  • Create Elipse project

There's JavaCPP Presets project. Seems useless. Seems useful.

Install

In Kubuntu:

Based on Feeding Tensorflow from GPU.

Build

Note

  • While running bazel ate all RAM (have 16GB) a few times and PC "hanged". To limit bazel's appetites try:
~$ bazel build --jobs 4 --local_ram_resources=4096 ...
~$ bazel test --jobs 4 --local_ram_resources=4096 ...
# I think that local_ram_resources is MBs per thread (have 8):
~$ bazel build --local_ram_resources=2048 ...
~$ bazel test --local_ram_resources=2048 ...

Quick

~/git/tensorflow-1.15.0/mvn_build.sh:

# Step 1 (Java):

bazel build -c opt //tensorflow/java:tensorflow //tensorflow/java:libtensorflow_jni //tensorflow/java:pom
mvn install:install-file -Dfile=bazel-bin/tensorflow/java/libtensorflow.jar -DpomFile=bazel-bin/tensorflow/java/pom.xml

# Step 2 (JNI):

bazel build -c opt //tensorflow/tools/lib_package:libtensorflow_jni.tar.gz

rm -rf bazel-bin/tensorflow/tools/lib_package/maven
mkdir -p bazel-bin/tensorflow/tools/lib_package/maven/org/tensorflow/native/linux-x86_64

POM="
<project>
  <modelVersion>4.0.0</modelVersion>
  <description>Platform-dependent native code for the TensorFlow Java library. CUDA support depends on the local build.</description>
  <groupId>org.tensorflow</groupId>
  <artifactId>libtensorflow_jni_gpu</artifactId>
  <version>1.15.0</version>
  <packaging>jar</packaging>
  <build>
    <resources>
      <resource>
        <directory>.</directory>
          <excludes>
            <exclude>target/**</exclude>
          </excludes>
      </resource>
    </resources>
  </build>
</project>
"

echo $POM > bazel-bin/tensorflow/tools/lib_package/maven/pom.xml

tar -zxvf bazel-bin/tensorflow/tools/lib_package/libtensorflow_jni.tar.gz -C bazel-bin/tensorflow/tools/lib_package/maven/org/tensorflow/native/linux-x86_64
cd bazel-bin/tensorflow/tools/lib_package/maven
mvn package
mvn install
cd ../../../../..

Detailed

cd ~/git/tensorflow-1.15.0
./configure # do not forget CUDA
bazel build -c opt //tensorflow/java:tensorflow //tensorflow/java:libtensorflow_jni //tensorflow/java:pom

With TF, bazel tends to rebuild everything from scratch - takes a ton of time. Is it because it gets restarted after idle timeout or something else? A somewhat solution might be

At launch bazel starts its server which, to prevent it, add to ~/.bazelrc:
startup --max_idle_secs=0

Artifacts of interest are in bazel-bin/tensorflow/java/:

libtensorflow_jni.so
libtensorflow.jar
pom.xml
  • xml and jar will be taken care of by mvn command.
  • so will have to be in the library path (alternatively see Build so package a little below and skip this linking). Link or copy to /usr/lib/ or go with "java -Djava.library.path=...".

[option 1] Link so library

# /usr/lib is in the default java.library.path
sudo ln -sf ~/GIT/tensorflow-1.15.0/bazel-bin/tensorflow/java/libtensorflow_jni.so /usr/lib/

preferred [option 2] Build so package

bazel build -c opt //tensorflow/tools/lib_package:libtensorflow_jni.tar.gz

It puts all libs into a single archive. Now to create a JAR to replace libtensorflow_jni_gpu, do this:

mkdir -p bazel-bin/tensorflow/tools/lib_package/maven/org/tensorflow/native/linux-x86_64
tar -zxvf bazel-bin/tensorflow/tools/lib_package/libtensorflow_jni.tar.gz -C bazel-bin/tensorflow/tools/lib_package/maven/org/tensorflow/native/linux-x86_64

Next create a pom.xml in bazel-bin/tensorflow/tools/lib_package/maven:

<project>
  <modelVersion>4.0.0</modelVersion>
  <description>Platform-dependent native code for the TensorFlow Java library. CUDA support depends on the local build.</description>
  <groupId>org.tensorflow</groupId>
  <artifactId>libtensorflow_jni_gpu</artifactId>
  <version>1.15.0</version>
  <packaging>jar</packaging>
  <build>
    <resources>
      <resource>
        <directory>.</directory>
          <excludes>
            <exclude>target/**</exclude>
          </excludes>
      </resource>
    </resources>
  </build>
</project>

Note: libtensorflow_jni_gpu - name can be any - just make sure you use it in your project's pom.xml. Next:

cd bazel-bin/tensorflow/tools/lib_package/maven
mvn package
mvn install
cd ../../../../..

Install JAR to local Maven Repository

~/GIT/tensorflow-1.15.0$ mvn install:install-file -Dfile=bazel-bin/tensorflow/java/libtensorflow.jar -DpomFile=bazel-bin/tensorflow/java/pom.xml

How to uninstall maven local repo - and switch back to official versions from Maven Central - this link. Or remove unneeded stuff from ~/.m2/repository/org/tensorflow

After *_jni.so is linked (or jar'd) and jar installed one can resume normal development. See below what to add to your project's pom.xml

Modify TF JNI functions

For example, one wants to create a new function in org.tensorflow.TensorFlow package. Then see inside:

tensorflow/java/src/main/java/org/tensorflow/
tensorflow/java/src/main/native/

Three places:

  • add native method to tensorflow/java/src/main/java/org/tensorflow/TensorFlow.java
  • add to header file tensorflow/java/src/main/native/tensorflow_jni.h
  • add to c file tensorflow/java/src/main/native/tensorflow_jni.cc

Rebuild and Reinstall.

The native header files seem to be regenerated but I haven't tested if they are actually used (need to test). In function naming - avoid underscores, e.g.:

Java_org_tensorflow_TensorFlow_<Name>

Java Maven project in Eclipse

Nothing special.

  • Create a new maven project
  • Edit pom.xml:
<project>
  ...
  <dependencies>
    ...
    <dependency>
      <groupId>org.tensorflow</groupId>
      <artifactId>libtensorflow</artifactId>
      <version>1.15.0</version>
    </dependency>
    ...
  </dependencies>
  ...
</project>
  • Write code as usual

Basic example code

tfhello.java:

import org.tensorflow.TensorFlow;

public class tfhello{
	public static void main(String[] args){
		System.out.println(TensorFlow.version());
	}
}

A few words on TF in Maven Central repository

libtensorflow

Record in pom.xml:

<dependency>
  <groupId>org.tensorflow</groupId>
  <artifactId>libtensorflow</artifactId>
  <version>1.15.0</version>
</dependency>

Archive contains Java classes.

libtensorflow_jni_gpu

Record in pom.xml:

<dependency>
  <groupId>org.tensorflow</groupId>
  <artifactId>libtensorflow_jni_gpu</artifactId>
  <version>1.15.0</version>
</dependency>

Archive contains native library:

├── META-INF
│   ├── MANIFEST.MF
│   └── maven
│       └── org.tensorflow
│           └── libtensorflow_jni_gpu
│               ├── pom.properties
│               └── pom.xml
└── org
    └── tensorflow
        └── native
            ├── linux-x86_64
            │   ├── libtensorflow_framework.so.1
            │   ├── libtensorflow_jni.so
            │   ├── LICENSE
            │   └── THIRD_PARTY_TF_JNI_LICENSES
            └── windows-x86_64
                ├── LICENSE
                └── tensorflow_jni.dll