مدل های تنظیم دقیق برای تشخیص بیماری های گیاهی

مشاهده در TensorFlow.org در Google Colab اجرا شود در GitHub مشاهده کنید دانلود دفترچه یادداشت مدل های TF Hub را ببینید

این نوت بوک به شما نشان می دهد که چگونه مدل های CropNet را از TensorFlow Hub روی مجموعه داده ای از TFDS یا مجموعه داده های تشخیص بیماری محصول خودتان تنظیم کنید.

شما:

  • مجموعه داده کاساوا TFDS یا داده های خود را بارگیری کنید
  • داده ها را با مثال های ناشناخته (منفی) غنی کنید تا مدل قوی تری به دست آورید
  • افزایش تصویر را روی داده ها اعمال کنید
  • یک مدل CropNet را از TF Hub بارگیری و تنظیم دقیق کنید
  • یک مدل TFLite را صادر کنید، آماده استقرار در برنامه شما با Task Library ، MLKit یا TFLite به طور مستقیم

واردات و وابستگی ها

قبل از شروع، باید برخی از وابستگی‌های مورد نیاز مانند Model Maker و آخرین نسخه TensorFlow Datasets را نصب کنید.

pip install --use-deprecated=legacy-resolver tflite-model-maker
pip install -U tensorflow-datasets
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
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_addons/utils/ensure_tf_install.py:67: UserWarning: Tensorflow Addons supports using Python ops for all Tensorflow versions above or equal to 2.5.0 and strictly below 2.8.0 (nightly versions are not supported). 
 The versions of TensorFlow you are currently using is 2.8.0-rc1 and is not supported. 
Some things might work, some things might not.
If you were to encounter a bug, do not file an issue.
If you want to make sure you're using a tested and supported configuration, either change the TensorFlow version or the TensorFlow Addons's version. 
You can find the compatibility matrix in TensorFlow Addon's readme:
https://github.com/tensorflow/addons
  UserWarning,
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/numba/core/errors.py:154: UserWarning: Insufficiently recent colorama version found. Numba requires colorama >= 0.3.9
  warnings.warn(msg)

یک مجموعه داده TFDS را برای تنظیم دقیق بارگذاری کنید

اجازه می‌دهیم از مجموعه داده‌های بیماری برگ Cassava در دسترس عموم از 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

یا به طور متناوب داده های خود را برای تنظیم دقیق بارگیری کنید

به جای استفاده از مجموعه داده TFDS، می توانید بر روی داده های خود نیز آموزش دهید. این قطعه کد نحوه بارگیری مجموعه داده سفارشی خود را نشان می دهد. برای مشاهده ساختار پشتیبانی شده داده ها به این پیوند مراجعه کنید. یک مثال در اینجا با استفاده از مجموعه داده بیماری برگ Cassava در دسترس عموم ارائه شده است.

# 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)

نمونه هایی از تقسیم قطار را تجسم کنید

بیایید به چند نمونه از مجموعه داده از جمله شناسه کلاس و نام کلاس برای نمونه های تصویر و برچسب های آنها نگاهی بیندازیم.

_ = tfds.show_examples(ds_train, ds_info)

png

تصاویر را برای استفاده به عنوان نمونه های ناشناخته از مجموعه داده های TFDS اضافه کنید

نمونه های ناشناخته (منفی) اضافی را به مجموعه داده آموزشی اضافه کنید و یک شماره برچسب کلاس ناشناخته جدید به آنها اختصاص دهید. هدف این است که مدلی داشته باشیم که وقتی در عمل از آن استفاده می‌شود (مثلاً در میدان)، گزینه پیش‌بینی «ناشناخته» را در صورت مشاهده چیز غیرمنتظره داشته باشد.

در زیر می‌توانید فهرستی از مجموعه داده‌ها را مشاهده کنید که برای نمونه‌برداری از تصاویر ناشناخته اضافی استفاده می‌شوند. این شامل 3 مجموعه داده کاملاً متفاوت برای افزایش تنوع است. یکی از آنها مجموعه داده بیماری برگ لوبیا است، به طوری که مدل در معرض گیاهان بیمار دیگری غیر از کاساوا قرار دارد.

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,
}]

مجموعه داده های ناشناخته نیز از 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.'))
Added 16968 negative examples.Training dataset has now 22624 examples in total.

افزایش ها را اعمال کنید

برای همه تصاویر، برای اینکه آنها را متنوع تر کنید، مقداری تقویت مانند تغییرات در موارد زیر را اعمال خواهید کرد:

  • روشنایی
  • تضاد
  • اشباع
  • رنگ
  • برش

این نوع تقویت‌ها به قوی‌تر شدن مدل در برابر تغییرات ورودی‌های تصویر کمک می‌کنند.

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)

برای اعمال افزایش، از روش map از کلاس 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)
INFO:tensorflow:Use default resize_bicubic.
INFO:tensorflow:Use default resize_bicubic.
INFO:tensorflow:Use customized resize method bilinear
INFO:tensorflow:Use customized resize method bilinear

داده ها را در قالب مدل ساز دوستانه قرار دهید

برای استفاده از این مجموعه داده ها با Model Maker، باید در کلاس 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)

آموزش اجرا کنید

TensorFlow Hub چندین مدل برای آموزش انتقال دارد.

در اینجا می‌توانید یکی را انتخاب کنید و همچنین می‌توانید با آزمایش‌های دیگر ادامه دهید تا نتایج بهتری بگیرید.

اگر می خواهید مدل های بیشتری را امتحان کنید، می توانید آنها را از این مجموعه اضافه کنید.

یک مدل پایه انتخاب کنید

برای تنظیم دقیق مدل، از Model Maker استفاده خواهید کرد. این راه حل کلی را آسان تر می کند زیرا پس از آموزش مدل، آن را به TFLite نیز تبدیل می کند.

Model Maker باعث می‌شود که این تبدیل به بهترین شکل ممکن و با تمام اطلاعات لازم باشد تا بعداً به راحتی مدل را روی دستگاه مستقر کنید.

مشخصات مدل این است که چگونه به Model Maker می‌گویید از کدام مدل پایه می‌خواهید استفاده کنید.

image_model_spec = ModelSpec(uri=model_handle)

یکی از جزئیات مهم در اینجا تنظیم train_whole_model است که باعث می‌شود مدل پایه در طول آموزش به خوبی تنظیم شود. این روند را کندتر می کند اما مدل نهایی دقت بالاتری دارد. تنظیم در هم shuffle اطمینان حاصل می کند که مدل داده ها را به ترتیب تصادفی می بیند که بهترین روش برای یادگیری مدل است.

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)
INFO:tensorflow:Retraining the models...
INFO:tensorflow:Retraining the models...
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 hub_keras_layer_v1v2 (HubKe  (None, 1280)             4226432   
 rasLayerV1V2)                                                   
                                                                 
 dropout (Dropout)           (None, 1280)              0         
                                                                 
 dense (Dense)               (None, 6)                 7686      
                                                                 
=================================================================
Total params: 4,234,118
Trainable params: 4,209,718
Non-trainable params: 24,400
_________________________________________________________________
None
Epoch 1/5
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/keras/optimizer_v2/gradient_descent.py:102: UserWarning: The `lr` argument is deprecated, use `learning_rate` instead.
  super(SGD, self).__init__(name, **kwargs)
176/176 [==============================] - 120s 488ms/step - loss: 0.8874 - accuracy: 0.9148 - val_loss: 1.1721 - val_accuracy: 0.7935
Epoch 2/5
176/176 [==============================] - 84s 444ms/step - loss: 0.7907 - accuracy: 0.9532 - val_loss: 1.0761 - val_accuracy: 0.8100
Epoch 3/5
176/176 [==============================] - 85s 441ms/step - loss: 0.7743 - accuracy: 0.9582 - val_loss: 1.0305 - val_accuracy: 0.8444
Epoch 4/5
176/176 [==============================] - 79s 409ms/step - loss: 0.7653 - accuracy: 0.9611 - val_loss: 1.0166 - val_accuracy: 0.8422
Epoch 5/5
176/176 [==============================] - 75s 402ms/step - loss: 0.7534 - accuracy: 0.9665 - val_loss: 0.9988 - val_accuracy: 0.8555

ارزیابی مدل در تقسیم آزمون

model.evaluate(test_data)
59/59 [==============================] - 10s 81ms/step - loss: 0.9956 - accuracy: 0.8594
[0.9956456422805786, 0.8594164252281189]

برای درک بهتر از مدل تنظیم شده، خوب است که ماتریس سردرگمی را تجزیه و تحلیل کنید. این نشان می دهد که چند بار یک کلاس به عنوان کلاس دیگر پیش بینی می شود.

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)

png

ارزیابی مدل بر روی داده های تست ناشناخته

در این ارزیابی ما انتظار داریم که دقت مدل تقریباً 1 باشد. تمام تصاویری که مدل روی آنها آزمایش می‌شود به مجموعه داده‌های معمولی مرتبط نیستند و از این رو انتظار داریم که مدل برچسب کلاس "ناشناخته" را پیش‌بینی کند.

model.evaluate(unknown_test_data)
259/259 [==============================] - 36s 127ms/step - loss: 0.6777 - accuracy: 0.9996
[0.677702784538269, 0.9996375441551208]

ماتریس سردرگمی را چاپ کنید.

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)

png

مدل را به عنوان TFLite و SavedModel صادر کنید

اکنون می‌توانیم مدل‌های آموزش‌دیده را در قالب‌های TFLite و SavedModel برای استقرار روی دستگاه و استفاده برای استنتاج در TensorFlow صادر کنیم.

tflite_filename = f'{TFLITE_NAME_PREFIX}_model_{model_name}.tflite'
model.export(export_dir='.', tflite_filename=tflite_filename)
2022-01-26 12:25:57.742415: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: /tmp/tmppliqmyki/assets
INFO:tensorflow:Assets written to: /tmp/tmppliqmyki/assets
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/lite/python/convert.py:746: UserWarning: Statistics for quantized inputs were expected, but not specified; continuing anyway.
  warnings.warn("Statistics for quantized inputs were expected, but not "
2022-01-26 12:26:07.247752: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:357] Ignored output_format.
2022-01-26 12:26:07.247806: W tensorflow/compiler/mlir/lite/python/tf_tfl_flatbuffer_helpers.cc:360] Ignored drop_control_dependency.
INFO:tensorflow:Label file is inside the TFLite model with metadata.
fully_quantize: 0, inference_type: 6, input_inference_type: 3, output_inference_type: 3
INFO:tensorflow:Label file is inside the TFLite model with metadata.
INFO:tensorflow:Saving labels in /tmp/tmp_k_gr9mu/labels.txt
INFO:tensorflow:Saving labels in /tmp/tmp_k_gr9mu/labels.txt
INFO:tensorflow:TensorFlow Lite model exported successfully: ./cassava_model_mobilenet_v3_large_100_224.tflite
INFO:tensorflow:TensorFlow Lite model exported successfully: ./cassava_model_mobilenet_v3_large_100_224.tflite
# Export saved model version.
model.export(export_dir='.', export_format=ExportFormat.SAVED_MODEL)
INFO:tensorflow:Assets written to: ./saved_model/assets
INFO:tensorflow:Assets written to: ./saved_model/assets

مراحل بعدی

مدلی که شما به تازگی آموزش داده اید را می توان در دستگاه های تلفن همراه استفاده کرد و حتی در این زمینه مستقر کرد!

برای دانلود مدل، روی نماد پوشه منوی Files در سمت چپ کولب کلیک کنید و گزینه دانلود را انتخاب کنید.

تکنیک مشابهی که در اینجا استفاده می‌شود می‌تواند برای سایر وظایف بیماری‌های گیاهی که ممکن است برای موارد استفاده شما یا هر نوع کار طبقه‌بندی تصویر دیگری مناسب‌تر باشد، اعمال شود. اگر می‌خواهید یک برنامه Android را پیگیری و اجرا کنید، می‌توانید این راهنمای شروع سریع Android را ادامه دهید.