Zobacz na TensorFlow.org | Uruchom w Google Colab | Wyświetl źródło na GitHub | Pobierz notatnik |
Ustawiać
import numpy as np
import tensorflow as tf
from tensorflow import keras
Wstęp
Learning transferu polega na pobraniu funkcje wyuczone na jeden problem, i wykorzystanie ich w nowym, podobnym problemem. Na przykład cechy modelu, który nauczył się rozpoznawać szopy pracze, mogą być przydatne do uruchomienia modelu przeznaczonego do identyfikacji tanuki.
Nauka transferu jest zwykle wykonywana w przypadku zadań, w których zestaw danych zawiera zbyt mało danych, aby wytrenować model w pełnej skali od podstaw.
Najczęstszym wcieleniem transfer learning w kontekście deep learningu jest następujący przepływ pracy:
- Pobierz warstwy z wcześniej wytrenowanego modelu.
- Zamroź je, aby podczas przyszłych rund treningowych nie zniszczyć jakichkolwiek zawartych w nich informacji.
- Dodaj kilka nowych, nadających się do trenowania warstw na wierzchu zamrożonych warstw. Nauczą się przekształcać stare funkcje w prognozy na nowym zbiorze danych.
- Trenuj nowe warstwy w swoim zbiorze danych.
Ostatnim, opcjonalny krok, to dostrajanie, który składa się z odmrożenie cały model uzyskany powyżej (lub jego część) i przekwalifikowanie go na nowych danych z bardzo małą szybkością uczenia się. Może to potencjalnie osiągnąć znaczące ulepszenia, stopniowo dostosowując wstępnie wytrenowane funkcje do nowych danych.
Najpierw pojedziemy nad Keras trainable API w szczegółach, które leży u podstaw większości uczenia Transfer & dostrajających przepływów pracy.
Następnie zademonstrujemy typowy przepływ pracy, biorąc model wstępnie przeszkolony w zestawie danych ImageNet i przeszkolając go ponownie w zestawie danych klasyfikacji „koty kontra psy” Kaggle.
To jest adaptacją głębokie nauki z Python i 2016 blogu „budowanie potężnych modeli klasyfikacyjnych obraz za pomocą bardzo mało danych” .
Zamrażanie warstw: Zrozumienie trainable atrybut
Warstwy i modele mają trzy atrybuty wagi:
-
weightslista wszystkich wag zmiennych warstwy. -
trainable_weightslista tych, które mają być aktualizowane (poprzez zejście gradientu), aby zminimalizować straty podczas treningu. -
non_trainable_weightslista tych, które nie mają być przeszkoleni. Zazwyczaj są one aktualizowane przez model podczas przejścia do przodu.
Przykład: Dense warstwa ma 2 nadającego masy (jądro i polaryzacji)
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
Ogólnie rzecz biorąc, wszystkie ciężary są ciężarami, które można trenować. Jedyny wbudowany w warstwę, która ma zakaz wyszkolić ciężarów jest BatchNormalization warstwa. Wykorzystuje ciężary, których nie można trenować, aby śledzić średnią i wariancję danych wejściowych podczas treningu. Aby dowiedzieć się, jak używać non-wyszkolić ciężarów we własnych niestandardowych warstw, zobacz przewodnik pisanie nowych warstw od zera .
Przykład: BatchNormalization warstwa ma 2 nadającego wagi i 2 nie nadającego wag
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
Warstwy i modele są również wyposażone w logiczną atrybutu trainable . Jego wartość można zmienić. Ustawianie layer.trainable do False ruchów wszystkie ciężary warstwa jest z wyszkolić do nieprzestrzegania wyszkolić. Nazywa się to „zamrożenie” warstwa: stan zamarzniętej warstwy nie będą aktualizowane w trakcie szkolenia (zarówno podczas szkolenia z fit() lub gdy szkolenie z dowolnej niestandardowej pętli, która opiera się na trainable_weights zastosować aktualizacje gradient).
Przykład: ustawiania trainable do 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
Kiedy waga możliwa do trenowania staje się niemożliwa do wytrenowania, jej wartość nie jest już aktualizowana podczas treningu.
# 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
Nie należy mylić layer.trainable atrybut z argumentem training w layer.__call__() (która określa, czy warstwa powinna prowadzić swoje podaniu w trybie wnioskowania lub trybu treningowego). Aby uzyskać więcej informacji, zobacz Keras nas .
Rekurencyjne ustawienie trainable atrybutu
Jeśli ustawisz trainable = False na modelu lub na dowolnej warstwy, która ma podwarstwy, wszystkie dzieci warstwy stać non-wyszkolić również.
Przykład:
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
Typowy przepływ pracy typu transfer-learning
To prowadzi nas do tego, jak typowy przepływ uczenia się transferowego można wdrożyć w Keras:
- Utwórz wystąpienie modelu podstawowego i załaduj do niego wstępnie wytrenowane wagi.
- Zamrozić wszystkie warstwy w modelu bazowym poprzez ustawienie
trainable = False. - Utwórz nowy model na podstawie danych wyjściowych jednej (lub kilku) warstw z modelu podstawowego.
- Wytrenuj nowy model na nowym zbiorze danych.
Zwróć uwagę, że alternatywnym, lżejszym przepływem pracy może być również:
- Utwórz wystąpienie modelu podstawowego i załaduj do niego wstępnie wytrenowane wagi.
- Przeprowadź przez niego nowy zestaw danych i zapisz dane wyjściowe jednej (lub kilku) warstw z modelu podstawowego. Jest to tak zwana funkcja ekstrakcji.
- Użyj tych danych wyjściowych jako danych wejściowych dla nowego, mniejszego modelu.
Kluczową zaletą tego drugiego przepływu pracy jest to, że model podstawowy jest uruchamiany tylko raz na danych, a nie raz na epokę uczenia. Więc jest o wiele szybciej i taniej.
Problem z tym drugim przepływem pracy polega jednak na tym, że nie pozwala on na dynamiczną modyfikację danych wejściowych nowego modelu podczas uczenia, co jest wymagane na przykład podczas rozszerzania danych. Uczenie się przenoszenia jest zwykle używane w przypadku zadań, w których nowy zestaw danych zawiera zbyt mało danych, aby można było wytrenować model w pełnej skali od podstaw, a w takich scenariuszach bardzo ważne jest rozszerzanie danych. W dalszej części skupimy się na pierwszym przepływie pracy.
Oto jak wygląda pierwszy przepływ pracy w Keras:
Najpierw utwórz wystąpienie modelu podstawowego ze wstępnie wytrenowanymi wagami.
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.
Następnie zamroź model podstawowy.
base_model.trainable = False
Utwórz nowy model na górze.
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)
Trenuj model na nowych danych.
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=...)
Strojenie
Gdy model osiągnie zbieżność na nowych danych, możesz spróbować odblokować całość lub część modelu podstawowego i przeszkolić cały model od początku do końca z bardzo niskim współczynnikiem uczenia.
Jest to opcjonalny ostatni krok, który może potencjalnie zapewnić stopniową poprawę. Może to również potencjalnie prowadzić do szybkiego overfittingu – miej to na uwadze.
Bardzo ważne jest, aby tylko zrobić ten krok po model z zamrożonych warstw został przeszkolony do konwergencji. Jeśli zmieszasz losowo inicjowane warstwy możliwe do trenowania z warstwami możliwymi do trenowania, które zawierają wstępnie wytrenowane funkcje, losowo zainicjowane warstwy spowodują bardzo duże aktualizacje gradientu podczas treningu, co zniszczy wstępnie wytrenowane funkcje.
Bardzo ważne jest również użycie bardzo niskiego współczynnika uczenia się na tym etapie, ponieważ trenujesz znacznie większy model niż w pierwszej rundzie uczenia, na zestawie danych, który jest zwykle bardzo mały. W rezultacie istnieje ryzyko bardzo szybkiego przeciążenia, jeśli zastosujesz duże aktualizacje wagi. Tutaj chcesz tylko dostosować wstępnie wytrenowane wagi w sposób przyrostowy.
Oto jak zaimplementować dostrojenie całego modelu podstawowego:
# 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=...)
Ważna uwaga o compile() i trainable
Wywoływanie compile() na modelu rozumie się „zamrożenia” zachowanie tego modelu. Oznacza to, że trainable wartości atrybutów w czasie model jest kompilowany powinny być zachowane przez cały okres użytkowania tego modelu, aż do compile nazywa się ponownie. Stąd, jeśli zmienić dowolny trainable wartość, upewnij się, aby zadzwonić do compile() ponownie w modelu na zmiany mają być brane pod uwagę.
Ważne informacje o BatchNormalization warstwie
Wiele modeli graficznych zawierają BatchNormalization warstw. Ta warstwa jest szczególnym przypadkiem pod każdym możliwym względem. Oto kilka rzeczy, o których należy pamiętać.
-
BatchNormalizationzawiera 2 non-wyszkolić ciężary, które aktualizowane w czasie treningu. Są to zmienne śledzące średnią i wariancję danych wejściowych. - Po ustawieniu
bn_layer.trainable = FalseTheBatchNormalizationwarstwa będzie działać w trybie wnioskowania i nie aktualizuje swoich średnich i wariancji statystyk. To nie jest sprawa dla innych warstw Ogólnie, jak waga trainability & wnioskowania tryby szkolenia / są dwie prostopadłe koncepcje . Ale dwa są związane w przypadkuBatchNormalizationwarstwy. - Kiedy odmrozić model, który zawiera
BatchNormalizationwarstw w tym celu dostrajania, należy zachowaćBatchNormalizationwarstwy w trybie wnioskowania o przejściutraining=Falsepodczas wywoływania modelu bazowego. W przeciwnym razie aktualizacje zastosowane do wag, których nie można wyszkolić, nagle zniszczą to, czego nauczył się model.
Zobaczysz ten wzorzec w akcji w kompletnym przykładzie na końcu tego przewodnika.
Przenieś naukę i dostrajanie za pomocą niestandardowej pętli treningowej
Jeśli zamiast fit() , używasz własną pętlę szkoleniowy niskim poziomie, pobyty workflow w zasadzie takie same. Należy uważać, aby wziąć pod uwagę tylko listy model.trainable_weights podczas stosowania aktualizacji 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))
Podobnie do dostrajania.
Kompleksowy przykład: dopracowanie modelu klasyfikacji obrazów w zestawie danych koty i psy
Aby utrwalić te koncepcje, przeprowadźmy Cię przez konkretny przykład kompleksowego uczenia się i dostrajania. Załadujemy model Xception, wstępnie wytrenowany w ImageNet, i użyjemy go w zestawie danych klasyfikacji „koty kontra psy” Kaggle.
Uzyskiwanie danych
Najpierw pobierzmy zestaw danych koty kontra psy za pomocą TFDS. Jeśli masz swój własny zestaw danych, prawdopodobnie będziesz chciał użyć narzędzia tf.keras.preprocessing.image_dataset_from_directory generować podobne obiekty oznaczone zestaw danych ze zbioru obrazów na dysku złożone w foldery klasy specyficzne.
Uczenie się transferu jest najbardziej przydatne podczas pracy z bardzo małymi zestawami danych. Aby nasz zestaw danych był niewielki, użyjemy 40% oryginalnych danych treningowych (25 000 obrazów) do trenowania, 10% do walidacji i 10% do testowania.
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
Oto pierwsze 9 obrazów w treningowym zbiorze danych — jak widać, wszystkie mają różne rozmiary.
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")

Widzimy również, że etykieta 1 to „pies”, a etykieta 0 to „kot”.
Standaryzacja danych
Nasze surowe obrazy mają różne rozmiary. Ponadto każdy piksel składa się z 3 wartości całkowitych z zakresu od 0 do 255 (wartości na poziomie RGB). Nie jest to idealne rozwiązanie do zasilania sieci neuronowej. Musimy zrobić 2 rzeczy:
- Standaryzuj do stałego rozmiaru obrazu. Wybieramy 150x150.
- Znormalizować wartości pikseli pomiędzy -1 a 1. Będziemy to robić za pomocą
Zobacz na TensorFlow.org
Uruchom w Google Colab
Wyświetl źródło na GitHub
Pobierz notatnik