Twórcy aplikacji mobilnych zazwyczaj wchodzą w interakcję z typowanymi obiektami, takimi jak mapy bitowe lub prymitywami, takimi jak liczby całkowite. Jednak interfejs API interpretera TensorFlow Lite, który uruchamia model uczenia maszynowego na urządzeniu, wykorzystuje tensory w postaci ByteBuffer, co może być trudne do debugowania i manipulowania. Biblioteka TensorFlow Lite Android Support Library została zaprojektowana, aby pomóc w przetwarzaniu danych wejściowych i wyjściowych modeli TensorFlow Lite i ułatwić korzystanie z interpretera TensorFlow Lite.
Pierwsze kroki
Importuj zależność Gradle i inne ustawienia
Skopiuj plik modelu .tflite
do katalogu asset modułu Android, w którym będzie uruchamiany model. Określ, że plik nie powinien być skompresowany, i dodaj bibliotekę TensorFlow Lite do pliku build.gradle
modułu:
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'
}
Zapoznaj się z Biblioteką pomocy technicznej TensorFlow Lite AAR hostowaną w MavenCentral, aby znaleźć różne wersje Biblioteki pomocy technicznej.
Podstawowa manipulacja obrazem i konwersja
Biblioteka wsparcia TensorFlow Lite zawiera zestaw podstawowych metod manipulacji obrazami, takich jak przycinanie i zmiana rozmiaru. Aby z niego skorzystać, utwórz ImagePreprocessor
i dodaj wymagane operacje. Aby przekonwertować obraz do formatu tensora wymaganego przez interpreter TensorFlow Lite, utwórz TensorImage
, który będzie używany jako dane wejściowe:
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);
DataType
tensora można odczytać za pomocą biblioteki wyodrębniania metadanych, a także innych informacji o modelu.
Podstawowe przetwarzanie danych audio
Biblioteka Wsparcia TensorFlow Lite definiuje również klasę TensorAudio
obejmującą niektóre podstawowe metody przetwarzania danych dźwiękowych. Jest używany głównie razem z AudioRecord i przechwytuje próbki audio w buforze pierścieniowym.
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()
Utwórz obiekty wyjściowe i uruchom model
Przed uruchomieniem modelu musimy stworzyć obiekty kontenera, które będą przechowywać wynik:
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);
Ładowanie modelu i uruchamianie wnioskowania:
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());
}
Dostęp do wyniku
Deweloperzy mogą uzyskać dostęp do danych wyjściowych bezpośrednio za pomocą probabilityBuffer.getFloatArray()
. Jeśli model generuje skwantowane dane wyjściowe, pamiętaj o przekonwertowaniu wyniku. W przypadku skwantyzowanego modelu MobileNet programista musi podzielić każdą wartość wyjściową przez 255, aby uzyskać prawdopodobieństwo w zakresie od 0 (najmniej prawdopodobne) do 1 (najbardziej prawdopodobne) dla każdej kategorii.
Opcjonalnie: mapowanie wyników na etykiety
Deweloperzy mogą również opcjonalnie mapować wyniki na etykiety. Najpierw skopiuj plik tekstowy zawierający etykiety do katalogu zasobów modułu. Następnie załaduj plik etykiety, używając następującego kodu:
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);
}
Poniższy fragment pokazuje, jak powiązać prawdopodobieństwa z etykietami kategorii:
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();
}
Bieżący zasięg przypadków użycia
Aktualna wersja Biblioteki Wsparcia TensorFlow Lite obejmuje:
- wspólne typy danych (float, uint8, obrazy, audio i tablica tych obiektów) jako wejścia i wyjścia modeli tflite.
- podstawowe operacje na obrazie (kadrowanie obrazu, zmiana rozmiaru i obracanie).
- normalizacja i kwantyzacja
- narzędzia do plików
Przyszłe wersje poprawią obsługę aplikacji tekstowych.
Architektura procesora obrazu
Konstrukcja ImageProcessor
pozwoliła na zdefiniowanie operacji manipulacji obrazem i zoptymalizowanie ich podczas procesu kompilacji. ImageProcessor
obsługuje obecnie trzy podstawowe operacje przetwarzania wstępnego, jak opisano w trzech komentarzach w poniższym fragmencie kodu:
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();
Zobacz więcej szczegółów tutaj o normalizacji i kwantyzacji.
Ostatecznym celem biblioteki wsparcia jest obsługa wszystkich przekształceń tf.image
. Oznacza to, że transformacja będzie taka sama jak w TensorFlow, a implementacja będzie niezależna od systemu operacyjnego.
Deweloperzy mogą również tworzyć niestandardowe procesory. W takich przypadkach ważne jest, aby dostosować się do procesu uczenia – tj. to samo wstępne przetwarzanie powinno dotyczyć zarówno uczenia, jak i wnioskowania, aby zwiększyć odtwarzalność.
Kwantyzacja
Podczas inicjowania obiektów wejściowych lub wyjściowych, takich jak TensorImage
lub TensorBuffer
, należy określić ich typy jako DataType.UINT8
lub DataType.FLOAT32
.
TensorImage tensorImage = new TensorImage(DataType.UINT8);
TensorBuffer probabilityBuffer =
TensorBuffer.createFixedSize(new int[]{1, 1001}, DataType.UINT8);
TensorProcessor
może być używany do kwantyzacji tensorów wejściowych lub dekwantyzacji tensorów wyjściowych. Na przykład podczas przetwarzania skwantyzowanego wyniku TensorBuffer
, programista może użyć DequantizeOp
do dekwantyzacji wyniku do prawdopodobieństwa zmiennoprzecinkowego z zakresu od 0 do 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);
Parametry kwantyzacji tensora można odczytać za pomocą biblioteki ekstraktorów metadanych .