عرض على TensorFlow.org | تشغيل في Google Colab | عرض المصدر على جيثب | تحميل دفتر |
يثبت
import numpy as np
import tensorflow as tf
from tensorflow import keras
مقدمة
يتكون التعلم نقل اتخاذ الميزات المستفادة من مشكلة واحدة، والاستفادة منها في مشكلة جديدة، مماثلة. على سبيل المثال ، قد تكون الميزات من نموذج تعلم التعرف على حيوانات الراكون مفيدة لبدء نموذج يهدف إلى تحديد التانوكيس.
عادةً ما يتم إجراء التعلم عن طريق النقل للمهام التي تحتوي فيها مجموعة البيانات الخاصة بك على القليل جدًا من البيانات لتدريب نموذج كامل النطاق من البداية.
التجسد الأكثر شيوعًا لنقل التعلم في سياق التعلم العميق هو سير العمل التالي:
- خذ طبقات من نموذج تم تدريبه مسبقًا.
- قم بتجميدها ، وذلك لتجنب إتلاف أي من المعلومات التي تحتويها خلال جولات التدريب المستقبلية.
- أضف بعض الطبقات الجديدة القابلة للتدريب فوق الطبقات المجمدة. سيتعلمون كيفية تحويل الميزات القديمة إلى تنبؤات على مجموعة بيانات جديدة.
- قم بتدريب الطبقات الجديدة على مجموعة البيانات الخاصة بك.
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:
- إنشاء نموذج أساسي وتحميل الأوزان المدربة مسبقًا فيه.
- تجميد جميع الطبقات في نموذج القاعدة من خلال وضع
trainable = False
. - قم بإنشاء نموذج جديد أعلى ناتج طبقة واحدة (أو عدة طبقات) من النموذج الأساسي.
- تدريب نموذجك الجديد على مجموعة البيانات الجديدة.
لاحظ أن سير العمل البديل والأكثر خفة يمكن أن يكون أيضًا:
- إنشاء نموذج أساسي وتحميل الأوزان المدربة مسبقًا فيه.
- قم بتشغيل مجموعة البيانات الجديدة من خلالها وسجل ناتج طبقة واحدة (أو عدة طبقات) من النموذج الأساسي. وهذا ما يسمى استخراج الميزة.
- استخدم هذا الإخراج كبيانات إدخال لنموذج جديد أصغر.
تتمثل الميزة الرئيسية لسير العمل الثاني في أنك تقوم بتشغيل النموذج الأساسي مرة واحدة فقط على بياناتك ، بدلاً من تشغيلها مرة واحدة لكل فترة تدريب. لذلك فهو أسرع بكثير وأرخص.
ومع ذلك ، هناك مشكلة في سير العمل الثاني ، وهي أنه لا يسمح لك بتعديل بيانات الإدخال الخاصة بنموذجك الجديد ديناميكيًا أثناء التدريب ، وهو أمر مطلوب عند القيام بزيادة البيانات ، على سبيل المثال. عادةً ما يتم استخدام التعلم عن طريق النقل للمهام عندما تحتوي مجموعة البيانات الجديدة على القليل جدًا من البيانات لتدريب نموذج كامل النطاق من البداية ، وفي مثل هذه السيناريوهات ، يكون زيادة البيانات أمرًا مهمًا للغاية. إذن في ما يلي ، سوف نركز على سير العمل الأول.
إليك ما يبدو عليه سير العمل الأول في 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 حقب ، يكسبنا الضبط الدقيق تحسينًا جيدًا هنا.