web-dev-qa-db-fra.com

Exécution d'un modèle Tensorflow sur Android

J'essaie de comprendre le flux de travail pour la formation et le déploiement d'un modèle Tensorflow sur Android. Je suis au courant des autres questions similaires à celle de StackOverflow, mais aucune d’entre elles ne semble régler les problèmes que j’ai rencontrés. 

Après avoir étudié l'exemple Android du référentiel Tensorflow, voici ce que je pense que le flux de travail devrait être:

  1. Construisez et entraînez le modèle Tensorflow en Python.
  2. Créez un nouveau graphe et transférez tous les nœuds pertinents (c’est-à-dire les nœuds responsables de la formation) vers ce nouveau graphe. Les variables de poids formées sont importées en tant que constantes afin que l'API C++ puisse les lire.
  3. Développez l'interface graphique Android en Java, en utilisant le mot clé natif pour interrompre un appel au modèle Tensorflow.
  4. Exécutez javah pour générer le code de remplacement C/C++ pour l'appel natif Tensorflow.
  5. Remplissez le stub à l'aide de l'API Tensorflow C++ pour lire et accéder au modèle formé/sérialisé.
  6. Utilisez Bazel pour créer à la fois l’application Java, l’interface native Tensorflow (en tant que fichier .so) et générer le fichier APK.
  7. Utilisez adb pour déployer l'APK.

    L'étape 6 est le problème. Bazel se fera un plaisir de compiler un fichier .dylib natif (sous OSX) que je pourrai appeler de Java via JNI. De même, Android Studio générera tout un tas de code XML qui rend l'interface graphique que je souhaite. Cependant, Bazel souhaite que tout le code de l'application Java se trouve dans le répertoire de niveau supérieur "WORKSPACE" (dans le référentiel Tensorflow), et Android Studio lie immédiatement toutes sortes de bibliothèques externes à partir du SDK pour créer des interfaces L’exécution de la compilation de Bazel échoue s’il ne trouve pas ces ressources). Le seul moyen de forcer Bazel à compiler de manière croisée un fichier .so consiste à en faire une règle dépendante d'une règle Android. C’est ce que je préférerais porter directement sous A.S. code à un projet Bazel.

    Comment puis-je régler cela? Bazel est censé compiler le code Android, mais Android Studio génère un code qu’il ne peut pas compiler. Tous les exemples de Google vous donnent simplement le code d'un dépôt sans aucune idée de la façon dont il a été généré. Autant que je sache, le code XML faisant partie d'une application Android Studio est censé être généré et non créé manuellement. Si cela peut être fait à la main, comment éviter le recours à toutes ces bibliothèques externes? 

    Je me trompe peut-être dans le flux de travail ou il y a un aspect de Bazel/Android Studio que je ne comprends pas. Toute aide appréciée. 

Merci!

Modifier:

Il y a plusieurs choses que j'ai finalement faites qui pourraient avoir contribué à la construction de la bibliothèque:

  1. Je suis passé au dernier Bazel.
  2. J'ai reconstruit TensorFlow à partir de la source.
  3. J'ai implémenté le fichier Bazel BUILD recommandé ci-dessous, avec quelques ajouts (extraits de l'exemple Android):

    cc_binary(
    name = "libName.so",
    srcs = ["org_tensorflowtest_MyActivity.cc", 
            "org_tensorflowtest_MyActivity.h",
            "jni.h",
            "jni_md.h",
            ":libpthread.so"],
    deps = ["//tensorflow/core:Android_tensorflow_lib",
            ],
    copts = [
        "-std=c++11",
        "-mfpu=neon",
        "-O2",
    ],
    linkopts = ["-llog -landroid -lm"],
    linkstatic = 1,
    linkshared = 1,
    )
    
    cc_binary(
         name = "libpthread.so",
         srcs = [],
         linkopts = ["-shared"],
         tags = [
             "manual",
             "notap",
         ],
    )
    

Je n'ai pas encore vérifié que cette bibliothèque peut être chargée et utilisée dans Android. Android Studio 1.5 semble très habile à reconnaître la présence de bibliothèques natives.

18
amm

Après avoir configuré un NDK Android dans votre fichier WORKSPACE, Bazel peut compiler de manière croisée un fichier .so pour Android, comme ceci:

cc_binary(
    name = "libfoo.so",
    srcs = ["foo.cc"],
    deps = [":bar"],
    linkstatic = 1,
    linkshared = 1,
)

$ bazel build foo:libfoo.so \
    --crosstool_top=//external:Android/crosstool --cpu=armeabi-v7a \
    --Host_crosstool_top=@bazel_tools//tools/cpp:toolchain
$ file bazel-bin/foo/libfoo.so
bazel-bin/foo/libfoo.so: ELF 32-bit LSB  shared object, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), not stripped

Bazel souhaite que tout le code de l'application Java se trouve dans le 'WORKSPACE' répertoire de niveau supérieur (dans le référentiel Tensorflow)

Lorsque la version 0.1.4 est publiée (mise à jour en ce moment) et que nous avons soumis certaines corrections à TensorFlow et à Protobuf, vous pouvez commencer à utiliser le référentiel TensorFlow en tant que référentiel distant. Après l'avoir configuré dans votre fichier WORKSPACE, vous pouvez vous référer aux règles TensorFlow à l'aide d'étiquettes @tensorflow//foo/bar.

10
Ulf Adams
git clone --recurse-submodules https://github.com/tensorflow/tensorflow.git

Remarque: Il est important de tirer les sous-modules.

Installez Bazel d'ici. Bazel est le système de construction principal de TensorFlow . Maintenant, éditez le WORKSPACE, vous pouvez trouver le fichier WORKSPACE dans le répertoire racine du TensorFlow que nous avons précédemment cloné.

# Uncomment and update the paths in these entries to build the Android demo.
#Android_sdk_repository(
#    name = "androidsdk",
#    api_level = 23,
#    build_tools_version = "25.0.1",
#    # Replace with path to Android SDK on your system
#    path = "<PATH_TO_SDK>",
#)
#
#Android_ndk_repository(
#    name="androidndk",
#    path="<PATH_TO_NDK>",
#    api_level=14)

Comme ci-dessous avec nos chemins sdk et ndk:

Android_sdk_repository(
    name = "androidsdk",
    api_level = 23,
    build_tools_version = "25.0.1",
    # Replace with path to Android SDK on your system
    path = "/Users/amitshekhar/Library/Android/sdk/",
)
Android_ndk_repository(
    name="androidndk",
    path="/Users/amitshekhar/Downloads/Android-ndk-r13/",
    api_level=14)

Ensuite, générez le fichier .so.

bazel build -c opt //tensorflow/contrib/Android:libtensorflow_inference.so \
   --crosstool_top=//external:Android/crosstool \
   --Host_crosstool_top=@bazel_tools//tools/cpp:toolchain \
   --cpu=armeabi-v7a

Remplacement d’armeabi-v7a par l’architecture cible souhaitée .. La bibliothèque sera située à l’adresse suivante:

bazel-bin/tensorflow/contrib/Android/libtensorflow_inference.so

Pour construire la contrepartie Java:

bazel build //tensorflow/contrib/Android:android_tensorflow_inference_Java

Nous pouvons trouver le fichier JAR à l'adresse:

bazel-bin/tensorflow/contrib/Android/libandroid_tensorflow_inference_Java.jar

Nous avons maintenant les fichiers jar et .so. J'ai déjà construit le fichier .so et jar, vous pouvez utiliser directement à partir du project .

Mettez libandroid_tensorflow_inference_Java.jar dans le dossier libs, cliquez avec le bouton droit de la souris et ajoutez-le en tant que bibliothèque.

compile files('libs/libandroid_tensorflow_inference_Java.jar')

Créez le dossier jniLibs dans le répertoire principal et mettez libtensorflow_inference.so dans le dossier jniLibs/armeabi-v7a /.

Nous pourrons maintenant appeler l'API Java TensorFlow.

L'API Java TensorFlow a exposé toutes les méthodes requises via une classe TensorFlowInferenceInterface.

Maintenant, nous devons appeler l'API Java TensorFlow avec le chemin du modèle et le charger.

J'ai écrit un blog complet ici .

1
Amit Shekhar