Les développeurs d'applications mobiles interagissent généralement avec des objets typés tels que des bitmaps ou des primitives telles que des entiers. Cependant, l'API de l'interpréteur TensorFlow Lite qui exécute le modèle d'apprentissage automatique sur l'appareil utilise des tenseurs sous la forme de ByteBuffer, qui peuvent être difficiles à déboguer et à manipuler. La bibliothèque de support Android TensorFlow Lite est conçue pour aider à traiter l'entrée et la sortie des modèles TensorFlow Lite et rendre l'interpréteur TensorFlow Lite plus facile à utiliser.
Commencer
Importer la dépendance Gradle et d'autres paramètres
Copiez le fichier de modèle .tflite
dans le répertoire assets du module Android où le modèle sera exécuté. Spécifiez que le fichier ne doit pas être compressé et ajoutez la bibliothèque TensorFlow Lite au fichier build.gradle
du module :
android {
// Other settings
// Specify tflite file should not be compressed for the app apk
aaptOptions {
noCompress "tflite"
}
}
dependencies {
// Other dependencies
// Import tflite dependencies
implementation 'org.tensorflow:tensorflow-lite:0.0.0-nightly-SNAPSHOT'
// The GPU delegate library is optional. Depend on it as needed.
implementation 'org.tensorflow:tensorflow-lite-gpu:0.0.0-nightly-SNAPSHOT'
implementation 'org.tensorflow:tensorflow-lite-support:0.0.0-nightly-SNAPSHOT'
}
Explorez l' AAR de la bibliothèque de support TensorFlow Lite hébergée sur MavenCentral pour différentes versions de la bibliothèque de support.
Manipulation et conversion d'images de base
La bibliothèque de prise en charge de TensorFlow Lite propose une suite de méthodes de manipulation d'image de base telles que le recadrage et le redimensionnement. Pour l'utiliser, créez un ImagePreprocessor
et ajoutez les opérations requises. Pour convertir l'image au format Tensor requis par l'interpréteur TensorFlow Lite, créez un TensorImage
à utiliser comme entrée :
import org.tensorflow.lite.DataType;
import org.tensorflow.lite.support.image.ImageProcessor;
import org.tensorflow.lite.support.image.TensorImage;
import org.tensorflow.lite.support.image.ops.ResizeOp;
// Initialization code
// Create an ImageProcessor with all ops required. For more ops, please
// refer to the ImageProcessor Architecture section in this README.
ImageProcessor imageProcessor =
new ImageProcessor.Builder()
.add(new ResizeOp(224, 224, ResizeOp.ResizeMethod.BILINEAR))
.build();
// Create a TensorImage object. This creates the tensor of the corresponding
// tensor type (uint8 in this case) that the TensorFlow Lite interpreter needs.
TensorImage tensorImage = new TensorImage(DataType.UINT8);
// Analysis code for every frame
// Preprocess the image
tensorImage.load(bitmap);
tensorImage = imageProcessor.process(tensorImage);
Le type de DataType
d'un tenseur peut être lu via la bibliothèque d'extraction de métadonnées ainsi que d'autres informations de modèle.
Traitement de base des données audio
La bibliothèque de prise en charge de TensorFlow Lite définit également une classe TensorAudio
certaines méthodes de base de traitement des données audio. Il est principalement utilisé avec AudioRecord et capture des échantillons audio dans une mémoire tampon en anneau.
import android.media.AudioRecord;
import org.tensorflow.lite.support.audio.TensorAudio;
// Create an `AudioRecord` instance.
AudioRecord record = AudioRecord(...)
// Create a `TensorAudio` object from Android AudioFormat.
TensorAudio tensorAudio = new TensorAudio(record.getFormat(), size)
// Load all audio samples available in the AudioRecord without blocking.
tensorAudio.load(record)
// Get the `TensorBuffer` for inference.
TensorBuffer buffer = tensorAudio.getTensorBuffer()
Créer des objets de sortie et exécuter le modèle
Avant d'exécuter le modèle, nous devons créer les objets conteneurs qui stockeront le résultat :
import org.tensorflow.lite.DataType;
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer;
// Create a container for the result and specify that this is a quantized model.
// Hence, the 'DataType' is defined as UINT8 (8-bit unsigned integer)
TensorBuffer probabilityBuffer =
TensorBuffer.createFixedSize(new int[]{1, 1001}, DataType.UINT8);
Chargement du modèle et exécution de l'inférence :
import java.nio.MappedByteBuffer;
import org.tensorflow.lite.InterpreterFactory;
import org.tensorflow.lite.InterpreterApi;
// Initialise the model
try{
MappedByteBuffer tfliteModel
= FileUtil.loadMappedFile(activity,
"mobilenet_v1_1.0_224_quant.tflite");
InterpreterApi tflite = new InterpreterFactory().create(
tfliteModel, new InterpreterApi.Options());
} catch (IOException e){
Log.e("tfliteSupport", "Error reading model", e);
}
// Running inference
if(null != tflite) {
tflite.run(tImage.getBuffer(), probabilityBuffer.getBuffer());
}
Accéder au résultat
Les développeurs peuvent accéder directement à la sortie probabilityBuffer.getFloatArray()
. Si le modèle produit une sortie quantifiée, n'oubliez pas de convertir le résultat. Pour le modèle quantifié MobileNet, le développeur doit diviser chaque valeur de sortie par 255 pour obtenir la probabilité allant de 0 (le moins probable) à 1 (le plus probable) pour chaque catégorie.
Facultatif : mapper les résultats aux libellés
Les développeurs peuvent également éventuellement mapper les résultats sur des étiquettes. Tout d'abord, copiez le fichier texte contenant les étiquettes dans le répertoire assets du module. Ensuite, chargez le fichier d'étiquettes à l'aide du code suivant :
import org.tensorflow.lite.support.common.FileUtil;
final String ASSOCIATED_AXIS_LABELS = "labels.txt";
List<String> associatedAxisLabels = null;
try {
associatedAxisLabels = FileUtil.loadLabels(this, ASSOCIATED_AXIS_LABELS);
} catch (IOException e) {
Log.e("tfliteSupport", "Error reading label file", e);
}
L'extrait de code suivant montre comment associer les probabilités aux étiquettes de catégorie :
import java.util.Map;
import org.tensorflow.lite.support.common.TensorProcessor;
import org.tensorflow.lite.support.common.ops.NormalizeOp;
import org.tensorflow.lite.support.label.TensorLabel;
// Post-processor which dequantize the result
TensorProcessor probabilityProcessor =
new TensorProcessor.Builder().add(new NormalizeOp(0, 255)).build();
if (null != associatedAxisLabels) {
// Map of labels and their corresponding probability
TensorLabel labels = new TensorLabel(associatedAxisLabels,
probabilityProcessor.process(probabilityBuffer));
// Create a map to access the result based on label
Map<String, Float> floatMap = labels.getMapWithFloatValue();
}
Couverture actuelle des cas d'utilisation
La version actuelle de la bibliothèque d'assistance TensorFlow Lite couvre :
- types de données communs (float, uint8, images, audio et tableau de ces objets) comme entrées et sorties des modèles tflite.
- opérations d'image de base (recadrer l'image, redimensionner et faire pivoter).
- normalisation et quantification
- utilitaires de fichiers
Les futures versions amélioreront la prise en charge des applications liées au texte.
Architecture du processeur d'image
La conception de l' ImageProcessor
a permis de définir les opérations de manipulation d'image à l'avance et de les optimiser pendant le processus de construction. L' ImageProcessor
prend actuellement en charge trois opérations de prétraitement de base, comme décrit dans les trois commentaires de l'extrait de code ci-dessous :
import org.tensorflow.lite.support.common.ops.NormalizeOp;
import org.tensorflow.lite.support.common.ops.QuantizeOp;
import org.tensorflow.lite.support.image.ops.ResizeOp;
import org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp;
import org.tensorflow.lite.support.image.ops.Rot90Op;
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int size = height > width ? width : height;
ImageProcessor imageProcessor =
new ImageProcessor.Builder()
// Center crop the image to the largest square possible
.add(new ResizeWithCropOrPadOp(size, size))
// Resize using Bilinear or Nearest neighbour
.add(new ResizeOp(224, 224, ResizeOp.ResizeMethod.BILINEAR));
// Rotation counter-clockwise in 90 degree increments
.add(new Rot90Op(rotateDegrees / 90))
.add(new NormalizeOp(127.5, 127.5))
.add(new QuantizeOp(128.0, 1/128.0))
.build();
Voir plus de détails ici sur la normalisation et la quantification.
L'objectif final de la bibliothèque de support est de prendre en charge toutes les transformations tf.image
. Cela signifie que la transformation sera la même que celle de TensorFlow et que l'implémentation sera indépendante du système d'exploitation.
Les développeurs sont également invités à créer des processeurs personnalisés. Il est important dans ces cas d'être aligné avec le processus de formation - c'est-à-dire que le même prétraitement doit s'appliquer à la fois à la formation et à l'inférence pour augmenter la reproductibilité.
Quantification
Lorsque vous lancez des objets d'entrée ou de sortie tels que TensorImage
ou TensorBuffer
vous devez spécifier que leurs types sont DataType.UINT8
ou DataType.FLOAT32
.
TensorImage tensorImage = new TensorImage(DataType.UINT8);
TensorBuffer probabilityBuffer =
TensorBuffer.createFixedSize(new int[]{1, 1001}, DataType.UINT8);
Le TensorProcessor
peut être utilisé pour quantifier les tenseurs d'entrée ou déquantifier les tenseurs de sortie. Par exemple, lors du traitement d'une sortie quantifiée TensorBuffer
, le développeur peut utiliser DequantizeOp
pour déquantifier le résultat à une probabilité en virgule flottante comprise entre 0 et 1 :
import org.tensorflow.lite.support.common.TensorProcessor;
// Post-processor which dequantize the result
TensorProcessor probabilityProcessor =
new TensorProcessor.Builder().add(new DequantizeOp(0, 1/255.0)).build();
TensorBuffer dequantizedBuffer = probabilityProcessor.process(probabilityBuffer);
Les paramètres de quantification d'un tenseur peuvent être lus via la bibliothèque d'extraction de métadonnées .