O que você criará
Neste tutorial, você fará o download de um modelo personalizado do TensorFlow Lite exportado do AutoML Vision Edge. Depois, você executará um app pré-desenvolvido para iOS que usará o modelo para identificar imagens de flores.
Objetivos
Neste tutorial de apresentação completo, você usará o código para fazer o seguinte:
- Executar um modelo pré-treinado em um app para iOS usando o intérprete do TFLite.
Antes de começar
Instalar o TensorFlow
Antes de começar o tutorial, você precisa instalar vários softwares:
- instale o tensorflow versão 1.7
- Instale o PILLOW
Se você tem uma instalação Python funcional, execute os seguintes comandos para fazer o download desse software:
pip install --upgrade "tensorflow==1.7.*" pip install PILLOW
Clonar o repositório Git
Usando a linha de comando, clone o repositório Git com o seguinte comando:
git clone https://github.com/googlecodelabs/tensorflow-for-poets-2
Acesse o diretório do clone local do repositório
(diretório tensorflow-for-poets-2
). Execute todos os exemplo de código a seguir
deste diretório:
cd tensorflow-for-poets-2
Configurar o app para iOS
O app de demonstração para iOS requer várias outras ferramentas:
- Xcode
- Ferramentas de linha de comando do Xcode
- Cocoapods
Fazer o download do Xcode
Use este link para fazer o download do Xcode no seu computador.
Instalar as ferramentas de linha de comando do Xcode
Para instalar as ferramentas de linha de comando do Xcode, execute o comando a seguir:
xcode-select --install
Instalar o Cocoapods
O Cocoapods usa o Ruby, que é instalado por padrão no macOS.
Para instalar o Cocoapods, execute o comando a seguir:
sudo gem install cocoapods Install TFLite Cocoapod
Navegar até o arquivo .xcworkspace
É necessário executar o restante deste codelab diretamente no macOS. Portanto, feche o Docker agora (use Ctrl + D).
Execute o comando a seguir para instalar o TensorFlow Lite e criar o arquivo
.xcworkspace
usando o Cocoapods:
pod install --project-directory=ios/tflite/
Abra o projeto com o Xcode. É possível fazer isso usando a linha de comando ou pela IU.
Para abrir o projeto por meio da linha de comando, execute o comando a seguir:
open ios/tflite/tflite_photos_example.xcworkspace
Para abrir o projeto pela IU, inicie o Xcode e selecione o botão "Open another Project".
Após abrir o projeto, navegue até o arquivo .xcworkspace
,
e não até .xcproject
.
Executar o app original
O app é um exemplo simples que executa um modelo de reconhecimento de imagens no iOS Simulator. Ele lê a biblioteca de fotos porque o Simulator não é compatível com a entrada da câmera.
Antes de inserir o modelo personalizado, teste a versão de valor de referência do app que usa a MobileNet básica treinada nas mil categorias do ImageNet (em inglês).
Para iniciar o app no Simulator, selecione o botão de reprodução no canto superior direio da janela do Xcode.
Com o botão Next Photo, você avança pelas fotos no dispositivo.
Para adicionar imagens à biblioteca de fotos do dispositivo, basta arrastá-las e soltá-las na janela do Simulator.
O resultado exibirá anotações assim:
Executar o app personalizado
Com a configuração original do app, as imagens são incluídas em uma das mil classes do ImageNet através do MobileNet padrão.
Modifique o app para que ele use o modelo treinado novamente com as categorias de imagens personalizadas.
Adicionar arquivos de modelo ao projeto
O projeto de demonstração é configurado para procurar os arquivos graph.lite
e labels.txt
no diretório android/tflite/app/src/main/assets/
.
Para substituir esses dois arquivos por suas versões, execute o comando a seguir:
cp tf_files/optimized_graph.lite ios/tflite/data/graph.lite cp tf_files/retrained_labels.txt ios/tflite/data/labels.txt
Executar o app
Para reiniciar o app no Simulator, selecione o botão de reprodução no canto superior direito da janela do Xcode.
Para testar as modificações, adicione arquivos de imagem do diretório flower_photos/
e receba previsões.
Os resultados serão assim:
As imagens padrão não são de flores.
Para realmente testar o modelo, adicione algumas das imagens de dados de treinamento salvas anteriormente ou faça o download das imagens de uma pesquisa do Google para usar na previsão.
Como funciona?
Agora que o app está em execução, analise o código específico do TensorFlow Lite.
Pod do TensorFlowLite
Neste app, um CocoaPod pré-compilado do TFLite é usado. O Podfile inclui o CocoaPod no projeto:
platform :ios, '8.0' inhibit_all_warnings! target 'tflite_photos_example' pod 'TensorFlowLite'
O código de interface do TFLite está todo no arquivo
CameraExampleViewController.mm
.
Configurar
O primeiro bloco relevante após as importações necessárias é o
método 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"; ...
A linha principal da primeira metade do método é a
model = tflite::FlatBufferModel::BuildFromFile([graph_path UTF8String]);
.
Com esse código, você cria um FlatBufferModel a partir do arquivo de gráfico.
Um FlatBuffer é uma estrutura de dados mapeável da memória. Esse é um dos recursos principais do TFLite, já que ele permite que o sistema gerencie melhor a memória usada pelo modelo. O sistema pode trocar partes do modelo de forma transparente para dentro ou fora da memória conforme necessário.
A segunda parte do método cria um intérprete do modelo, anexando implementações de operação à estrutura de dados do gráfico carregada anteriormente:
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 você tiver familiaridade com o TensorFlow em Python, isso equivale
a criar um tf.Session()
.
Executar o modelo
Com o método UpdatePhoto
, você processa todos os detalhes da busca da próxima foto,
da atualização da janela de visualização e da execução do modelo na 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]; } }
As linhas relevantes são as três últimas.
Com o método CGImageToPixels
, você converte a CGImage retornada pela biblioteca de fotos do iOS
em uma estrutura simples que contém os dados de largura, altura, canais e
pixels.
typedef struct {
int width;
int height;
int channels;
std::vector<uint8_t> data;
} image_data;
Com o método inputImageToModel
, você processa a inserção da imagem na
memória do intérprete. Isso inclui o redimensionamento e o ajuste dos valores de pixel para
corresponder ao que é esperado pelo modelo.
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;
}
}
}
}
Como o modelo tem apenas uma entrada, a linha float* out = interpreter->typed_input_tensor<float>(0);
solicita ao
intérprete um ponteiro para a memória na entrada 0 (zero). O restante do método
processa o dimensionamento aritmético e de pixels do ponteiro para copiar os dados para
essa matriz de entrada.
Por último, com o método runModel
, você executa o modelo:
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); ... }
Depois, runModel
lê os resultados. Para isso, ele solicita ao intérprete
um ponteiro para os dados de matriz da saída. A saída é uma matriz simples de
flutuantes. Com o método GetTopN
, você processa a extração dos cinco principais resultados
usando uma fila de prioridade.
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);
...
}
As próximas linhas convertem os cinco principais pares de (probability, class_id)
em pares (probability, label)
e, em seguida, transmitem esse resultado
de forma assíncrona ao método setPredictionValues
, que atualiza o
relatório exibido:
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];
});
}
A seguir
Você concluiu o tutorial de um app de classificação de flores do iOS usando um modelo do Edge. Você usou um modelo treinado do Tensorflow Lite para Edge para testar um app de classificação de imagens antes de modificá-las e de receber amostras de anotações. Em seguida, você examinou o código específico do TensorFlow Lite para entender a funcionalidade subjacente.
Use os recursos a seguir para saber mais sobre os modelos do TensorFlow e o AutoML Vision Edge:
- Saiba mais sobre o TFLite na documentação oficial e no repositório de códigos (links em inglês).
- Teste a versão da câmera deste app de demonstração (em inglês), que usa uma versão quantizada do modelo. Ela fornece a mesma funcionalidade em um pacote menor e mais eficiente.
- Tente outros modelos prontos do TFLite para casos de uso específicos.
- Veja mais informações gerais sobre o TensorFlow na documentação de primeiros passos (em inglês) relacionada.