Cosa creerai
In questo tutorial scaricherai un modello TensorFlow Lite personalizzato esportato da AutoML Vision Edge. Eseguirai quindi un'app per iOS predefinita che utilizza il modello per identificare le immagini di fiori.
Obiettivi
In questa procedura dettagliata introduttiva end-to-end, utilizzerai il codice per:
- Esegui un modello preaddestrato in un'app per iOS utilizzando l'interprete TFLite.
Prima di iniziare
Installa TensorFlow
Prima di iniziare il tutorial, devi installare diversi software:
- Installa tensorflow versione 1.7
- installa PILLOW
Se hai un'installazione Python funzionante, esegui questi comandi per scaricare questo software:
pip install --upgrade "tensorflow==1.7.*" pip install PILLOW
Clona il repository Git
Utilizzando la riga di comando, clona il repository Git con il seguente comando:
git clone https://github.com/googlecodelabs/tensorflow-for-poets-2
Vai alla directory del clone locale del repository (directory tensorflow-for-poets-2
). Da questa directory eseguirai tutti i seguenti esempi di codice:
cd tensorflow-for-poets-2
Configura l'app per iOS
L'app demo per iOS richiede diversi strumenti aggiuntivi:
- Xcode
- Strumenti a riga di comando Xcode
- Cocoapodi
Scarica Xcode
Utilizza il seguente link per scaricare Xcode sulla tua macchina.
Installa strumenti a riga di comando Xcode
Installa gli strumenti a riga di comando Xcode eseguendo questo comando:
xcode-select --install
Installazione di Cocoapods
Cocoapods utilizza Ruby, che è installato per impostazione predefinita su macOS.
Per installare cocoapods, esegui questo comando:
sudo gem install cocoapods Install TFLite Cocoapod
Vai al file .xcworkspace
Il resto di questo codelab deve essere eseguito direttamente in macOS, quindi chiudi Docker ora (Ctrl-D chiuderà Docker).
Utilizza questo comando per installare TensorFlow Lite e creare il file .xcworkspace
utilizzando cocoapods:
pod install --project-directory=ios/tflite/
Apri il progetto con Xcode. Puoi aprire il progetto tramite riga di comando o UI.
Per aprire il progetto dalla riga di comando, esegui questo comando:
open ios/tflite/tflite_photos_example.xcworkspace
Per aprire il progetto tramite l'interfaccia utente, avvia Xcode e seleziona il pulsante "Apri un altro progetto".
Dopo aver aperto il progetto, vai al file .xcworkspace
(non al file .xcproject
).
Esegui l'app originale
L'app è un semplice esempio su cui viene eseguito un modello di riconoscimento delle immagini nel simulatore iOS. L'app legge dalla raccolta fotografica, poiché il simulatore non supporta l'input della fotocamera.
Prima di inserire il tuo modello personalizzato, testa la versione di base dell'app che utilizza la base "mobilenet" addestrata sulle 1000 categorie ImageNet.
Per avviare l'app nel simulatore, seleziona il pulsante di riproduzione nell'angolo in alto a destra della finestra di Xcode.
Il pulsante "Foto successiva" fa avanzare tra le foto sul dispositivo.
Puoi aggiungere foto alla raccolta fotografica del dispositivo trascinandole e rilasciandole nella finestra del simulatore.
Il risultato dovrebbe visualizzare annotazioni simili a questa immagine:
Esegui l'app personalizzata
La configurazione originale dell'app classifica le immagini in una delle 1000 classi ImageNet, utilizzando il MobileNet standard.
Modifica l'app in modo che utilizzi il modello riaddestrato con categorie di immagini personalizzate.
Aggiungi i file del modello al progetto
Il progetto dimostrativo è configurato per cercare file graph.lite
e labels.txt
nella directory android/tflite/app/src/main/assets/
.
Per sostituire questi due file con le tue versioni, esegui questo comando:
cp tf_files/optimized_graph.lite ios/tflite/data/graph.lite cp tf_files/retrained_labels.txt ios/tflite/data/labels.txt
Esegui l'app
Per riavviare l'app nel simulatore, seleziona il pulsante di riproduzione nell'angolo in alto a destra della finestra di Xcode.
Per testare le modifiche, aggiungi file immagine dalla directory flower_photos/
e ottieni previsioni.
I risultati dovrebbero essere simili a questi:
Tieni presente che le immagini predefinite non riguardano fiori.
Per provare davvero il modello, aggiungi alcune delle immagini dei dati di addestramento scaricate in precedenza oppure scarica alcune immagini da una ricerca Google da usare per la previsione.
Come funziona?
Ora che l'app è in esecuzione, controlla il codice specifico di TensorFlow Lite.
Pod TensorFlowLite
Questa app utilizza un TFLite Cocoapod precompilato. Il Podfile include cocoapod nel progetto:
platform :ios, '8.0' inhibit_all_warnings! target 'tflite_photos_example' pod 'TensorFlowLite'
Il codice che interfaccia con TFLite è tutto contenuto nel
file CameraExampleViewController.mm
.
Configurazione
Il primo blocco di interesse (dopo le importazioni necessarie) è il metodo viewDidLoad
:
CameraExampleViewController.mm
#include "tensorflow/contrib/lite/kernels/register.h" #include "tensorflow/contrib/lite/model.h" #include "tensorflow/contrib/lite/string_util.h" #include "tensorflow/contrib/lite/tools/mutable_op_resolver.h" ... - (void)viewDidLoad { [super viewDidLoad]; labelLayers = [[NSMutableArray alloc] init]; NSString* graph_path = FilePathForResourceName(model_file_name, model_file_type); model = tflite::FlatBufferModel::BuildFromFile([graph_path UTF8String]); if (!model) { LOG(FATAL) << "Failed to mmap model " << graph_path; } LOG(INFO) << "Loaded model " << graph_path; model->error_reporter(); LOG(INFO) << "resolved reporter"; ...
La riga chiave in questa prima metà del metodo è la riga model = tflite::FlatBufferModel::BuildFromFile([graph_path UTF8String]);
.
Questo codice crea un FlatBufferModel dal file del grafico.
Un FlatBuffer è una struttura di dati mappabile in memoria. Queste sono una funzionalità chiave TFLite in quanto consentono al sistema di gestire meglio la memoria utilizzata dal modello. Il sistema può trasferire in modo trasparente parti del modello all'interno o all'esterno della memoria in base alle esigenze.
La seconda parte del metodo crea un interprete per il modello, collegando le implementazioni dell'operatore alla struttura dei dati del grafico che abbiamo caricato in precedenza:
CameraExampleViewController.mm
- (void)viewDidLoad { ... tflite::ops::builtin::BuiltinOpResolver resolver; LoadLabels(labels_file_name, labels_file_type, &labels); tflite::InterpreterBuilder(*model, resolver)(&interpreter); if (!interpreter) { LOG(FATAL) << "Failed to construct interpreter"; } if (interpreter->AllocateTensors() != kTfLiteOk) { LOG(FATAL) << "Failed to allocate tensors!"; } [self attachPreviewLayer]; }
Se conosci TensorFlow in Python, grosso modo equivale
alla creazione di un elemento tf.Session()
.
Esegui il modello
Il metodo UpdatePhoto
gestisce tutti i dettagli relativi al recupero della foto successiva, all'aggiornamento della finestra di anteprima e all'esecuzione del modello sulla foto.
CameraExampleViewController.mm
- (void)UpdatePhoto{ PHAsset* asset; if (photos==nil || photos_index >= photos.count){ [self updatePhotosLibrary]; photos_index=0; } if (photos.count){ asset = photos[photos_index]; photos_index += 1; input_image = [self convertImageFromAsset:asset targetSize:CGSizeMake(wanted_input_width, wanted_input_height) mode:PHImageContentModeAspectFill]; display_image = [self convertImageFromAsset:asset targetSize:CGSizeMake(asset.pixelWidth,asset.pixelHeight) mode:PHImageContentModeAspectFit]; [self DrawImage]; } if (input_image != nil){ image_data image = [self CGImageToPixels:input_image.CGImage]; [self inputImageToModel:image]; [self runModel]; } }
Sono le ultime tre righe che ci interessano.
Il metodo CGImageToPixels
converte il valore CGImage restituito dalla libreria Foto di iOS in una struttura semplice contenente larghezza, altezza, canali e dati dei pixel.
typedef struct {
int width;
int height;
int channels;
std::vector<uint8_t> data;
} image_data;
Il metodo inputImageToModel
gestisce l'inserimento dell'immagine nella memoria dell'interprete. Ciò include il ridimensionamento dell'immagine e la regolazione dei valori dei pixel
in modo che corrispondano a quanto previsto dal modello.
CameraExampleViewController.mm
- (void)inputImageToModel:(image_data)image{
float* out = interpreter->typed_input_tensor<float>(0);
const float input_mean = 127.5f;
const float input_std = 127.5f;
assert(image.channels >= wanted_input_channels);
uint8_t* in = image.data.data();
for (int y = 0; y < wanted_input_height; ++y) {
const int in_y = (y * image.height) / wanted_input_height;
uint8_t* in_row = in + (in_y * image.width * image.channels);
float* out_row = out + (y * wanted_input_width * wanted_input_channels);
for (int x = 0; x < wanted_input_width; ++x) {
const int in_x = (x * image.width) / wanted_input_width;
uint8_t* in_pixel = in_row + (in_x * image.channels);
float* out_pixel = out_row + (x * wanted_input_channels);
for (int c = 0; c < wanted_input_channels; ++c) {
out_pixel[c] = (in_pixel[c] - input_mean) / input_std;
}
}
}
}
Sappiamo che il modello ha un solo input, quindi la riga float* out = interpreter->typed_input_tensor<float>(0);
chiede all'interprete di un puntatore alla memoria per l'input 0. Il resto del metodo gestisce l'aritmetica del puntatore e il ridimensionamento dei pixel per copiare i dati nell'array di input.
Infine, il metodo runModel
esegue il modello:
CameraExampleViewController.mm
- (void)runModel { double startTimestamp = [[NSDate new] timeIntervalSince1970]; if (interpreter->Invoke() != kTfLiteOk) { LOG(FATAL) << "Failed to invoke!"; } double endTimestamp = [[NSDate new] timeIntervalSince1970]; total_latency += (endTimestamp - startTimestamp); total_count += 1; NSLog(@"Time: %.4lf, avg: %.4lf, count: %d", endTimestamp - startTimestamp, total_latency / total_count, total_count); ... }
Il prossimo runModel
rilegge i risultati. Per farlo, viene chiesto all'interprete
un puntatore ai dati dell'array di output. L'output è un semplice array di
float. Il metodo GetTopN
gestisce l'estrazione dei primi 5 risultati (utilizzando una coda di priorità).
CameraExampleViewController.mm
- (void)runModel {
...
const int output_size = (int)labels.size();
const int kNumResults = 5;
const float kThreshold = 0.1f;
std::vector<std::pair<float, int>> top_results;
float* output = interpreter->typed_output_tensor<float>(0);
GetTopN(output, output_size, kNumResults, kThreshold, &top_results);
...
}
Le righe successive convertono semplicemente le prime 5 coppie (probability, class_id)
in coppie (probability, label)
e poi passano il risultato, in modo asincrono, al metodo setPredictionValues
, che aggiorna
il report sullo schermo:
CameraExampleViewController.mm
- (void)runModel {
...
std::vector<std::pair<float, std::string>> newValues;
for (const auto& result : top_results) {
std::pair<float, std::string> item;
item.first = result.first;
item.second = labels[result.second];
newValues.push_back(item);
}
dispatch_async(dispatch_get_main_queue(), ^(void) {
[self setPredictionValues:newValues];
});
}
Passaggio successivo
Hai completato la procedura dettagliata di un'app di classificazione dei fiori per iOS utilizzando un modello Edge. Hai utilizzato un modello Edge Tensorflow Lite addestrato per testare un'app di classificazione delle immagini prima di apportarvi modifiche e ottenere annotazioni di esempio. Hai quindi esaminato il codice specifico di TensorFlow Lite per comprenderne la funzionalità.
Le seguenti risorse possono aiutarti a continuare a conoscere i modelli TensorFlow e AutoML Vision Edge:
- Scopri di più su TFLite nella documentazione ufficiale e nel repository di codice.
- Prova la versione della fotocamera di questa app demo, che utilizza una versione quantizzata del modello. Questo fornisce la stessa potenza in un pacchetto più piccolo ed efficiente.
- Prova altri modelli TFLite ready per casi d'uso specifici.
- Scopri di più su TensorFlow in generale nella documentazione di introduzione di TensorFlow.