نقل التعلم والضبط

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

يثبت

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

مقدمة

يتكون التعلم نقل اتخاذ الميزات المستفادة من مشكلة واحدة، والاستفادة منها في مشكلة جديدة، مماثلة. على سبيل المثال ، قد تكون الميزات من نموذج تعلم التعرف على حيوانات الراكون مفيدة لبدء نموذج يهدف إلى تحديد التانوكيس.

عادةً ما يتم إجراء التعلم عن طريق النقل للمهام التي تحتوي فيها مجموعة البيانات الخاصة بك على القليل جدًا من البيانات لتدريب نموذج كامل النطاق من البداية.

التجسد الأكثر شيوعًا لنقل التعلم في سياق التعلم العميق هو سير العمل التالي:

  1. خذ طبقات من نموذج تم تدريبه مسبقًا.
  2. قم بتجميدها ، وذلك لتجنب إتلاف أي من المعلومات التي تحتويها خلال جولات التدريب المستقبلية.
  3. أضف بعض الطبقات الجديدة القابلة للتدريب فوق الطبقات المجمدة. سيتعلمون كيفية تحويل الميزات القديمة إلى تنبؤات على مجموعة بيانات جديدة.
  4. قم بتدريب الطبقات الجديدة على مجموعة البيانات الخاصة بك.

A، خطوة اختيارية الماضية، هو صقل، والذي يتألف من فك تجميد نموذج كامل حصلت فوق (أو جزء منه)، وإعادة التدريب على البيانات الجديدة مع معدل التعلم منخفضة للغاية. يمكن أن يحقق ذلك تحسينات ذات مغزى ، من خلال تكييف الميزات المحددة مسبقًا بشكل تدريجي مع البيانات الجديدة.

أولا، فإننا سوف يذهب أكثر من Keras trainable API في التفاصيل، الذي يكمن وراء معظم التعلم نقل وصقل سير العمل.

بعد ذلك ، سنعرض سير العمل النموذجي من خلال أخذ نموذج تم اختباره مسبقًا على مجموعة بيانات ImageNet ، وإعادة تدريبه على مجموعة بيانات تصنيف Kaggle "cats vs dogs".

ويتم تكييف هذا من أعماق التعلم مع بيثون وبلوق وظيفة 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

طبقات ونماذج أيضا ميزة سمة منطقية 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 "cats vs. Dog".

الحصول على البيانات

أولاً ، لنجلب مجموعة بيانات القطط مقابل الكلاب باستخدام 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")

بي إن جي

يمكننا أن نرى أيضًا أن التصنيف 1 هو "كلب" وأن التصنيف 0 هو "قطة".

توحيد البيانات

صورنا الخام لها أحجام متنوعة. بالإضافة إلى ذلك ، يتكون كل بكسل من 3 قيم صحيحة بين 0 و 255 (قيم مستوى RGB). هذا ليس مناسبًا تمامًا لتغذية الشبكة العصبية. نحتاج إلى القيام بأمرين:

  • التوحيد إلى حجم صورة ثابت. نختار 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.

بي إن جي

بناء نموذج

الآن دعونا نبني نموذجًا يتبع المخطط الذي شرحناه سابقًا.

لاحظ أن:

  • نضيف 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 حقب ، يكسبنا الضبط الدقيق تحسينًا جيدًا هنا.