انتقال یادگیری و تنظیم دقیق

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

برپایی

import numpy as np
import tensorflow as tf
from tensorflow import keras

معرفی

آموزش انتقال شامل گرفتن ویژگی های به دست بر روی یک مشکل، و استفاده از آنها را در یک جدید، مشکل مشابه. به عنوان مثال، ویژگی‌های مدلی که شناسایی راکون‌ها را آموخته است، ممکن است برای راه‌اندازی مدلی برای شناسایی تانوکی‌ها مفید باشد.

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

رایج ترین تجسم یادگیری انتقالی در زمینه یادگیری عمیق، گردش کار زیر است:

  1. لایه ها را از یک مدل آموزش دیده قبلی بگیرید.
  2. آنها را فریز کنید تا از تخریب هر یک از اطلاعات موجود در دوره های آموزشی بعدی جلوگیری کنید.
  3. چند لایه جدید و قابل آموزش روی لایه های منجمد اضافه کنید. آنها یاد خواهند گرفت که ویژگی های قدیمی را به پیش بینی در یک مجموعه داده جدید تبدیل کنند.
  4. لایه های جدید در مجموعه داده خود را آموزش دهید.

تاریخ و زمان آخرین، مرحله اختیاری، ریز تنظیم، که متشکل از آزاد کردن کل مدل بالا به دست آمده (یا بخشی از آن)، و دوباره آموزش آن را در داده های جدید با نرخ یادگیری بسیار کم است. این به طور بالقوه می تواند با تطبیق تدریجی ویژگی های از پیش آموزش دیده با داده های جدید، به پیشرفت های معناداری دست یابد.

اول، ما را بیش از Keras رفتن trainable API در جزئیات، که زیربنای ترین آموزش انتقال و ریز تنظیم گردش.

سپس، با استفاده از یک مدل از قبل آموزش‌دیده شده در مجموعه داده ImageNet، و آموزش مجدد آن در مجموعه داده‌های طبقه‌بندی «گربه‌ها در مقابل سگ‌ها» Kaggle، گردش کار معمولی را نشان می‌دهیم.

این است که از اقتباس یادگیری عمیق با پایتون و 2016 پست وبلاگ "ساخت مدل های قدرتمند طبقه بندی تصویر با استفاده از داده های بسیار اندکی" .

لایه های انجماد: درک trainable ویژگی

لایه ها و مدل ها دارای سه ویژگی وزنی هستند:

  • weights لیستی از تمام متغیرهای وزن لایه است.
  • trainable_weights لیست کسانی هستند که به معنای به روز می شود (از طریق گرادیان نزولی) برای به حداقل رساندن از دست دادن در طول آموزش است.
  • non_trainable_weights لیست کسانی هستند که به معنای نه به آموزش است. به طور معمول آنها توسط مدل در طول پاس رو به جلو به روز می شوند.

به عنوان مثال: Dense لایه دارای 2 وزنه تربیت شدنی (هسته و تعصب)

layer = keras.layers.Dense(3)
layer.build((None, 4))  # Create the weights

print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 2
trainable_weights: 2
non_trainable_weights: 0

به طور کلی همه وزنه ها وزنه های قابل تمرین هستند. تنها ساخته شده در لایه است که وزن غیر تربیت شدنی است BatchNormalization لایه. از وزنه های غیر قابل تمرین برای پیگیری میانگین و واریانس ورودی های خود در طول تمرین استفاده می کند. برای آشنایی با نحوه استفاده از وزن غیر تربیت شدنی در لایه های سفارشی خود را، را ببینید راهنمایی برای نوشتن لایه جدید، از ابتدا .

به عنوان مثال: BatchNormalization لایه دارای 2 وزنه تربیت شدنی و 2 وزن غیر تربیت شدنی

layer = keras.layers.BatchNormalization()
layer.build((None, 4))  # Create the weights

print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 4
trainable_weights: 2
non_trainable_weights: 2

لایه ها و مدل های نیز از ویژگی های یک خصوصیت boolean trainable . مقدار آن قابل تغییر است. تنظیم layer.trainable به False حرکت می کند تمام وزن لایه را از تربیت شدنی به غیر تربیت شدنی این است که به نام "انجماد" لایه: دولت از یک لایه یخ زده خواهد شد در طول آموزش به روز رسانی (خواه در هنگام آموزش با fit() و یا در هنگام آموزش با هر حلقه های سفارشی که متکی trainable_weights به درخواست به روز رسانی شیب).

به عنوان مثال: تنظیم trainable به False

layer = keras.layers.Dense(3)
layer.build((None, 4))  # Create the weights
layer.trainable = False  # Freeze the layer

print("weights:", len(layer.weights))
print("trainable_weights:", len(layer.trainable_weights))
print("non_trainable_weights:", len(layer.non_trainable_weights))
weights: 2
trainable_weights: 0
non_trainable_weights: 2

هنگامی که یک وزنه قابل تمرین غیر قابل تمرین می شود، ارزش آن دیگر در طول تمرین به روز نمی شود.

# Make a model with 2 layers
layer1 = keras.layers.Dense(3, activation="relu")
layer2 = keras.layers.Dense(3, activation="sigmoid")
model = keras.Sequential([keras.Input(shape=(3,)), layer1, layer2])

# Freeze the first layer
layer1.trainable = False

# Keep a copy of the weights of layer1 for later reference
initial_layer1_weights_values = layer1.get_weights()

# Train the model
model.compile(optimizer="adam", loss="mse")
model.fit(np.random.random((2, 3)), np.random.random((2, 3)))

# Check that the weights of layer1 have not changed during training
final_layer1_weights_values = layer1.get_weights()
np.testing.assert_allclose(
    initial_layer1_weights_values[0], final_layer1_weights_values[0]
)
np.testing.assert_allclose(
    initial_layer1_weights_values[1], final_layer1_weights_values[1]
)
1/1 [==============================] - 1s 640ms/step - loss: 0.0945

آیا اشتباه نیست layer.trainable ویژگی با این استدلال training در layer.__call__() (که کنترل میکند که آیا لایه باید پاس رو به جلو خود را در حالت استنتاج یا حالت آموزش اجرا). برای اطلاعات بیشتر، نگاه کنید به Keras پرسش و پاسخ .

تنظیم بازگشتی از trainable ویژگی

اگر شما در راه trainable = False بر روی یک مدل و یا در هر لایه است که زیر لایه، همه کودکان لایه تبدیل غیر تربیت شدنی است.

مثال:

inner_model = keras.Sequential(
    [
        keras.Input(shape=(3,)),
        keras.layers.Dense(3, activation="relu"),
        keras.layers.Dense(3, activation="relu"),
    ]
)

model = keras.Sequential(
    [keras.Input(shape=(3,)), inner_model, keras.layers.Dense(3, activation="sigmoid"),]
)

model.trainable = False  # Freeze the outer model

assert inner_model.trainable == False  # All layers in `model` are now frozen
assert inner_model.layers[0].trainable == False  # `trainable` is propagated recursively

گردش کار انتقال-یادگیری معمولی

این ما را به چگونگی پیاده‌سازی یک گردش کار یادگیری انتقال معمولی در Keras راهنمایی می‌کند:

  1. یک مدل پایه را نمونه برداری کنید و وزنه های از پیش آموزش دیده را در آن قرار دهید.
  2. مسدود کردن تمام لایه ها در مدل پایه با تنظیم trainable = False .
  3. یک مدل جدید در بالای خروجی یک (یا چند) لایه از مدل پایه ایجاد کنید.
  4. مدل جدید خود را بر روی مجموعه داده جدید خود آموزش دهید.

توجه داشته باشید که یک گردش کار جایگزین و سبک تر نیز می تواند باشد:

  1. یک مدل پایه را نمونه برداری کنید و وزنه های از پیش آموزش دیده را در آن قرار دهید.
  2. مجموعه داده جدید خود را از طریق آن اجرا کنید و خروجی یک (یا چند) لایه را از مدل پایه ضبط کنید. این استخراج ویژگی به نام.
  3. از آن خروجی به عنوان داده ورودی برای یک مدل جدید و کوچکتر استفاده کنید.

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

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

در اینجا اولین گردش کار در Keras به نظر می رسد:

ابتدا یک مدل پایه با وزنه های از پیش آموزش دیده نمونه سازی کنید.

base_model = keras.applications.Xception(
    weights='imagenet',  # Load weights pre-trained on ImageNet.
    input_shape=(150, 150, 3),
    include_top=False)  # Do not include the ImageNet classifier at the top.

سپس مدل پایه را فریز کنید.

base_model.trainable = False

یک مدل جدید در بالا ایجاد کنید.

inputs = keras.Input(shape=(150, 150, 3))
# We make sure that the base_model is running in inference mode here,
# by passing `training=False`. This is important for fine-tuning, as you will
# learn in a few paragraphs.
x = base_model(inputs, training=False)
# Convert features of shape `base_model.output_shape[1:]` to vectors
x = keras.layers.GlobalAveragePooling2D()(x)
# A Dense classifier with a single unit (binary classification)
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

مدل را بر روی داده های جدید آموزش دهید.

model.compile(optimizer=keras.optimizers.Adam(),
              loss=keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=[keras.metrics.BinaryAccuracy()])
model.fit(new_dataset, epochs=20, callbacks=..., validation_data=...)

تنظیم دقیق

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

این آخرین مرحله اختیاری است که به طور بالقوه می تواند به شما پیشرفت های تدریجی بدهد. همچنین می تواند به طور بالقوه منجر به بیش از حد برازش سریع شود - این را در نظر داشته باشید.

بسیار مهم است که تنها انجام این مرحله پس از مدل با لایه های منجمد شده است به همگرایی آموزش داده است. اگر لایه‌های آموزش‌پذیر اولیه‌سازی‌شده به‌طور تصادفی را با لایه‌های آموزش‌پذیری که ویژگی‌های از پیش آموزش داده شده را در خود جای داده‌اند، ترکیب کنید، لایه‌هایی که به‌طور تصادفی راه‌اندازی شده‌اند، به‌روزرسانی‌های گرادیان بسیار بزرگی را در طول آموزش ایجاد می‌کنند که ویژگی‌های از پیش آموزش‌دیده‌شده شما را از بین می‌برد.

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

نحوه اجرای تنظیم دقیق کل مدل پایه به این صورت است:

# Unfreeze the base model
base_model.trainable = True

# It's important to recompile your model after you make any changes
# to the `trainable` attribute of any inner layer, so that your changes
# are take into account
model.compile(optimizer=keras.optimizers.Adam(1e-5),  # Very low learning rate
              loss=keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=[keras.metrics.BinaryAccuracy()])

# Train end-to-end. Be careful to stop before you overfit!
model.fit(new_dataset, epochs=10, callbacks=..., validation_data=...)

نکته مهم در مورد compile() و trainable

تماس compile() بر روی یک مدل است به معنای "توقف" رفتار آن مدل. این به معنی این که trainable مقادیر ویژگی در زمان مدل وارد شده است باید در تمام طول عمر که مدل حفظ شده است، تا زمانی که compile است به نام دوباره. از این رو، اگر شما هر گونه تغییر trainable ارزش، به پاسخ مطمئن شوید compile() دوباره در مدل خود را برای تغییرات خود را به در نظر گرفته شود.

نکات مهم در مورد BatchNormalization لایه

بسیاری از مدل های تصویر حاوی BatchNormalization لایه. آن لایه در هر تعداد قابل تصور یک مورد خاص است. در اینجا چند نکته وجود دارد که باید در نظر داشته باشید.

  • BatchNormalization شامل 2 وزن غیر تربیت شدنی که از در طول آموزش به روز شد. اینها متغیرهایی هستند که میانگین و واریانس ورودی ها را ردیابی می کنند.
  • هنگامی که به شما در تنظیم bn_layer.trainable = False از BatchNormalization لایه در حالت استنتاج اجرا خواهد شد، و آمار میانگین و واریانس آن به روز رسانی کنید. این مورد برای لایه های دیگر به طور کلی، به عنوان trainability وزن و استنتاج / حالت آموزش دو مفهوم متعامد هستند . اما این دو در مورد گره خورده است BatchNormalization لایه.
  • هنگامی که شما یک مدل است که شامل آزادسازی BatchNormalization لایه به منظور انجام ریز تنظیم، شما باید حفظ BatchNormalization لایه در حالت استنتاج با عبور training=False در هنگام فراخوانی مدل پایه. در غیر این صورت، به‌روزرسانی‌های اعمال‌شده برای وزنه‌های غیرقابل آموزش، به‌طور ناگهانی آنچه را که مدل آموخته، از بین می‌برد.

در مثال سرتاسری در انتهای این راهنما این الگو را در عمل مشاهده خواهید کرد.

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

اگر به جای fit() ، شما با استفاده از خود حلقه آموزش سطح پایین خود را، اقامت گردش کار اساسا همان. شما باید مراقب باشید به تنها به حساب لیست model.trainable_weights در هنگام استفاده از به روز رسانی شیب:

# Create base model
base_model = keras.applications.Xception(
    weights='imagenet',
    input_shape=(150, 150, 3),
    include_top=False)
# Freeze base model
base_model.trainable = False

# Create new model on top.
inputs = keras.Input(shape=(150, 150, 3))
x = base_model(inputs, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

loss_fn = keras.losses.BinaryCrossentropy(from_logits=True)
optimizer = keras.optimizers.Adam()

# Iterate over the batches of a dataset.
for inputs, targets in new_dataset:
    # Open a GradientTape.
    with tf.GradientTape() as tape:
        # Forward pass.
        predictions = model(inputs)
        # Compute the loss value for this batch.
        loss_value = loss_fn(targets, predictions)

    # Get gradients of loss wrt the *trainable* weights.
    gradients = tape.gradient(loss_value, model.trainable_weights)
    # Update the weights of the model.
    optimizer.apply_gradients(zip(gradients, model.trainable_weights))

به همین ترتیب برای تنظیم دقیق.

یک مثال سرتاسر: تنظیم دقیق مدل طبقه‌بندی تصویر در مجموعه داده‌های گربه در مقابل سگ

برای تثبیت این مفاهیم، ​​بیایید شما را از طریق یک مثال یادگیری و تنظیم دقیق انتقال پایان به انتها آشنا کنیم. ما مدل Xception را که از قبل در ImageNet آموزش داده شده است بارگذاری می کنیم و از آن در مجموعه داده های طبقه بندی Kaggle "گربه ها در برابر سگ ها" استفاده می کنیم.

گرفتن داده ها

ابتدا، بیایید مجموعه داده های گربه ها در مقابل سگ ها را با استفاده از TFDS واکشی کنیم. اگر شما مجموعه داده های خود را، شما احتمالا می خواهید به استفاده از ابزار tf.keras.preprocessing.image_dataset_from_directory برای تولید مجموعه داده با برچسب مشابه اشیاء از مجموعه ای از تصاویر بر روی دیسک ثبت شده را به پوشه طبقه خاص.

یادگیری انتقالی هنگام کار با مجموعه داده های بسیار کوچک بسیار مفید است. برای کوچک نگه داشتن مجموعه داده های خود، از 40 درصد داده های آموزشی اصلی (25000 تصویر) برای آموزش، 10 درصد برای اعتبارسنجی و 10 درصد برای آزمایش استفاده خواهیم کرد.

import tensorflow_datasets as tfds

tfds.disable_progress_bar()

train_ds, validation_ds, test_ds = tfds.load(
    "cats_vs_dogs",
    # Reserve 10% for validation and 10% for test
    split=["train[:40%]", "train[40%:50%]", "train[50%:60%]"],
    as_supervised=True,  # Include labels
)

print("Number of training samples: %d" % tf.data.experimental.cardinality(train_ds))
print(
    "Number of validation samples: %d" % tf.data.experimental.cardinality(validation_ds)
)
print("Number of test samples: %d" % tf.data.experimental.cardinality(test_ds))
Number of training samples: 9305
Number of validation samples: 2326
Number of test samples: 2326

اینها 9 تصویر اول در مجموعه داده آموزشی هستند -- همانطور که می بینید، همه آنها اندازه های متفاوتی دارند.

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 10))
for i, (image, label) in enumerate(train_ds.take(9)):
    ax = plt.subplot(3, 3, i + 1)
    plt.imshow(image)
    plt.title(int(label))
    plt.axis("off")

png

همچنین می توانیم ببینیم که برچسب 1 "سگ" و برچسب 0 "گربه" است.

استاندارد کردن داده ها

تصاویر خام ما اندازه های مختلفی دارند. علاوه بر این، هر پیکسل از 3 مقدار صحیح بین 0 تا 255 (مقادیر سطح RGB) تشکیل شده است. این برای تغذیه شبکه عصبی مناسب نیست. ما باید 2 کار را انجام دهیم:

  • استاندارد کردن به اندازه تصویر ثابت. ما 150x150 را انتخاب می کنیم.
  • مقادیر پیکسل عادی بین -1 و 1. ما این با استفاده از یک انجام Normalization لایه را به عنوان بخشی از مدل خود را.

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

در اینجا، تغییر اندازه تصویر را در خط لوله داده انجام خواهیم داد (زیرا یک شبکه عصبی عمیق تنها می‌تواند دسته‌ای از داده‌ها را پردازش کند)، و زمانی که آن را ایجاد می‌کنیم، مقیاس ارزش ورودی را به عنوان بخشی از مدل انجام می‌دهیم.

بیایید اندازه تصاویر را به 150x150 تغییر دهیم:

size = (150, 150)

train_ds = train_ds.map(lambda x, y: (tf.image.resize(x, size), y))
validation_ds = validation_ds.map(lambda x, y: (tf.image.resize(x, size), y))
test_ds = test_ds.map(lambda x, y: (tf.image.resize(x, size), y))

علاوه بر این، بیایید داده ها را دسته بندی کنیم و از کش و واکشی اولیه برای بهینه سازی سرعت بارگذاری استفاده کنیم.

batch_size = 32

train_ds = train_ds.cache().batch(batch_size).prefetch(buffer_size=10)
validation_ds = validation_ds.cache().batch(batch_size).prefetch(buffer_size=10)
test_ds = test_ds.cache().batch(batch_size).prefetch(buffer_size=10)

با استفاده از افزایش تصادفی داده ها

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

from tensorflow import keras
from tensorflow.keras import layers

data_augmentation = keras.Sequential(
    [layers.RandomFlip("horizontal"), layers.RandomRotation(0.1),]
)

بیایید تجسم کنیم که اولین تصویر از اولین دسته پس از تبدیل‌های تصادفی مختلف چگونه به نظر می‌رسد:

import numpy as np

for images, labels in train_ds.take(1):
    plt.figure(figsize=(10, 10))
    first_image = images[0]
    for i in range(9):
        ax = plt.subplot(3, 3, i + 1)
        augmented_image = data_augmentation(
            tf.expand_dims(first_image, 0), training=True
        )
        plt.imshow(augmented_image[0].numpy().astype("int32"))
        plt.title(int(labels[0]))
        plt.axis("off")
2021-09-01 18:45:34.772284: W tensorflow/core/kernels/data/cache_dataset_ops.cc:768] The calling iterator did not fully read the dataset being cached. In order to avoid unexpected truncation of the dataset, the partially cached contents of the dataset  will be discarded. This can happen if you have an input pipeline similar to `dataset.cache().take(k).repeat()`. You should use `dataset.take(k).cache().repeat()` instead.

png

یک مدل بسازید

حالا بیایید مدلی بسازیم که از طرحی که قبلا توضیح دادیم پیروی کند.

توجه داشته باشید که:

  • ما اضافه کردن یک Rescaling لایه را به مقادیر ورودی مقیاس (در ابتدا در [0, 255] وسیعی) به [-1, 1] محدوده.
  • ما اضافه کردن یک Dropout لایه قبل از لایه طبقه بندی، برای تنظیم.
  • ما مطمئن شوید که به تصویب training=False در هنگام فراخوانی مدل پایه، به طوری که آن را در حالت استنتاج اجرا می شود، به طوری که آمار batchnorm انجام به روز نمی حتی پس از ما مدل پایه برای ریز تنظیم را آزاد.
base_model = keras.applications.Xception(
    weights="imagenet",  # Load weights pre-trained on ImageNet.
    input_shape=(150, 150, 3),
    include_top=False,
)  # Do not include the ImageNet classifier at the top.

# Freeze the base_model
base_model.trainable = False

# Create new model on top
inputs = keras.Input(shape=(150, 150, 3))
x = data_augmentation(inputs)  # Apply random data augmentation

# Pre-trained Xception weights requires that input be scaled
# from (0, 255) to a range of (-1., +1.), the rescaling layer
# outputs: `(inputs * scale) + offset`
scale_layer = keras.layers.Rescaling(scale=1 / 127.5, offset=-1)
x = scale_layer(x)

# The base model contains batchnorm layers. We want to keep them in inference mode
# when we unfreeze the base model for fine-tuning, so we make sure that the
# base_model is running in inference mode here.
x = base_model(x, training=False)
x = keras.layers.GlobalAveragePooling2D()(x)
x = keras.layers.Dropout(0.2)(x)  # Regularize with dropout
outputs = keras.layers.Dense(1)(x)
model = keras.Model(inputs, outputs)

model.summary()
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/xception/xception_weights_tf_dim_ordering_tf_kernels_notop.h5
83689472/83683744 [==============================] - 2s 0us/step
83697664/83683744 [==============================] - 2s 0us/step
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_5 (InputLayer)         [(None, 150, 150, 3)]     0         
_________________________________________________________________
sequential_3 (Sequential)    (None, 150, 150, 3)       0         
_________________________________________________________________
rescaling (Rescaling)        (None, 150, 150, 3)       0         
_________________________________________________________________
xception (Functional)        (None, 5, 5, 2048)        20861480  
_________________________________________________________________
global_average_pooling2d (Gl (None, 2048)              0         
_________________________________________________________________
dropout (Dropout)            (None, 2048)              0         
_________________________________________________________________
dense_7 (Dense)              (None, 1)                 2049      
=================================================================
Total params: 20,863,529
Trainable params: 2,049
Non-trainable params: 20,861,480
_________________________________________________________________

لایه بالایی را آموزش دهید

model.compile(
    optimizer=keras.optimizers.Adam(),
    loss=keras.losses.BinaryCrossentropy(from_logits=True),
    metrics=[keras.metrics.BinaryAccuracy()],
)

epochs = 20
model.fit(train_ds, epochs=epochs, validation_data=validation_ds)
Epoch 1/20
151/291 [==============>...............] - ETA: 3s - loss: 0.1979 - binary_accuracy: 0.9096
Corrupt JPEG data: 65 extraneous bytes before marker 0xd9
268/291 [==========================>...] - ETA: 1s - loss: 0.1663 - binary_accuracy: 0.9269
Corrupt JPEG data: 239 extraneous bytes before marker 0xd9
282/291 [============================>.] - ETA: 0s - loss: 0.1628 - binary_accuracy: 0.9284
Corrupt JPEG data: 1153 extraneous bytes before marker 0xd9
Corrupt JPEG data: 228 extraneous bytes before marker 0xd9
291/291 [==============================] - ETA: 0s - loss: 0.1620 - binary_accuracy: 0.9286
Corrupt JPEG data: 2226 extraneous bytes before marker 0xd9
291/291 [==============================] - 29s 63ms/step - loss: 0.1620 - binary_accuracy: 0.9286 - val_loss: 0.0814 - val_binary_accuracy: 0.9686
Epoch 2/20
291/291 [==============================] - 8s 29ms/step - loss: 0.1178 - binary_accuracy: 0.9511 - val_loss: 0.0785 - val_binary_accuracy: 0.9695
Epoch 3/20
291/291 [==============================] - 9s 30ms/step - loss: 0.1121 - binary_accuracy: 0.9536 - val_loss: 0.0748 - val_binary_accuracy: 0.9712
Epoch 4/20
291/291 [==============================] - 9s 29ms/step - loss: 0.1082 - binary_accuracy: 0.9554 - val_loss: 0.0754 - val_binary_accuracy: 0.9703
Epoch 5/20
291/291 [==============================] - 8s 29ms/step - loss: 0.1034 - binary_accuracy: 0.9570 - val_loss: 0.0721 - val_binary_accuracy: 0.9725
Epoch 6/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0975 - binary_accuracy: 0.9602 - val_loss: 0.0748 - val_binary_accuracy: 0.9699
Epoch 7/20
291/291 [==============================] - 9s 29ms/step - loss: 0.0989 - binary_accuracy: 0.9595 - val_loss: 0.0732 - val_binary_accuracy: 0.9716
Epoch 8/20
291/291 [==============================] - 8s 29ms/step - loss: 0.1027 - binary_accuracy: 0.9566 - val_loss: 0.0787 - val_binary_accuracy: 0.9678
Epoch 9/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0959 - binary_accuracy: 0.9614 - val_loss: 0.0734 - val_binary_accuracy: 0.9729
Epoch 10/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0995 - binary_accuracy: 0.9588 - val_loss: 0.0717 - val_binary_accuracy: 0.9721
Epoch 11/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0957 - binary_accuracy: 0.9612 - val_loss: 0.0731 - val_binary_accuracy: 0.9725
Epoch 12/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0936 - binary_accuracy: 0.9622 - val_loss: 0.0751 - val_binary_accuracy: 0.9716
Epoch 13/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0965 - binary_accuracy: 0.9610 - val_loss: 0.0821 - val_binary_accuracy: 0.9695
Epoch 14/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0939 - binary_accuracy: 0.9618 - val_loss: 0.0742 - val_binary_accuracy: 0.9712
Epoch 15/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0974 - binary_accuracy: 0.9585 - val_loss: 0.0771 - val_binary_accuracy: 0.9712
Epoch 16/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0947 - binary_accuracy: 0.9621 - val_loss: 0.0823 - val_binary_accuracy: 0.9699
Epoch 17/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0947 - binary_accuracy: 0.9625 - val_loss: 0.0718 - val_binary_accuracy: 0.9708
Epoch 18/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0928 - binary_accuracy: 0.9616 - val_loss: 0.0738 - val_binary_accuracy: 0.9716
Epoch 19/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0922 - binary_accuracy: 0.9644 - val_loss: 0.0743 - val_binary_accuracy: 0.9716
Epoch 20/20
291/291 [==============================] - 8s 29ms/step - loss: 0.0885 - binary_accuracy: 0.9635 - val_loss: 0.0745 - val_binary_accuracy: 0.9695
<keras.callbacks.History at 0x7f849a3b2950>

یک دور تنظیم دقیق کل مدل را انجام دهید

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

نکته مهم، اگر چه مدل پایه تربیت شدنی می شود، آن است که هنوز در حالت استنتاج در حال اجرا از ما گذشت training=False هنگام فراخوانی آن زمانی که ما مدل ساخته شده است. این بدان معنی است که لایه های نرمال سازی دسته ای در داخل، آمار دسته ای خود را به روز نمی کنند. اگر این کار را می کردند، بازنمایی هایی را که مدل تاکنون آموخته بود، ویران می کردند.

# Unfreeze the base_model. Note that it keeps running in inference mode
# since we passed `training=False` when calling it. This means that
# the batchnorm layers will not update their batch statistics.
# This prevents the batchnorm layers from undoing all the training
# we've done so far.
base_model.trainable = True
model.summary()

model.compile(
    optimizer=keras.optimizers.Adam(1e-5),  # Low learning rate
    loss=keras.losses.BinaryCrossentropy(from_logits=True),
    metrics=[keras.metrics.BinaryAccuracy()],
)

epochs = 10
model.fit(train_ds, epochs=epochs, validation_data=validation_ds)
Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_5 (InputLayer)         [(None, 150, 150, 3)]     0         
_________________________________________________________________
sequential_3 (Sequential)    (None, 150, 150, 3)       0         
_________________________________________________________________
rescaling (Rescaling)        (None, 150, 150, 3)       0         
_________________________________________________________________
xception (Functional)        (None, 5, 5, 2048)        20861480  
_________________________________________________________________
global_average_pooling2d (Gl (None, 2048)              0         
_________________________________________________________________
dropout (Dropout)            (None, 2048)              0         
_________________________________________________________________
dense_7 (Dense)              (None, 1)                 2049      
=================================================================
Total params: 20,863,529
Trainable params: 20,809,001
Non-trainable params: 54,528
_________________________________________________________________
Epoch 1/10
291/291 [==============================] - 43s 131ms/step - loss: 0.0802 - binary_accuracy: 0.9692 - val_loss: 0.0580 - val_binary_accuracy: 0.9764
Epoch 2/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0542 - binary_accuracy: 0.9792 - val_loss: 0.0529 - val_binary_accuracy: 0.9764
Epoch 3/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0400 - binary_accuracy: 0.9832 - val_loss: 0.0510 - val_binary_accuracy: 0.9798
Epoch 4/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0313 - binary_accuracy: 0.9879 - val_loss: 0.0505 - val_binary_accuracy: 0.9819
Epoch 5/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0272 - binary_accuracy: 0.9904 - val_loss: 0.0485 - val_binary_accuracy: 0.9807
Epoch 6/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0284 - binary_accuracy: 0.9901 - val_loss: 0.0497 - val_binary_accuracy: 0.9824
Epoch 7/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0198 - binary_accuracy: 0.9937 - val_loss: 0.0530 - val_binary_accuracy: 0.9802
Epoch 8/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0173 - binary_accuracy: 0.9930 - val_loss: 0.0572 - val_binary_accuracy: 0.9819
Epoch 9/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0113 - binary_accuracy: 0.9958 - val_loss: 0.0555 - val_binary_accuracy: 0.9837
Epoch 10/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0091 - binary_accuracy: 0.9966 - val_loss: 0.0596 - val_binary_accuracy: 0.9832
<keras.callbacks.History at 0x7f83982d4cd0>

پس از 10 دوره، تنظیم دقیق پیشرفت خوبی در اینجا به ما می دهد.