Trang này được dịch bởi Cloud Translation API.
Switch to English

Chuyển giao việc học & amp; tinh chỉnh

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ tay

Thiết lập

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

 

Giới thiệu

Học chuyển giao bao gồm lấy các tính năng đã học về một vấn đề và tận dụng chúng cho một vấn đề tương tự mới. Ví dụ, các tính năng từ một mô hình đã học cách xác định các loài chim có thể hữu ích để khởi động một mô hình dùng để xác định tanukis.

Học chuyển giao thường được thực hiện cho các nhiệm vụ mà tập dữ liệu của bạn có quá ít dữ liệu để đào tạo một mô hình quy mô đầy đủ từ đầu.

Hóa thân phổ biến nhất của học chuyển giao trong bối cảnh học sâu là worfklow sau đây:

  1. Lấy các lớp từ một mô hình được đào tạo trước đó.
  2. Đóng băng chúng, để tránh phá hủy bất kỳ thông tin nào mà chúng chứa trong các vòng huấn luyện trong tương lai.
  3. Thêm một số lớp mới, có thể đào tạo trên đầu các lớp đã đóng băng. Họ sẽ học cách biến các tính năng cũ thành dự đoán trên một tập dữ liệu mới.
  4. Huấn luyện các lớp mới trên tập dữ liệu của bạn.

Bước cuối cùng, tùy chọn, là tinh chỉnh , bao gồm việc mở toàn bộ mô hình bạn đã thu được ở trên (hoặc một phần của nó) và đào tạo lại nó trên dữ liệu mới với tỷ lệ học tập rất thấp. Điều này có khả năng có thể đạt được những cải tiến có ý nghĩa, bằng cách điều chỉnh tăng dần các tính năng được sàng lọc trước với dữ liệu mới.

Đầu tiên, chúng ta sẽ tìm hiểu chi tiết về API có thể trainable luyện của Keras, làm nền tảng cho hầu hết các quy trình học tập và tinh chỉnh chuyển giao.

Sau đó, chúng tôi sẽ chứng minh quy trình làm việc điển hình bằng cách lấy một mô hình được xử lý trước trên tập dữ liệu ImageNet và đào tạo lại nó trên bộ dữ liệu phân loại "mèo vs chó" của Kaggle.

Điều này được điều chỉnh từ Deep Learning với Python và bài đăng trên blog năm 2016 "xây dựng các mô hình phân loại hình ảnh mạnh mẽ bằng cách sử dụng rất ít dữ liệu" .

Các lớp đóng băng: hiểu thuộc tính có thể trainable

Lớp & mô hình có ba thuộc tính trọng lượng:

  • weights là danh sách tất cả các biến trọng số của lớp.
  • trainable_weights là danh sách được cập nhật (thông qua gradient descent) để giảm thiểu tổn thất trong quá trình đào tạo.
  • non_trainable_weights là danh sách những thứ không được đào tạo. Thông thường, chúng được cập nhật bởi mô hình trong quá trình chuyển tiếp.

Ví dụ: lớp Dense có 2 trọng lượng có thể huấn luyện (nhân & thiên vị)

 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

Nói chung, tất cả các trọng lượng là trọng lượng có thể đào tạo. Lớp tích hợp duy nhất có trọng số không thể đào tạo là lớp BatchNormalization . Nó sử dụng trọng lượng không thể đào tạo để theo dõi giá trị trung bình và phương sai của đầu vào trong quá trình đào tạo. Để tìm hiểu cách sử dụng trọng số không thể đào tạo trong các lớp tùy chỉnh của riêng bạn, hãy xem hướng dẫn viết các lớp mới từ đầu .

Ví dụ: lớp BatchNormalization có 2 trọng lượng có thể huấn luyện và 2 trọng lượng không thể huấn luyện

 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

Các lớp và mô hình cũng có tính năng boolean có thể trainable . Giá trị của nó có thể được thay đổi. Đặt layer.trainable thành False sẽ di chuyển tất cả các trọng số của lớp từ có thể huấn luyện sang không thể huấn luyện. Đây được gọi là "đóng băng" lớp: trạng thái của lớp bị đóng băng sẽ không được cập nhật trong quá trình đào tạo (khi đào tạo với fit() hoặc khi đào tạo với bất kỳ vòng lặp tùy chỉnh nào dựa vào trainable_weights để áp dụng cập nhật gradient).

Ví dụ: cài đặt trainable thành 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

Khi một trọng lượng có thể huấn luyện trở nên không thể huấn luyện, giá trị của nó không còn được cập nhật trong quá trình huấn luyện.

 # 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 [==============================] - 0s 1ms/step - loss: 0.0855

Đừng nhầm lẫn thuộc tính layer.trainable với training đối số trong layer.__call__() (điều khiển lớp này sẽ chạy vượt qua chuyển tiếp của nó trong chế độ suy luận hoặc chế độ đào tạo). Để biết thêm thông tin, hãy xem Câu hỏi thường gặp về Keras .

Cài đặt đệ quy của thuộc tính có thể trainable

Nếu bạn đặt trainable = False trên một mô hình hoặc trên bất kỳ lớp nào có các lớp con, tất cả các lớp con cũng trở thành không thể huấn luyện.

Thí dụ:

 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

 

Quy trình học chuyển giao điển hình

Điều này dẫn chúng ta đến cách một quy trình học tập chuyển giao điển hình có thể được thực hiện trong Keras:

  1. Khởi tạo mô hình cơ sở và tải các trọng lượng được đào tạo trước vào đó.
  2. Đóng băng tất cả các lớp trong mô hình cơ sở bằng cách đặt trainable = False .
  3. Tạo một mô hình mới trên đầu ra của một (hoặc một số) lớp từ mô hình cơ sở.
  4. Đào tạo mô hình mới của bạn trên tập dữ liệu mới của bạn.

Lưu ý rằng một quy trình thay thế, nhẹ hơn cũng có thể là:

  1. Khởi tạo một mô hình cơ sở và tải trọng lượng được đào tạo trước vào nó.
  2. Chạy tập dữ liệu mới của bạn thông qua nó và ghi lại kết quả của một (hoặc một số) lớp từ mô hình cơ sở. Điều này được gọi là khai thác tính năng .
  3. Sử dụng đầu ra đó làm dữ liệu đầu vào cho một mô hình mới, nhỏ hơn.

Một ưu điểm chính của quy trình làm việc thứ hai đó là bạn chỉ chạy mô hình cơ sở một lần dữ liệu của bạn, thay vì một lần cho mỗi kỷ nguyên đào tạo. Vì vậy, nó nhanh hơn và rẻ hơn rất nhiều.

Tuy nhiên, một vấn đề với quy trình công việc thứ hai đó là nó không cho phép bạn tự động sửa đổi dữ liệu đầu vào của mô hình mới trong quá trình đào tạo, điều cần thiết khi thực hiện tăng dữ liệu. Học chuyển thường được sử dụng cho các tác vụ khi tập dữ liệu mới của bạn có quá ít dữ liệu để huấn luyện một mô hình quy mô đầy đủ từ đầu và trong các tình huống như vậy, việc tăng dữ liệu là rất quan trọng. Vì vậy, trong những gì tiếp theo, chúng tôi sẽ tập trung vào quy trình làm việc đầu tiên.

Đây là quy trình công việc đầu tiên trông như thế nào trong Keras:

Đầu tiên, khởi tạo một mô hình cơ sở với các weigts được đào tạo trước.

 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.
 

Sau đó, đóng băng mô hình cơ sở.

 base_model.trainable = False
 

Tạo một mô hình mới trên đầu trang.

 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)
 

Đào tạo mô hình trên dữ liệu mới.

 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=...)
 

Tinh chỉnh

Khi mô hình của bạn đã hội tụ dữ liệu mới, bạn có thể cố gắng giải phóng tất cả hoặc một phần của mô hình cơ sở và đào tạo lại toàn bộ mô hình từ đầu đến cuối với tốc độ học tập rất thấp.

Đây là bước cuối cùng tùy chọn có khả năng mang lại cho bạn những cải tiến gia tăng. Nó cũng có thể dẫn đến tình trạng quá tải nhanh chóng - hãy ghi nhớ điều đó.

Điều quan trọng là chỉ thực hiện bước này sau khi mô hình với các lớp đông lạnh đã được huấn luyện để hội tụ. Nếu bạn trộn các lớp có thể khởi tạo ngẫu nhiên với các lớp có thể huấn luyện chứa các tính năng được đào tạo trước, các lớp được khởi tạo ngẫu nhiên sẽ gây ra các cập nhật độ dốc rất lớn trong quá trình đào tạo, sẽ phá hủy các tính năng được đào tạo trước của bạn.

Điều quan trọng là sử dụng tỷ lệ học tập rất thấp ở giai đoạn này, bởi vì bạn đang đào tạo một mô hình lớn hơn nhiều so với vòng đào tạo đầu tiên, trên một tập dữ liệu thường rất nhỏ. Do đó, bạn có nguy cơ bị thừa cân rất nhanh nếu áp dụng các biện pháp cập nhật trọng lượng lớn. Ở đây, bạn chỉ muốn đọc các trọng số được đào tạo trước theo cách tăng dần.

Đây là cách thực hiện tinh chỉnh toàn bộ mô hình cơ sở:

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

Lưu ý quan trọng về compile()trainable

Gọi compile() trên một mô hình có nghĩa là "đóng băng" hành vi của mô hình đó. Điều này ngụ ý rằng các giá trị thuộc tính có thể trainable tại thời điểm mô hình được biên dịch nên được bảo toàn trong suốt vòng đời của mô hình đó, cho đến khi compile được gọi lại. Do đó, nếu bạn thay đổi bất kỳ giá trị có thể trainable nào, hãy đảm bảo gọi lại compile() trên mô hình của bạn để các thay đổi của bạn được tính đến.

Lưu ý quan trọng về lớp BatchNormalization

Nhiều mô hình hình ảnh chứa các lớp BatchNormalization . Lớp đó là một trường hợp đặc biệt trên mọi số lượng có thể tưởng tượng được. Dưới đây là một số điều cần ghi nhớ.

  • BatchNormalization chứa 2 trọng lượng không thể đào tạo được cập nhật trong quá trình đào tạo. Đây là các biến theo dõi giá trị trung bình và phương sai của đầu vào.
  • Khi bạn đặt bn_layer.trainable = False , lớp BatchNormalization sẽ chạy ở chế độ suy luận và sẽ không cập nhật thống kê trung bình & phương sai của nó. Điều này không đúng đối với các lớp khác nói chung, vì khả năng tập tạ & chế độ suy luận / huấn luyện là hai khái niệm trực giao . Nhưng cả hai được gắn với nhau trong trường hợp của lớp BatchNormalization .
  • Khi bạn hủy mô hình có chứa các lớp BatchNormalization để tinh chỉnh, bạn nên giữ các lớp BatchNormalization ở chế độ suy luận bằng cách chuyển training=False khi gọi mô hình cơ sở. Nếu không, các cập nhật được áp dụng cho các trọng số không thể đào tạo sẽ đột ngột phá hủy những gì mà mô hình đã học được.

Bạn sẽ thấy mô hình này hoạt động trong ví dụ từ đầu đến cuối ở cuối hướng dẫn này.

Chuyển giao việc học và tinh chỉnh với vòng đào tạo tùy chỉnh

Nếu thay vì fit() , bạn đang sử dụng vòng đào tạo cấp thấp của riêng mình, thì quy trình làm việc vẫn giữ nguyên. Bạn nên cẩn thận khi chỉ tính đến danh sách model.trainable_weights khi áp dụng các bản cập nhật gradient:

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

Tương tự như vậy để tinh chỉnh.

Ví dụ từ đầu đến cuối: tinh chỉnh mô hình phân loại hình ảnh trên mèo so với chó

tập dữ liệu

Để củng cố các khái niệm này, chúng ta hãy cùng bạn đi qua một ví dụ cụ thể về học tập và điều chỉnh chuyển giao cụ thể. Chúng tôi sẽ tải mô hình Xception, được đào tạo trước trên ImageNet và sử dụng nó trên bộ dữ liệu phân loại "mèo so với chó" của Kaggle.

Lấy dữ liệu

Đầu tiên, hãy tìm nạp tập dữ liệu mèo và chó bằng TFDS. Nếu bạn có tập dữ liệu của riêng mình, có lẽ bạn sẽ muốn sử dụng tiện ích tf.keras.preprocessing.image_dataset_from_directory để tạo các đối tượng tập dữ liệu có nhãn tương tự từ một tập hợp hình ảnh trên đĩa được gửi vào các thư mục cụ thể của lớp.

Học Tansfer hữu ích nhất khi làm việc với các dữ liệu rất nhỏ. Để giữ cho tập dữ liệu của chúng tôi nhỏ, chúng tôi sẽ sử dụng 40% dữ liệu đào tạo ban đầu (25.000 hình ảnh) để đào tạo, 10% để xác thực và 10% để kiểm tra.

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

 
Downloading and preparing dataset cats_vs_dogs/4.0.0 (download: 786.68 MiB, generated: Unknown size, total: 786.68 MiB) to /home/kbuilder/tensorflow_datasets/cats_vs_dogs/4.0.0...

/usr/lib/python3/dist-packages/urllib3/connectionpool.py:860: InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
  InsecureRequestWarning)
WARNING:absl:1738 images were corrupted and were skipped

Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/cats_vs_dogs/4.0.0.incompleteKQQCXE/cats_vs_dogs-train.tfrecord
Dataset cats_vs_dogs downloaded and prepared to /home/kbuilder/tensorflow_datasets/cats_vs_dogs/4.0.0. Subsequent calls will reuse this data.
Number of training samples: 9305
Number of validation samples: 2326
Number of test samples: 2326

Đây là 9 hình ảnh đầu tiên trong tập dữ liệu đào tạo - như bạn có thể thấy, chúng đều có kích thước khác nhau.

 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

Chúng ta cũng có thể thấy rằng nhãn 1 là "chó" và nhãn 0 là "mèo".

Chuẩn hóa dữ liệu

Hình ảnh thô của chúng tôi có nhiều kích cỡ. Ngoài ra, mỗi pixel bao gồm 3 giá trị nguyên từ 0 đến 255 (giá trị mức RGB). Đây không phải là một sự phù hợp tuyệt vời để nuôi dưỡng một mạng lưới thần kinh. Chúng ta cần làm 2 việc:

  • Chuẩn hóa thành một kích thước hình ảnh cố định. Chúng tôi chọn 150x150.
  • Chuẩn hóa các giá trị pixel giữa -1 và 1. Chúng tôi sẽ thực hiện việc này bằng cách sử dụng lớp Normalization như một phần của chính mô hình.

Nói chung, bạn nên phát triển các mô hình lấy dữ liệu thô làm đầu vào, trái ngược với các mô hình lấy dữ liệu đã được xử lý trước. Lý do là, nếu mô hình của bạn mong đợi dữ liệu được xử lý trước, bất cứ khi nào bạn xuất mô hình của mình để sử dụng nó ở nơi khác (trong trình duyệt web, trong ứng dụng dành cho thiết bị di động), bạn sẽ cần phải thực hiện lại đường ống tiền xử lý chính xác. Điều này nhận được rất khó khăn rất nhanh chóng. Vì vậy, chúng ta nên thực hiện số lượng tiền xử lý ít nhất có thể trước khi nhấn vào mô hình.

Ở đây, chúng tôi sẽ thực hiện thay đổi kích thước hình ảnh trong đường ống dữ liệu (vì mạng nơ-ron sâu chỉ có thể xử lý các lô dữ liệu liền kề) và chúng tôi sẽ thực hiện điều chỉnh giá trị đầu vào như một phần của mô hình, khi chúng tôi tạo nó.

Hãy thay đổi kích thước hình ảnh thành 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))

 

Bên cạnh đó, hãy tập hợp dữ liệu và sử dụng bộ nhớ đệm & tìm nạp trước để tối ưu hóa tốc độ tải.

 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)

 

Sử dụng tăng dữ liệu ngẫu nhiên

Khi bạn không có tập dữ liệu hình ảnh lớn, bạn nên đưa vào một cách giả tạo tính đa dạng mẫu bằng cách áp dụng các phép biến đổi ngẫu nhiên nhưng thực tế cho hình ảnh huấn luyện, chẳng hạn như lật ngang ngẫu nhiên hoặc xoay ngẫu nhiên nhỏ. Điều này giúp mô hình hiển thị các khía cạnh khác nhau của dữ liệu đào tạo trong khi làm chậm quá trình trang bị quá mức.

 from tensorflow import keras
from tensorflow.keras import layers

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

 

Hãy hình dung hình ảnh đầu tiên của đợt đầu tiên trông như thế nào sau nhiều phép biến đổi ngẫu nhiên:

 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[i]))
        plt.axis("off")

 

png

Xây dựng mô hình

Bây giờ hãy xây dựng một mô hình theo kế hoạch chi tiết mà chúng tôi đã giải thích trước đó.

Lưu ý rằng:

  • Chúng tôi thêm một lớp Normalization để chia tỷ lệ các giá trị đầu vào (ban đầu trong phạm vi [0, 255] ) thành phạm vi [-1, 1] .
  • Chúng tôi thêm một lớp Dropout trước lớp phân loại, để chuẩn hóa.
  • Chúng tôi đảm bảo vượt qua training=False khi gọi mô hình cơ sở, để nó chạy ở chế độ suy luận, do đó thống kê batchnorm không được cập nhật ngay cả sau khi chúng tôi giải phóng mô hình cơ sở để tinh chỉnh.
 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 normalized
# from (0, 255) to a range (-1., +1.), the normalization layer
# does the following, outputs = (inputs - mean) / sqrt(var)
norm_layer = keras.layers.experimental.preprocessing.Normalization()
mean = np.array([127.5] * 3)
var = mean ** 2
# Scale inputs to [-1, +1]
x = norm_layer(x)
norm_layer.set_weights([mean, var])

# 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 [==============================] - 1s 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         
_________________________________________________________________
normalization (Normalization (None, 150, 150, 3)       7         
_________________________________________________________________
xception (Model)             (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,536
Trainable params: 2,049
Non-trainable params: 20,861,487
_________________________________________________________________

Huấn luyện lớp trên cùng

 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
291/291 [==============================] - 10s 33ms/step - loss: 0.1843 - binary_accuracy: 0.9177 - val_loss: 0.0878 - val_binary_accuracy: 0.9682
Epoch 2/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1404 - binary_accuracy: 0.9389 - val_loss: 0.0881 - val_binary_accuracy: 0.9673
Epoch 3/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1270 - binary_accuracy: 0.9452 - val_loss: 0.0881 - val_binary_accuracy: 0.9669
Epoch 4/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1238 - binary_accuracy: 0.9472 - val_loss: 0.0826 - val_binary_accuracy: 0.9686
Epoch 5/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1265 - binary_accuracy: 0.9443 - val_loss: 0.0780 - val_binary_accuracy: 0.9686
Epoch 6/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1258 - binary_accuracy: 0.9450 - val_loss: 0.0754 - val_binary_accuracy: 0.9721
Epoch 7/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1217 - binary_accuracy: 0.9481 - val_loss: 0.0949 - val_binary_accuracy: 0.9652
Epoch 8/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1094 - binary_accuracy: 0.9530 - val_loss: 0.0723 - val_binary_accuracy: 0.9733
Epoch 9/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1177 - binary_accuracy: 0.9466 - val_loss: 0.0806 - val_binary_accuracy: 0.9695
Epoch 10/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1174 - binary_accuracy: 0.9496 - val_loss: 0.0823 - val_binary_accuracy: 0.9660
Epoch 11/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1094 - binary_accuracy: 0.9545 - val_loss: 0.0770 - val_binary_accuracy: 0.9712
Epoch 12/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1125 - binary_accuracy: 0.9511 - val_loss: 0.0932 - val_binary_accuracy: 0.9643
Epoch 13/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1163 - binary_accuracy: 0.9505 - val_loss: 0.0838 - val_binary_accuracy: 0.9690
Epoch 14/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1116 - binary_accuracy: 0.9510 - val_loss: 0.0875 - val_binary_accuracy: 0.9678
Epoch 15/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1160 - binary_accuracy: 0.9488 - val_loss: 0.0798 - val_binary_accuracy: 0.9712
Epoch 16/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1133 - binary_accuracy: 0.9511 - val_loss: 0.0793 - val_binary_accuracy: 0.9703
Epoch 17/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1158 - binary_accuracy: 0.9521 - val_loss: 0.0823 - val_binary_accuracy: 0.9686
Epoch 18/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1124 - binary_accuracy: 0.9519 - val_loss: 0.0759 - val_binary_accuracy: 0.9733
Epoch 19/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1079 - binary_accuracy: 0.9560 - val_loss: 0.0833 - val_binary_accuracy: 0.9721
Epoch 20/20
291/291 [==============================] - 8s 28ms/step - loss: 0.1079 - binary_accuracy: 0.9524 - val_loss: 0.0857 - val_binary_accuracy: 0.9703

<tensorflow.python.keras.callbacks.History at 0x7f9a556069e8>

Thực hiện một vòng tinh chỉnh toàn bộ mô hình

Cuối cùng, hãy giải phóng mô hình cơ sở và huấn luyện toàn bộ mô hình từ đầu đến cuối với tỷ lệ học tập thấp.

Điều quan trọng, mặc dù mô hình cơ sở trở nên có thể huấn luyện, nó vẫn chạy trong chế độ suy luận kể từ khi chúng tôi vượt qua training=False khi gọi nó khi chúng tôi xây dựng mô hình. Điều này có nghĩa là các lớp chuẩn hóa hàng loạt bên trong sẽ không cập nhật số liệu thống kê lô. Nếu họ làm vậy, họ sẽ tàn phá các đại diện mà mô hình đã học cho đến nay.

 # 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         
_________________________________________________________________
normalization (Normalization (None, 150, 150, 3)       7         
_________________________________________________________________
xception (Model)             (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,536
Trainable params: 20,809,001
Non-trainable params: 54,535
_________________________________________________________________
Epoch 1/10
291/291 [==============================] - 38s 131ms/step - loss: 0.0916 - binary_accuracy: 0.9629 - val_loss: 0.0531 - val_binary_accuracy: 0.9768
Epoch 2/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0617 - binary_accuracy: 0.9759 - val_loss: 0.0467 - val_binary_accuracy: 0.9794
Epoch 3/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0462 - binary_accuracy: 0.9823 - val_loss: 0.0444 - val_binary_accuracy: 0.9802
Epoch 4/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0365 - binary_accuracy: 0.9868 - val_loss: 0.0480 - val_binary_accuracy: 0.9828
Epoch 5/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0298 - binary_accuracy: 0.9905 - val_loss: 0.0444 - val_binary_accuracy: 0.9832
Epoch 6/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0273 - binary_accuracy: 0.9903 - val_loss: 0.0491 - val_binary_accuracy: 0.9811
Epoch 7/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0223 - binary_accuracy: 0.9911 - val_loss: 0.0404 - val_binary_accuracy: 0.9845
Epoch 8/10
291/291 [==============================] - 37s 127ms/step - loss: 0.0195 - binary_accuracy: 0.9931 - val_loss: 0.0413 - val_binary_accuracy: 0.9841
Epoch 9/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0192 - binary_accuracy: 0.9928 - val_loss: 0.0402 - val_binary_accuracy: 0.9845
Epoch 10/10
291/291 [==============================] - 37s 128ms/step - loss: 0.0153 - binary_accuracy: 0.9937 - val_loss: 0.0394 - val_binary_accuracy: 0.9841

<tensorflow.python.keras.callbacks.History at 0x7f967e5efd68>

Sau 10 kỷ nguyên, việc tinh chỉnh mang lại cho chúng tôi một cải tiến tốt ở đây.