Как выполнить перенос обучения без весов ImageNet?

#python #tensorflow #keras

#python #тензорный поток #keras

Вопрос:

Это описание моего проекта:

Dataset1: больший набор данных, содержит двоичные классы изображений.

Dataset2: содержит 2 классы , которые очень похожи по внешнему виду на Dataset1 . Я хочу создать модель , в которой используется передача обучения путем обучения Dataset1 и применения весов с меньшей скоростью обучения Dataset2 .

Поэтому я собираюсь обучить весь VGG16 dataset1 процесс, а затем использовать transfer learning для точной настройки последних слоев dataset2 . Я не хочу использовать предварительно подготовленную базу данных imagenet. Это код, который я использую, и я сохранил из него записи:

 
from tensorflow.keras.layers import Input, Lambda, Dense, Flatten
from tensorflow.keras.models import Model
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.applications.vgg16 import preprocess_input
from tensorflow.keras.preprocessing import image
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
import numpy as np
from glob import glob
import matplotlib.pyplot as plt

vgg = VGG16(input_shape=(244, 244, 3), weights=None, include_top=False)

# don't train existing weights
for layer in vgg.layers:
    layer.trainable = False
    
x = Flatten()(vgg.output)   

import tensorflow.keras
prediction = tensorflow.keras.layers.Dense(2, activation='softmax')(x)

model = Model(inputs=vgg.input, outputs=prediction)

model.compile(
  loss='categorical_crossentropy',
  optimizer='adam',
  metrics=['accuracy']
)

from keras.preprocessing.image import ImageDataGenerator

train_datagen = ImageDataGenerator(rescale = 1./255,
                                   shear_range = 0.2,
                                   zoom_range = 0.2,
                                   horizontal_flip = True)

test_datagen = ImageDataGenerator(rescale = 1./255)

training_set = train_datagen.flow_from_directory('chest_xray/train',
                                                 target_size = (224, 224),
                                                 batch_size = 32,
                                                 class_mode = 'categorical')

test_set = train_datagen.flow_from_directory('chest_xray/test',
                                                 target_size = (224, 224),
                                                 batch_size = 32,
                                                 class_mode = 'categorical')

# fit the model
r = model.fit_generator(
  training_set,
  validation_data=test_set,
  epochs=5,
  steps_per_epoch=len(training_set),
  validation_steps=len(test_set)
)

model.save_weights('first_try.h5') 
 

Комментарии:

1. с какой именно проблемой вы столкнулись? Делай все, что думаешь, что в этом плохого?

2. Спасибо за ваш ответ, моя проблема в том, что я не знаю, как настроить / перенести-изучить модель на другом наборе данных, используя те же веса?

3. Если вы сделали это с предварительно подготовленными весами, то и в этом случае будет то же самое.

4. Что вы имеете в виду? не могли бы вы объяснить подробнее. Спасибо

5. Я не использовал предварительно обученные веса (как imagnet) для dataset1

Ответ №1:

Обновить

Основываясь на вашем запросе, кажется, что номер класса не будет отличаться в Dataset2. В то же время вы также не хотите использовать чистый вес изображения. Итак, в этом случае вам не нужно сопоставлять или сохранять вес (как описано ниже). Просто загрузите модель и вес и тренируйтесь на Dataset2. Заморозьте весь обученный слой из Dataset1 и обучите последний слой в Dataset2; действительно прямолинейно.

В моем приведенном ниже ответе, хотя вам не нужна полная информация, я все равно сохраняю ее для дальнейшего использования.


Вот небольшая демонстрация того, что вам, вероятно, нужно. Надеюсь, это даст вам некоторое представление. Здесь мы обучим CIRFAR набор данных, который имеет 10 классы, и попытаемся использовать его для обучения передаче с другим набором данных, который, вероятно, имеет разные размеры входных данных и разное количество классов.

Подготовка CIFAR (10 классов)

 import numpy as np
import tensorflow as tf 
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Dropout

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()

# train set / data 
x_train = x_train.astype('float32') / 255

# validation set / data 
x_test = x_test.astype('float32') / 255

# train set / target 
y_train = tf.keras.utils.to_categorical(y_train, num_classes=10)
# validation set / target 
y_test = tf.keras.utils.to_categorical(y_test, num_classes=10)

print(x_train.shape, y_train.shape) 
print(x_test.shape, y_test.shape)  
'''
(50000, 32, 32, 3) (50000, 10)
(10000, 32, 32, 3) (10000, 10)
'''
 

Модель

 # declare input shape 
input = tf.keras.Input(shape=(32,32,3))
# Block 1
x = tf.keras.layers.Conv2D(32, 3, strides=2, activation="relu")(input)
x = tf.keras.layers.MaxPooling2D(3)(x)

# Now that we apply global max pooling.
gap = tf.keras.layers.GlobalMaxPooling2D()(x)

# Finally, we add a classification layer.
output = tf.keras.layers.Dense(10, activation='softmax')(gap)

# bind all
func_model = tf.keras.Model(input, output)

'''
Model: "functional_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         [(None, 32, 32, 3)]       0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 15, 15, 32)        896       
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 32)          0         
_________________________________________________________________
global_max_pooling2d_1 (Glob (None, 32)                0         
_________________________________________________________________
dense_1 (Dense)              (None, 10)                330       
=================================================================
Total params: 1,226
Trainable params: 1,226
Non-trainable params: 0
'''
 

Запустите модель, чтобы получить некоторые весовые матрицы следующим образом:

 # compile 
print('nFunctional API')
func_model.compile(
          loss      = tf.keras.losses.CategoricalCrossentropy(),
          metrics   = tf.keras.metrics.CategoricalAccuracy(),
          optimizer = tf.keras.optimizers.Adam())
# fit 
func_model.fit(x_train, y_train, batch_size=128, epochs=1)
 

Перенос обучения

Давайте используем его для MNIST. В нем также есть 10 классы, но из-за необходимости другого количества классов мы создадим even odd из него и категории (2 класса). Ниже приведено, как мы подготовим эти наборы данных

 (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()

# train set / data 
x_train = np.expand_dims(x_train, axis=-1)
x_train = np.repeat(x_train, 3, axis=-1)
x_train = x_train.astype('float32') / 255
# train set / target 
y_train = tf.keras.utils.to_categorical((y_train % 2 == 0).astype(int), 
                                        num_classes=2)

# validation set / data 
x_test = np.expand_dims(x_test, axis=-1)
x_test = np.repeat(x_test, 3, axis=-1)
x_test = x_test.astype('float32') / 255
# validation set / target 

y_test = tf.keras.utils.to_categorical((y_test % 2 == 0).astype(int), 
                                       num_classes=2)

print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)  
'''
(60000, 28, 28, 3) (60000, 2)
(10000, 28, 28, 3) (10000, 2)
'''
 

Если вы знакомы с использованием предварительно обученного веса ImageNet в keras модели, вы, вероятно, используете include_top . Установив его False , мы можем легко загрузить файл весов, в котором нет верхней информации о предварительно обученных моделях. Итак, здесь нам нужно вручную (вроде) это сделать. Нам нужно захватить весовые матрицы до последнего уровня активации (в нашем случае, который есть Dense(10, softmax) ). И поместите его в новый экземпляр базовой модели, и мы добавим новый слой классификатора (в нашем случае это будет Dense(2, softmax) .

 for i, layer in enumerate(func_model.layers):
    print(i,'t',layer.trainable,'t  :',layer.name)

'''
  Train_Bool  : Layer Names
0    True     : input_1
1    True     : conv2d
2    True     : max_pooling2d
3    True     : global_max_pooling2d # < we go till here to grab the weight and biases
4    True     : dense  # 10 classes (from previous model)
'''
 

Получить веса

 sparsified_weights = []
for w in func_model.get_layer(name='global_max_pooling2d').get_weights():
    sparsified_weights.append(w)
 

Таким образом, мы сопоставляем вес из старой модели, за исключением слоев классификатора ( Dense ). Пожалуйста, обратите внимание, здесь мы берем вес до GAP слоя, который находится прямо перед классификатором.

Теперь мы создадим новую модель, такую же, как и старая модель, за исключением последнего слоя ( 10 Dense ), и в то же время добавим новую Dense с 2 помощью unit .

 predictions    = Dense(2, activation='softmax')(func_model.layers[-2].output)
new_func_model = Model(inputs=func_model.inputs, outputs = predictions) 
 

И теперь мы можем установить вес для новой модели следующим образом:

 new_func_model.get_layer(name='global_max_pooling2d').set_weights(sparsified_weights)
 

Вы можете проверить, чтобы проверить следующим образом; все будет таким же, кроме последнего слоя.

 func_model.get_weights()      # last layer, Dense (10)
new_func_model.get_weights()  # last layer, Dense (2)
 

Теперь вы можете обучить модель с новым набором данных, в нашем случае это был MNIST

 new_func_model.compile(optimizer='adam', loss='categorical_crossentropy')
new_func_model.summary()

'''
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 32, 32, 3)]       0         
_________________________________________________________________
conv2d (Conv2D)              (None, 15, 15, 32)        896       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 5, 5, 32)          0         
_________________________________________________________________
global_max_pooling2d (Global (None, 32)                0         
_________________________________________________________________
dense_6 (Dense)              (None, 2)                 66        
=================================================================
Total params: 962
Trainable params: 962
Non-trainable params: 0
'''

# compile 
print('nFunctional API')
new_func_model.compile(
          loss      = tf.keras.losses.CategoricalCrossentropy(),
          metrics   = tf.keras.metrics.CategoricalAccuracy(),
          optimizer = tf.keras.optimizers.Adam())
# fit 
new_func_model.fit(x_train, y_train, batch_size=128, epochs=1)
 
 WARNING:tensorflow:Model was constructed with shape (None, 32, 32, 3) for input Tensor("input_1:0", shape=(None, 32, 32, 3), dtype=float32), but it was called on an input with incompatible shape (None, 28, 28, 3).
WARNING:tensorflow:Model was constructed with shape (None, 32, 32, 3) for input Tensor("input_1:0", shape=(None, 32, 32, 3), dtype=float32), but it was called on an input with incompatible shape (None, 28, 28, 3).
469/469 [==============================] - 1s 3ms/step - loss: 0.6453 - categorical_accuracy: 0.6447
<tensorflow.python.keras.callbacks.History at 0x7f7af016feb8>
 

Ответ №2:

пара проблем. Вы не используете веса imagenet (не могу представить, почему нет), тогда вы устанавливаете весь уровень сети VGG как необучаемый. Итак, вы начнете со случайных весов и останетесь случайными. Затем вы добавляете слои выравнивания и прогнозирования и пытаетесь обучить. Все, что вы будете тренировать, — это один плотный слой. Сомневаюсь, что это будет работать очень хорошо, но я думаю, это чему-то научит. Я бы использовал веса imagenet как минимум, я также предпочитаю обучать всю модель для получения наилучших результатов. Тогда следующая проблема — это код

 test_set = train_datagen.flow_from_directory('chest_xray/test',
                                                 target_size = (224, 224),
                                                 batch_size = 32,
                                                 class_mode = 'categorical')
#where
train_datagen = ImageDataGenerator(rescale = 1./255,
                                   shear_range = 0.2,
                                   zoom_range = 0.2,
                                   horizontal_flip = True)
 

Вы не хотите выполнять увеличение изображения в тестовом наборе, что вы и будете делать, поэтому используйте

 test_set =  ImageDataGenerator(rescale = 1./255).flow_from_directory('chest_xray/test',
                                                 target_size = (224, 224),
                                                 batch_size = 32,
                                                 class_mode = 'categorical'
                                                 shuffle=False)
 

Затем вы используете model.fit_generator, который пока будет работать, но обесценивается в будущих версиях tensorflow. Используйте модель.теперь он работает с генераторами, я думаю, начиная с tensorflow 1.5. То, что вы называете набором тестов, обычно называется набором проверки, так что все в порядке. Вы указали batch_size=32 .Однако у вас есть код в model.fit

 steps_per_epoch=len(training_set),
validation_steps=len(test_set)
# what you want is
steps_per epoch=len(training_set)//32
validation_steps=len(test_set)//32
 

После того, как вы обучите свою модель, у нее будут веса, которые вы хотите использовать для dataset2. Просто создайте новые генераторы для datset2 и переобучите его с помощью model.fit

Комментарии:

1. Большое вам спасибо за ваше четкое и полезное объяснение, я его очень одобряю.