Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/pt-br/hub/tutorials/cropnet_on_device.ipynb
25118 views
Kernel: Python 3

Licensed under the Apache License, Version 2.0 (the "License");

#@title Copyright 2021 The TensorFlow Hub Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ==============================================================================

Ajuste fino de modelos para detecção de doenças em plantas

Este notebook mostra como fazer o ajuste fino de modelos CropNet do TensorFlow Hub para um dataset do TFDS ou seu próprio dataset de detecção de doenças em plantações.

Você vai:

  • Carregar o dataset cassava do TFDS ou seus próprios dados

  • Enriquecer os dados com exemplos desconhecidos (negativos) para obter um modelo mais robusto

  • Aplicar ampliação de imagem aos dados

  • Carregar e fazer ajustes finos de um modelo CropNet do TF Hub

  • Exportar um modelo do TF Lite, pronto para ser implantado em seu aplicativo com a Biblioteca Task, o MLKit ou o TF Lite diretamente

Importações e dependências

Antes de começar, você precisará instalar algumas das dependências que serão necessárias, como Model Maker e a última versão do TensorFlow Datasets.

!sudo apt install -q libportaudio2 ## image_classifier library requires numpy <= 1.23.5 !pip install "numpy<=1.23.5" !pip install --use-deprecated=legacy-resolver tflite-model-maker-nightly !pip install -U tensorflow-datasets ## scann library requires tensorflow < 2.9.0 !pip install "tensorflow<2.9.0" !pip install "tensorflow-datasets~=4.8.0" # protobuf>=3.12.2 !pip install tensorflow-metadata~=1.10.0 # protobuf>=3.13 ## tensorflowjs requires packaging < 20.10 !pip install "packaging<20.10"
import matplotlib.pyplot as plt import os import seaborn as sns import tensorflow as tf import tensorflow_datasets as tfds from tensorflow_examples.lite.model_maker.core.export_format import ExportFormat from tensorflow_examples.lite.model_maker.core.task import image_preprocessing from tflite_model_maker import image_classifier from tflite_model_maker import ImageClassifierDataLoader from tflite_model_maker.image_classifier import ModelSpec

Carregue um dataset do TFDS para fazer os ajustes finos nele

Vamos usar o dataset Cassava Leaf Disease, disponível publicamente no TFDS.

tfds_name = 'cassava' (ds_train, ds_validation, ds_test), ds_info = tfds.load( name=tfds_name, split=['train', 'validation', 'test'], with_info=True, as_supervised=True) TFLITE_NAME_PREFIX = tfds_name

Ou você pode carregar seus próprios dados para fazer o ajuste fino neles

Em vez de usar um dataset do TFDS, você também pode treinar usando seus próprios dados. O trecho de código abaixo mostra como carregar seu próprio dataset personalizado. Confira a estrutura de dados compatível neste link. É fornecido um exemplo aqui usando o dataset Cassava Leaf Disease, disponível publicamente.

# data_root_dir = tf.keras.utils.get_file( # 'cassavaleafdata.zip', # 'https://storage.googleapis.com/emcassavadata/cassavaleafdata.zip', # extract=True) # data_root_dir = os.path.splitext(data_root_dir)[0] # Remove the .zip extension # builder = tfds.ImageFolder(data_root_dir) # ds_info = builder.info # ds_train = builder.as_dataset(split='train', as_supervised=True) # ds_validation = builder.as_dataset(split='validation', as_supervised=True) # ds_test = builder.as_dataset(split='test', as_supervised=True)

Visualize as amostras do dataset de treinamento

Vamos dar uma olhada em alguns exemplos do dataset, incluindo o ID e o nome de classes para as amostras de imagens e seus rótulos.

_ = tfds.show_examples(ds_train, ds_info)

Adicione imagens a serem usadas como exemplos desconhecidos pelos datasets do TFDS

Adicione exemplos desconhecidos (negativos) extras ao dataset de treinamento e atribua um novo número de rótulo de classe desconhecido a eles. O objetivo é ter um modelo que, ao ser usado na prática (ou seja, em campo), ele tenha a opção de prever "desconhecido" quando observar algo inesperado.

Abaixo você pode ver uma lista de datasets que serão usados para amostrar as imagens desconhecidas adicionais. Ela inclui três datasets totalmente diferentes para aumentar a diversidade. Um deles é um dataset de doenças em folhas de feijão para que o modelo seja exposto a plantas doentes que não sejam mandioca.

UNKNOWN_TFDS_DATASETS = [{ 'tfds_name': 'imagenet_v2/matched-frequency', 'train_split': 'test[:80%]', 'test_split': 'test[80%:]', 'num_examples_ratio_to_normal': 1.0, }, { 'tfds_name': 'oxford_flowers102', 'train_split': 'train', 'test_split': 'test', 'num_examples_ratio_to_normal': 1.0, }, { 'tfds_name': 'beans', 'train_split': 'train', 'test_split': 'test', 'num_examples_ratio_to_normal': 1.0, }]

Os datasets DESCONHECIDOS também são carregados a partir do TFDS.

# Load unknown datasets. weights = [ spec['num_examples_ratio_to_normal'] for spec in UNKNOWN_TFDS_DATASETS ] num_unknown_train_examples = sum( int(w * ds_train.cardinality().numpy()) for w in weights) ds_unknown_train = tf.data.Dataset.sample_from_datasets([ tfds.load( name=spec['tfds_name'], split=spec['train_split'], as_supervised=True).repeat(-1) for spec in UNKNOWN_TFDS_DATASETS ], weights).take(num_unknown_train_examples) ds_unknown_train = ds_unknown_train.apply( tf.data.experimental.assert_cardinality(num_unknown_train_examples)) ds_unknown_tests = [ tfds.load( name=spec['tfds_name'], split=spec['test_split'], as_supervised=True) for spec in UNKNOWN_TFDS_DATASETS ] ds_unknown_test = ds_unknown_tests[0] for ds in ds_unknown_tests[1:]: ds_unknown_test = ds_unknown_test.concatenate(ds) # All examples from the unknown datasets will get a new class label number. num_normal_classes = len(ds_info.features['label'].names) unknown_label_value = tf.convert_to_tensor(num_normal_classes, tf.int64) ds_unknown_train = ds_unknown_train.map(lambda image, _: (image, unknown_label_value)) ds_unknown_test = ds_unknown_test.map(lambda image, _: (image, unknown_label_value)) # Merge the normal train dataset with the unknown train dataset. weights = [ ds_train.cardinality().numpy(), ds_unknown_train.cardinality().numpy() ] ds_train_with_unknown = tf.data.Dataset.sample_from_datasets( [ds_train, ds_unknown_train], [float(w) for w in weights]) ds_train_with_unknown = ds_train_with_unknown.apply( tf.data.experimental.assert_cardinality(sum(weights))) print((f"Added {ds_unknown_train.cardinality().numpy()} negative examples." f"Training dataset has now {ds_train_with_unknown.cardinality().numpy()}" ' examples in total.'))

Aplique ampliações

Para deixar as imagens mais diversas, você aplicará algumas ampliações a todas elas, como mudanças dos seguintes aspectos:

  • Brilho

  • Contraste

  • Saturação

  • Matiz

  • Recorte

Esses tipos de ampliação ajudam a deixar o modelo mais robusto a variações das imagens de entrada.

def random_crop_and_random_augmentations_fn(image): # preprocess_for_train does random crop and resize internally. image = image_preprocessing.preprocess_for_train(image) image = tf.image.random_brightness(image, 0.2) image = tf.image.random_contrast(image, 0.5, 2.0) image = tf.image.random_saturation(image, 0.75, 1.25) image = tf.image.random_hue(image, 0.1) return image def random_crop_fn(image): # preprocess_for_train does random crop and resize internally. image = image_preprocessing.preprocess_for_train(image) return image def resize_and_center_crop_fn(image): image = tf.image.resize(image, (256, 256)) image = image[16:240, 16:240] return image no_augment_fn = lambda image: image train_augment_fn = lambda image, label: ( random_crop_and_random_augmentations_fn(image), label) eval_augment_fn = lambda image, label: (resize_and_center_crop_fn(image), label)

Para aplicar a ampliação, é utilizado o método map da classe Dataset.

ds_train_with_unknown = ds_train_with_unknown.map(train_augment_fn) ds_validation = ds_validation.map(eval_augment_fn) ds_test = ds_test.map(eval_augment_fn) ds_unknown_test = ds_unknown_test.map(eval_augment_fn)

Encapsule os dados no formato amigável do Model Maker

Para usar esse dataset com o Model Maker, ele precisa estar em uma classe de ImageClassifierDataLoader.

label_names = ds_info.features['label'].names + ['UNKNOWN'] train_data = ImageClassifierDataLoader(ds_train_with_unknown, ds_train_with_unknown.cardinality(), label_names) validation_data = ImageClassifierDataLoader(ds_validation, ds_validation.cardinality(), label_names) test_data = ImageClassifierDataLoader(ds_test, ds_test.cardinality(), label_names) unknown_test_data = ImageClassifierDataLoader(ds_unknown_test, ds_unknown_test.cardinality(), label_names)

Execute o treinamento

Há diversos modelos disponíveis para aprendizado por transferência no TensorFlow Hub.

Aqui, você pode escolher um e também pode continuar testando outros para tentar alcançar os melhores resultados.

Se você quiser testar ainda mais modelos, pode adicioná-los a partir desta coleção.

#@title Choose a base model model_name = 'mobilenet_v3_large_100_224' #@param ['cropnet_cassava', 'cropnet_concat', 'cropnet_imagenet', 'mobilenet_v3_large_100_224'] map_model_name = { 'cropnet_cassava': 'https://tfhub.dev/google/cropnet/feature_vector/cassava_disease_V1/1', 'cropnet_concat': 'https://tfhub.dev/google/cropnet/feature_vector/concat/1', 'cropnet_imagenet': 'https://tfhub.dev/google/cropnet/feature_vector/imagenet/1', 'mobilenet_v3_large_100_224': 'https://tfhub.dev/google/imagenet/mobilenet_v3_large_100_224/feature_vector/5', } model_handle = map_model_name[model_name]

Para fazer os ajustes finos do modelo, você usará o Model Maker, o que facilita a solução geral após o treinamento do modelo. Além disso, ele será convertido para o TF Lite.

O Model Maker deixa essa conversão a melhor possível e com todas as informações necessárias para implantar o modelo em dispositivos posteriormente com facilidade.

A especificação do modelo é usada para indicar ao Model Maker qual modelo base você deseja utilizar.

image_model_spec = ModelSpec(uri=model_handle)

Um detalhe importante é a definição de train_whole_model, que fará os ajustes finos do modelo base durante o treinamento. Isso deixa o processo mais lento, mas o modelo final terá uma exatidão maior. Definir shuffle também vai garantir que o modelo observe os dados em uma ordem embaralhada de forma aleatória, que é a prática recomendada em aprendizado de modelos.

model = image_classifier.create( train_data, model_spec=image_model_spec, batch_size=128, learning_rate=0.03, epochs=5, shuffle=True, train_whole_model=True, validation_data=validation_data)

Avalie o modelo com o dataset de teste

model.evaluate(test_data)

Para compreender ainda melhor o modelo ajustado, é uma boa ideia analisar a matriz de confusão, que mostrará com que frequência uma classe é prevista como outra.

def predict_class_label_number(dataset): """Runs inference and returns predictions as class label numbers.""" rev_label_names = {l: i for i, l in enumerate(label_names)} return [ rev_label_names[o[0][0]] for o in model.predict_top_k(dataset, batch_size=128) ] def show_confusion_matrix(cm, labels): plt.figure(figsize=(10, 8)) sns.heatmap(cm, xticklabels=labels, yticklabels=labels, annot=True, fmt='g') plt.xlabel('Prediction') plt.ylabel('Label') plt.show()
confusion_mtx = tf.math.confusion_matrix( list(ds_test.map(lambda x, y: y)), predict_class_label_number(test_data), num_classes=len(label_names)) show_confusion_matrix(confusion_mtx, label_names)

Avalie o modelo com dados de teste desconhecidos

Nesta avaliação, esperamos que o modelo tenha uma exatidão de quase 1. Todas as imagens usadas para teste do modelo não estão relacionadas ao dataset normal e, portanto, esperamos que o modelo preveja o rótulo de classe "Desconhecido".

model.evaluate(unknown_test_data)

Exiba a matriz de confusão via print.

unknown_confusion_mtx = tf.math.confusion_matrix( list(ds_unknown_test.map(lambda x, y: y)), predict_class_label_number(unknown_test_data), num_classes=len(label_names)) show_confusion_matrix(unknown_confusion_mtx, label_names)

Exporte o modelo como TF Lite e SavedModel

Agora podemos exportar os modelos treinados para os formatos TF Lite e SavedModel para implantação em dispositivos e uso para inferência no TensorFlow.

tflite_filename = f'{TFLITE_NAME_PREFIX}_model_{model_name}.tflite' model.export(export_dir='.', tflite_filename=tflite_filename)
# Export saved model version. model.export(export_dir='.', export_format=ExportFormat.SAVED_MODEL)

Próximos passos

O modelo que você acabou de treinar pode ser usado em dispositivos móveis e até mesmo implantado em campo!

Para baixar o modelo, clique no ícone da pasta no menu Arquivos na parte direita do Colab e escolha a opção de download.

A mesma técnica usada aqui poderia ser aplicada a outras tarefas de doenças em plantas que podem ser mais adequadas para seu caso de uso ou para qualquer outro tipo de tarefa de classificação de imagens. Se você quiser se aprofundar e implantar em um aplicativo para Android, confira este Guia de início rápido para Android.